package com.google.code.joto.ast.valueholder.util; import java.lang.ref.SoftReference; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.util.Collection; import java.util.Date; import java.util.IdentityHashMap; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.code.joto.ast.valueholder.ValueHolderAST.AbstractObjectValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.CollectionValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.FieldValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.ImmutableObjectValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.MapValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.ObjectValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.PrimitiveArrayValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.PrimitiveFieldValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.RefArrayValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.RefFieldValueHolder; import com.google.code.joto.reflect.ReflectUtils; import com.thoughtworks.xstream.converters.reflection.ReflectionProvider; /** * Convert a "real" jvm Object, into a generic AST tree instance AbstractObjectValueHolder * (a sort of Map<Map<Key,Value>> representation) * */ public class ObjectToValueHolderBuilder { private static Logger log = LoggerFactory.getLogger(ObjectToValueHolderBuilder.class); private Map<Object,AbstractObjectValueHolder> identityMap = new IdentityHashMap<Object,AbstractObjectValueHolder>(); // ------------------------------------------------------------------------- public ObjectToValueHolderBuilder() { super(); } // ------------------------------------------------------------------------- public Map<Object,AbstractObjectValueHolder> getResultMap() { return identityMap; } public AbstractObjectValueHolder buildValue(Object obj) { if (obj == null) { return null; // NullValueHolder.getInstance(); } if (obj.getClass().isArray()) { Class<?> compType = obj.getClass().getComponentType(); if (compType.isPrimitive()) { return casePrimitiveArray(obj); } else { return caseRefArray((Object[]) obj); } } if (obj instanceof Collection) { return caseCollection((Collection<?>) obj); } else if (obj instanceof Map) { return caseMap((Map<?,?>) obj); } else if (ReflectUtils.isPrimitiveWrapperType(obj.getClass()) || obj instanceof String) { return caseImmutableObject(obj); } else { return caseObject(obj); } } protected ObjectValueHolder caseObject(Object obj) { ObjectValueHolder res = (ObjectValueHolder) identityMap.get(obj); if (res == null) { res = new ObjectValueHolder(obj.getClass()); identityMap.put(obj, res); caseObject(obj, res); } return res; } protected ImmutableObjectValueHolder caseImmutableObject(Object obj) { ImmutableObjectValueHolder res = (ImmutableObjectValueHolder) identityMap.get(obj); if (res == null) { res = new ImmutableObjectValueHolder(obj); identityMap.put(obj, res); } return res; } @SuppressWarnings({ "rawtypes", "unchecked" }) protected PrimitiveArrayValueHolder casePrimitiveArray(Object obj) { PrimitiveArrayValueHolder res = (PrimitiveArrayValueHolder) identityMap.get(obj); if (res == null) { int len = Array.getLength(obj); res = new PrimitiveArrayValueHolder(obj.getClass(), len); identityMap.put(obj, res); casePrimitiveArray(obj, res); } return res; } protected RefArrayValueHolder caseRefArray(Object[] obj) { if (obj == null) { return null; } RefArrayValueHolder res = (RefArrayValueHolder) identityMap.get(obj); if (res == null) { res = new RefArrayValueHolder(obj.getClass(), obj.length); identityMap.put(obj, res); caseRefArray(obj, res); } return res; } protected CollectionValueHolder caseCollection(Collection<?> obj) { if (obj == null) { return null; } CollectionValueHolder res = (CollectionValueHolder) identityMap.get(obj); if (res == null) { res = new CollectionValueHolder(); // obj.getClass()); identityMap.put(obj, res); caseCollection(obj, res); } return res; } protected MapValueHolder caseMap(Map<?,?> obj) { MapValueHolder res = (MapValueHolder) identityMap.get(obj); if (res == null) { res = new MapValueHolder(); identityMap.put(obj, res); caseMap(obj, res); } return res; } // ------------------------------------------------------------------------- protected void caseObject(final Object obj, final ObjectValueHolder node) { // BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass()); // final Class<?> objClass = obj.getClass(); if (obj instanceof SoftReference) { return; } if (obj instanceof StackTraceElement) { return; } if (obj instanceof ClassLoader) { return; } if (obj instanceof java.security.ProtectionDomain) { return; } String className = obj.getClass().getName(); if (className.startsWith("org.apache.log4j.")) { return; //?? } else if (className.startsWith("org.hibernate.") || className.startsWith("net.sf.hibernate.")) { return; } else if (className.startsWith("org.springframework.aop.") || className.startsWith("org.springframework.transaction.")) { return; //?? } if (obj instanceof java.lang.reflect.InvocationHandler || obj instanceof java.lang.reflect.Proxy) { return; } if (className.startsWith("com.google.code.joto.eventrecorder.")) { return; } if (className.startsWith("GeneratedMethodAccessor")) { return; } if (className.indexOf("EnhancerByCGLIB") != -1) { return; } final ReflectionProvider reflectionProvider = ReflectUtils.getReflectionProvider(); if (obj instanceof java.util.Date || obj instanceof java.sql.Date ) { Field timeField = reflectionProvider.getField(java.util.Date.class, "fastTime"); Date dateObj = (Date) obj; long time = dateObj.getTime(); FieldValueHolder fvh = node.getFieldValue(timeField); PrimitiveFieldValueHolder fvh2 = (PrimitiveFieldValueHolder) fvh; fvh2.setValue(time); return; } reflectionProvider.visitSerializableFields(obj, new ReflectionProvider.Visitor() { @SuppressWarnings("rawtypes") @Override public void visit(String fieldName, Class fieldType, Class definedIn, Object value ) { if (fieldName.equals("hashCode")) { return; } if (fieldName.equals("_objParent")) { return; } String fieldClassName = fieldType.getClass().getName(); if (fieldClassName.indexOf("ByCGLIB") != -1) { return; } Field field = reflectionProvider.getField(definedIn, fieldName); FieldValueHolder fvh = node.getFieldValue(field); if (fieldType.isPrimitive()) { PrimitiveFieldValueHolder fvh2 = (PrimitiveFieldValueHolder) fvh; fvh2.setValue(value); } else { // HACK?? if (fieldClassName.startsWith("org.hibernate.") || fieldClassName.startsWith("net.sf.hibernate.")) { return; } // System.out.println("obj:" + obj.getClass() + " field:" + fieldName + " (" + fieldType + ")"); RefFieldValueHolder fvh2 = (RefFieldValueHolder) fvh; //.. recurse AbstractObjectValueHolder valueHolder = buildValue(value); fvh2.setTo(valueHolder); } } }); } @SuppressWarnings("unchecked") protected void casePrimitiveArray(final Object obj, @SuppressWarnings("rawtypes") final PrimitiveArrayValueHolder valueHolder) { int len = Array.getLength(obj); Class<?> compType = obj.getClass().getComponentType(); if (compType == Boolean.TYPE) { boolean[] array = (boolean[]) obj; for (int i = 0; i < len; i++) { valueHolder.setValueAt(i, array[i]); } } else if (compType == Byte.TYPE) { byte[] array = (byte[]) obj; for (int i = 0; i < len; i++) { valueHolder.setValueAt(i, array[i]); } } else if (compType == Character.TYPE) { char[] array = (char[]) obj; for (int i = 0; i < len; i++) { valueHolder.setValueAt(i, array[i]); } } else if (compType == Short.TYPE) { short[] array = (short[]) obj; for (int i = 0; i < len; i++) { valueHolder.setValueAt(i, array[i]); } } else if (compType == Integer.TYPE) { int[] array = (int[]) obj; for (int i = 0; i < len; i++) { valueHolder.setValueAt(i, array[i]); } } else if (compType == Long.TYPE) { long[] array = (long[]) obj; for (int i = 0; i < len; i++) { valueHolder.setValueAt(i, array[i]); } } else if (compType == Float.TYPE) { float[] array = (float[]) obj; for (int i = 0; i < len; i++) { valueHolder.setValueAt(i, array[i]); } } else if (compType == Double.TYPE) { double[] array = (double[]) obj; for (int i = 0; i < len; i++) { valueHolder.setValueAt(i, array[i]); } } else { throw new RuntimeException("unrecognized primitive array " + compType); } } protected void caseRefArray(final Object[] obj, final RefArrayValueHolder valueHolder) { int len = obj.length; for (int i = 0; i < len; i++) { AbstractObjectValueHolder eltVH = buildValue(obj[i]); valueHolder.setValueAt(i, eltVH); } } protected void caseCollection(Collection<?> obj, CollectionValueHolder valueHolder) { String className = obj.getClass().getName(); if (className.startsWith("org.hibernate.") || className.startsWith("net.sf.hibernate.")) { return; // NOT SUPPORTED YET (can have side-effect of lazy loading elts!!) } try { for(Object objElt : obj) { try { AbstractObjectValueHolder eltVH = buildValue(objElt); valueHolder.addRefElt(eltVH); } catch(Exception ex) { log.error("Failed to build ValueHolder corresponding to collection elt ... ignore!", ex); } } } catch(Exception ex) { log.error("Failed to build ValueHolder corresponding to collection ... ignore!", ex); } } protected <K,T> void caseMap(Map<K,T> obj, MapValueHolder valueHolder) { try { for(Map.Entry<K,T> entry : obj.entrySet()) { try { AbstractObjectValueHolder keyVH = buildValue(entry.getKey()); AbstractObjectValueHolder valueVH = buildValue(entry.getValue()); valueHolder.putEntry(keyVH, valueVH); } catch(Exception ex) { log.error("Failed to build ValueHolder corresponding to Map entry ... ignore!", ex); } } } catch(Exception ex) { log.error("Failed to build ValueHolder corresponding to Map ... ignore!", ex); } } }