001: // Copyright 2006, 2007 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.tapestry.services;
016:
017: import java.lang.annotation.Annotation;
018: import java.util.List;
019:
020: import org.apache.commons.logging.Log;
021: import org.apache.tapestry.ioc.AnnotationProvider;
022:
023: /**
024: * Contains class-specific information used when transforming an raw class into an executable class.
025: * Much of this information is somewhat like ordinary reflection, but applies to a class that has
026: * not yet been loaded.
027: * <p>
028: * Transformation is primarily about identifying annotations on fields and on methods and changing
029: * the class, adding new interfaces, fields and methods, and deleting some existing fields.
030: * <p>
031: * A ClassTransformation contains all the state data specific to a particular class being
032: * transformed. A number of <em>workers</em> will operate upon the ClassTransformation to effect
033: * the desired changes before the true class is loaded into memory.
034: * <p>
035: * Instances of this class are not designed to be thread safe, access to an instance should be
036: * restricted to a single thread. In fact, the design of this type is to allow stateless singletons
037: * in multiple threads to work on thread-specific data (within the ClassTransformation). *
038: * <p>
039: * The majority of methods concern the <em>declared</em> members (field and methods) of a specific
040: * class, rather than any fields or methods inherited from a base class.
041: */
042: public interface ClassTransformation extends AnnotationProvider {
043: /**
044: * Returns the fully qualified class name of the class being transformed.
045: */
046: String getClassName();
047:
048: /**
049: * Returns the name of a new member (field or method). Ensures that the resulting name does not
050: * conflict with any existing member (declared by the underlying class, or inherited from a base
051: * class).
052: *
053: * @param suggested
054: * the suggested value for the member
055: * @return a unique name for the member
056: */
057: String newMemberName(String suggested);
058:
059: /**
060: * As with {@link #newMemberName(String)}, but the suggested name is constructed from the
061: * prefix and base name. An underscore will seperate the prefix from the base name.
062: *
063: * @param prefix
064: * for the generated name
065: * @param baseName
066: * an name, often of an existing field or method
067: * @return a unique name
068: */
069: String newMemberName(String prefix, String baseName);
070:
071: /**
072: * Generates a list of the names of declared instance fields that have the indicated annotation.
073: * Non-private and static fields are ignored. Only the names of private instance fields are
074: * returned. Any {@link #claimField(String, Object) claimed} fields are excluded.
075: */
076: List<String> findFieldsWithAnnotation(
077: Class<? extends Annotation> annotationClass);
078:
079: /**
080: * Generates a list of the names of declared instance fields that exactly match the specified
081: * type. Only the names of private instance fields are returned. Any
082: * {@link #claimField(String, Object) claimed} fields are excluded.
083: */
084: List<String> findFieldsOfType(String type);
085:
086: /**
087: * Finds all methods defined in the class that are marked with the provided annotation.
088: *
089: * @param annotationClass
090: * @return a list of method signature (which may be empty) in ascending order
091: */
092: List<MethodSignature> findMethodsWithAnnotation(
093: Class<? extends Annotation> annotationClass);
094:
095: /**
096: * Finds all methods matched by the provided filter.
097: *
098: * @param filter
099: * Passed each method signature, it may include or exclude each potential
100: * @return a list of matching method signatures (which may be empty) in ascending order
101: */
102: List<MethodSignature> findMethods(MethodFilter filter);
103:
104: /**
105: * Finds all unclaimed fields matched by the provided filter. Only considers unclaimed, private,
106: * instance fields.
107: *
108: * @param filter
109: * passed each field name and field type
110: * @return the names of all matched fields, in ascending order
111: */
112: List<String> findFields(FieldFilter filter);
113:
114: /**
115: * Finds an annotation on a declared instance field.
116: *
117: * @param <T>
118: * constrains parameter and return value to Annotation types
119: * @param fieldName
120: * the name of the field, which must exist
121: * @param annotationClass
122: * the type of annotation to access
123: * @return the annotation if present, or null otherwise
124: * @throws IllegalArgumentException
125: * if the fieldName does not correspond to a declared field
126: */
127: <T extends Annotation> T getFieldAnnotation(String fieldName,
128: Class<T> annotationClass);
129:
130: /**
131: * Finds an annotation on a declared method.
132: *
133: * @param <T>
134: * constrains parameter and return value to Annotation types
135: * @param method
136: * the method signature to search
137: * @param annotationClass
138: * the type of annotation to access
139: * @return the annotation if present, or null otherwise
140: * @throws IllegalArgumentException
141: * if the method signature does not correspond to a declared method
142: */
143: <T extends Annotation> T getMethodAnnotation(
144: MethodSignature method, Class<T> annotationClass);
145:
146: /**
147: * Claims a field so as to ensure that only a single annotation is applied to any single field.
148: * When a transformation occurs (driven by a field annotation), the first thing that occurs is
149: * to claim the field, on behalf of the annotation.
150: *
151: * @param fieldName
152: * the name of the field that is being claimed
153: * @param tag
154: * a non-null object that represents why the field is being tagged (this is typically
155: * a specific annotation on the field)
156: * @throws IllegalArgumentException
157: * if the fieldName does not correspond to a declared instance field
158: * @throws IllegalStateException
159: * if the field is already claimed for some other tag
160: */
161: void claimField(String fieldName, Object tag);
162:
163: /**
164: * Changes the field to be read only. Any existing code that changes the field will cause a
165: * runtime exception.
166: *
167: * @param fieldName
168: * name of field to so change
169: */
170: void makeReadOnly(String fieldName);
171:
172: /**
173: * Finds any declared <em>instance</em> fields that have not been claimed (via
174: * {@link #claimField(String, Object)}) and returns the names of those fields. May return an
175: * empty array.
176: */
177: List<String> findUnclaimedFields();
178:
179: /**
180: * Obtains the type of a declared instance field.
181: *
182: * @param fieldName
183: * @return the type of the field, as a string
184: * @throws IllegalArgumentException
185: * if the fieldName does not correspond to a declared instance field
186: */
187: String getFieldType(String fieldName);
188:
189: /**
190: * Returns true if the indicated name is a private instance field.
191: *
192: * @param fieldName
193: * @return true if field exists
194: */
195: boolean isField(String fieldName);
196:
197: /**
198: * Defines a new declared field for the class. The suggestedName may be modified to ensure
199: * uniqueness.
200: *
201: * @param modifiers
202: * modifiers for the field (typically, {@link java.lang.reflect.Modifier#PRIVATE})
203: * @param type
204: * the type for the field, as a string
205: * @param suggestedName
206: * the desired name for the field, which may be modified (for uniqueness) when
207: * returned
208: * @return the (uniqued) name for the field
209: */
210: String addField(int modifiers, String type, String suggestedName);
211:
212: /**
213: * Defines a new <strong>protected</strong> instance variable whose initial value is provided
214: * statically, via a constructor parameter. The transformation caches the result, so calling
215: * this method repeatedly with the same type and value will return the same field name. Caching
216: * extends to the parent transformation, so that a value injected into a parent class will be
217: * available (via the protected instance variable) to subclasses.
218: *
219: * @param type
220: * the type of object to inject
221: * @param suggestedName
222: * the suggested name for the new field
223: * @param value
224: * to be injected. This value is retained.
225: * @return the actual name of the injected field
226: */
227: String addInjectedField(Class type, String suggestedName,
228: Object value);
229:
230: /**
231: * Converts the field into a read only field whose value is the provided value. This is used
232: * when converting an existing field into a read-only injected value.
233: *
234: * @param fieldName
235: * name of field to convert
236: * @param value
237: * the value provided by the field
238: */
239: void injectField(String fieldName, Object value);
240:
241: /**
242: * Transforms the class to implement the indicated interface. If the class (or its super class)
243: * does not already implement the interface, then the interface is added, and default
244: * implementations of any methods of the interface are added.
245: * <p>
246: * TODO: Checking that the names of methods in the interface do not conflict with the names of
247: * methods present in the (unmodified) class.
248: *
249: * @param interfaceClass
250: * the interface to be implemented by the class
251: * @throws IllegalArgumentException
252: * if the interfaceClass argument does not represent an interface
253: */
254: void addImplementedInterface(Class interfaceClass);
255:
256: /**
257: * Extends an existing method. The provided method body is inserted at the end of the existing
258: * method (i.e. {@link javassist.CtBehavior#insertAfter(java.lang.String)}). To access or
259: * change the return value, use the <code>$_</code> pseudo variable.
260: * <p>
261: * The method may be declared in the class, or may be inherited from a super-class. For
262: * inherited methods, a method is added that first invokes the super implementation. Use
263: * {@link #addMethod(MethodSignature, String)} when it is necessary to control when the
264: * super-class method is invoked.
265: *
266: * @param signature
267: * the signature of the method to extend
268: * @param methodBody
269: * the body of code
270: * @throws IllegalArgumentException
271: * if the provided Javassist method body can not be compiled
272: */
273: void extendMethod(MethodSignature methodSignature, String methodBody);
274:
275: /**
276: * Returns the name of a field that provides the {@link org.apache.tapestry.ComponentResources}
277: * for the transformed component. This will be a protected field, accessible to the class and
278: * subclasses.
279: *
280: * @return name of field
281: */
282: String getResourcesFieldName();
283:
284: /**
285: * Adds a new method to the transformed class. Replaces any existing method declared for the
286: * class. When overriding a super-class method, you should use
287: * {@link #extendMethod(MethodSignature, String)}, or you should remember to invoke the super
288: * class implemetation explicitly. Use this method to control when the super-class
289: * implementation is invoked.
290: */
291: void addMethod(MethodSignature signature, String methodBody);
292:
293: /**
294: * Adds a statement to the constructor. The statement is added as is, though a newline is added.
295: *
296: * @param statement
297: * the statement to add, which should end with a semicolon
298: */
299: void extendConstructor(String statement);
300:
301: /**
302: * Replaces all read-references to the specified field with invocations of the specified method
303: * name. Replacements do not occur in methods added via
304: * {@link #addMethod(MethodSignature, String)} or {@link #extendMethod(MethodSignature, String)}.
305: */
306: void replaceReadAccess(String fieldName, String methodName);
307:
308: /**
309: * Replaces all write accesses to the specified field with invocations of the specified method
310: * name. The method should take a single parameter of the same type as the field. Replacements
311: * do not occur in methods added via {@link #addMethod(MethodSignature, String)} or
312: * {@link #extendMethod(MethodSignature, String)}.
313: */
314: void replaceWriteAccess(String fieldName, String methodName);
315:
316: /**
317: * Removes a field entirely; this is useful for fields that are replaced entirely by computed
318: * values.
319: *
320: * @param fieldName
321: * the name of the field to remove
322: * @see #replaceReadAccess(String, String)
323: * @see #replaceWriteAccess(String, String)
324: */
325: void removeField(String fieldName);
326:
327: /**
328: * Converts a type name into a corresponding class (possibly, a transformed class). Primitive
329: * type names are returned as wrapper types.
330: */
331:
332: Class toClass(String type);
333:
334: /**
335: * Returns a log, based on the class name being transformed, to which warnings or errors
336: * concerning the class being transformed may be logged.
337: */
338: Log getLog();
339:
340: /** Returns the modifiers for the named field. */
341: int getFieldModifiers(String fieldName);
342:
343: /**
344: * Converts a signature to a string used to identify the method; this consists of the
345: * {@link MethodSignature#getMediumDescription()} appended with source file information and line
346: * number information (when available).
347: *
348: * @param signature
349: * @return a string that identifies the class, method name, types of parameters, source file and
350: * source line number
351: */
352: String getMethodIdentifier(MethodSignature signature);
353: }
|