001: /*
002: * Copyright 2006 Davide Deidda
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: /*
018: * MetadataInspector.java
019: *
020: * Created on 9 aprile 2005, 8.12
021: */
022: package it.biobytes.ammentos.util;
023:
024: import static it.biobytes.ammentos.FieldTypeEnum.AUTO;
025: import static it.biobytes.ammentos.FieldTypeEnum.ENTITY;
026: import it.biobytes.ammentos.Field;
027: import it.biobytes.ammentos.FieldType;
028: import it.biobytes.ammentos.FieldTypeEnum;
029: import it.biobytes.ammentos.ListField;
030: import it.biobytes.ammentos.MapField;
031: import it.biobytes.ammentos.Metadata;
032: import it.biobytes.ammentos.OnDelete;
033: import it.biobytes.ammentos.OnLoad;
034: import it.biobytes.ammentos.OnSave;
035: import it.biobytes.ammentos.PersistenceException;
036: import it.biobytes.ammentos.PersistentEntity;
037: import it.biobytes.ammentos.PersistentField;
038: import it.biobytes.ammentos.PersistentList;
039: import it.biobytes.ammentos.PersistentMap;
040: import it.biobytes.ammentos.fieldtypes.EnumType;
041: import it.biobytes.ammentos.fieldtypes.FactoryType;
042: import it.biobytes.ammentos.validation.Validator;
043:
044: import java.lang.reflect.Method;
045: import java.lang.reflect.ParameterizedType;
046: import java.lang.reflect.Type;
047: import java.util.HashMap;
048: import java.util.Map;
049: import java.util.logging.Logger;
050:
051: /**
052: *
053: * @author davide
054: */
055: public class MetadataInspector {
056:
057: private Logger m_logger = Logger.getLogger("ammentos");
058: /**
059: * Used to perform lookups of wrapper classes (java.lang.Double,
060: * java.lang.Integer etc.) to the corresponding primitive types
061: * (double, int). Doubles can then be mapped automatically to DoubleType,
062: * Integers to IntegerTypes etc.
063: */
064: private Map wrapperTypeMap;
065:
066: /** Creates a new instance of MetadataInspector */
067: public MetadataInspector() {
068: // Create wrapper type map - used by deductFieldType()
069: wrapperTypeMap = new HashMap();
070: wrapperTypeMap.put(Boolean.class, Boolean.TYPE);
071: wrapperTypeMap.put(Double.class, Double.TYPE);
072: wrapperTypeMap.put(Float.class, Float.TYPE);
073: wrapperTypeMap.put(Integer.class, Integer.TYPE);
074: wrapperTypeMap.put(Long.class, Long.TYPE);
075: wrapperTypeMap.put(Short.class, Short.TYPE);
076: }
077:
078: public Metadata loadMetadata(Class c) throws PersistenceException {
079: Metadata res = new Metadata(c);
080:
081: // Initializing entity
082: PersistentEntity pea = (PersistentEntity) c
083: .getAnnotation(PersistentEntity.class);
084: if (pea == null) {
085: throw new PersistenceException("Class " + c
086: + " is not persistent");
087: } else {
088: res.setSourceDomain(pea.sourceDomain());
089:
090: // If target domain is not specified is considered the same as source domain
091: String target = pea.targetDomain();
092: res.setTargetDomain("".equals(target) ? pea.sourceDomain()
093: : target);
094:
095: res.setCreatingSupported(pea.createSupported());
096: res.setEditingSupported(pea.editSupported());
097: res.setDeletingSupported(pea.deleteSupported());
098: res.setPersistenceMode(pea.persistenceMode());
099: if (!"".equals(pea.validator())) {
100: try {
101: Validator v = (Validator) Class.forName(
102: pea.validator()).newInstance();
103: res.setValidator(v);
104: } catch (Exception e) {
105: m_logger.severe("Invalid validator class: "
106: + pea.validator());
107: }
108: }
109: }
110:
111: // Initializing fields
112: java.lang.reflect.Field[] fields = c.getDeclaredFields();
113: for (java.lang.reflect.Field f : fields) {
114: // Looking for persistent fields
115: PersistentField pa = f.getAnnotation(PersistentField.class);
116: if (pa != null) {
117: Field field = generateField(pa, f);
118: res.addField(field);
119: }
120:
121: // Looking for list fields
122: PersistentList pl = f.getAnnotation(PersistentList.class);
123: if (pl != null) {
124: ListField listField = generateListField(pl, f);
125: res.addCollectionField(listField);
126: }
127:
128: // Looking for map fields
129: PersistentMap pm = f.getAnnotation(PersistentMap.class);
130: if (pm != null) {
131: MapField mapField = generateMapField(pm, f);
132: res.addCollectionField(mapField);
133: }
134: }
135:
136: // Setting primary key
137: String[] pkeys = pea.primaryKey().split(",");
138: if (pkeys.length > 1) {
139: // Trimming values
140: for (int i = 0; i < pkeys.length; i++) {
141: pkeys[i] = pkeys[i].trim();
142: }
143:
144: res.setPrimaryKeys(pkeys);
145: m_logger.info("Set multiple primary key: " + pkeys);
146: } else {
147: res.setPrimaryKey(pea.primaryKey(), pea
148: .primaryKeyExternal());
149: m_logger.info("Set primary key: " + pea.primaryKey());
150: }
151:
152: // Setting parent key
153: res.setSuperKey(pea.super Key());
154: m_logger.info("Setted super key: " + pea.super Key());
155:
156: // Loading handlers
157: for (Method m : c.getDeclaredMethods()) {
158: OnSave handler = m.getAnnotation(OnSave.class);
159: if (handler != null) {
160: switch (handler.value()) {
161: case BEFORE:
162: res.addBeforeSaveHandler(m);
163: break;
164: case AFTER:
165: res.addAfterSaveHandler(m);
166: break;
167: case ALWAYS:
168: res.addBeforeSaveHandler(m);
169: res.addAfterSaveHandler(m);
170: break;
171: }
172: }
173: }
174:
175: for (Method m : c.getDeclaredMethods()) {
176: OnDelete handler = m.getAnnotation(OnDelete.class);
177: if (handler != null) {
178: switch (handler.value()) {
179: case BEFORE:
180: res.addBeforeDeleteHandler(m);
181: break;
182: case AFTER:
183: res.addAfterDeleteHandler(m);
184: break;
185: case ALWAYS:
186: res.addBeforeDeleteHandler(m);
187: res.addAfterDeleteHandler(m);
188: break;
189: }
190: }
191: }
192:
193: for (Method m : c.getDeclaredMethods()) {
194: OnLoad handler = m.getAnnotation(OnLoad.class);
195: if (handler != null) {
196: res.addAfterLoadHandler(m);
197: }
198: }
199: return res;
200: }
201:
202: private Field generateField(PersistentField p,
203: java.lang.reflect.Field field) throws PersistenceException {
204: FieldType ft = null;
205:
206: // Inspecting FieldType
207: // 1) First checking typeClass declaration
208: String typeClass = p.typeClass();
209: if (!"".equals(typeClass)) {
210: try {
211: ft = (FieldType) Class.forName(typeClass).newInstance();
212: } catch (Exception e) {
213: throw new PersistenceException("Class <<" + typeClass
214: + ">> not found");
215: }
216: } // Else checking type decalration
217: else if (p.type() != AUTO) {
218: ft = p.type().getFieldType();
219: } // Else deducting the type from field declaration
220: else {
221: ft = deductFieldType(field);
222: }
223:
224: // Inspecting field name
225: String fn = p.fieldName();
226: if ("".equals(fn)) {
227: fn = deductFieldName(field);
228: }
229:
230: // Inspecting field description
231: String fd = p.description();
232: if ("".equals(fd)) {
233: fd = deductFieldDescription(field);
234: }
235:
236: Field res = new Field(fn, fd, ft, field);
237:
238: res.setAutomatic(p.automatic());
239: res.setAutomaticType(p.automaticType());
240: res.setDynamic(p.dynamic());
241: res.setExternal(p.external());
242: res.setVisible(p.visible());
243: res.setUseCache(p.useCache());
244: if (!"".equals(p.size())) {
245: res.setSize(p.size());
246: }
247: return res;
248: }
249:
250: private ListField generateListField(PersistentList pl,
251: java.lang.reflect.Field field) throws PersistenceException {
252:
253: if (!field.getType().isAssignableFrom(java.util.List.class)) {
254: throw new PersistenceException(
255: "java.util.List expected as declaring class "
256: + "for PersistentList " + field.getName()
257: + " in class " + field.getDeclaringClass());
258: }
259:
260: ListField res = null;
261: Class itemsClass = null;
262:
263: // First look for itemsClass declaration
264: if (!"".equals(pl.itemsClass())) {
265: try {
266: itemsClass = Class.forName(pl.itemsClass());
267: } catch (Exception e) {
268: throw new PersistenceException("Invalid items class: "
269: + pl.itemsClass());
270: }
271: } // Else tries deducting the items class from declaration
272: else {
273: try {
274: Type type = field.getGenericType();
275: ParameterizedType pType = (ParameterizedType) type;
276: itemsClass = (Class<?>) pType.getActualTypeArguments()[0];
277: } catch (Exception e) {
278: throw new PersistenceException(
279: "Unable to find items class for field "
280: + field.getName()
281: + " in class "
282: + field.getDeclaringClass().getName()
283: + ". Ensure you are declaring valid type parameters, or use itemsClass"
284: + " annotation attribute instead.");
285: }
286: }
287:
288: String strQuery = pl.query();
289:
290: res = new ListField(itemsClass, field, strQuery);
291: res.setSourceDomain(pl.sourceDomain());
292: res.setCascadeOnSave(pl.cascadeOnSave());
293: res.setCascadeOnDelete(pl.cascadeOnDelete());
294: res.setDeleteOnRemove(pl.deleteOnRemove());
295: return res;
296: }
297:
298: private MapField generateMapField(PersistentMap pm,
299: java.lang.reflect.Field field) throws PersistenceException {
300:
301: if (!field.getType().isAssignableFrom(java.util.Map.class)) {
302: System.out.println("Lancio la eccezione mappa");
303: throw new PersistenceException(
304: "java.util.Map expected as declaring class "
305: + "for PersistentMap " + field.getName()
306: + " in class " + field.getDeclaringClass());
307: }
308:
309: MapField res = null;
310: Class itemsClass = null;
311:
312: // First look for itemsClass declaration
313: if (!"".equals(pm.itemsClass())) {
314: try {
315: itemsClass = Class.forName(pm.itemsClass());
316: } catch (Exception e) {
317: throw new PersistenceException("Invalid items class: "
318: + pm.itemsClass());
319: }
320: } // Else tries deducting the items class from declaration
321: else {
322: try {
323: Type type = field.getGenericType();
324: ParameterizedType pType = (ParameterizedType) type;
325: itemsClass = (Class<?>) pType.getActualTypeArguments()[1];
326: } catch (Exception e) {
327: throw new PersistenceException(
328: "Unable to find items class for field "
329: + field.getName()
330: + " in class "
331: + field.getDeclaringClass().getName()
332: + ". Ensure you are declaring valid type parameters, or use itemsClass"
333: + " annotation attribute instead.");
334: }
335: }
336:
337: String strQuery = pm.query();
338:
339: res = new MapField(itemsClass, field, strQuery);
340: res.setSourceDomain(pm.sourceDomain());
341: res.setCascadeOnSave(pm.cascadeOnSave());
342: res.setCascadeOnDelete(pm.cascadeOnDelete());
343: res.setDeleteOnRemove(pm.deleteOnRemove());
344: if (!"".equals(pm.keyField())) {
345: res.setKeyFieldName(pm.keyField());
346: }
347: return res;
348: }
349:
350: /**
351: * Tries deducting the fieldType of a declared field from its declaring class.
352: */
353: private FieldType deductFieldType(java.lang.reflect.Field f) {
354: FieldType res = null;
355:
356: Class fType = f.getType();
357: for (FieldTypeEnum fte : FieldTypeEnum.values()) {
358: FieldType ft = fte.getFieldType();
359: if (ft == null) {
360: continue;
361: }
362:
363: if (fType == ft.getMappedClass()) {
364: res = ft;
365: break;
366: }
367:
368: if (wrapperTypeMap.containsKey(fType)
369: && wrapperTypeMap.get(fType) == ft.getMappedClass()) {
370: res = ft;
371: break;
372: }
373: }
374:
375: /**
376: * Next check to see if the field is enum type.
377: */
378: if (res == null && fType.isEnum()) {
379: try {
380: res = new EnumType(fType);
381: } catch (Exception e) {
382: /* Empty */
383: }
384: }
385:
386: /**
387: * Next let generic FactoryType determine if the type adheres to its
388: * declared reflections-based API. If yes, use that instead.
389: */
390: if (res == null && FactoryType.isFactoryType(fType)) {
391: try {
392: res = new FactoryType(fType);
393: } catch (Exception e) {
394: /* Empty */
395: }
396: }
397:
398: if (res == null) {
399: res = ENTITY.getFieldType();
400: }
401:
402: return res;
403: }
404:
405: private String deductFieldName(java.lang.reflect.Field f) {
406: return f.getName();
407: }
408:
409: private String deductFieldDescription(java.lang.reflect.Field f) {
410: return f.getName();
411: }
412: }
|