001/* 002 * Copyright 2013 Chris Pheby 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 */ 016package org.jadira.reflection.cloning; 017 018import java.lang.invoke.MethodHandle; 019import java.lang.reflect.Field; 020import java.util.ArrayList; 021import java.util.GregorianCalendar; 022import java.util.HashMap; 023import java.util.HashSet; 024import java.util.IdentityHashMap; 025import java.util.LinkedList; 026import java.util.Map; 027import java.util.Set; 028import java.util.TreeMap; 029import java.util.TreeSet; 030import java.util.concurrent.ConcurrentHashMap; 031 032import org.jadira.reflection.cloning.annotation.Immutable; 033import org.jadira.reflection.cloning.annotation.NonCloneable; 034import org.jadira.reflection.cloning.annotation.Transient; 035import org.jadira.reflection.cloning.api.CloneDriver; 036import org.jadira.reflection.cloning.api.CloneImplementor; 037import org.jadira.reflection.cloning.api.CloneStrategy; 038import org.jadira.reflection.cloning.api.Cloner; 039import org.jadira.reflection.cloning.collection.FastIdentityHashSet; 040import org.jadira.reflection.cloning.implementor.AsmCloneStrategy; 041import org.jadira.reflection.cloning.implementor.PortableCloneStrategy; 042import org.jadira.reflection.cloning.implementor.UnsafeCloneStrategy; 043import org.jadira.reflection.cloning.implementor.types.ArrayListImplementor; 044import org.jadira.reflection.cloning.implementor.types.ConcurrentHashMapImplementor; 045import org.jadira.reflection.cloning.implementor.types.GregorianCalendarImplementor; 046import org.jadira.reflection.cloning.implementor.types.HashMapImplementor; 047import org.jadira.reflection.cloning.implementor.types.HashSetImplementor; 048import org.jadira.reflection.cloning.implementor.types.LinkedListImplementor; 049import org.jadira.reflection.cloning.implementor.types.TreeMapImplementor; 050import org.jadira.reflection.core.platform.FeatureDetection; 051 052/** 053 * This class is for performing deep clones. <br> 054 * 055 * Inspired by the Rits Deep Cloning API, this library uses Unsafe to provide maximum performance 056 * and a variety of extensibility and annotation configuration strategies. 057 * 058 * Most users will interact with the cloning library via this class and the {@link Cloner} 059 * interface. This is the most functional {@link Cloner} implementation built in to Jadira Cloning. 060 * The alternative, {@link MinimalCloner} is recommended for situations where only basic 061 * deep-cloning functionality is required and maximum throughput desirable. <br> 062 * 063 * BasicCloner is a fully configurable cloning implementation. <br> 064 * 065 * The class features two pluggable {@link CloneStrategy}, with implementations available in the 066 * library. {@link UnsafeCloneStrategy} makes use of sun.misc.Unsafe and delivers fast performance, 067 * particularly when using Server VMs with Java 6 and later. However, it requires sun.misc.Unsafe to 068 * be available. VMs providing this class include Oracle and IBM's JVMs. A fully functional Unsafe 069 * implementation is not available on Android. For platforms where UnsafeCloneStrategy is not 070 * suitable, {@link PortableCloneStrategy} should be used instead. In general this delivers good, 071 * but lower performance, giving about half the cloning throughput. Functionality however it is 072 * identical. If you use the PortableCloneStrategy, Objenesis must be included on the classpath. <br> 073 * 074 * There are a variety of ways to customise cloning. This class can be configured with 075 * {@link CloneImplementor}s which can be implemented to override the normal cloning behaviour for 076 * particular classes. Using these, must be enabled by setting {@link #useCloneImplementors} if 077 * required to be used. <br> 078 * 079 * Specific classes can be indicated as being immutable and/or non-cloneable - these classes do not 080 * need to be copied during the clone. Cloning of immutables can be disabled or enabled using 081 * {@link #setCloneImmutable(boolean)}, whilst non-cloneable classes may never be cloned. <br> 082 * 083 * Setting {@link #cloneTransientFields} to false will prevent the cloning of transient fields which 084 * will be set to their null (or default primitive) values instead of being cloned. <br> 085 * 086 * BasicCloner can detect classes that implement {@link java.lang.Cloneable} and invoke their 087 * clone() method. To enable this function, set {@link #useCloneable}. <br> 088 * 089 * Finally, the operation of the class can be customised using annotations. 090 * {@link org.jadira.reflection.cloning.annotation.Cloneable} annotation can be used to customise the treatment 091 * of particular classes being cloned. @Cloner can be used to specify a particular method 092 * within a class to be used to fulfil the clone for that specific class. {@link NonCloneable} 093 * indicates that a class should not be cloned. Finally {@link Transient} annotation can be used on 094 * any class field to indicate that the field is transient. In the case of this last annotation, use 095 * {{@link #cloneTransientAnnotatedFields} to enable or disable the capability (by default these 096 * fields are not cloned). <br> 097 * 098 * {@link Immutable} or {@link javax.annotation.concurrent.Immutable} provide an alternative 099 * mechanism for indicating that a class is immutable. <br> 100 * 101 * If the Mutability Detector library is on the classpath this will automatically be used to 102 * determine immutability for all classes being cloned. If a class is identified as IMMUTABLE or 103 * EFFECTIVELY_IMMUTABLE by Mutability Detector it will be treated as immutable. 104 * 105 * @see MinimalCloner A most optimised implementation of Cloner that only provides basic 106 * deep cloning functionality but no configurability. 107 */ 108public class BasicCloner implements Cloner, CloneDriver, CloneImplementor { 109 110 private final CloneStrategy cloneStrategy; 111 112 private Map<Class<?>, CloneImplementor> builtInImplementors = new IdentityHashMap<Class<?>, CloneImplementor>(); 113 private Map<Class<?>, CloneImplementor> allImplementors = new IdentityHashMap<Class<?>, CloneImplementor>(); 114 private Map<Class<?>, CloneImplementor> annotationImplementors = new IdentityHashMap<Class<?>, CloneImplementor>(); 115 116 private Map<Class<?>, MethodHandle> cloneMethods = new IdentityHashMap<Class<?>, MethodHandle>(); 117 118 private Set<Class<?>> immutableClasses = new FastIdentityHashSet<Class<?>>(); 119 private Set<Class<?>> nonCloneableClasses = new FastIdentityHashSet<Class<?>>(); 120 121 private boolean useCloneable = false; 122 private boolean useCloneImplementors = true; 123 private boolean cloneTransientFields = true; 124 private boolean cloneTransientAnnotatedFields = false; 125 private boolean cloneImmutable = false; 126 private boolean cloneSyntheticFields = false; 127 128 private boolean trackReferences = true; 129 private boolean trackReferencesForFlatClasses; 130 131 private Map<Class<?>, Object> builtInImmutableInstances = new HashMap<Class<?>, Object>(); 132 private IdentityHashMap<Object, Object> allImmutableInstances = new IdentityHashMap<Object, Object>(); 133 private Object[] allImmutableInstancesArray; 134 135 /** 136 * Create a new instance with {@link UnsafeCloneStrategy}, unless it is not available in which 137 * case {@link PortableCloneStrategy} will be used. 138 */ 139 public BasicCloner() { 140 if (FeatureDetection.hasUnsafe()) { 141 this.cloneStrategy = UnsafeCloneStrategy.getInstance(); 142 } else { 143 this.cloneStrategy = AsmCloneStrategy.getInstance(); 144 } 145 146 initialize(); 147 } 148 149 /** 150 * Creates a new instance with the given {@link CloneStrategy} 151 * @param cloneStrategy CloneStrategy to be used 152 */ 153 public BasicCloner(final CloneStrategy cloneStrategy) { 154 this.cloneStrategy = cloneStrategy; 155 156 initialize(); 157 } 158 159 /** 160 * Initialise a new instance 161 */ 162 private void initialize() { 163 initializeBuiltInImplementors(); 164 initializeBuiltInImmutableInstances(); 165 } 166 167 /** 168 * Initialise a set of built in CloneImplementors for commonly used JDK types 169 */ 170 private void initializeBuiltInImplementors() { 171 builtInImplementors.put(ArrayList.class, new ArrayListImplementor()); 172 builtInImplementors.put(ConcurrentHashMap.class, new ConcurrentHashMapImplementor()); 173 builtInImplementors.put(GregorianCalendar.class, new GregorianCalendarImplementor()); 174 builtInImplementors.put(HashMap.class, new HashMapImplementor()); 175 builtInImplementors.put(HashSet.class, new HashSetImplementor()); 176 builtInImplementors.put(LinkedList.class, new LinkedListImplementor()); 177 builtInImplementors.put(TreeMap.class, new TreeMapImplementor()); 178 allImplementors.putAll(builtInImplementors); 179 } 180 181 private void initializeBuiltInImmutableInstances() { 182 try { 183 Field field = TreeSet.class.getDeclaredField("PRESENT"); 184 field.setAccessible(true); 185 Object treeSetPresent = field.get(null); 186 builtInImmutableInstances.put(TreeSet.class, treeSetPresent); 187 putImmutableInstance(treeSetPresent); 188 189 field = HashSet.class.getDeclaredField("PRESENT"); 190 field.setAccessible(true); 191 Object hashSetPresent = field.get(null); 192 builtInImmutableInstances.put(HashSet.class, hashSetPresent); 193 putImmutableInstance(hashSetPresent); 194 } catch (final SecurityException e) { 195 throw new IllegalStateException(e); 196 } catch (final NoSuchFieldException e) { 197 throw new IllegalStateException(e); 198 } catch (final IllegalArgumentException e) { 199 throw new IllegalStateException(e); 200 } catch (final IllegalAccessException e) { 201 throw new IllegalStateException(e); 202 } 203 } 204 205 @Override 206 public <T> T clone(T obj) { 207 return clone(obj, this, trackReferences ? new IdentityHashMap<Object, Object>(10) : null, 0L); 208 } 209 210 @Override 211 public <T> T newInstance(Class<T> c) { 212 return cloneStrategy.newInstance(c); 213 } 214 215 @Override 216 public boolean canClone(Class<?> clazz) { 217 return cloneStrategy.canClone(clazz); 218 } 219 220 @Override 221 public <T> T clone(T obj, CloneDriver context, IdentityHashMap<Object, Object> referencesToReuse, long stackDepth) { 222 223 return cloneStrategy.clone(obj, context, referencesToReuse, stackDepth); 224 } 225 226 @Override 227 public CloneImplementor getBuiltInImplementor(Class<?> clazz) { 228 229 return allImplementors.get(clazz); 230 } 231 232 @Override 233 public CloneImplementor getImplementor(Class<?> clazz) { 234 235 return allImplementors.get(clazz); 236 } 237 238 @Override 239 public CloneImplementor getAnnotationImplementor(Class<?> clazz) { 240 241 return annotationImplementors.get(clazz); 242 } 243 244 /** 245 * Sets CloneImplementors to be used. 246 * @param implementors The implementors 247 */ 248 public void setImplementors(Map<Class<?>, CloneImplementor> implementors) { 249 // this.implementors = implementors; 250 251 this.allImplementors = new HashMap<Class<?>, CloneImplementor>(); 252 allImplementors.putAll(builtInImplementors); 253 allImplementors.putAll(implementors); 254 } 255 256 @Override 257 public Set<Class<?>> getImmutableClasses() { 258 return immutableClasses; 259 } 260 261 /** 262 * Indicates classes which are immutable. 263 * @param immutableClasses Classes which should be treated as immutable 264 */ 265 public void setImmutableClasses(Set<Class<?>> immutableClasses) { 266 this.immutableClasses = immutableClasses; 267 } 268 269 @Override 270 public Set<Class<?>> getNonCloneableClasses() { 271 return nonCloneableClasses; 272 } 273 274 /** 275 * Indicates classes that are not Cloneable. 276 * @param nonCloneableClasses Set of non-cloneable classes 277 */ 278 public void setNonCloneableClasses(Set<Class<?>> nonCloneableClasses) { 279 this.nonCloneableClasses = nonCloneableClasses; 280 } 281 282 @Override 283 public boolean isUseCloneable() { 284 return useCloneable; 285 } 286 287 /** 288 * If true, the clone() method of classes implementing {@link java.lang.Cloneable} will be 289 * delegated to where appropriate 290 * @param useCloneable True if the cloneable method should be used 291 */ 292 public void setUseCloneable(boolean useCloneable) { 293 this.useCloneable = useCloneable; 294 } 295 296 @Override 297 public MethodHandle getCloneMethod(Class<?> clazz) { 298 return cloneMethods.get(clazz); 299 } 300 301 @Override 302 public boolean isCloneTransientFields() { 303 return cloneTransientFields; 304 } 305 306 /** 307 * If true, fields marked with the transient keyword should be cloned 308 * @param cloneTransientFields true if fields decorated with transient keyword should be cloned 309 */ 310 public void setCloneTransientFields(boolean cloneTransientFields) { 311 this.cloneTransientFields = cloneTransientFields; 312 } 313 314 @Override 315 public boolean isCloneTransientAnnotatedFields() { 316 return cloneTransientAnnotatedFields; 317 } 318 319 /** 320 * If true, fields annotated with @Transient should be cloned 321 * @param cloneTransientAnnotatedFields True if fields annotated with @Transient should be cloned 322 */ 323 public void setCloneTransientAnnotatedFields(boolean cloneTransientAnnotatedFields) { 324 this.cloneTransientAnnotatedFields = cloneTransientAnnotatedFields; 325 } 326 327 @Override 328 public boolean isCloneImmutable() { 329 return cloneImmutable; 330 } 331 332 /** 333 * If true, immutable classes should be cloned. 334 * @param cloneImmutable True if immutable classes should be cloned. 335 */ 336 public void setCloneImmutable(boolean cloneImmutable) { 337 this.cloneImmutable = cloneImmutable; 338 } 339 340 @Override 341 public void initialiseFor(Class<?> classes) { 342 cloneStrategy.initialiseFor(classes); 343 } 344 345 @Override 346 public boolean isUseCloneImplementors() { 347 return useCloneImplementors; 348 } 349 350 /** 351 * If true, cloning may be delegates to clone implementors for specific tasks. 352 * @param useCloneImplementors True if CloneImplementors may be delegated to 353 */ 354 public void setUseCloneImplementors(boolean useCloneImplementors) { 355 this.useCloneImplementors = useCloneImplementors; 356 } 357 358 @Override 359 public boolean isCloneSyntheticFields() { 360 return cloneSyntheticFields; 361 } 362 363 /** 364 * If true, synthetic fields should be cloned 365 * @param cloneSyntheticFields True if synthetic fields should be cloned 366 */ 367 public void setCloneSyntheticFields(boolean cloneSyntheticFields) { 368 this.cloneSyntheticFields = cloneSyntheticFields; 369 } 370 371 @Override 372 public void putAnnotationImplementor(Class<?> clazz, CloneImplementor implementor) { 373 this.annotationImplementors.put(clazz, implementor); 374 } 375 376 @Override 377 public void putCloneMethod(Class<?> clazz, MethodHandle handle) { 378 this.cloneMethods.put(clazz, handle); 379 } 380 381 @Override 382 public boolean isImmutableInstance(Object instance) { 383 384 if (allImmutableInstances != null) { 385 for (int i = 0; i < allImmutableInstancesArray.length; i++) { 386 if (allImmutableInstancesArray[i] == instance) { 387 return true; 388 } 389 } 390 return false; 391 } else { 392 return allImmutableInstances.containsKey(instance); 393 } 394 } 395 396 @Override 397 public void putImmutableInstance(Object instance) { 398 this.allImmutableInstances.put(instance, Boolean.TRUE); 399 Set<Object> keySet = this.allImmutableInstances.keySet(); 400 this.allImmutableInstancesArray = new Object[keySet.size()]; 401 402 int i = 0; 403 for (Object next: keySet) { 404 this.allImmutableInstancesArray[i] = next; 405 i++; 406 if (i >= allImmutableInstancesArray.length) { 407 break; 408 } 409 } 410 } 411 412 public boolean isTrackReferences() { 413 return trackReferences; 414 } 415 416 /** 417 * If false, do not track references so that objects are reused if they appear twice 418 * @param trackReferences True if references should be tracked 419 */ 420 public void setTrackReferences(boolean trackReferences) { 421 this.trackReferences = trackReferences; 422 } 423 424 public boolean isTrackReferencesForFlatClasses() { 425 return trackReferencesForFlatClasses; 426 } 427 428 /** 429 * If false, do not track references for flat classes when tracking references 430 * @param trackReferencesForFlatClasses False if references should not be tracked for flat classes 431 */ 432 public void setTrackReferencesForFlatClasses(boolean trackReferencesForFlatClasses) { 433 this.trackReferencesForFlatClasses = trackReferencesForFlatClasses; 434 } 435}