001: package com.completex.objective.components.persistency.mapper.impl;
002:
003: import com.completex.objective.components.log.Log;
004: import com.completex.objective.components.persistency.CompoundPersistentObject;
005: import com.completex.objective.components.persistency.Link;
006: import com.completex.objective.components.persistency.MetaColumn;
007: import com.completex.objective.components.persistency.MetaTable;
008: import com.completex.objective.components.persistency.OdalRuntimePersistencyException;
009: import com.completex.objective.components.persistency.PersistentObject;
010: import com.completex.objective.components.persistency.Record;
011: import com.completex.objective.components.persistency.core.impl.LinkIterator;
012: import com.completex.objective.components.persistency.mapper.AdHocPoBeanConverter;
013: import com.completex.objective.components.persistency.mapper.Mapper;
014: import com.completex.objective.components.persistency.mapper.OdalMappingRuntimeException;
015: import com.completex.objective.components.persistency.mapper.RequestContext;
016: import com.completex.objective.components.persistency.type.TypeConverter;
017: import com.completex.objective.components.persistency.type.TypeConverterImpl;
018: import com.completex.objective.tools.generators.NameHelper;
019:
020: import java.beans.BeanInfo;
021: import java.beans.IntrospectionException;
022: import java.beans.Introspector;
023: import java.beans.PropertyDescriptor;
024: import java.lang.reflect.InvocationTargetException;
025: import java.lang.reflect.Method;
026: import java.util.Collection;
027: import java.util.Iterator;
028: import java.util.Map;
029:
030: /**
031: * Default AdHocPoBeanConverter implementation.
032: * Converts persistent objects to corresponding beans using implicit mapping based on POs column names and the bean
033: * field names.
034: *
035: * @author Gennady Krizhevsky
036: * @see AdHocPoBeanConverter
037: */
038: public class AdHocPoBeanConverterImpl implements AdHocPoBeanConverter {
039:
040: private Log logger = Log.NULL_LOGGER;
041:
042: private boolean notOverwriteWithNulls;
043:
044: private TypeConverter typeConverter = new TypeConverterImpl();
045:
046: private boolean useFieldNameExtrapolationOnFail = true;
047: private boolean useFieldNameExtrapolationAlways = false;
048: private boolean tolerateTypeMismatch = true;
049:
050: private boolean mapPositionally;
051:
052: private DynamicMapperImpl mapper;
053:
054: public AdHocPoBeanConverterImpl(Mapper mapper) {
055: setMapper(mapper);
056: }
057:
058: /**
059: * @param useFieldNameExtrapolationOnFail
060: * true name extapolation is to be used when bean name corresponding
061: * to the persistent object one is not found. Extrapolation can convert names from usual database
062: * to the Java one. For instance, "NAME1_NAME2" will be converted to "name1Nae2" and after that matched. Default is
063: * true.
064: * @param useFieldNameExtrapolationAlways
065: * true name extapolation is to be used all the time. Setting it to true
066: * superceeds the value assigned to the useFieldNameExtrapolationOnFail parameter. Default is false.
067: * @param tolerateTypeMismatch if true no exception will be thrown when type conversion fails.
068: */
069: public AdHocPoBeanConverterImpl(
070: boolean useFieldNameExtrapolationOnFail,
071: boolean useFieldNameExtrapolationAlways,
072: boolean tolerateTypeMismatch) {
073: this .useFieldNameExtrapolationOnFail = useFieldNameExtrapolationOnFail;
074: this .useFieldNameExtrapolationAlways = useFieldNameExtrapolationAlways;
075: this .tolerateTypeMismatch = tolerateTypeMismatch;
076: }
077:
078: public TypeConverter getTypeConverter() {
079: return typeConverter;
080: }
081:
082: /**
083: * Sets type converter that allows to control value by value conversion
084: *
085: * @param typeConverter
086: */
087: public void setTypeConverter(TypeConverter typeConverter) {
088: this .typeConverter = typeConverter;
089: }
090:
091: public boolean isUseFieldNameExtrapolationOnFail() {
092: return useFieldNameExtrapolationOnFail;
093: }
094:
095: public void setUseFieldNameExtrapolationOnFail(
096: boolean useFieldNameExtrapolationOnFail) {
097: this .useFieldNameExtrapolationOnFail = useFieldNameExtrapolationOnFail;
098: }
099:
100: public boolean isMapPositionally() {
101: return mapPositionally;
102: }
103:
104: public void setMapPositionally(boolean mapPositionally) {
105: this .mapPositionally = mapPositionally;
106: }
107:
108: /**
109: * @see AdHocPoBeanConverter#convert(PersistentObject po, Class beanClass)
110: */
111: public final Object convert(PersistentObject po, Class beanClass) {
112: return convert(po, beanClass, null);
113: }
114:
115: public final Object convert(PersistentObject po, Class beanClass,
116: Map typeMap) {
117: return convert(null, po, beanClass, typeMap);
118: }
119:
120: protected final Object convert(String valuePath,
121: PersistentObject po, Class beanClass, Map typeMap) {
122: if (po == null) {
123: return null;
124: }
125: PersistentObject preConvertedPo = doPreConvert(valuePath, po,
126: typeMap);
127: Object bean = doConvert(valuePath, preConvertedPo, beanClass,
128: typeMap);
129: bean = doPostConvert(valuePath, preConvertedPo, bean, typeMap);
130: return bean;
131: }
132:
133: protected PersistentObject doPreConvert(String valuePath,
134: PersistentObject po, Map typeMap) {
135: return po;
136: }
137:
138: protected Object doPostConvert(String valuePath,
139: PersistentObject po, Object bean, Map typeMap) {
140: return bean;
141: }
142:
143: protected Object doConvert(String valuePath,
144: PersistentObject clonedPo, Class beanClass, Map typeMap) {
145: RequestContext context = new RequestContext(typeMap);
146: return toBean0(valuePath, clonedPo, beanClass, context);
147: }
148:
149: protected Object toBean0(String valuePath, PersistentObject po,
150: Class beanClass, RequestContext context) {
151: Object convertedBean;
152: BeanInfo beanInfo = extractBeanInfo(beanClass);
153: Object bean = objectNewInstance(beanClass, po);
154: convertedBean = doConvertToBean(valuePath, beanInfo, po, bean,
155: context, beanClass);
156: return convertedBean;
157: }
158:
159: private BeanInfo extractBeanInfo(Class beanClass) {
160: BeanInfo beanInfo;
161: try {
162: beanInfo = Introspector.getBeanInfo(beanClass);
163: } catch (IntrospectionException e) {
164: throw new OdalMappingRuntimeException(e);
165: }
166: return beanInfo;
167: }
168:
169: protected Object doConvertToBean(String valuePath,
170: BeanInfo beanInfo, PersistentObject po, Object bean,
171: RequestContext context, Class beanClass) {
172: if (po.flattened()) {
173: po.unflatten();
174: }
175: Record record = po.record();
176: MetaTable poInfo = record.getTable();
177: PropertyDescriptor[] beanDescriptors = beanInfo
178: .getPropertyDescriptors();
179: for (int i = 0; i < poInfo.size(); i++) {
180: MetaColumn metaColumn = poInfo.getColumn(i);
181: String childValuePath = addValuePath(valuePath, po,
182: metaColumn.getColumnAlias());
183: context.setParentBean(bean);
184: assignValueCtl(childValuePath, context, bean,
185: beanDescriptors, po, metaColumn, beanClass);
186: }
187: //
188: // Process children:
189: //
190: processChildren(valuePath, po, beanDescriptors, bean, context);
191:
192: return bean;
193: }
194:
195: private void processChildren(String valuePath, PersistentObject po,
196: PropertyDescriptor[] beanDescriptors, Object bean,
197: RequestContext context) {
198: Link parentLink = po.record().toLink();
199: if (parentLink.hasChildren()) {
200: processChildren(valuePath, parentLink, beanDescriptors,
201: bean, context);
202: }
203: }
204:
205: private void processChildren(String valuePath, Link parentLink,
206: PropertyDescriptor[] beanDescriptors, Object bean,
207: RequestContext context) {
208: for (LinkIterator it = parentLink.linkIterator(); it.hasNext();) {
209: Link child = it.nextLink();
210: processChild(valuePath, child, beanDescriptors, bean,
211: context);
212: }
213: }
214:
215: private void processChild(String valuePath, Link child,
216: PropertyDescriptor[] beanDescriptors, Object bean,
217: RequestContext context) {
218: String name = child.getName();
219:
220: Object result = child.getResult();
221: boolean isCollection = result instanceof Collection;
222:
223: for (int i = 0; i < beanDescriptors.length; i++) {
224: PropertyDescriptor beanDescriptor = beanDescriptors[i];
225: String beanFieldName = beanDescriptor.getName();
226: String extrapolatedSetterName = getExtrapolatedSetterName(
227: name, beanFieldName, isCollection);
228: Method writeMethod = beanDescriptor.getWriteMethod();
229: if (writeMethod != null
230: && extrapolatedSetterName.equals(writeMethod
231: .getName())) {
232: if (result instanceof Collection) {
233: processCollection(valuePath, ((Collection) result),
234: beanDescriptor, bean, context, name);
235: } else if (result instanceof PersistentObject) {
236: PersistentObject po = (PersistentObject) result;
237: Object childBean = convertPersistentObject(
238: valuePath, po, context, name);
239: try {
240: writeMethod.invoke(bean,
241: new Object[] { childBean });
242: } catch (Exception e) {
243: handleException(e, valuePath);
244: }
245: }
246: }
247: }
248: }
249:
250: private void processCollection(String valuePath,
251: Collection collection, PropertyDescriptor beanDescriptor,
252: Object currentBean, RequestContext context, String name) {
253: Method beanReadMethod = beanDescriptor.getReadMethod();
254: if (beanReadMethod != null) {
255: Object beanPropertyValue = BasicDynamicMapperImpl
256: .extractPropertyValue(beanReadMethod, currentBean);
257: if (beanPropertyValue instanceof Collection) {
258: Collection beanCollection = (Collection) beanPropertyValue;
259: for (Iterator iterator = collection.iterator(); iterator
260: .hasNext();) {
261: Object value = iterator.next();
262: if (value instanceof PersistentObject) {
263: Object bean = convertPersistentObject(
264: valuePath, ((PersistentObject) value),
265: context, name);
266: if (bean != null) {
267: beanCollection.add(bean);
268: }
269: }
270: }
271: }
272: }
273: }
274:
275: private Object convertPersistentObject(String valuePath,
276: PersistentObject po, RequestContext context, String name) {
277: Object bean = null;
278: if (context.typeMappingExists(po.mappingType())) {
279: Class beanClass = context.getByFieldName(name);
280: bean = toBean0(valuePath, po, beanClass, context);
281: } else if (mapper != null
282: && mapper.mappingExists(null, po.getClass())) {
283: bean = mapper.convertPoToBean(po, false);
284: }
285: return bean;
286: }
287:
288: protected void assignValueCtl(String childValuePath,
289: RequestContext context, Object currentBean,
290: PropertyDescriptor[] currentBeanDescriptors,
291: Object currentPo, MetaColumn metaColumn, Class beanClass) {
292: getLogger().debug(
293: "assignValue: childValuePath = " + childValuePath);
294:
295: boolean found;
296: found = assignValue(childValuePath, context, currentBean,
297: currentBeanDescriptors, currentPo, metaColumn,
298: beanClass);
299: printNonFound(found, metaColumn, childValuePath);
300: }
301:
302: protected boolean assignValue(String valuePath,
303: RequestContext context, Object currentBean,
304: PropertyDescriptor[] currentBeanDescriptors,
305: Object currentPo, MetaColumn currentPoDescriptor,
306: Class beanClass) {
307: try {
308: boolean found;
309: String poFieldName = currentPoDescriptor.getColumnAlias();
310: String beanPredictedSetterName = getExtrapolatedSetterName(poFieldName);
311:
312: found = processBean(beanPredictedSetterName, context,
313: valuePath, currentBean, currentBeanDescriptors,
314: currentPo, currentPoDescriptor, beanClass);
315: // Fail over : if we did not find it in "normal" way - try to extrapolate the fieled name:
316: if (!found && useFieldNameExtrapolationOnFail
317: && !useFieldNameExtrapolationAlways) {
318: beanPredictedSetterName = extrapolatedSetterName(poFieldName);
319: found = processBean(beanPredictedSetterName, context,
320: valuePath, currentBean, currentBeanDescriptors,
321: currentPo, currentPoDescriptor, beanClass);
322: }
323:
324: return found;
325: } catch (Exception e) {
326: throw new OdalMappingRuntimeException(
327: "Error at valuePath : " + valuePath, e);
328: }
329: }
330:
331: private String extrapolatedSetterName(String poFieldName) {
332: return extrapolatedAccessorName(poFieldName, null, "set", false);
333: }
334:
335: private String extrapolatedAccessorName(String poFieldName,
336: String beanFieldName, String prefix, boolean isCollection) {
337: String beanPredictedSetterName;
338: beanPredictedSetterName = prefix
339: + NameHelper.javaName(poFieldName, null, null, true);
340: beanPredictedSetterName = correctCollectionName(isCollection,
341: beanFieldName, poFieldName, beanPredictedSetterName);
342: return beanPredictedSetterName;
343: }
344:
345: private String getExtrapolatedSetterName(String poFieldName) {
346: return getExtrapolatedAccessorName(poFieldName, null, "set",
347: false);
348: }
349:
350: private String getExtrapolatedSetterName(String poFieldName,
351: String beanFieldName, boolean isCollection) {
352: return getExtrapolatedAccessorName(poFieldName, beanFieldName,
353: "set", isCollection);
354: }
355:
356: private String getExtrapolatedAccessorName(String poFieldName,
357: String beanFieldName, String prefix, boolean isCollection) {
358: String beanPredictedSetterName;
359: if (useFieldNameExtrapolationAlways) {
360: beanPredictedSetterName = extrapolatedAccessorName(
361: poFieldName, beanFieldName, prefix, isCollection);
362: } else {
363: beanPredictedSetterName = prefix
364: + NameHelper.capitalizeField(poFieldName);
365: }
366:
367: beanPredictedSetterName = correctCollectionName(isCollection,
368: beanFieldName, poFieldName, beanPredictedSetterName);
369:
370: return beanPredictedSetterName;
371: }
372:
373: private String correctCollectionName(boolean isCollection,
374: String beanFieldName, String poFieldName,
375: String beanPredictedSetterName) {
376: if ((useFieldNameExtrapolationAlways || useFieldNameExtrapolationOnFail)
377: && isCollection
378: && beanFieldName != null
379: && !poFieldName.equals(beanFieldName)) {
380: if (!beanPredictedSetterName.endsWith("s")) {
381: beanPredictedSetterName += "s";
382: }
383: }
384: return beanPredictedSetterName;
385: }
386:
387: private void printNonFound(boolean found, MetaColumn metaColumn,
388: String valuePath) {
389: if (!found) {
390: getLogger().debug(
391: "Did not find setter for property name "
392: + metaColumn.getColumnAlias()
393: + " , valuePath = " + valuePath);
394: }
395: }
396:
397: protected boolean processBean(String beanPredictedSetterName,
398: RequestContext context, String valuePath,
399: Object currentBean,
400: PropertyDescriptor[] currentBeanDescriptors,
401: Object currentPo, MetaColumn metaColumn, Class beanClass)
402: throws IntrospectionException, IllegalAccessException,
403: InvocationTargetException, CloneNotSupportedException {
404:
405: if (!(currentPo instanceof PersistentObject)) {
406: getLogger().debug("processBean : seen path : " + valuePath);
407: return false;
408: }
409: context.addToSeen(valuePath);
410:
411: PersistentObject persistent = (PersistentObject) currentPo;
412:
413: Object poReturnValue = extractPoReturnValue(metaColumn,
414: persistent);
415:
416: boolean found = false;
417:
418: if (mapPositionally) {
419: int j = metaColumn.getColumnIndex();
420: PropertyDescriptor beanDescriptor = currentBeanDescriptors[j];
421: Method beanWriteMethod = beanDescriptor.getWriteMethod();
422: found = processNonCollection(beanWriteMethod,
423: beanPredictedSetterName, poReturnValue,
424: currentBean, beanClass, valuePath, context);
425: } else {
426: for (int j = 0; j < currentBeanDescriptors.length; j++) {
427: PropertyDescriptor beanDescriptor = currentBeanDescriptors[j];
428: Method beanWriteMethod = beanDescriptor
429: .getWriteMethod();
430: found = processNonCollection(beanWriteMethod,
431: beanPredictedSetterName, poReturnValue,
432: currentBean, beanClass, valuePath, context);
433: if (found) {
434: break;
435: }
436: }
437: }
438: return found;
439: }
440:
441: protected Object extractPoReturnValue(MetaColumn metaColumn,
442: PersistentObject persistent) {
443: Object poReturnValue;
444: if (mapPositionally) {
445: int columnIndex = metaColumn.getColumnIndex();
446: poReturnValue = persistent.record().getObject(columnIndex);
447: } else {
448: String columnName = metaColumn.getColumnName();
449: poReturnValue = persistent.record().getObject(columnName);
450: }
451: return poReturnValue;
452: }
453:
454: protected boolean processNonCollection(Method beanWriteMethod,
455: String beanPredictedSetterName, Object poReturnValue,
456: Object currentBean, Class beanClass, String valuePath,
457: RequestContext context) throws IllegalAccessException,
458: InvocationTargetException {
459: boolean found = false;
460: if (beanWriteMethod != null) {
461: if (mapPositionally
462: || beanPredictedSetterName.equals(beanWriteMethod
463: .getName())) {
464: Class[] beanParamTypes = beanWriteMethod
465: .getParameterTypes();
466: Class beanParamType = beanParamTypes[0];
467:
468: try {
469: if (poReturnValue instanceof PersistentObject) {
470: Object value = processPo(
471: (PersistentObject) poReturnValue,
472: beanClass, valuePath, context);
473: beanWriteMethod.invoke(currentBean,
474: new Object[] { value });
475: found = true;
476: } else {
477: Object value = typeConverter
478: .convertPoValueToBeanValue(
479: poReturnValue, beanParamType,
480: null);
481: beanWriteMethod.invoke(currentBean,
482: new Object[] { value });
483: found = true;
484: }
485: } catch (RuntimeException e) {
486: handleException(e, valuePath);
487: }
488: }
489: }
490: return found;
491: }
492:
493: protected void handleException(Exception e, String valuePath) {
494: if (e instanceof RuntimeException) {
495: if (tolerateTypeMismatch) {
496: getLogger().debug(
497: "AdHocPoMappingHandler::processNonCollection: valuePath: "
498: + valuePath + ": " + e);
499: } else {
500: throw ((RuntimeException) e);
501: }
502: } else if (e instanceof IllegalAccessException) {
503: if (tolerateTypeMismatch) {
504: getLogger().debug(
505: "AdHocPoMappingHandler::processNonCollection: valuePath: "
506: + valuePath + ": " + e);
507: } else {
508: throw new OdalRuntimePersistencyException("", e);
509: }
510: } else if (e instanceof InvocationTargetException) {
511: if (tolerateTypeMismatch) {
512: getLogger().debug(
513: "AdHocPoMappingHandler::processNonCollection: valuePath: "
514: + valuePath + ": " + e);
515: } else {
516: throw new OdalRuntimePersistencyException("", e);
517: }
518: } else {
519: if (tolerateTypeMismatch) {
520: getLogger().debug(
521: "AdHocPoMappingHandler::processNonCollection: valuePath: "
522: + valuePath + ": " + e);
523: } else {
524: throw new OdalRuntimePersistencyException("", e);
525: }
526: }
527: }
528:
529: private Object processPo(PersistentObject nestedPo,
530: Class beanClass, String valuePath, RequestContext context) {
531: getLogger().debug("---- Nested po begin ----");
532: Object value = toBean0(valuePath, nestedPo, beanClass, context);
533: getLogger().debug("---- Nested po end ----");
534: return value;
535: }
536:
537: protected Object objectNewInstance(Class beanClass,
538: Object fromObject) {
539: return objectNewInstance(beanClass);
540: }
541:
542: protected Object objectNewInstance(Class beanClass) {
543: Object bean;
544: try {
545: bean = beanClass.newInstance();
546: } catch (InstantiationException e) {
547: throw new OdalRuntimePersistencyException(
548: "InstantiationException: of " + beanClass + "; "
549: + e.getMessage());
550: } catch (IllegalAccessException e) {
551: throw new OdalRuntimePersistencyException(
552: "IllegalAccessException: to " + beanClass + "; "
553: + e.getMessage());
554: }
555: return bean;
556: }
557:
558: protected String addValuePath(String valuePath, Object po,
559: String poFieldName) {
560: valuePath = valuePath == null ? po.getClass().getName() + "#"
561: + poFieldName : valuePath + "." + poFieldName;
562: return valuePath;
563: }
564:
565: protected String addValuePath(String valuePath, int index) {
566: return valuePath + "[" + index + "]";
567: }
568:
569: /**
570: * @see AdHocPoBeanConverter#convert(com.completex.objective.components.persistency.CompoundPersistentObject, Class[])
571: */
572: public Object[] convert(CompoundPersistentObject cpo,
573: Class[] beanClasses) {
574: PersistentObject[] persistentObjects = cpo.compoundEntries();
575: Object[] beans = new Object[persistentObjects.length];
576: for (int i = 0; i < persistentObjects.length; i++) {
577: PersistentObject persistent = persistentObjects[i];
578: Object bean = persistent;
579: if (beanClasses != null && beanClasses.length > i) {
580: bean = convert(persistent, beanClasses[i]);
581: }
582: beans[i] = bean;
583: }
584: return beans;
585: }
586:
587: public Log getLogger() {
588: return logger;
589: }
590:
591: public void setLogger(Log logger) {
592: if (logger != null) {
593: this .logger = logger;
594: }
595: }
596:
597: public boolean isNotOverwriteWithNulls() {
598: return notOverwriteWithNulls;
599: }
600:
601: public void setNotOverwriteWithNulls(boolean notOverwriteWithNulls) {
602: this .notOverwriteWithNulls = notOverwriteWithNulls;
603: }
604:
605: public Mapper getMapper() {
606: return mapper;
607: }
608:
609: public void setMapper(Mapper mapper) {
610: this .mapper = (DynamicMapperImpl) mapper;
611: }
612:
613: }
|