1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.jdo.impl.model.java.reflection;
19
20 import java.util.Map;
21 import java.util.HashMap;
22
23 import java.lang.reflect.Field;
24 import java.lang.reflect.Method;
25
26 import org.apache.jdo.impl.model.java.PredefinedType;
27 import org.apache.jdo.impl.model.java.BaseReflectionJavaType;
28 import org.apache.jdo.impl.model.java.JavaPropertyImpl;
29 import org.apache.jdo.model.ModelFatalException;
30 import org.apache.jdo.model.java.JavaField;
31 import org.apache.jdo.model.java.JavaMethod;
32 import org.apache.jdo.model.java.JavaModel;
33 import org.apache.jdo.model.java.JavaProperty;
34 import org.apache.jdo.model.java.JavaType;
35 import org.apache.jdo.model.jdo.JDOClass;
36 import org.apache.jdo.model.jdo.JDOField;
37
38 /***
39 * A reflection based JavaType implementation used at runtime.
40 * The implementation takes <code>java.lang.Class</code> and
41 * <code>java.lang.reflect.Field</code> instances to get Java related
42 * metadata about types and fields.
43 *
44 * @author Michael Bouschen
45 * @since JDO 1.1
46 * @version JDO 2.0
47 */
48 public class ReflectionJavaType
49 extends BaseReflectionJavaType
50 {
51 /*** The declaring JavaModel instance. */
52 protected final ReflectionJavaModel declaringJavaModel;
53
54 /*** Flag indicating whether the superclass is checked already. */
55 private boolean superclassUnchecked = true;
56
57 /*** Flag indicating whether the JDOClass info is retrieved already. */
58 private boolean jdoClassUnchecked = true;
59
60 /*** The JDO metadata, if this type represents a pc class. */
61 private JDOClass jdoClass;
62
63 /*** Map of JavaField instances, key is the field name. */
64 protected Map declaredJavaFields = new HashMap();
65
66 /*** Map of JavaProperty instances, key is the property name. */
67 protected Map declaredJavaProperties = new HashMap();
68
69 /*** Flag indicating whether thsi JavaTYpe has been introspected. */
70 private boolean introspected = false;
71
72 /*** Constructor. */
73 public ReflectionJavaType(Class clazz,
74 ReflectionJavaModel declaringJavaModel)
75 {
76
77
78 super(clazz, null);
79 this.declaringJavaModel = declaringJavaModel;
80 }
81
82 /***
83 * Determines if this JavaType object represents an array type.
84 * @return <code>true</code> if this object represents an array type;
85 * <code>false</code> otherwise.
86 */
87 public boolean isArray()
88 {
89 return clazz.isArray();
90 }
91
92 /***
93 * Returns <code>true</code> if this JavaType represents a persistence
94 * capable class.
95 * <p>
96 * A {@link org.apache.jdo.model.ModelFatalException} indicates a
97 * problem accessing the JDO meta data for this JavaType.
98 * @return <code>true</code> if this JavaType represents a persistence
99 * capable class; <code>false</code> otherwise.
100 * @exception ModelFatalException if there is a problem accessing the
101 * JDO metadata
102 */
103 public boolean isPersistenceCapable()
104 throws ModelFatalException
105 {
106 return (getJDOClass() != null);
107 }
108
109 /***
110 * Returns the JavaType representing the superclass of the entity
111 * represented by this JavaType. If this JavaType represents either the
112 * Object class, an interface, a primitive type, or <code>void</code>,
113 * then <code>null</code> is returned. If this object represents an
114 * array class then the JavaType instance representing the Object class
115 * is returned.
116 * @return the superclass of the class represented by this JavaType.
117 */
118 public synchronized JavaType getSuperclass()
119 {
120 if (superclassUnchecked) {
121 superclassUnchecked = false;
122 superclass = getJavaTypeForClass(clazz.getSuperclass());
123 }
124 return superclass;
125 }
126
127 /***
128 * Returns the JDOClass instance if this JavaType represents a
129 * persistence capable class. The method returns <code>null</code>,
130 * if this JavaType does not represent a persistence capable class.
131 * <p>
132 * A {@link org.apache.jdo.model.ModelFatalException} indicates a
133 * problem accessing the JDO meta data for this JavaType.
134 * @return the JDOClass instance if this JavaType represents a
135 * persistence capable class; <code>null</code> otherwise.
136 * @exception ModelFatalException if there is a problem accessing the
137 * JDO metadata
138 */
139 public synchronized JDOClass getJDOClass()
140 throws ModelFatalException
141 {
142 if (jdoClassUnchecked) {
143 jdoClassUnchecked = false;
144 jdoClass = declaringJavaModel.getJDOModel().getJDOClass(getName());
145 }
146 return jdoClass;
147 }
148
149 /***
150 * Returns the JavaType representing the component type of an array.
151 * If this JavaType does not represent an array type this method
152 * returns <code>null</code>.
153 * @return the JavaType representing the component type of this
154 * JavaType if this class is an array; <code>null</code> otherwise.
155 */
156 public JavaType getArrayComponentType()
157 {
158 JavaType componentType = null;
159 if (isArray()) {
160 Class componentClass = clazz.getComponentType();
161 if (componentClass != null)
162 componentType = getJavaTypeForClass(componentClass);
163 }
164 return componentType;
165 }
166
167 /***
168 * Returns a JavaField instance that reflects the field with the
169 * specified name of the class or interface represented by this
170 * JavaType instance. The method returns <code>null</code>, if the
171 * class or interface (or one of its superclasses) does not have a
172 * field with that name.
173 * @param fieldName the name of the field
174 * @return the JavaField instance for the specified field in this class
175 * or <code>null</code> if there is no such field.
176 */
177 public JavaField getJavaField(String fieldName)
178 {
179 JavaField javaField = getDeclaredJavaField(fieldName);
180 if (javaField == null) {
181
182 JavaType superclass = getSuperclass();
183 if ((superclass != null) &&
184 (superclass != PredefinedType.objectType)) {
185 javaField = superclass.getJavaField(fieldName);
186 }
187 }
188 return javaField;
189 }
190
191 /***
192 * Returns an array of JavaField instances representing the declared
193 * fields of the class represented by this JavaType instance. Note, this
194 * method does not return JavaField instances representing inherited
195 * fields.
196 * @return an array of declared JavaField instances.
197 */
198 public JavaField[] getDeclaredJavaFields()
199 {
200 introspectClass();
201 return (JavaField[]) declaredJavaFields.values().toArray(
202 new JavaField[0]);
203 }
204
205 /***
206 * Returns a JavaProperty instance that reflects the property with the
207 * specified name of the class or interface represented by this
208 * JavaType instance. The method returns <code>null</code>, if the
209 * class or interface (or one of its superclasses) does not have a
210 * field with that name.
211 * @param name the name of the property
212 * @return the JavaProperty instance for the specified property in this
213 * class or <code>null</code> if there is no such property.
214 */
215 public JavaProperty getJavaProperty(String name)
216 {
217 JavaProperty javaProperty = getDeclaredJavaProperty(name);
218 if (javaProperty == null) {
219
220 JavaType superclass = getSuperclass();
221 if ((superclass != null) &&
222 (superclass != PredefinedType.objectType)) {
223 javaProperty = superclass.getJavaProperty(name);
224 }
225 }
226 return javaProperty;
227 }
228
229 /***
230 * Returns an array of JavaProperty instances representing the declared
231 * properties of the class represented by this JavaType instance. Note,
232 * this method does not return JavaField instances representing inherited
233 * properties.
234 * @return an array of declared JavaField instances.
235 */
236 public JavaProperty[] getDeclaredJavaProperties()
237 {
238 introspectClass();
239 return (JavaProperty[]) declaredJavaProperties.values().toArray(
240 new JavaProperty[0]);
241 }
242
243
244
245 /***
246 * Returns a JavaField instance that reflects the declared field with
247 * the specified name of the class or interface represented by this
248 * JavaType instance. The method returns <code>null</code>, if the
249 * class or interface does not declared a field with that name. It does
250 * not check whether one of its superclasses declares such a field.
251 * @param fieldName the name of the field
252 * @return the JavaField instance for the specified field in this class
253 */
254 public synchronized JavaField getDeclaredJavaField(String fieldName)
255 {
256 JavaField javaField = (JavaField)declaredJavaFields.get(fieldName);
257 if (javaField == null) {
258 JDOClass jdoClass = getJDOClass();
259 if (jdoClass != null) {
260
261 if (jdoClass.getDeclaredField(fieldName) != null) {
262
263
264 javaField = newJavaFieldInstance(fieldName, null);
265 declaredJavaFields.put(fieldName, javaField);
266 }
267 }
268
269
270 if (javaField == null) {
271 Field field = ReflectionJavaField.getDeclaredFieldPrivileged(
272 clazz, fieldName);
273 if (field != null) {
274 javaField = newJavaFieldInstance(field);
275 declaredJavaFields.put(fieldName, javaField);
276 }
277 }
278 }
279 return javaField;
280 }
281
282 /***
283 * Returns a JavaProperty instance that reflects the declared property
284 * with the specified name of the class or interface represented by this
285 * JavaType instance. The method returns <code>null</code>, if the
286 * class or interface does not declared a property with that name. It does
287 * not check whether one of its superclasses declares such a property.
288 * @param name the name of the property
289 * @return the JavaField instance for the specified property in this class
290 */
291 public JavaProperty getDeclaredJavaProperty(String name)
292 {
293 introspectClass();
294 return (JavaProperty)declaredJavaProperties.get(name);
295 }
296
297 /***
298 * Returns a JavaType instance for the specified Class object.
299 * This method provides a hook such that ReflectionJavaType subclasses can
300 * implement their own mapping of Class objects to JavaType instances.
301 */
302 public JavaType getJavaTypeForClass(Class clazz)
303 {
304 return declaringJavaModel.getDeclaringJavaModelFactory().getJavaType(clazz);
305 }
306
307 /***
308 * Creates a new JavaProperty instance and adds it to the list of
309 * declared properties of this class.
310 * @param name the name of the property
311 * @param getter the getter method
312 * @param setter the setter method
313 * @param type the ytpe of the property
314 * @return a new JavaProperty declared by this class
315 */
316 public synchronized JavaProperty createJavaProperty(
317 String name, JavaMethod getter, JavaMethod setter, JavaType type)
318 throws ModelFatalException
319 {
320 JavaProperty javaProperty =
321 newJavaPropertyInstance(name, getter, setter, type);
322 declaredJavaProperties.put(name, javaProperty);
323 return javaProperty;
324 }
325
326 /***
327 * Creates a new JavaMethod instance.
328 * @param method the java.lang.reflect.Method instance
329 * @return a new JavaMethod declared by this class
330 */
331 public JavaMethod createJavaMethod(Method method)
332 {
333 return newJavaMethodInstance(method);
334 }
335
336 /***
337 * Creates a new instance of the JavaField implementation class.
338 * <p>
339 * This implementation returns a <code>ReflectionJavaField</code>
340 * instance.
341 * @return a new JavaField instance.
342 */
343 protected JavaField newJavaFieldInstance(String fieldName, JavaType type)
344 {
345 return new ReflectionJavaField(fieldName, type, this);
346 }
347
348 /***
349 * Creates a new instance of the JavaField implementation class.
350 * <p>
351 * This implementation returns a <code>ReflectionJavaField</code>
352 * instance.
353 * @return a new JavaField instance.
354 */
355 protected JavaField newJavaFieldInstance(Field field)
356 {
357 return new ReflectionJavaField(field, this);
358 }
359
360 /***
361 * Creates a new instance of the JavaProperty implementation class.
362 * <p>
363 * This implementation returns a <code>JavaPropertyImpl</code>
364 * instance.
365 * @return a new JavaProperty instance.
366 */
367 protected JavaProperty newJavaPropertyInstance(String name,
368 JavaMethod getter, JavaMethod setter, JavaType type)
369 throws ModelFatalException
370 {
371 return new JavaPropertyImpl(name, getter, setter, type, this);
372 }
373
374 /***
375 * Creates a new instance of the JavaMethod implementation class.
376 * <p>
377 * This implementation returns a <code>ReflectionJavaMethod</code>
378 * instance.
379 * @return a new JavaMethod instance.
380 */
381 protected JavaMethod newJavaMethodInstance(Method method)
382 {
383 return new ReflectionJavaMethod(method, this);
384 }
385
386 /***
387 * Helper method to introspect the class and set the declared fields and
388 * properties.
389 */
390 protected synchronized void introspectClass()
391 {
392 if (introspected)
393
394 return;
395
396 introspected = true;
397
398 new ReflectionJavaTypeIntrospector().addDeclaredJavaProperties(this);
399
400
401 Field[] fields = ReflectionJavaField.getDeclaredFieldsPrivileged(clazz);
402 for (int i = 0; i < fields.length; i++) {
403 Field field = fields[i];
404 String fieldName = field.getName();
405 if (declaredJavaFields.get(fieldName) == null) {
406 JavaField javaField = newJavaFieldInstance(field);
407 declaredJavaFields.put(fieldName, javaField);
408 }
409 }
410 }
411 }