| // Copyright 2006, 2007, 2008 The Apache Software Foundation |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package org.apache.tapestry5.services; |
| |
| import javassist.CtBehavior; |
| import org.apache.tapestry5.ioc.AnnotationProvider; |
| import org.slf4j.Logger; |
| |
| import java.lang.annotation.Annotation; |
| import java.util.List; |
| |
| /** |
| * Contains class-specific information used when transforming a raw component class into an executable component class. |
| * An executable class is one that has been transformed to work within Tapestry. This includes adding interfaces |
| * ({@link org.apache.tapestry5.runtime.Component}) but also transforming access to fields, based on annotations and |
| * naming conventions. Most of the changes are provided by different implementations of {@link |
| * ComponentClassTransformWorker}. |
| * <p/> |
| * Much of this information is somewhat like ordinary reflection, but applies to a class that has not yet been loaded. |
| * <p/> |
| * Transformation is primarily about identifying annotations on fields and on methods and changing the class, adding new |
| * interfaces, fields and methods, and deleting some existing fields. |
| * <p/> |
| * A ClassTransformation contains all the state data specific to a particular class being transformed. A number of |
| * <em>workers</em> will operate upon the ClassTransformation to effect the desired changes before the true class is |
| * loaded into memory. |
| * <p/> |
| * Instances of this class are not designed to be thread safe, access to an instance should be restricted to a single |
| * thread. In fact, the design of this type is to allow stateless singletons in multiple threads to work on |
| * thread-specific data (within the ClassTransformation). |
| * <p/> |
| * The majority of methods concern the <em>declared</em> members (field and methods) of a specific class, rather than |
| * any fields or methods inherited from a base class. |
| * |
| * @see org.apache.tapestry5.services.TapestryModule#contributeComponentClassTransformWorker(org.apache.tapestry5.ioc.OrderedConfiguration, |
| * org.apache.tapestry5.ioc.ObjectLocator, InjectionProvider, Environment, ComponentClassResolver, |
| * org.apache.tapestry5.internal.services.RequestPageCache, BindingSource) |
| */ |
| public interface ClassTransformation extends AnnotationProvider |
| { |
| /** |
| * Returns the fully qualified class name of the class being transformed. |
| */ |
| String getClassName(); |
| |
| /** |
| * Returns the name of a new member (field or method). Ensures that the resulting name does not conflict with any |
| * existing member (declared by the underlying class, or inherited from a base class). |
| * |
| * @param suggested the suggested value for the member |
| * @return a unique name for the member |
| */ |
| String newMemberName(String suggested); |
| |
| /** |
| * As with {@link #newMemberName(String)}, but the suggested name is constructed from the prefix and base name. An |
| * underscore will seperate the prefix from the base name. |
| * |
| * @param prefix for the generated name |
| * @param baseName an name, often of an existing field or method |
| * @return a unique name |
| */ |
| String newMemberName(String prefix, String baseName); |
| |
| /** |
| * Generates a list of the names of declared instance fields that have the indicated annotation. Non-private and |
| * static fields are ignored. Only the names of private instance fields are returned. Any {@link #claimField(String, |
| * Object) claimed} fields are excluded. |
| */ |
| List<String> findFieldsWithAnnotation(Class<? extends Annotation> annotationClass); |
| |
| /** |
| * As with {@link #findFieldsWithAnnotation(Class)}, but finds fields even if they are claimed. |
| */ |
| List<String> findAllFieldsWithAnnotation(Class<? extends Annotation> annotationClass); |
| |
| /** |
| * Finds all methods defined in the class that are marked with the provided annotation. |
| * |
| * @param annotationClass |
| * @return a list of method signature (which may be empty) in ascending order |
| * @see #findMethods(MethodFilter) |
| */ |
| List<TransformMethodSignature> findMethodsWithAnnotation(Class<? extends Annotation> annotationClass); |
| |
| /** |
| * Finds all methods matched by the provided filter. |
| * |
| * @param filter Passed each method signature, it may include or exclude each potential |
| * @return a list of matching method signatures (which may be empty) in ascending order (by method name), but |
| * descending order (by parameter count) within overrides of a single method name. |
| */ |
| List<TransformMethodSignature> findMethods(MethodFilter filter); |
| |
| /** |
| * Finds all unclaimed fields matched by the provided filter. Only considers unclaimed, private, instance fields. |
| * |
| * @param filter passed each field name and field type |
| * @return the names of all matched fields, in ascending order |
| */ |
| List<String> findFields(FieldFilter filter); |
| |
| /** |
| * Finds an annotation on a declared instance field. |
| * |
| * @param <T> constrains parameter and return value to Annotation types |
| * @param fieldName the name of the field, which must exist |
| * @param annotationClass the type of annotation to access |
| * @return the annotation if present, or null otherwise |
| * @throws IllegalArgumentException if the fieldName does not correspond to a declared field |
| */ |
| <T extends Annotation> T getFieldAnnotation(String fieldName, Class<T> annotationClass); |
| |
| /** |
| * Finds an annotation on a declared method. |
| * |
| * @param <T> constrains parameter and return value to Annotation types |
| * @param method the method signature to search |
| * @param annotationClass the type of annotation to access |
| * @return the annotation if present, or null otherwise |
| * @throws IllegalArgumentException if the method signature does not correspond to a declared method |
| */ |
| <T extends Annotation> T getMethodAnnotation(TransformMethodSignature method, Class<T> annotationClass); |
| |
| /** |
| * Claims a field so as to ensure that only a single annotation is applied to any single field. When a |
| * transformation occurs (driven by a field annotation), the first thing that occurs is to claim the field, on |
| * behalf of the annotation. |
| * |
| * @param fieldName the name of the field that is being claimed |
| * @param tag a non-null object that represents why the field is being tagged (this is typically a specific |
| * annotation on the field) |
| * @throws IllegalArgumentException if the fieldName does not correspond to a declared instance field |
| * @throws IllegalStateException if the field is already claimed for some other tag |
| */ |
| void claimField(String fieldName, Object tag); |
| |
| /** |
| * Changes the field to be read only. Any existing code that changes the field will cause a runtime exception. |
| * |
| * @param fieldName name of field to so change |
| */ |
| void makeReadOnly(String fieldName); |
| |
| /** |
| * Finds any declared <em>instance</em> fields that have not been claimed (via {@link #claimField(String, Object)}) |
| * and returns the names of those fields. May return an empty array. |
| */ |
| List<String> findUnclaimedFields(); |
| |
| /** |
| * Obtains the type of a declared instance field. |
| * |
| * @param fieldName |
| * @return the type of the field, as a string |
| * @throws IllegalArgumentException if the fieldName does not correspond to a declared instance field |
| */ |
| String getFieldType(String fieldName); |
| |
| /** |
| * Returns true if the indicated name is a private instance field. |
| * |
| * @param fieldName |
| * @return true if field exists |
| */ |
| boolean isField(String fieldName); |
| |
| /** |
| * Defines a new declared field for the class. The suggestedName may be modified to ensure uniqueness. |
| * |
| * @param modifiers modifiers for the field (typically, {@link java.lang.reflect.Modifier#PRIVATE}) |
| * @param type the type for the field, as a string |
| * @param suggestedName the desired name for the field, which may be modified (for uniqueness) when returned |
| * @return the (uniqued) name for the field |
| */ |
| String addField(int modifiers, String type, String suggestedName); |
| |
| /** |
| * Defines a new <strong>protected</strong> instance variable whose initial value is provided statically, via a |
| * constructor parameter. The transformation caches the result, so calling this method repeatedly with the same type |
| * and value will return the same field name. Caching extends to the parent transformation, so that a value injected |
| * into a parent class will be available (via the protected instance variable) to subclasses. |
| * |
| * @param type the type of object to inject |
| * @param suggestedName the suggested name for the new field |
| * @param value to be injected. This value is retained. |
| * @return the actual name of the injected field |
| */ |
| String addInjectedField(Class type, String suggestedName, Object value); |
| |
| /** |
| * Converts the field into a read only field whose value is the provided value. This is used when converting an |
| * existing field into a read-only injected value. |
| * |
| * @param fieldName name of field to convert |
| * @param value the value provided by the field |
| */ |
| void injectField(String fieldName, Object value); |
| |
| /** |
| * Transforms the class to implement the indicated interface. If the class (or its super class) does not already |
| * implement the interface, then the interface is added, and default implementations of any methods of the interface |
| * are added. |
| * <p/> |
| * TODO: Checking that the names of methods in the interface do not conflict with the names of methods present in |
| * the (unmodified) class. |
| * |
| * @param interfaceClass the interface to be implemented by the class |
| * @throws IllegalArgumentException if the interfaceClass argument does not represent an interface |
| */ |
| void addImplementedInterface(Class interfaceClass); |
| |
| /** |
| * Extends an existing method. The provided method body is inserted at the end of the existing method (i.e. {@link |
| * javassist.CtBehavior#insertAfter(java.lang.String)}). To access or change the return value, use the |
| * <code>$_</code> pseudo variable. |
| * <p/> |
| * The method may be declared in the class, or may be inherited from a super-class. For inherited methods, a method |
| * is added that first invokes the super implementation. Use {@link #addMethod(TransformMethodSignature, String)} |
| * when it is necessary to control when the super-class method is invoked. |
| * <p/> |
| * The extended method is considered <em>new</em>. New methods <em>are not</em> scanned for {@linkplain |
| * #removeField(String)} removed}, {@linkplain #replaceReadAccess(String, String)} read replaced}, or {@linkplain |
| * #replaceWriteAccess(String, String) write replaced} fields. Generally that's what you want! |
| * |
| * @param methodSignature the signature of the method to extend |
| * @param methodBody the body of code |
| * @throws org.apache.tapestry5.internal.services.MethodCompileException |
| * if the provided Javassist method body can not be compiled |
| * @see #extendExistingMethod(TransformMethodSignature, String) |
| */ |
| void extendMethod(TransformMethodSignature methodSignature, String methodBody); |
| |
| /** |
| * Like {@link #extendMethod(TransformMethodSignature, String)}, but the extension does not mark the method as new, |
| * and field changes <em>will</em> be processed. |
| * |
| * @param methodSignature signature of the method to extend |
| * @param methodBody the body of code |
| * @throws org.apache.tapestry5.internal.services.MethodCompileException |
| * if the provided method body can not be compiled |
| * @see #prefixMethod(TransformMethodSignature, String) |
| */ |
| void extendExistingMethod(TransformMethodSignature methodSignature, String methodBody); |
| |
| /** |
| * Inserts code at the beginning of a method body (i.e. {@link CtBehavior#insertBefore(String)}. |
| * <p/> |
| * The method may be declared in the class, or may be inherited from a super-class. For inherited methods, a method |
| * is added that first invokes the super implementation. Use {@link #addMethod(TransformMethodSignature, String)} |
| * when it is necessary to control when the super-class method is invoked. |
| * <p/> |
| * <p/> |
| * Like {@link #extendExistingMethod(TransformMethodSignature, String)}, this method is generally used to "wrap" an |
| * existing method adding additional functionality such as caching or transaction support. |
| * |
| * @param methodSignature |
| * @param methodBody |
| * @throws org.apache.tapestry5.internal.services.MethodCompileException |
| * if the provided method body can not be compiled |
| */ |
| void prefixMethod(TransformMethodSignature methodSignature, String methodBody); |
| |
| /** |
| * Returns the name of a field that provides the {@link org.apache.tapestry5.ComponentResources} for the transformed |
| * component. This will be a protected field, accessible to the class and subclasses. |
| * |
| * @return name of field |
| */ |
| String getResourcesFieldName(); |
| |
| /** |
| * Adds a new method to the transformed class. Replaces any existing method declared for the class. When overriding |
| * a super-class method, you should use {@link #extendMethod(TransformMethodSignature, String)}, or you should |
| * remember to invoke the super class implemetation explicitly. Use this method to control when the super-class |
| * implementation is invoked. |
| */ |
| void addMethod(TransformMethodSignature signature, String methodBody); |
| |
| /** |
| * As with {@link #addMethod(TransformMethodSignature, String)}, but field references inside the method |
| * <em>will</em> be transformed. |
| */ |
| void addTransformedMethod(TransformMethodSignature methodSignature, String methodBody); |
| |
| /** |
| * Adds a statement to the constructor. The statement is added as is, though a newline is added. |
| * |
| * @param statement the statement to add, which should end with a semicolon |
| */ |
| void extendConstructor(String statement); |
| |
| /** |
| * Replaces all read-references to the specified field with invocations of the specified method name. Replacements |
| * do not occur in methods added via {@link #addMethod(TransformMethodSignature, String)} or {@link |
| * #extendMethod(TransformMethodSignature, String)}. |
| */ |
| void replaceReadAccess(String fieldName, String methodName); |
| |
| /** |
| * Replaces all write accesses to the specified field with invocations of the specified method name. The method |
| * should take a single parameter of the same type as the field. Replacements do not occur in methods added via |
| * {@link #addMethod(TransformMethodSignature, String)} or {@link #extendMethod(TransformMethodSignature, String)}. |
| */ |
| void replaceWriteAccess(String fieldName, String methodName); |
| |
| /** |
| * Removes a field entirely; this is useful for fields that are replaced entirely by computed values. |
| * |
| * @param fieldName the name of the field to remove |
| * @see #replaceReadAccess(String, String) |
| * @see #replaceWriteAccess(String, String) |
| */ |
| void removeField(String fieldName); |
| |
| /** |
| * Converts a type name into a corresponding class (possibly, a transformed class). Primitive type names are |
| * returned as wrapper types. |
| */ |
| |
| Class toClass(String type); |
| |
| /** |
| * Returns a logger, based on the class name being transformed, to which warnings or errors concerning the class |
| * being transformed may be logged. |
| */ |
| Logger getLogger(); |
| |
| /** |
| * Returns the modifiers for the named field. |
| */ |
| int getFieldModifiers(String fieldName); |
| |
| /** |
| * Converts a signature to a string used to identify the method; this consists of the {@link |
| * TransformMethodSignature#getMediumDescription()} appended with source file information and line number |
| * information (when available). |
| * |
| * @param signature |
| * @return a string that identifies the class, method name, types of parameters, source file and source line number |
| */ |
| String getMethodIdentifier(TransformMethodSignature signature); |
| |
| /** |
| * Returns true if this transformation represents a root class (one that extends directly from Object), or false if |
| * this transformation is an extension of another transformed class. |
| * |
| * @return true if root class, false if sub-class |
| */ |
| boolean isRootTransformation(); |
| |
| |
| /** |
| * Adds a catch block to the method. The body should end with a return or a throw. The special Javassist variable |
| * $e is the exception instance. |
| * |
| * @param methodSignature method to be extended. |
| * @param exceptionType fully qualified class name of exception |
| * @param body code to execute |
| */ |
| void addCatch(TransformMethodSignature methodSignature, String exceptionType, String body); |
| |
| /** |
| * Adds method advice for the indicated method. |
| */ |
| void advise(TransformMethodSignature methodSignature, ComponentMethodAdvice advice); |
| } |