001: package com.bm.introspectors;
002:
003: import java.beans.PropertyDescriptor;
004: import java.lang.annotation.Annotation;
005: import java.lang.reflect.Field;
006: import java.lang.reflect.Modifier;
007: import java.util.ArrayList;
008: import java.util.HashMap;
009: import java.util.HashSet;
010: import java.util.List;
011: import java.util.Map;
012: import java.util.Set;
013:
014: import javax.persistence.Column;
015: import javax.persistence.EmbeddedId;
016: import javax.persistence.Entity;
017: import javax.persistence.GeneratedValue;
018: import javax.persistence.Id;
019: import javax.persistence.JoinColumn;
020: import javax.persistence.ManyToOne;
021: import javax.persistence.MappedSuperclass;
022: import javax.persistence.OneToMany;
023: import javax.persistence.Transient;
024:
025: import org.apache.commons.beanutils.PropertyUtilsBean;
026: import org.apache.log4j.Logger;
027:
028: import com.bm.introspectors.relations.GlobalPrimaryKeyStore;
029: import com.bm.introspectors.relations.GlobalRelationStore;
030: import com.bm.introspectors.relations.ManyToOneReleation;
031: import com.bm.introspectors.relations.OneToManyReleation;
032: import com.bm.introspectors.relations.RelationPropertyResolver;
033:
034: /**
035: * This class implements the common methods for all concrete inspectors.
036: *
037: * @author Daniel Wiese
038: * @param <T> -
039: * the type of the class to inspect
040: * @since 07.10.2005
041: */
042: public abstract class AbstractPersistentClassIntrospector<T> implements
043: Introspector<T> {
044:
045: // private final static Logger log =
046: // Logger.getLogger(AbstractIntrospector.class);
047:
048: /** if no lengths are defined, use a large number as default length. * */
049: private static final int DEFAULTLENGTH = 652345;
050:
051: /** holds the persistent fields of the class * */
052: private final List<Property> persitentProperties = new ArrayList<Property>();
053:
054: /** holds the transient fields of the class * */
055: private final List<Property> transientFields = new ArrayList<Property>();
056:
057: /** holds the meta information for persistent fields * */
058: protected final Map<Property, PersistentPropertyInfo> fieldInfo = new HashMap<Property, PersistentPropertyInfo>();
059:
060: /** holds the pk fields of the class * */
061: private final Set<Property> pkFields = new HashSet<Property>();
062:
063: /** holds the meta information for primary key fields * */
064: protected final Map<Property, PrimaryKeyInfo> pkFieldInfo = new HashMap<Property, PrimaryKeyInfo>();
065:
066: private final PropertyUtilsBean propUtils = new PropertyUtilsBean();
067:
068: /** the class represented by this introspector (e.g. the entity bean)* */
069: private Class<T> representingClass;
070:
071: /**
072: * If the access type is field, we will extract all the necessary meta informations form the fields
073: *
074: * @param toInspect -
075: * the class to inspect
076: */
077: protected void processAccessTypeField(Class<T> toInspect) {
078: this .representingClass = toInspect;
079: // extract meta information
080: Class<? super T> clazz = toInspect;
081: do {
082: Field[] fields = clazz.getDeclaredFields();
083: for (Field aktField : fields) {
084: // dontīs introspect fields generated by hibernate
085: if (!this .isStatic(aktField)
086: && !aktField.getName().startsWith("$")) {
087: Annotation[] fieldAnnotations = aktField
088: .getAnnotations();
089: this .processAnnotations(toInspect, new Property(
090: aktField), fieldAnnotations);
091: }
092: }
093: clazz = clazz.getSuperclass();
094: } while (clazz != null
095: && (clazz.getAnnotation(Entity.class) != null || clazz
096: .getAnnotation(MappedSuperclass.class) != null));
097: }
098:
099: /**
100: * If the access type is property, we will extract all the necessary meta informations form the getter
101: * methods
102: *
103: * @param toInspect -
104: * the class to inspect
105: */
106: protected void processAccessTypeProperty(Class<T> toInspect) {
107: this .representingClass = toInspect;
108: // extract meta information
109: PropertyDescriptor[] properties = this .propUtils
110: .getPropertyDescriptors(toInspect);
111:
112: for (PropertyDescriptor aktProperty : properties) {
113: // dontīs introspect fields generated by hibernate
114: if (!aktProperty.getReadMethod().getName().equals(
115: "getClass")) {
116: Annotation[] methodAnnotations = aktProperty
117: .getReadMethod().getAnnotations();
118: this .processAnnotations(toInspect, new Property(
119: toInspect, aktProperty), methodAnnotations);
120:
121: }
122: }
123: }
124:
125: /**
126: * This method returns informations about a persistent field.
127: *
128: * @param toCheck
129: * the property for persistent db field
130: * @see com.bm.introspectors.Introspector#getPresistentFieldInfo(Property)
131: * @return persistent property info
132: */
133: public PersistentPropertyInfo getPresistentFieldInfo(
134: Property toCheck) {
135: if (this .fieldInfo.containsKey(toCheck)) {
136: return fieldInfo.get(toCheck);
137: }
138: throw new RuntimeException("The field " + toCheck.getName()
139: + " is not a persitent field");
140:
141: }
142:
143: /**
144: * This method returns informations about a persistent field.
145: *
146: * @param toCheck
147: * the property for primary key
148: * @see com.bm.introspectors.Introspector#getPrimaryKeyInfo(Property)
149: * @return pk info
150: */
151: public PrimaryKeyInfo getPrimaryKeyInfo(Property toCheck) {
152: if (this .pkFieldInfo.containsKey(toCheck)) {
153: return pkFieldInfo.get(toCheck);
154: }
155: throw new RuntimeException("The field " + toCheck.getName()
156: + " is not a primary key field");
157: }
158:
159: /**
160: * Returns the persistent fields.
161: *
162: * @see com.bm.introspectors.Introspector#getPersitentProperties()
163: * @return the list of persistent fields
164: */
165: public List<Property> getPersitentProperties() {
166: return persitentProperties;
167: }
168:
169: /**
170: * Return the primary key fields.
171: *
172: * @see com.bm.introspectors.Introspector#getPkFields()
173: * @return the list of primary key fields
174: */
175: public Set<Property> getPkFields() {
176: return pkFields;
177: }
178:
179: /**
180: * Returns the transientFields.
181: *
182: * @return Returns the transientFields.
183: */
184: public List<Property> getTransientFields() {
185: return transientFields;
186: }
187:
188: /**
189: * Returns a value of an field.
190: *
191: * @author Daniel Wiese
192: * @param instance -
193: * the instance (Typed)
194: * @param toGet -
195: * the parameter to get
196: * @return - the value of the parameter of the instance
197: * @throws IllegalAccessException -
198: * in error case
199: * @since 15.10.2005
200: */
201: public Object getField(T instance, Property toGet)
202: throws IllegalAccessException {
203:
204: return toGet.getField(instance);
205: }
206:
207: /**
208: * Sets a value of the field.
209: *
210: * @author Daniel Wiese
211: * @param instance -
212: * the instance (Typed)
213: * @param toSet -
214: * the parameter to set
215: * @param value -
216: * the new value
217: * @throws IllegalAccessException -
218: * in error case
219: * @since 15.10.2005
220: */
221: public void setField(T instance, Property toSet, Object value)
222: throws IllegalAccessException {
223: toSet.setField(instance, value);
224: }
225:
226: /**
227: * Check if the field is static
228: *
229: * @param toCheck
230: * -the field to check
231: * @return - true if static
232: */
233: private boolean isStatic(Field toCheck) {
234: return Modifier.isStatic(toCheck.getModifiers());
235:
236: }
237:
238: /**
239: * Analyse the annotation of a (field or getterMethod)
240: *
241: * @param aktProperty -
242: * the property
243: * @param propertyAnnotations -
244: * the corresponding annotations
245: * @param classToInspect -
246: * the class to inspect
247: */
248: private void processAnnotations(Class<T> classToInspect,
249: Property aktProperty, Annotation[] propertyAnnotations) {
250: boolean isTransient = false;
251: String joinColumnName = null;
252:
253: // create a instance if the file is a persistent field
254: final PersistentPropertyInfo aktFieldInfo = new PersistentPropertyInfo();
255: // initial name
256: aktFieldInfo.setDbName(aktProperty.getName());
257: aktFieldInfo.setLength(DEFAULTLENGTH);
258:
259: // look into the annotations
260: for (Annotation a : propertyAnnotations) {
261: // skip transient fields
262: if (a instanceof Transient) {
263: isTransient = true;
264: }
265:
266: // extract column size & name
267: if (a instanceof Column) {
268: final Column ac = (Column) a;
269: aktFieldInfo.setLength(ac.length());
270: aktFieldInfo.setDbName(ac.name());
271: aktFieldInfo.setNullable(ac.nullable());
272:
273: }
274:
275: // store PK field separataly
276: if (a instanceof Id) {
277: this .pkFields.add(aktProperty);
278: PrimaryKeyInfo info = new PrimaryKeyInfo(((Id) a));
279: this .extractGenerator(propertyAnnotations, info);
280: this .pkFieldInfo.put(aktProperty, info);
281: GlobalPrimaryKeyStore.getStore().put(classToInspect,
282: pkFieldInfo);
283: }
284:
285: if (a instanceof EmbeddedId) {
286: this .pkFields.add(aktProperty);
287: PrimaryKeyInfo info = new PrimaryKeyInfo(
288: ((EmbeddedId) a));
289: this .extractGenerator(propertyAnnotations, info);
290: this .pkFieldInfo.put(aktProperty, info);
291: }
292:
293: if (a instanceof EmbeddedId) {
294: this .pkFields.add(aktProperty);
295: PrimaryKeyInfo info = new PrimaryKeyInfo(
296: ((EmbeddedId) a));
297: this .pkFieldInfo.put(aktProperty, info);
298: }
299:
300: // relations
301: if (a instanceof OneToMany) {
302: final OneToMany aC = (OneToMany) a;
303: // put this property to the global store
304: GlobalRelationStore.getStore().put(classToInspect,
305: aktProperty);
306: String mappedBy = aC.mappedBy();
307:
308: if (aktProperty.getGenericTypeClass() != null) {
309:
310: // the n side> source side is a collection
311: Class<Object> ty = aktProperty
312: .getGenericTypeClass();
313: Property relProp = RelationPropertyResolver
314: .findAttributeForRelationAtOtherSide(ty,
315: mappedBy);
316: final OneToManyReleation o2mReleation = new OneToManyReleation(
317: classToInspect, ty, aktProperty, relProp,
318: aC);
319:
320: aktFieldInfo.setEntityReleationInfo(o2mReleation);
321:
322: } else {
323: throw new RuntimeException(
324: "The N part (Collection) of a OneToMany collection must be Parametrized");
325: }
326:
327: } else if (a instanceof ManyToOne) {
328: final ManyToOne aC = (ManyToOne) a;
329: // put this property to the global store
330: GlobalRelationStore.getStore().put(classToInspect,
331: aktProperty);
332:
333: Property relProp = RelationPropertyResolver
334: .findAttributeForRelationAtOtherSide(aktProperty);
335: // the one side > the target class is declaring class
336: final ManyToOneReleation o2mReleation = new ManyToOneReleation(
337: classToInspect, relProp.getDeclaringClass(),
338: aktProperty, relProp, aC);
339:
340: aktFieldInfo.setEntityReleationInfo(o2mReleation);
341: if (joinColumnName == null) {
342: // No JoinColumn annotation seen (yet), set default
343: // Mark as being not set, by setting it explicitly to null,
344: // Note that this value might be overwritten by a JoinColumn annotation.
345: aktFieldInfo.setDbName(null);
346: }
347: } else if (a instanceof JoinColumn) {
348: JoinColumn joinColumnAnnotation = (JoinColumn) a;
349: joinColumnName = joinColumnAnnotation.name();
350: // put this property to the global store
351: GlobalRelationStore.getStore().put(classToInspect,
352: aktProperty); // I guess this is useless...
353: }
354:
355: // TODO create one2one introspection
356:
357: }
358:
359: if (!isTransient) {
360: final Property toAdd = aktProperty;
361: this .persitentProperties.add(toAdd);
362: this .fieldInfo.put(toAdd, aktFieldInfo);
363: }
364:
365: if (joinColumnName != null) {
366: // Must pass join column info to relation info; can't be done while processing the
367: // JoinColumn annotation, as it might be processed before the ManyToOne.
368: if (aktFieldInfo.getEntityReleationInfo() != null) {
369: if (aktFieldInfo.getEntityReleationInfo() instanceof ManyToOneReleation) { // TODO (Pd): unify this later with other relation types
370: aktFieldInfo.setDbName(joinColumnName);
371: }
372: } else {
373: // Later, this should be an exception, but as long as we do not process all relation types (correctly),
374: // just log as warning.
375: getLogger().warn(
376: "Detected joinColumn annotation without relation annotation (joinColumn="
377: + joinColumnName + ")");
378: }
379: }
380: }
381:
382: /**
383: * Returns the representingClass.
384: *
385: * @return Returns the representingClass.
386: */
387: public Class<T> getRepresentingClass() {
388: return this .representingClass;
389: }
390:
391: protected void extractGenerator(Annotation[] propertyAnnotations,
392: PrimaryKeyInfo info) {
393: for (Annotation current : propertyAnnotations) {
394: if (current instanceof GeneratedValue) {
395: info.setGenValue((GeneratedValue) current);
396: break;
397: }
398: }
399: }
400:
401: /**
402: * Returns the logger for this class.
403: * @return
404: */
405: abstract protected Logger getLogger();
406: }
|