001    // Copyright 2006, 2007, 2008, 2010 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 javassist.CtBehavior;
021    
022    import org.apache.tapestry5.ComponentResources;
023    import org.apache.tapestry5.func.Predicate;
024    import org.apache.tapestry5.internal.transform.ReadOnlyFieldValueConduit;
025    import org.apache.tapestry5.ioc.AnnotationProvider;
026    import org.apache.tapestry5.model.MutableComponentModel;
027    import org.apache.tapestry5.runtime.Component;
028    import org.apache.tapestry5.runtime.Event;
029    import org.slf4j.Logger;
030    
031    /**
032     * Contains class-specific information used when transforming a raw component class into an
033     * executable component class.
034     * An executable class is one that has been transformed to work within Tapestry. This includes
035     * adding interfaces
036     * ({@link org.apache.tapestry5.runtime.Component}) but also transforming access to fields, based on
037     * annotations and
038     * naming conventions. Most of the changes are provided by different implementations of
039     * {@link ComponentClassTransformWorker}.
040     * <p/>
041     * Much of this information is somewhat like ordinary reflection, but applies to a class that has not yet been loaded.
042     * Field types, return types, parameter types and exception types are represented as string names, since any of them may
043     * be a class that has not yet been loaded and transformed as well.
044     * <p/>
045     * Transformation is primarily about identifying annotations on fields and on methods and changing the class, adding new
046     * interfaces, fields and methods, and deleting some existing fields.
047     * <p/>
048     * A ClassTransformation contains all the state data specific to a particular class being transformed. A number of
049     * <em>workers</em> will operate upon the ClassTransformation to effect the desired changes before the true class is
050     * loaded into memory.
051     * <p/>
052     * Instances of this class are not designed to be thread safe, access to an instance should be restricted to a single
053     * thread. In fact, the design of this type is to allow stateless singletons in multiple threads to work on
054     * thread-specific data (within the ClassTransformation).
055     * <p/>
056     * The majority of methods concern the <em>declared</em> members (field and methods) of a specific class, rather than
057     * any fields or methods inherited from a base class.
058     * 
059     * @see org.apache.tapestry5.services.TapestryModule#contributeComponentClassTransformWorker(org.apache.tapestry5.ioc.OrderedConfiguration,
060     *      org.apache.tapestry5.ioc.ObjectLocator, InjectionProvider, ComponentClassResolver)
061     */
062    public interface ClassTransformation extends AnnotationProvider
063    {
064        /**
065         * Returns the fully qualified class name of the class being transformed.
066         */
067        String getClassName();
068    
069        /**
070         * Returns the name of a new member (field or method). Ensures that the resulting name does not
071         * conflict with any
072         * existing member (declared by the underlying class, or inherited from a base class).
073         * 
074         * @param suggested
075         *            the suggested value for the member
076         * @return a unique name for the member
077         */
078        String newMemberName(String suggested);
079    
080        /**
081         * As with {@link #newMemberName(String)}, but the suggested name is constructed from the prefix
082         * and base name. An
083         * underscore will separate the prefix from the base name.
084         * 
085         * @param prefix
086         *            for the generated name
087         * @param baseName
088         *            a name, often of an existing field or method
089         * @return a unique name
090         */
091        String newMemberName(String prefix, String baseName);
092    
093        /**
094         * Generates a list of the names of declared instance fields that have the indicated annotation.
095         * Non-private and
096         * static fields are ignored. Only the names of private instance fields are returned.
097         * 
098         * @deprecated Use {@link #matchFieldsWithAnnotation(Class)} instead
099         */
100        List<String> findFieldsWithAnnotation(Class<? extends Annotation> annotationClass);
101    
102        /**
103         * Returns a sorted list of declared instance fields with the indicated annotation. Non-private
104         * and static fields are ignored.
105         * 
106         * @since 5.2.0
107         */
108        List<TransformField> matchFieldsWithAnnotation(Class<? extends Annotation> annotationClass);
109    
110        /**
111         * Finds all methods defined in the class that are marked with the provided annotation.
112         * 
113         * @param annotationClass
114         * @return a list of method signature (which may be empty) in ascending order
115         * @see #findMethods(MethodFilter)
116         * @deprecated Use {@link #matchMethodsWithAnnotation(Class)} instead
117         */
118        List<TransformMethodSignature> findMethodsWithAnnotation(Class<? extends Annotation> annotationClass);
119    
120        /**
121         * Finds all methods matched by the provided filter.
122         * 
123         * @param filter
124         *            Passed each method signature, it may include or exclude each potential
125         * @return a list of matching method signatures (which may be empty) in ascending order (by
126         *         method name), but
127         *         descending order (by parameter count) within overrides of a single method name.
128         * @deprecated Use {@link #matchMethods(Predicate)} instead
129         */
130        List<TransformMethodSignature> findMethods(MethodFilter filter);
131    
132        /**
133         * Finds all methods matched by the provided predicate.
134         * 
135         * @param predicate
136         *            Used to filter the list
137         * @return a list of matching methods (which may be empty) in ascending order (by
138         *         method name), but descending order (by parameter count) within overrides of a single method name.
139         */
140        List<TransformMethod> matchMethods(Predicate<TransformMethod> predicate);
141    
142        /**
143         * Finds all methods matched by the provided predicate.
144         * 
145         * @param annotationType
146         *            Used to filter the list
147         * @return a list of matching methods (which may be empty) in ascending order (by
148         *         method name), but descending order (by parameter count) within overrides of a single method name.
149         */
150        List<TransformMethod> matchMethodsWithAnnotation(Class<? extends Annotation> annotationType);
151    
152        /**
153         * Finds all unclaimed fields matched by the provided filter. Only considers private instance
154         * fields.
155         * 
156         * @param filter
157         *            passed each field name and field type
158         * @return the names of all matched fields, in ascending order
159         * @deprecated Use {@link #matchFields(Predicate)} instead
160         */
161        List<String> findFields(FieldFilter filter);
162    
163        /**
164         * Finds all unclaimed fields matched by the provided predicate. Only considers instance fields.
165         * Added, removed and claimed fields are excluded.
166         * 
167         * @param predicate
168         *            used for matching
169         * @return sorted list of matching fields
170         * @since 5.2.0
171         */
172        List<TransformField> matchFields(Predicate<TransformField> predicate);
173    
174        /**
175         * Finds an annotation on a declared instance field.
176         * 
177         * @param <T>
178         *            constrains parameter and return value to Annotation types
179         * @param fieldName
180         *            the name of the field, which must exist
181         * @param annotationClass
182         *            the type of annotation to access
183         * @return the annotation if present, or null otherwise
184         * @throws IllegalArgumentException
185         *             if the fieldName does not correspond to a declared field
186         * @deprecated Use {@link TransformField#getAnnotation(Class)} instead
187         */
188        <T extends Annotation> T getFieldAnnotation(String fieldName, Class<T> annotationClass);
189    
190        /**
191         * Locates a declared field by its field name. The field must exist.
192         * 
193         * @param name
194         *            of declared field
195         * @return field information
196         * @throws RuntimeException
197         *             if no such field
198         * @since 5.2.0
199         */
200        TransformField getField(String fieldName);
201    
202        /**
203         * Finds an annotation on a declared method.
204         * 
205         * @param <T>
206         *            constrains parameter and return value to Annotation types
207         * @param method
208         *            the method signature to search
209         * @param annotationClass
210         *            the type of annotation to access
211         * @return the annotation if present, or null otherwise
212         * @throws IllegalArgumentException
213         *             if the method signature does not correspond to a declared method
214         * @deprecated Use {@link TransformMethod#getAnnotation(Class)} instead
215         */
216        <T extends Annotation> T getMethodAnnotation(TransformMethodSignature method, Class<T> annotationClass);
217    
218        /**
219         * Claims a field so as to ensure that only a single annotation is applied to any single field.
220         * When a
221         * transformation occurs (driven by a field annotation), the field is claimed (using the
222         * annotation object as the
223         * tag). If a field has multiple conflicting annotations, this will be discovered when the code
224         * attempts to claim
225         * the field a second time.
226         * 
227         * @param fieldName
228         *            the name of the field that is being claimed
229         * @param tag
230         *            a non-null object that represents why the field is being tagged (this is typically
231         *            a specific
232         *            annotation on the field)
233         * @throws IllegalArgumentException
234         *             if the fieldName does not correspond to a declared instance field
235         * @throws IllegalStateException
236         *             if the field is already claimed for some other tag
237         * @deprecated Use {@link TransformField#claim(Object)} instead
238         */
239        void claimField(String fieldName, Object tag);
240    
241        /**
242         * Changes the field to be read only. Any existing code that changes the field will cause a
243         * runtime exception.
244         * 
245         * @param fieldName
246         *            name of field to so change
247         * @deprecated Use {@link TransformField#replaceAccess(TransformField)} instead
248         * @see ReadOnlyFieldValueConduit
249         */
250        void makeReadOnly(String fieldName);
251    
252        /**
253         * Finds any declared <em>instance</em> fields that have not been claimed (via {@link #claimField(String, Object)})
254         * and have not been added , and returns the names of those fields. May return an empty array.
255         * 
256         * @deprecated Use {@link #matchUnclaimedFields()} instead
257         */
258        List<String> findUnclaimedFields();
259    
260        /**
261         * Matches all fields that are not claimed. This may include static fields and final fields, but will not
262         * include fields that have been added as part of the transformation.
263         * 
264         * @since 5.2.0
265         * @return sorted list of unclaimed fields
266         */
267        List<TransformField> matchUnclaimedFields();
268    
269        /**
270         * Obtains the type of a declared instance field.
271         * 
272         * @param fieldName
273         * @return the type of the field, as a string
274         * @throws RuntimeException
275         *             if the fieldName does not correspond to a declared instance field
276         * @deprecated Use {@link TransformField#getType()} instead
277         */
278        String getFieldType(String fieldName);
279    
280        /**
281         * Returns true if the indicated name is a private instance field.
282         * 
283         * @param fieldName
284         * @return true if field exists
285         */
286        boolean isField(String fieldName);
287    
288        /**
289         * Defines a new declared field for the class. The suggestedName may be modified to ensure
290         * uniqueness.
291         * 
292         * @param modifiers
293         *            modifiers for the field (typically, {@link java.lang.reflect.Modifier#PRIVATE})
294         * @param type
295         *            the type for the field, as a string
296         * @param suggestedName
297         *            the desired name for the field, which may be modified (for uniqueness) when
298         *            returned
299         * @return the (uniqued) name for the field
300         * @deprecated Use {@link #createField(int, String, String)} instead
301         */
302        String addField(int modifiers, String type, String suggestedName);
303    
304        /**
305         * Defines a new declared field for the class. Suggested name may be modified to ensure uniqueness.
306         * 
307         * @param modifiers
308         *            modifiers for the field (typically, {@link java.lang.reflect.Modifier#PRIVATE})
309         * @param type
310         *            the type for the field, as a string
311         * @param suggestedName
312         *            the desired name for the field, which may be modified (for uniqueness) when
313         *            returned
314         * @return new field instance
315         */
316        TransformField createField(int modifiers, String type, String suggestedName);
317    
318        /**
319         * Defines a new <strong>protected</strong> instance variable whose initial value is provided
320         * statically, via a
321         * constructor parameter. The transformation caches the result, so calling this method
322         * repeatedly with the same type
323         * and value will return the same field name. Caching extends to the parent transformation, so
324         * that a value injected
325         * into a parent class will be available (via the protected instance variable) to subclasses.
326         * This is primarily used to inject service dependencies into components, though it has a number
327         * of other uses as well.
328         * 
329         * @param type
330         *            the type of object to inject
331         * @param suggestedName
332         *            the suggested name for the new field
333         * @param value
334         *            to be injected. This value is retained.
335         * @return the actual name of the injected field
336         */
337        String addInjectedField(Class type, String suggestedName, Object value);
338    
339        /**
340         * Like {@link #addInjectedField(Class, String, Object)}, but instead of specifying the value,
341         * a provider for the value is specified. In the generated class' constructor, the provider
342         * will be passed the {@link ComponentResources} and will return the final value; thus
343         * each component <em>instance</em> will receive a matching unique instance via the provider.
344         * 
345         * @param <T>
346         * @param type
347         *            type of value to inject
348         * @param suggestedName
349         *            suggested name for the new field
350         * @param provider
351         *            injected into the component to provide the value
352         * @return the actual name of the injected field
353         * @since 5.2.0
354         */
355        <T> TransformField addIndirectInjectedField(Class<T> type, String suggestedName, ComponentValueProvider<T> provider);
356    
357        /**
358         * Converts an <em>existing</em> field into a read only field whose value is the provided
359         * value. This is used
360         * when converting an
361         * existing field into a read-only injected value.
362         * 
363         * @param fieldName
364         *            name of field to convert
365         * @param value
366         *            the value provided by the field
367         * @deprecated Use {@link TransformField#inject(Object)} instead
368         */
369        void injectField(String fieldName, Object value);
370    
371        /**
372         * Transforms the class to implement the indicated interface. If the class (or its super class)
373         * does not already
374         * implement the interface, then the interface is added, and default implementations of any
375         * methods of the interface
376         * are added.
377         * <p/>
378         * TODO: Checking that the names of methods in the interface do not conflict with the names of methods present in
379         * the (unmodified) class.
380         * 
381         * @param interfaceClass
382         *            the interface to be implemented by the class
383         * @throws IllegalArgumentException
384         *             if the interfaceClass argument does not represent an interface
385         */
386        void addImplementedInterface(Class interfaceClass);
387    
388        /**
389         * Extends an existing method. The provided method body is inserted at the end of the existing
390         * method (i.e. {@link javassist.CtBehavior#insertAfter(java.lang.String)}). To access or change
391         * the return value, use the <code>$_</code> pseudo variable.
392         * <p/>
393         * The method may be declared in the class, or may be inherited from a super-class. For inherited methods, a method
394         * body is added that first invokes the super implementation. Use
395         * {@link #addMethod(TransformMethodSignature, String)} when it is necessary to control when the super-class method
396         * is invoked.
397         * <p/>
398         * The extended method is considered <em>new</em>. New methods <em>are not</em> scanned for
399         * {@linkplain #removeField(String)} removed}, {@linkplain #replaceReadAccess(String, String)} read replaced}, or
400         * {@linkplain #replaceWriteAccess(String, String) write replaced} fields. Generally that's what you want!
401         * 
402         * @param methodSignature
403         *            the signature of the method to extend
404         * @param methodBody
405         *            the body of code
406         * @throws org.apache.tapestry5.internal.services.MethodCompileException
407         *             if the provided Javassist method body can not be compiled
408         * @deprecated Use {@link TransformMethod#addAdvice(ComponentMethodAdvice)} instead. This method is non-functional
409         *             as of Tapestry 5.2.
410         */
411        void extendMethod(TransformMethodSignature methodSignature, String methodBody);
412    
413        /**
414         * Like {@link #extendMethod(TransformMethodSignature, String)}, but the extension does not mark
415         * the method as new,
416         * and field changes <em>will</em> be processed. Note: at some point, this is not longer true; extend and
417         * extendMethod work identically.
418         * 
419         * @param methodSignature
420         *            signature of the method to extend
421         * @param methodBody
422         *            the body of code
423         * @throws org.apache.tapestry5.internal.services.MethodCompileException
424         *             if the provided method body can not be compiled
425         * @see #prefixMethod(TransformMethodSignature, String)
426         * @deprecated Use {@link TransformMethod#addAdvice(ComponentMethodAdvice) instead}. This method is non-functional
427         *             as of Tapestry 5.2.
428         */
429        void extendExistingMethod(TransformMethodSignature methodSignature, String methodBody);
430    
431        /**
432         * Inserts code at the beginning of a method body (i.e. {@link CtBehavior#insertBefore(String)}.
433         * <p/>
434         * The method may be declared in the class, or may be inherited from a super-class. For inherited methods, a method
435         * is added that first invokes the super implementation. Use {@link #addMethod(TransformMethodSignature, String)}
436         * when it is necessary to control when the super-class method is invoked.
437         * <p/>
438         * Like {@link #extendExistingMethod(TransformMethodSignature, String)}, this method is generally used to "wrap" an
439         * existing method adding additional functionality such as caching or transaction support.
440         * 
441         * @param methodSignature
442         * @param methodBody
443         * @throws org.apache.tapestry5.internal.services.MethodCompileException
444         *             if the provided method body can not be compiled
445         * @deprecated Use {@link TransformMethod#addAdvice(ComponentMethodAdvice)} instead. This method is non-functional
446         *             as of Tapestry 5.2.
447         */
448        void prefixMethod(TransformMethodSignature methodSignature, String methodBody);
449    
450        /**
451         * Returns the name of a field that provides the {@link org.apache.tapestry5.ComponentResources} for the transformed
452         * component. This will be a protected field, accessible to the class and subclasses.
453         * 
454         * @return name of field
455         * @deprecated Obtain the resources from {@link ComponentMethodInvocation#getComponentResources()} or
456         *             as passed to {@link ComponentValueProvider#get(ComponentResources)} instead
457         */
458        String getResourcesFieldName();
459    
460        /**
461         * Adds a new method to the transformed class. Replaces any existing method declared for the
462         * class. When overriding
463         * a super-class method, you should use {@link #extendMethod(TransformMethodSignature, String)},
464         * or you should
465         * remember to invoke the super class implemetation explicitly. Use this method to control when
466         * the super-class
467         * implementation is invoked.
468         * 
469         * @deprecated Use {@link #getOrCreateMethod(TransformMethodSignature)} instead. This method is non-functional as of
470         *             Tapestry 5.2.
471         */
472        void addMethod(TransformMethodSignature signature, String methodBody);
473    
474        /**
475         * As with {@link #addMethod(TransformMethodSignature, String)}, but field references inside the
476         * method <em>will</em> be transformed, and the method <em>must not already exist</em>.
477         * 
478         * @deprecated Use {@link #getOrCreateMethod(TransformMethodSignature)} instead. This method is non-functional as of
479         *             Tapestry 5.2.
480         */
481        void addTransformedMethod(TransformMethodSignature methodSignature, String methodBody);
482    
483        /**
484         * Adds a statement to the constructor. The statement is added as is, though a newline is added.
485         * 
486         * @param statement
487         *            the statement to add, which should end with a semicolon
488         * @deprecated Use methods that create or inject fields (directly or indirectly)
489         * @see ComponentValueProvider
490         */
491        void extendConstructor(String statement);
492    
493        /**
494         * Replaces all read-references to the specified field with invocations of the specified method
495         * name. Replacements
496         * do not occur in methods added via {@link #addMethod(TransformMethodSignature, String)} or
497         * {@link #extendMethod(TransformMethodSignature, String)}.
498         * 
499         * @deprecated Use {@link TransformField#replaceAccess(ComponentValueProvider) instead
500         */
501        void replaceReadAccess(String fieldName, String methodName);
502    
503        /**
504         * Replaces all write accesses to the specified field with invocations of the specified method
505         * name. The method
506         * should take a single parameter of the same type as the field. Replacements do not occur in
507         * methods added via {@link #addMethod(TransformMethodSignature, String)} or
508         * {@link #extendMethod(TransformMethodSignature, String)}.
509         * 
510         * @deprecated Use {@link TransformField#replaceAccess(ComponentValueProvider) instead
511         */
512        void replaceWriteAccess(String fieldName, String methodName);
513    
514        /**
515         * Removes a field entirely; this is useful for fields that are replaced entirely by computed
516         * values.
517         * 
518         * @param fieldName
519         *            the name of the field to remove
520         * @see #replaceReadAccess(String, String)
521         * @see #replaceWriteAccess(String, String)
522         * @deprecated This method is non-functional as of Tapestry 5.2
523         */
524        void removeField(String fieldName);
525    
526        /**
527         * Converts a type name into a corresponding class (possibly, a transformed class). Primitive
528         * type names are returned as wrapper types.
529         */
530        Class toClass(String type);
531    
532        /**
533         * Returns a logger, based on the class name being transformed, to which warnings or errors
534         * concerning the class being transformed may be logged.
535         */
536        Logger getLogger();
537    
538        /**
539         * Returns the modifiers for the named field.
540         * 
541         * @deprecated Use {@link TransformField#getModifiers()} instead
542         */
543        int getFieldModifiers(String fieldName);
544    
545        /**
546         * Converts a signature to a string used to identify the method; this consists of the
547         * {@link TransformMethodSignature#getMediumDescription()} appended with source file information
548         * and line number
549         * information (when available).
550         * 
551         * @param signature
552         * @return a string that identifies the class, method name, types of parameters, source file and
553         *         source line number
554         * @deprecated Use {@link TransformMethod#getMethodIdentifier()} instead
555         */
556        String getMethodIdentifier(TransformMethodSignature signature);
557    
558        /**
559         * Returns true if this transformation represents a root class (one that extends directly from
560         * Object), or false if this transformation is an sub-class of another transformed class.
561         * 
562         * @return true if root class, false if sub-class
563         */
564        boolean isRootTransformation();
565    
566        /**
567         * Adds a catch block to the method. The body should end with a return or a throw. The special
568         * Javassist variable
569         * $e is the exception instance.
570         * 
571         * @param methodSignature
572         *            method to be extended.
573         * @param exceptionType
574         *            fully qualified class name of exception
575         * @param body
576         *            code to execute
577         * @deprecated Use {@link TransformMethod#addAdvice(ComponentMethodAdvice)} instead. This method is non-functional
578         *             as of Tapestry 5.2.
579         */
580        void addCatch(TransformMethodSignature methodSignature, String exceptionType, String body);
581    
582        /**
583         * Adds method advice for the indicated method.
584         * 
585         * @deprecated Use {@link TransformMethod#addAdvice(ComponentMethodAdvice)} instead
586         */
587        void advise(TransformMethodSignature methodSignature, ComponentMethodAdvice advice);
588    
589        /**
590         * Returns true if the method is an override of a method from the parent class.
591         * 
592         * @param methodSignature
593         *            signature of method to check
594         * @return true if the parent class contains a method with the name signature
595         * @deprecated Use {@link TransformMethod#isOverride()} instead
596         */
597        boolean isMethodOverride(TransformMethodSignature methodSignature);
598    
599        /**
600         * Locates and returns the method if declared in this class; If not,
601         * the method is added to the class. If the method is an override
602         * of a base class method, then the method will delegate to the base
603         * class method (invoke it, return its value). If the method is entirely
604         * new, it will ignore its parameters and return a default value (null, 0 or false).
605         * 
606         * @param signature
607         *            identifies the method to locate, override or create
608         * @since 5.2.0
609         */
610        TransformMethod getOrCreateMethod(TransformMethodSignature signature);
611    
612        /**
613         * Determines if the class being transformed includes a declared (not inherited) method
614         * with the provided signature.
615         * 
616         * @since 5.2.0
617         * @param signature
618         *            identifies method to search for
619         * @return true if a such a method exists
620         */
621        boolean isDeclaredMethod(TransformMethodSignature signature);
622    
623        /**
624         * Adds advice to the {@link Component#dispatchComponentEvent(org.apache.tapestry5.runtime.ComponentEvent)} method.
625         * If the handler is invoked,
626         * the return value of the method will be overriden to true. Updates
627         * {@linkplain MutableComponentModel#addEventHandler(String) the model} to
628         * indicate that there is a handler for the named event. Existing handlers, or super-class handlers,
629         * are invoked <em>first</em>.
630         * 
631         * @param eventType
632         *            name of event to be handled
633         * @param minContextValues
634         *            minimum number of event context values required to invoke the method
635         * @param methodDescription
636         *            Text description of what the handler does (used with {@link Event#setMethodDescription(String)})
637         * @param handler
638         *            the handler to invoke
639         * @since 5.2.0
640         */
641        void addComponentEventHandler(String eventType, int minContextValues, String methodDescription,
642                ComponentEventHandler handler);
643    }