View Javadoc

1   package org.sourceforge.jemm.weaver.transformation.field;
2   
3   import java.util.HashSet;
4   import java.util.Set;
5   
6   import javassist.CtClass;
7   import javassist.CtField;
8   import javassist.NotFoundException;
9   import javassist.bytecode.Descriptor;
10  
11  import org.sourceforge.jemm.Entity;
12  import org.sourceforge.jemm.collections.JemmList;
13  import org.sourceforge.jemm.collections.JemmMap;
14  import org.sourceforge.jemm.collections.JemmSet;
15  import org.sourceforge.jemm.util.JEMMObject;
16  import org.sourceforge.jemm.weaver.transformation.ClassPoolFactory;
17  import org.sourceforge.jemm.weaver.transformation.ShadowTransformation;
18  
19  /**
20   * Determines if a field on a class is either a jemm created field
21   * or specifically using the JEMM API and hence should be ignored.
22   * 
23   * @author <a href="mailto:csuml@yahoo.co.uk">Paul Keeble</a>
24   *
25   */
26  public class FieldClassifier {
27  	Set<String> jemmClasses;
28  	Set<String> primitiveDescriptors;
29  	Set<String> knownImmutables;
30  
31  	/**
32  	 * A classification of a CtField as needed by the transformation process.
33  	 *
34  	 */
35  	public enum Classification {
36  	    /** A Jemm Collection type. */
37  	    COLLECTION,
38  	    /** A Shadow object field. */
39  	    SHADOW,
40  	    /** A known valid immutable type (String,Integer,Long etc.) */
41  	    KNOWNIMMUTABLE,
42  	    /** A supported reference type (typically to another JemmObject type. */
43  	    SUPPORTED,
44  	    /** A field that doesn't fall into any other category. */
45  	    NORMALOBJECT,
46  	    /** A primitive type field. */	   
47  	    PRIMITIVE,
48  	    /** A field that has already been transformed for internal use. */
49  	    ALREADYTRANSFORMED
50      }
51  	
52  	/**
53  	 * Creates a new instance of a FieldClassifier.
54  	 */
55  	public FieldClassifier() {
56  		jemmClasses = new HashSet<String>();
57  		jemmClasses.add(Descriptor.of(JemmList.class.getName()));
58  		jemmClasses.add(Descriptor.of(JemmMap.class.getName()));
59  		jemmClasses.add(Descriptor.of(JemmSet.class.getName()));
60  		
61  		primitiveDescriptors = new HashSet<String>();
62  		primitiveDescriptors.add(Descriptor.of(CtClass.shortType));
63  		primitiveDescriptors.add(Descriptor.of(CtClass.intType));
64  		primitiveDescriptors.add(Descriptor.of(CtClass.longType));
65  		primitiveDescriptors.add(Descriptor.of(CtClass.floatType));
66  		primitiveDescriptors.add(Descriptor.of(CtClass.doubleType));
67  		primitiveDescriptors.add(Descriptor.of(CtClass.booleanType));
68  		primitiveDescriptors.add(Descriptor.of(CtClass.byteType));
69  		primitiveDescriptors.add(Descriptor.of(CtClass.charType));
70  		
71  		knownImmutables = new HashSet<String>();
72  		knownImmutables.add(Descriptor.of("java.lang.Short"));
73  		knownImmutables.add(Descriptor.of("java.lang.Integer"));
74  		knownImmutables.add(Descriptor.of("java.lang.Long"));
75  		knownImmutables.add(Descriptor.of("java.lang.Float"));
76  		knownImmutables.add(Descriptor.of("java.lang.Double"));
77  		knownImmutables.add(Descriptor.of("java.lang.Boolean"));
78  		knownImmutables.add(Descriptor.of("java.lang.Byte"));
79  		knownImmutables.add(Descriptor.of("java.lang.Character"));
80  		knownImmutables.add(Descriptor.of("java.lang.String"));
81          knownImmutables.add(Descriptor.of("org.sourceforge.jemm.types.JemmDate"));
82  	}
83  	
84  	/**
85  	 * Classifies the passed in field into a number of different states so that the compiler
86  	 * can determine what to do with a particular field.
87  	 * 
88  	 * @param field The field to check
89  	 * @return A classification of the field
90  	 * @throws NotFoundException
91  	 */
92  	public Classification classifySingular(CtField field) throws NotFoundException {
93  		CtClass type = field.getType();
94  		String fieldName = field.getName();
95  		
96  		if(isPrimitive(field))
97  			return Classification.PRIMITIVE;
98  		if(isJemmCollection(field))
99  			return Classification.COLLECTION;
100 		else if (fieldName.equals(ShadowTransformation.SHADOW_VARIABLE_NAME) 
101 				&& type.getName().equals(ShadowTransformation.SHADOW_CLASS))
102 			return Classification.SHADOW;
103 		else if (isKnownImmutable(field))
104 			return Classification.KNOWNIMMUTABLE;
105 		else if (type.subtypeOf(ClassPoolFactory.getPool().get(JEMMObject.class.getName()))
106 				|| type.subtypeOf(ClassPoolFactory.getPool().get(JEMMObject.class.getName() + "[]")))
107 			return Classification.SUPPORTED;
108 		else if (isEntity(field)) 
109 			return Classification.SUPPORTED;
110 		return Classification.NORMALOBJECT;
111 	}
112 	
113 	/**
114 	 * Returned whether the type referred to by the field is another entity (jemm object).
115 	 * @param field The target field.
116 	 * @return True if the field is an entity type, false otherwise.
117 	 */
118 	public boolean isEntity(CtField field){
119 		Object[] annotations;
120 		try {
121 			CtClass type = field.getType().isArray() ?  field.getType().getComponentType() : field.getType();
122 			annotations = type.getAnnotations();
123 		} catch (Exception e) {
124 			throw new RuntimeException("Couldn't find Entity class, shouldn't be possible");
125 		}
126 		for(Object each : annotations) {
127 			if(Entity.class.isInstance(each))
128 				return true;
129 		}
130 		return false;
131 	}
132 	
133 	/**
134 	 * Is a special field if the field is the shadow object or has the class type
135 	 * of one of the Jemm Collections.
136 	 * 
137 	 * @param field The field to check
138 	 * @return true if the field is Jemm specific, false otherwise.
139 	 * @throws NotFoundException ClassPool JA exception if the CtClass can't be found.
140 	 */
141 	public boolean isJemmSpecialField(CtField field) throws NotFoundException {
142 		Classification c = classifySingular(field);
143 		return c == Classification.COLLECTION || c == Classification.SHADOW;
144 	}
145 	
146 	/**
147 	 * If a field can be converted to a JEMMReference then this returns true, else false
148 	 * @param field the field to check
149 	 * @return true if convertable, else false.
150 	 * @throws NotFoundException
151 	 */
152 	public boolean isJEMMReferenceTransformable(CtField field) throws NotFoundException {
153 		Classification c = classifySingular(field);
154 		return c == Classification.SUPPORTED && !isArray(field);
155 	}
156 	
157 	/**
158 	 * If an array field can be converted to a JEMMArrayReference then this returns true, else false.
159 	 * 
160 	 * Arrays of NORMALOBJECT, primatives, Strings and JemmObject's should be supported.
161 	 * 
162 	 * @param field The field to check
163 	 * @return true if convertable, else false
164 	 * @throws NotFoundException
165 	 */
166 	public boolean isArrayReferenceTransformable(CtField field) throws NotFoundException {
167 		Classification c = classifySingular(field);
168 		return c == Classification.SUPPORTED && isArray(field);
169 	}
170 	
171 	/**
172 	 * Returns the dimensions of an array field type.
173 	 * @param field The target field.
174 	 * @return The number of array dimensions.
175 	 * @throws NotFoundException In an internal error occurs whilst loading the field type.
176 	 */
177 	public int arrayDimensions(CtField field) throws NotFoundException {
178 		String desc = Descriptor.of(field.getType());
179 		return Descriptor.arrayDimension(desc);
180 	}
181 	
182 	/**
183 	 * Returns whether the given field is an array type.
184 	 * @param field The target field.
185 	 * @return True if the target field is an array type, false otherwise.
186      * @throws NotFoundException In an internal error occurs whilst loading the field type.
187 	 */
188 	public boolean isArray(CtField field) throws NotFoundException {
189 		return field.getType().isArray();
190 	}
191 	
192 	/**
193 	 * If the field is a Jemm specific type then this returns true, else false.
194 	 * 
195 	 * Jemm specific types include all the Collections.
196 	 * 
197 	 * @param field The field to check the type of
198 	 * @return true if the field is a jemm type, false otherwise
199      * @throws NotFoundException In an internal error occurs whilst loading the field type.
200 	 */
201 	public boolean isJemmCollection(CtField field) throws NotFoundException {
202 		String fieldType = underlyingDescriptor(field);
203 		
204 		return jemmClasses.contains(fieldType);
205 	}
206 	
207 	/**
208 	 * Returns the Descriptor of a field, but removes any ['s before
209 	 * the descriptor that signify arrays.
210 	 * 
211 	 * @param field The field to determine the descriptor of
212 	 * @return The underlying type descriptor
213 	 */
214 	private String underlyingDescriptor(CtField field) throws NotFoundException {
215 		String fieldDesc = Descriptor.of(field.getType());
216 		
217 		int toIgnoreCharacters = arrayDimensions(field);
218 		String adjustedDescriptor = fieldDesc.substring(toIgnoreCharacters);
219 		return adjustedDescriptor;
220 	}
221 	
222 	/**
223 	 * Uses the descriptors to determine if the type of the field is a primitive, 
224 	 * even if it is an array this will return true if the type of the array is
225 	 * based on a primitive.
226 	 * 
227 	 * @param field The field to check
228 	 * @return true if a primitive, false otherwise
229 	 * @throws NotFoundException
230 	 */
231 	public boolean isPrimitive(CtField field) throws NotFoundException {
232 		String fieldType = underlyingDescriptor(field);
233 		
234 		return primitiveDescriptors.contains(fieldType);
235 	}
236 	
237 	/**
238 	 * The wrapper types for the Primitives (Integer, Long, Float etc)
239 	 * are covered by this definition as are the Jemm special immutable types
240 	 * such as JemmDate.
241 	 * 
242 	 * @param field The field to test
243 	 * @return true if it is a known immutable, else false.
244      * @throws NotFoundException In an internal error occurs whilst loading the field type.
245 	 */
246 	public boolean isKnownImmutable(CtField field) throws NotFoundException {
247 		String fieldType = underlyingDescriptor(field);
248 		
249 		return knownImmutables.contains(fieldType);
250 	}
251 }