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}