001    // Copyright 2006, 2007, 2008, 2010, 2011 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    // http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry5.services;
016    
017    import java.lang.annotation.Annotation;
018    import java.util.List;
019    
020    import org.apache.tapestry5.ComponentResources;
021    import org.apache.tapestry5.func.Predicate;
022    import org.apache.tapestry5.ioc.AnnotationProvider;
023    import org.apache.tapestry5.model.MutableComponentModel;
024    import org.apache.tapestry5.plastic.PlasticClass;
025    import org.apache.tapestry5.runtime.Component;
026    import org.apache.tapestry5.runtime.Event;
027    import org.slf4j.Logger;
028    
029    /**
030     * Contains class-specific information used when transforming a raw component class into an
031     * executable component class.
032     * An executable class is one that has been transformed to work within Tapestry. This includes
033     * adding interfaces
034     * ({@link org.apache.tapestry5.runtime.Component}) but also transforming access to fields, based on
035     * annotations and
036     * naming conventions. Most of the changes are provided by different implementations of
037     * {@link ComponentClassTransformWorker}.
038     * <p/>
039     * Much of this information is somewhat like ordinary reflection, but applies to a class that has not yet been loaded.
040     * Field types, return types, parameter types and exception types are represented as string names, since any of them may
041     * be a class that has not yet been loaded and transformed as well.
042     * <p/>
043     * Transformation is primarily about identifying annotations on fields and on methods and changing the class, adding new
044     * interfaces, fields and methods, and deleting some existing fields.
045     * <p/>
046     * A ClassTransformation contains all the state data specific to a particular class being transformed. A number of
047     * <em>workers</em> will operate upon the ClassTransformation to effect the desired changes before the true class is
048     * loaded into memory.
049     * <p/>
050     * Instances of this class are not designed to be thread safe, access to an instance should be restricted to a single
051     * thread. In fact, the design of this type is to allow stateless singletons in multiple threads to work on
052     * thread-specific data (within the ClassTransformation).
053     * <p/>
054     * The majority of methods concern the <em>declared</em> members (field and methods) of a specific class, rather than
055     * any fields or methods inherited from a base class.
056     * 
057     * @deprecated In 5.3
058     * @see PlasticClass
059     */
060    public interface ClassTransformation extends AnnotationProvider
061    {
062        /**
063         * Returns the fully qualified class name of the class being transformed.
064         */
065        String getClassName();
066    
067        /**
068         * Returns the name of a new member (field or method). Ensures that the resulting name does not
069         * conflict with any
070         * existing member (declared by the underlying class, or inherited from a base class).
071         * 
072         * @param suggested
073         *            the suggested value for the member
074         * @return a unique name for the member
075         */
076        String newMemberName(String suggested);
077    
078        /**
079         * As with {@link #newMemberName(String)}, but the suggested name is constructed from the prefix
080         * and base name. An
081         * underscore will separate the prefix from the base name.
082         * 
083         * @param prefix
084         *            for the generated name
085         * @param baseName
086         *            a name, often of an existing field or method
087         * @return a unique name
088         */
089        String newMemberName(String prefix, String baseName);
090    
091        /**
092         * Returns a sorted list of declared instance fields with the indicated annotation. Non-private
093         * and static fields are ignored.
094         * 
095         * @since 5.2.0
096         */
097        List<TransformField> matchFieldsWithAnnotation(Class<? extends Annotation> annotationClass);
098    
099        /**
100         * Finds all methods matched by the provided predicate.
101         * 
102         * @param predicate
103         *            Used to filter the list
104         * @return a list of matching methods (which may be empty) in ascending order (by
105         *         method name), but descending order (by parameter count) within overrides of a single method name.
106         */
107        List<TransformMethod> matchMethods(Predicate<TransformMethod> predicate);
108    
109        /**
110         * Finds all methods matched by the provided predicate.
111         * 
112         * @param annotationType
113         *            Used to filter the list
114         * @return a list of matching methods (which may be empty) in ascending order (by
115         *         method name), but descending order (by parameter count) within overrides of a single method name.
116         */
117        List<TransformMethod> matchMethodsWithAnnotation(Class<? extends Annotation> annotationType);
118    
119        /**
120         * Finds all unclaimed fields matched by the provided predicate. Only considers instance fields.
121         * Added, removed and claimed fields are excluded.
122         * 
123         * @param predicate
124         *            used for matching
125         * @return sorted list of matching fields
126         * @since 5.2.0
127         */
128        List<TransformField> matchFields(Predicate<TransformField> predicate);
129    
130        /**
131         * Locates a declared field by its field name. The field must exist.
132         * 
133         * @param fieldName
134         *            of declared field
135         * @return field information
136         * @throws RuntimeException
137         *             if no such field
138         * @since 5.2.0
139         */
140        TransformField getField(String fieldName);
141    
142        /**
143         * Matches all fields that are not claimed. This may include static fields and final fields, but will not
144         * include fields that have been added as part of the transformation.
145         * 
146         * @since 5.2.0
147         * @return sorted list of unclaimed fields
148         */
149        List<TransformField> matchUnclaimedFields();
150    
151        /**
152         * Returns true if the indicated name is a private instance field.
153         * 
154         * @return true if field exists
155         */
156        boolean isField(String fieldName);
157    
158        /**
159         * Defines a new declared field for the class. Suggested name may be modified to ensure uniqueness.
160         * 
161         * @param modifiers
162         *            modifiers for the field (typically, {@link java.lang.reflect.Modifier#PRIVATE})
163         * @param type
164         *            the type for the field, as a string
165         * @param suggestedName
166         *            the desired name for the field, which may be modified (for uniqueness) when
167         *            returned
168         * @return new field instance
169         */
170        TransformField createField(int modifiers, String type, String suggestedName);
171    
172        /**
173         * Defines a new <strong>protected</strong> instance variable whose initial value is provided
174         * statically, via a
175         * constructor parameter. The transformation caches the result, so calling this method
176         * repeatedly with the same type
177         * and value will return the same field name. Caching extends to the parent transformation, so
178         * that a value injected
179         * into a parent class will be available (via the protected instance variable) to subclasses.
180         * This is primarily used to inject service dependencies into components, though it has a number
181         * of other uses as well.
182         * 
183         * @param type
184         *            the type of object to inject
185         * @param suggestedName
186         *            the suggested name for the new field
187         * @param value
188         *            to be injected. This value is retained.
189         * @return the actual name of the injected field
190         */
191        String addInjectedField(Class type, String suggestedName, Object value);
192    
193        /**
194         * Like {@link #addInjectedField(Class, String, Object)}, but instead of specifying the value,
195         * a provider for the value is specified. In the generated class' constructor, the provider
196         * will be passed the {@link ComponentResources} and will return the final value; thus
197         * each component <em>instance</em> will receive a matching unique instance via the provider.
198         * 
199         * @param type
200         *            type of value to inject
201         * @param suggestedName
202         *            suggested name for the new field
203         * @param provider
204         *            injected into the component to provide the value
205         * @return the actual name of the injected field
206         * @since 5.2.0
207         */
208        <T> TransformField addIndirectInjectedField(Class<T> type, String suggestedName, ComponentValueProvider<T> provider);
209    
210        /**
211         * Transforms the class to implement the indicated interface. If the class (or its super class)
212         * does not already
213         * implement the interface, then the interface is added, and default implementations of any
214         * methods of the interface
215         * are added.
216         * <p/>
217         * TODO: Checking that the names of methods in the interface do not conflict with the names of methods present in
218         * the (unmodified) class.
219         * 
220         * @param interfaceClass
221         *            the interface to be implemented by the class
222         * @throws IllegalArgumentException
223         *             if the interfaceClass argument does not represent an interface
224         */
225        void addImplementedInterface(Class interfaceClass);
226    
227        /**
228         * Converts a type name into a corresponding class (possibly, a transformed class). Primitive
229         * type names are returned as wrapper types.
230         */
231        Class toClass(String type);
232    
233        /**
234         * Returns a logger, based on the class name being transformed, to which warnings or errors
235         * concerning the class being transformed may be logged.
236         */
237        Logger getLogger();
238    
239        /**
240         * Returns true if this transformation represents a root class (one that extends directly from
241         * Object), or false if this transformation is an sub-class of another transformed class.
242         * 
243         * @return true if root class, false if sub-class
244         */
245        boolean isRootTransformation();
246    
247        /**
248         * Locates and returns the method if declared in this class; If not,
249         * the method is added to the class. If the method is an override
250         * of a base class method, then the method will delegate to the base
251         * class method (invoke it, return its value). If the method is entirely
252         * new, it will ignore its parameters and return a default value (null, 0 or false).
253         * 
254         * @param signature
255         *            identifies the method to locate, override or create
256         * @since 5.2.0
257         */
258        TransformMethod getOrCreateMethod(TransformMethodSignature signature);
259    
260        /**
261         * Determines if the class being transformed includes a declared (not inherited) method
262         * with the provided signature.
263         * 
264         * @since 5.2.0
265         * @param signature
266         *            identifies method to search for
267         * @return true if a such a method exists
268         */
269        boolean isDeclaredMethod(TransformMethodSignature signature);
270    
271        /**
272         * Adds advice to the {@link Component#dispatchComponentEvent(org.apache.tapestry5.runtime.ComponentEvent)} method.
273         * If the handler is invoked,
274         * the return value of the method will be overriden to true. Updates
275         * {@linkplain MutableComponentModel#addEventHandler(String) the model} to
276         * indicate that there is a handler for the named event. Existing handlers, or super-class handlers,
277         * are invoked <em>first</em>.
278         * 
279         * @param eventType
280         *            name of event to be handled
281         * @param minContextValues
282         *            minimum number of event context values required to invoke the method
283         * @param methodDescription
284         *            Text description of what the handler does (used with {@link Event#setMethodDescription(String)})
285         * @param handler
286         *            the handler to invoke
287         * @since 5.2.0
288         */
289        void addComponentEventHandler(String eventType, int minContextValues, String methodDescription,
290                ComponentEventHandler handler);
291    }