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 }