1 package org.apache.ojb.otm.copy; 2 3 17 18 import java.lang.reflect.Array ; 19 import java.lang.reflect.Constructor ; 20 import java.lang.reflect.Field ; 21 import java.lang.reflect.Modifier ; 22 import java.util.HashMap ; 23 import java.util.HashSet ; 24 import java.util.Map ; 25 import java.util.Set ; 26 27 import org.apache.ojb.broker.PersistenceBroker; 28 import org.apache.ojb.broker.util.IdentityMapFactory; 29 30 35 public final class ReflectiveObjectCopyStrategy implements ObjectCopyStrategy 36 { 37 private static final Set FINAL_IMMUTABLE_CLASSES; 38 private static final Object [] EMPTY_OBJECT_ARRAY = new Object [0]; 39 private static final Class [] EMPTY_CLASS_ARRAY = new Class [0]; 40 private static final SerializeObjectCopyStrategy _serialize = new SerializeObjectCopyStrategy(); 41 42 static 43 { 44 FINAL_IMMUTABLE_CLASSES = new HashSet (17); 45 FINAL_IMMUTABLE_CLASSES.add(String .class); 46 FINAL_IMMUTABLE_CLASSES.add(Byte .class); 47 FINAL_IMMUTABLE_CLASSES.add(Short .class); 48 FINAL_IMMUTABLE_CLASSES.add(Integer .class); 49 FINAL_IMMUTABLE_CLASSES.add(Long .class); 50 FINAL_IMMUTABLE_CLASSES.add(Float .class); 51 FINAL_IMMUTABLE_CLASSES.add(Double .class); 52 FINAL_IMMUTABLE_CLASSES.add(Character .class); 53 FINAL_IMMUTABLE_CLASSES.add(Boolean .class); 54 } 55 56 61 public final Object copy(final Object toCopy, PersistenceBroker broker) 62 { 63 return clone(toCopy, IdentityMapFactory.getIdentityMap(), new HashMap ()); 64 } 65 66 69 private static final class ClassMetadata 70 { 71 Constructor m_noArgConstructor; 72 Field [] m_declaredFields; 73 boolean m_noArgConstructorAccessible; 74 boolean m_fieldsAccessible; 75 boolean m_hasNoArgConstructor = true; 76 } 77 78 private static Object clone(final Object toCopy, final Map objMap, final Map metadataMap) 79 { 80 84 if (objMap.containsKey(toCopy)) return objMap.get(toCopy); 85 final Class objClass = toCopy.getClass(); 86 final Object retval; 87 if (objClass.isArray()) 88 { 89 retval = handleArray(toCopy, objMap, objClass, metadataMap); 90 } 91 else if (FINAL_IMMUTABLE_CLASSES.contains(objClass)) 92 { 93 objMap.put(toCopy, toCopy); 94 retval = toCopy; 95 } 96 else 97 { 98 retval = handleObjectWithNoArgsConstructor(metadataMap, objClass, objMap, toCopy); 99 } 100 return retval; 101 } 102 103 private static Object handleObjectWithNoArgsConstructor(final Map metadataMap, final Class objClass, final Map objMap, final Object toCopy) 104 { 105 Object retval = null; 106 ClassMetadata metadata = (ClassMetadata) metadataMap.get(objClass); 107 if (metadata == null) 108 { 109 metadata = new ClassMetadata(); 110 metadataMap.put(objClass, metadata); 111 } 112 Constructor noArg = metadata.m_noArgConstructor; 113 if (metadata.m_hasNoArgConstructor) 114 { 115 if (noArg == null) 116 { 117 try 118 { 119 noArg = objClass.getDeclaredConstructor(EMPTY_CLASS_ARRAY); 120 metadata.m_noArgConstructor = noArg; 121 } 122 catch (Exception e) 123 { 124 metadata.m_hasNoArgConstructor = false; 125 } 127 } 128 } 129 if (metadata.m_hasNoArgConstructor) 130 { 131 if (!metadata.m_noArgConstructorAccessible && (Modifier.PUBLIC & noArg.getModifiers()) == 0) 132 { 133 try 134 { 135 noArg.setAccessible(true); 136 } 137 catch (SecurityException e) 138 { 139 throw new ObjectCopyException("cannot access noArg constructor [" + noArg + "] of class [" + objClass.getName() + "]: " + e.toString(), e); 140 } 141 metadata.m_noArgConstructorAccessible = true; 142 } 143 try 144 { 145 148 retval = noArg.newInstance(EMPTY_OBJECT_ARRAY); 149 objMap.put(toCopy, retval); 150 } 151 catch (Exception e) 152 { 153 throw new ObjectCopyException("cannot instantiate class [" + objClass.getName() + "] using noArg constructor: " + e.toString(), e); 154 } 155 for (Class c = objClass; c != Object .class; c = c.getSuperclass()) 156 { 157 copyClass(metadataMap, c, toCopy, retval, objMap); 158 } 159 } 160 else 161 { 162 retval = _serialize.copy(toCopy, null); 163 } 164 return retval; 165 } 166 167 private static void copyClass(final Map metadataMap, final Class c, final Object obj, final Object retval, final Map objMap) 168 { 169 ClassMetadata metadata; 170 metadata = (ClassMetadata) metadataMap.get(c); 171 if (metadata == null) 172 { 173 metadata = new ClassMetadata(); 174 metadataMap.put(c, metadata); 175 } 176 Field [] declaredFields = metadata.m_declaredFields; 177 if (declaredFields == null) 178 { 179 declaredFields = c.getDeclaredFields(); 180 metadata.m_declaredFields = declaredFields; 181 } 182 setFields(obj, retval, declaredFields, metadata.m_fieldsAccessible, objMap, metadataMap); 183 metadata.m_fieldsAccessible = true; 184 } 185 186 private static Object handleArray(final Object obj, final Map objMap, final Class objClass, final Map metadataMap) 187 { 188 final Object retval; 189 final int arrayLength = Array.getLength(obj); 190 193 if (arrayLength == 0) 194 { 195 objMap.put(obj, obj); 196 retval = obj; 197 } 198 else 199 { 200 final Class componentType = objClass.getComponentType(); 201 205 retval = Array.newInstance(componentType, arrayLength); 206 objMap.put(obj, retval); 207 if (componentType.isPrimitive() || FINAL_IMMUTABLE_CLASSES.contains(componentType)) 208 { 209 System.arraycopy(obj, 0, retval, 0, arrayLength); 210 } 211 else 212 { 213 for (int i = 0; i < arrayLength; ++i) 214 { 215 218 final Object slot = Array.get(obj, i); 219 if (slot != null) 220 { 221 final Object slotClone = clone(slot, objMap, metadataMap); 222 Array.set(retval, i, slotClone); 223 } 224 } 225 } 226 } 227 return retval; 228 } 229 230 239 private static void setFields(final Object from, final Object to, 240 final Field [] fields, final boolean accessible, 241 final Map objMap, final Map metadataMap) 242 { 243 for (int f = 0, fieldsLength = fields.length; f < fieldsLength; ++f) 244 { 245 final Field field = fields[f]; 246 final int modifiers = field.getModifiers(); 247 if ((Modifier.STATIC & modifiers) != 0) continue; 248 if ((Modifier.FINAL & modifiers) != 0) 249 throw new ObjectCopyException("cannot set final field [" + field.getName() + "] of class [" + from.getClass().getName() + "]"); 250 if (!accessible && ((Modifier.PUBLIC & modifiers) == 0)) 251 { 252 try 253 { 254 field.setAccessible(true); 255 } 256 catch (SecurityException e) 257 { 258 throw new ObjectCopyException("cannot access field [" + field.getName() + "] of class [" + from.getClass().getName() + "]: " + e.toString(), e); 259 } 260 } 261 try 262 { 263 cloneAndSetFieldValue(field, from, to, objMap, metadataMap); 264 } 265 catch (Exception e) 266 { 267 throw new ObjectCopyException("cannot set field [" + field.getName() + "] of class [" + from.getClass().getName() + "]: " + e.toString(), e); 268 } 269 } 270 } 271 272 private static void cloneAndSetFieldValue(final Field field, final Object src, final Object dest, final Map objMap, final Map metadataMap) throws IllegalAccessException 273 { 274 Object value = field.get(src); 275 if (value == null) 276 { 277 281 field.set(dest, null); 282 } 283 else 284 { 285 final Class valueType = value.getClass(); 286 if (!valueType.isPrimitive() && !FINAL_IMMUTABLE_CLASSES.contains(valueType)) 287 { 288 292 value = clone(value, objMap, metadataMap); 293 } 294 field.set(dest, value); 295 } 296 } 297 } 298 | Popular Tags |