Added 'finer' logging for the clone() method to provide visibility for
debugging. Added a setClonedValue() method to force uncloned or precloned references in some specific use-cases. Added an isCloned() method to tell if an object has already been cloned in this cloner's 'session'.
This commit is contained in:
parent
c6aac78f42
commit
2028f3b3f8
@ -37,6 +37,8 @@ import java.lang.reflect.InvocationTargetException;
|
|||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import java.util.logging.Level;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
@ -97,6 +99,8 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
*/
|
*/
|
||||||
public class Cloner {
|
public class Cloner {
|
||||||
|
|
||||||
|
static Logger log = Logger.getLogger(Cloner.class.getName());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keeps track of the objects that have been cloned so far.
|
* Keeps track of the objects that have been cloned so far.
|
||||||
*/
|
*/
|
||||||
@ -190,14 +194,24 @@ public class Cloner {
|
|||||||
* method called.
|
* method called.
|
||||||
*/
|
*/
|
||||||
public <T> T clone( T object, boolean useFunctions ) {
|
public <T> T clone( T object, boolean useFunctions ) {
|
||||||
|
|
||||||
if( object == null ) {
|
if( object == null ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( log.isLoggable(Level.FINER) ) {
|
||||||
|
log.finer("cloning:" + object.getClass() + "@" + System.identityHashCode(object));
|
||||||
|
}
|
||||||
|
|
||||||
Class<T> type = objectClass(object);
|
Class<T> type = objectClass(object);
|
||||||
|
|
||||||
// Check the index to see if we already have it
|
// Check the index to see if we already have it
|
||||||
Object clone = index.get(object);
|
Object clone = index.get(object);
|
||||||
if( clone != null ) {
|
if( clone != null ) {
|
||||||
|
if( log.isLoggable(Level.FINER) ) {
|
||||||
|
log.finer("cloned:" + object.getClass() + "@" + System.identityHashCode(object)
|
||||||
|
+ " as cached:" + clone.getClass() + "@" + System.identityHashCode(clone));
|
||||||
|
}
|
||||||
return type.cast(clone);
|
return type.cast(clone);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,6 +227,15 @@ public class Cloner {
|
|||||||
// Now call the function again to deep clone the fields
|
// Now call the function again to deep clone the fields
|
||||||
f.cloneFields(this, result, object);
|
f.cloneFields(this, result, object);
|
||||||
|
|
||||||
|
if( log.isLoggable(Level.FINER) ) {
|
||||||
|
if( result == null ) {
|
||||||
|
log.finer("cloned:" + object.getClass() + "@" + System.identityHashCode(object)
|
||||||
|
+ " as transformed:null");
|
||||||
|
} else {
|
||||||
|
log.finer("clone:" + object.getClass() + "@" + System.identityHashCode(object)
|
||||||
|
+ " as transformed:" + result.getClass() + "@" + System.identityHashCode(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,6 +269,10 @@ public class Cloner {
|
|||||||
throw new IllegalArgumentException("Object is not cloneable, type:" + type);
|
throw new IllegalArgumentException("Object is not cloneable, type:" + type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( log.isLoggable(Level.FINER) ) {
|
||||||
|
log.finer("cloned:" + object.getClass() + "@" + System.identityHashCode(object)
|
||||||
|
+ " as " + clone.getClass() + "@" + System.identityHashCode(clone));
|
||||||
|
}
|
||||||
return type.cast(clone);
|
return type.cast(clone);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,6 +299,27 @@ public class Cloner {
|
|||||||
return (CloneFunction<T>)functions.get(type);
|
return (CloneFunction<T>)functions.get(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forces an object to be added to the indexing cache such that attempts
|
||||||
|
* to clone the 'original' will always result in the 'clone' being returned.
|
||||||
|
* This can be used to stub out specific values from being cloned or to
|
||||||
|
* force global shared instances to be used even if the object is cloneable
|
||||||
|
* normally.
|
||||||
|
*/
|
||||||
|
public <T> void setClonedValue( T original, T clone ) {
|
||||||
|
index.put(original, clone);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the specified object has already been cloned
|
||||||
|
* by this cloner during this session. Cloned objects are cached
|
||||||
|
* for later use and it's sometimes convenient to know if some
|
||||||
|
* objects have already been cloned.
|
||||||
|
*/
|
||||||
|
public boolean isCloned( Object o ) {
|
||||||
|
return index.containsKey(o);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the object index allowing the cloner to be reused for a brand new
|
* Clears the object index allowing the cloner to be reused for a brand new
|
||||||
* cloning operation.
|
* cloning operation.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user