99import java .net .URI ;
1010import java .net .URL ;
1111import java .util .ArrayList ;
12+ import java .util .Collection ;
13+ import java .util .Collections ;
1214import java .util .GregorianCalendar ;
1315import java .util .HashMap ;
1416import java .util .HashSet ;
2325import java .util .concurrent .ConcurrentHashMap ;
2426import java .util .regex .Pattern ;
2527
26- import org .objenesis .Objenesis ;
27- import org .objenesis .ObjenesisStd ;
28+ import javax .activation .FileDataSource ;
2829
2930/**
3031 * Cloner: deep clone objects.
3738 */
3839public class Cloner
3940{
40- private final Objenesis objenesis ;
41+ private final IInstantiationStrategy instantiationStrategy ;
4142 private final Set <Class <?>> ignored = new HashSet <Class <?>>();
43+ private final Set <Class <?>> ignoredInstanceOf = new HashSet <Class <?>>();
4244 private final Set <Class <?>> nullInstead = new HashSet <Class <?>>();
4345 private final Map <Class <?>, IFastCloner > fastCloners = new HashMap <Class <?>, IFastCloner >();
4446 private final Map <Object , Boolean > ignoredInstances = new IdentityHashMap <Object , Boolean >();
@@ -50,13 +52,13 @@ public class Cloner
5052
5153 public Cloner ()
5254 {
53- objenesis = new ObjenesisStd ();
55+ this . instantiationStrategy = ObjenesisInstantiationStrategy . getInstance ();
5456 init ();
5557 }
5658
57- public Cloner (final Objenesis objenesis )
59+ public Cloner (final IInstantiationStrategy instantiationStrategy )
5860 {
59- this .objenesis = objenesis ;
61+ this .instantiationStrategy = instantiationStrategy ;
6062 init ();
6163 }
6264
@@ -190,7 +192,6 @@ public void registerStaticFields(final Class<?>... classes)
190192 final int mods = field .getModifiers ();
191193 if (Modifier .isStatic (mods ) && !field .getType ().isPrimitive ())
192194 {
193- // System.out.println(c + " . " + field.getName());
194195 registerConstant (c , field .getName ());
195196 }
196197 }
@@ -220,6 +221,27 @@ public void dontClone(final Class<?>... c)
220221 ignored .add (cl );
221222 }
222223 }
224+ public boolean isIgnored (Class <?> clz )
225+ {
226+ for (final Class <?> c : ignored )
227+ {
228+ if (c .isAssignableFrom (clz ))
229+ return true ;
230+ }
231+ return false ;
232+ }
233+ public void dontCloneInstanceOf (final Class <?>... c )
234+ {
235+ for (final Class <?> cl : c )
236+ {
237+ ignoredInstanceOf .add (cl );
238+ }
239+ }
240+
241+ public void setDontCloneInstanceOf (final Class <?>... c )
242+ {
243+ dontCloneInstanceOf (c );
244+ }
223245
224246 /**
225247 * instead of cloning these classes will set the field to null
@@ -276,10 +298,9 @@ public void registerFastCloner(final Class<?> c, final IFastCloner fastCloner)
276298 * @param c the class
277299 * @return a new instance of c
278300 */
279- @ SuppressWarnings ("unchecked" )
280- public <T > T newInstance (final Class <T > c )
301+ protected <T > T newInstance (final Class <T > c )
281302 {
282- return ( T ) objenesis .newInstance (c );
303+ return instantiationStrategy .newInstance (c );
283304 }
284305
285306 @ SuppressWarnings ("unchecked" )
@@ -366,46 +387,85 @@ public <T> T shallowClone(final T o)
366387 }
367388
368389 // caches immutables for quick reference
369- private final ConcurrentHashMap <Class <?>, Boolean > immutables = new ConcurrentHashMap <Class <?>, Boolean >();
370- private boolean isIgnored (final Class <?> clz )
390+ private final ConcurrentHashMap <Class <?>, Boolean > immutables = new ConcurrentHashMap <Class <?>, Boolean >();
391+ private boolean cloneAnonymousParent = true ;
392+
393+ /**
394+ * override this to decide if a class is immutable. Immutable classes are not cloned.
395+ *
396+ * @param clz the class under check
397+ * @return true to mark clz as immutable and skip cloning it
398+ */
399+ protected boolean considerImmutable (final Class <?> clz )
371400 {
372- for (Class <?> c : ignored )
373- {
374- if (c .isAssignableFrom (clz ))
375- return true ;
376- }
377401 return false ;
378402 }
403+
404+ protected Class <?> getImmutableAnnotation ()
405+ {
406+ return Immutable .class ;
407+ }
408+
409+ /**
410+ * decides if a class is to be considered immutable or not
411+ *
412+ * @param clz the class under check
413+ * @return true if the clz is considered immutable
414+ */
379415 private boolean isImmutable (final Class <?> clz )
380416 {
381- final Boolean b = immutables .get (clz );
382- if (b != null && b ) return true ;
383- for (final Annotation annotation : clz .getDeclaredAnnotations ())
384- {
385- if (annotation .annotationType () == Immutable .class )
386- {
387- immutables .put (clz , Boolean .TRUE );
388- return true ;
389- }
390- }
391- Class <?> c = clz .getSuperclass ();
392- while (c != null && c != Object .class )
393- {
394- for (final Annotation annotation : c .getDeclaredAnnotations ())
395- {
396- if (annotation .annotationType () == Immutable .class )
397- {
398- final Immutable im = (Immutable ) annotation ;
399- if (im .subClass ())
400- {
401- immutables .put (clz , Boolean .TRUE );
402- return true ;
403- }
404- }
405- }
406- c = c .getSuperclass ();
417+ if (
418+ clz .getName ().equals ("java.util.HashSet" )
419+ //clz.getName().startsWith("java.util")
420+ //&& !clz.getName().equals("java.util.HashMap")
421+ //&& !clz.getName().equals("java.util.LinkedHashMap")
422+ //&& !clz.getName().startsWith("java.util.Hashtable")
423+ //&& !clz.getName().equals("java.util.ArrayList")
424+ //&& !clz.getName().contains("Map")
425+ //&& ! clz.getName().startsWith("java.util.Collections")
426+ ////&& !clz.getName().equals("java.util.HashSet") //bad
427+ //&& !clz.getName().startsWith("java.util.Linked")
428+ //&& !clz.getName().contains("Vector")
429+ //&& !clz.getName().equals("java.util.concurrent.CopyOnWriteArrayList")
430+ //&& !clz.getName().equals("java.util.Arrays$ArrayList")
431+ )
432+ {
433+ // System.out.println(clz.getName());
434+ return true ;
407435 }
436+ final Boolean isIm = immutables .get (clz );
437+ if (isIm != null ) return isIm ;
438+ if (considerImmutable (clz )) return true ;
408439 return false ;
440+
441+ // final Class<?> immutableAnnotation = getImmutableAnnotation();
442+ // for (final Annotation annotation : clz.getDeclaredAnnotations())
443+ // {
444+ // if (annotation.annotationType() == immutableAnnotation)
445+ // {
446+ // immutables.put(clz, Boolean.TRUE);
447+ // return true;
448+ // }
449+ // }
450+ // Class<?> c = clz.getSuperclass();
451+ // while (c != null && c != Object.class)
452+ // {
453+ // for (final Annotation annotation : c.getDeclaredAnnotations())
454+ // {
455+ // if (annotation.annotationType() == Immutable.class)
456+ // {
457+ // final Immutable im = (Immutable) annotation;
458+ // if (im.subClass())
459+ // {
460+ // immutables.put(clz, Boolean.TRUE);
461+ // return true;
462+ // }
463+ // }
464+ // }
465+ // c = c.getSuperclass();
466+ // }
467+ // immutables.put(clz, Boolean.FALSE);
468+ // return false;
409469 }
410470
411471 /**
@@ -422,9 +482,12 @@ public <T> T cloneInternal(final T o, final Map<Object, Object> clones) throws I
422482 if (clz .isEnum ()) return o ;
423483 // skip cloning ignored classes
424484 if (nullInstead .contains (clz )) return null ;
425- if (ignored .contains (clz )) return o ;
426- if (isImmutable (clz )) return o ;
427485 if (isIgnored (clz )) return o ;
486+ for (final Class <?> iClz : ignoredInstanceOf )
487+ {
488+ if (iClz .isAssignableFrom (clz )) return o ;
489+ }
490+ if (isImmutable (clz )) return o ;
428491 if (o instanceof IFreezable )
429492 {
430493 final IFreezable f = (IFreezable ) o ;
@@ -451,13 +514,21 @@ public <T> T cloneInternal(final T o, final Map<Object, Object> clones) throws I
451514 {
452515 final int length = Array .getLength (o );
453516 final T newInstance = (T ) Array .newInstance (clz .getComponentType (), length );
454- if (clones != null )
517+ if (clones != null )
518+ {
455519 clones .put (o , newInstance );
520+ }
456521 for (int i = 0 ; i < length ; i ++)
457522 {
458523 final Object v = Array .get (o , i );
459524 final Object clone = clones != null ? cloneInternal (v , clones ) : v ;
525+ try {
460526 Array .set (newInstance , i , clone );
527+ }
528+ catch (Exception ex )
529+ {
530+ Array .set (newInstance , i , v );
531+ }
461532 }
462533 return newInstance ;
463534 }
@@ -484,9 +555,17 @@ public <T> T cloneInternal(final T o, final Map<Object, Object> clones) throws I
484555 } else
485556 {
486557 final Object fieldObject = field .get (o );
487- final boolean shouldClone = cloneSynthetics || (!cloneSynthetics && !field .isSynthetic ());
558+ final boolean shouldClone = ( cloneSynthetics || (!cloneSynthetics && !field .isSynthetic ())) && ( cloneAnonymousParent || ((! cloneAnonymousParent && ! isAnonymousParent ( field )) ));
488559 final Object fieldObjectClone = clones != null ? (shouldClone ? cloneInternal (fieldObject , clones ) : fieldObject ) : fieldObject ;
489- field .set (newInstance , fieldObjectClone );
560+ try
561+ {
562+ field .set (newInstance , fieldObjectClone );
563+ }
564+ catch (Exception ex )
565+ {
566+ // ex.printStackTrace();
567+ // System.exit(-1);
568+ }
490569 if (dumpClonedClasses && fieldObjectClone != fieldObject )
491570 {
492571 System .out .println ("cloned field>" + field + " -- of class " + o .getClass ());
@@ -497,6 +576,11 @@ public <T> T cloneInternal(final T o, final Map<Object, Object> clones) throws I
497576 return newInstance ;
498577 }
499578
579+ private boolean isAnonymousParent (final Field field )
580+ {
581+ return "this$0" .equals (field .getName ());
582+ }
583+
500584 /**
501585 * copies all properties from src to dest. Src and dest can be of different class, provided they contain same field names/types
502586 *
@@ -552,16 +636,18 @@ private void addAll(final List<Field> l, final Field[] fields)
552636 {
553637 for (final Field field : fields )
554638 {
555- if (!field .isAccessible ()) field .setAccessible (true );
556- if (!Modifier .isStatic (field .getModifiers ()))
557- l .add (field );
639+ if (!field .isAccessible ())
640+ {
641+ field .setAccessible (true );
642+ }
643+ l .add (field );
558644 }
559645 }
560646
561647 /**
562- * reflection utils
648+ * reflection utils, override this to choose which fields to clone
563649 */
564- private List <Field > allFields (final Class <?> c )
650+ protected List <Field > allFields (final Class <?> c )
565651 {
566652 List <Field > l = fieldsCache .get (c );
567653 if (l == null )
@@ -603,4 +689,17 @@ public void setCloningEnabled(final boolean cloningEnabled)
603689 {
604690 this .cloningEnabled = cloningEnabled ;
605691 }
692+
693+ /**
694+ * if false, anonymous classes parent class won't be cloned. Default is true
695+ */
696+ public void setCloneAnonymousParent (final boolean cloneAnonymousParent )
697+ {
698+ this .cloneAnonymousParent = cloneAnonymousParent ;
699+ }
700+
701+ public boolean isCloneAnonymousParent ()
702+ {
703+ return cloneAnonymousParent ;
704+ }
606705}
0 commit comments