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
015package org.apache.tapestry5.services;
016
017import java.lang.annotation.Annotation;
018import java.util.List;
019
020import org.apache.tapestry5.ComponentResources;
021import org.apache.tapestry5.func.Predicate;
022import org.apache.tapestry5.ioc.AnnotationProvider;
023import org.apache.tapestry5.model.MutableComponentModel;
024import org.apache.tapestry5.plastic.PlasticClass;
025import org.apache.tapestry5.runtime.Component;
026import org.apache.tapestry5.runtime.Event;
027import 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 */
060public 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}