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