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 }