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 }