Class Cloner
By default, objects that do not implement JmeCloneable will be treated like normal Java Cloneable objects. If the object does not implement the JmeCloneable or the regular JDK Cloneable interfaces AND has no special handling defined then an IllegalArgumentException will be thrown.
Enhanced object cloning is done in a two step process. First, the object is cloned using the normal Java clone() method and stored in the clone registry. After that, if it implements JmeCloneable then its cloneFields() method is called to deep clone any of the fields. This two step process has a few benefits. First, it means that objects can easily have a regular shallow clone implementation just like any normal Java objects. Second, the deep cloning of fields happens after creation which means that the clone is available to future field cloning to resolve circular references.
Similar to Java serialization, the handling of specific object types can be customized. This allows certain objects to be cloned gracefully even if they aren't normally Cloneable. This can also be used as a sort of filter to keep certain types of objects from being cloned. (For example, adding the IdentityCloneFunction for Mesh.class would cause all mesh instances to be shared with the original object graph.)
By default, the Cloner registers several default clone functions as follows:
- java.util.ArrayList: ListCloneFunction
- java.util.LinkedList: ListCloneFunction
- java.util.concurrent.CopyOnWriteArrayList: ListCloneFunction
- java.util.Vector: ListCloneFunction
- java.util.Stack: ListCloneFunction
- com.jme3.util.SafeArrayList: ListCloneFunction
Usage:
// Example 1: using an instantiated, reusable cloner. Cloner cloner = new Cloner(); Foo fooClone = cloner.clone(foo); cloner.clearIndex(); // prepare it for reuse Foo fooClone2 = cloner.clone(foo); // Example 2: using the utility method that self-instantiates a temporary cloner. Foo fooClone = Cloner.deepClone(foo);
-
Constructor Summary
ConstructorDescriptionCloner()
Creates a new cloner with only default clone functions and an empty object index. -
Method Summary
Modifier and TypeMethodDescriptionprotected <T> T
arrayClone
(T object) Clones a primitive array by coping it and clones an object array by coping it and then running each of its values through Cloner.clone().void
Clears the object index allowing the cloner to be reused for a brand-new cloning operation.<T> T
clone
(T object) Deeps clones the specified object, reusing previous clones when possible.<T> T
clone
(T object, boolean useFunctions) Deeps clones the specified object, reusing previous clones when possible.static <T> T
deepClone
(T object) Convenience utility function that creates a new Cloner, uses it to deep clone the object, and then returns the result.<T> CloneFunction<T>
getCloneFunction
(Class<T> type) Returns a previously registered clone function for the specified type or null if there is no custom clone function for the type.boolean
Returns true if the specified object has already been cloned by this cloner during this session.<T> T
javaClone
(T object) Performs a raw shallow Java clone using reflection.<T> void
setClonedValue
(T original, T clone) 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.<T> void
setCloneFunction
(Class<T> type, CloneFunction<T> function) Sets a custom CloneFunction for implementations of the specified Java type.
-
Constructor Details
-
Cloner
public Cloner()Creates a new cloner with only default clone functions and an empty object index.
-
-
Method Details
-
deepClone
public static <T> T deepClone(T object) Convenience utility function that creates a new Cloner, uses it to deep clone the object, and then returns the result.- Type Parameters:
T
- the type of object to be cloned- Parameters:
object
- the object to be cloned (may be null)- Returns:
- a new instance, or a cached value, or null
-
clone
public <T> T clone(T object) Deeps clones the specified object, reusing previous clones when possible.Object cloning priority works as follows:
- If the object has already been cloned then its clone is returned.
- If there is a custom CloneFunction then it is called to clone the object.
- If the object implements Cloneable then its clone() method is called, arrays are deep cloned with entries passing through clone().
- If the object implements JmeCloneable then its cloneFields() method is called on the clone.
- Else an IllegalArgumentException is thrown.
- Type Parameters:
T
- the type of object to be cloned- Parameters:
object
- the object to be cloned (may be null)- Returns:
- a new instance, or a cached value, or null
-
clone
public <T> T clone(T object, boolean useFunctions) Deeps clones the specified object, reusing previous clones when possible.Object cloning priority works as follows:
- If the object has already been cloned then its clone is returned.
- If useFunctions is true and there is a custom CloneFunction then it is called to clone the object.
- If the object implements Cloneable then its clone() method is called, arrays are deep cloned with entries passing through clone().
- If the object implements JmeCloneable then its cloneFields() method is called on the clone.
- Else an IllegalArgumentException is thrown.
The ability to selectively use clone functions is useful when being called from a clone function.
Note: objects returned by this method may not have yet had their cloneField() method called.- Type Parameters:
T
- the type of object to be cloned- Parameters:
object
- the object to be cloned (may be null)useFunctions
- true→use custom clone functions, false→don't use- Returns:
- a new instance, or a cached value, or null
-
setCloneFunction
Sets a custom CloneFunction for implementations of the specified Java type. Some inheritance checks are made but no disambiguation is performed.Note: in the general case, it is better to register against specific classes and not super-classes or super-interfaces unless you know specifically that they are cloneable.
By default ListCloneFunction is registered for ArrayList, LinkedList, CopyOnWriteArrayList, Vector, Stack, and JME's SafeArrayList.
- Type Parameters:
T
- the type of object to be cloned- Parameters:
type
- the type of object to be clonedfunction
- the function to set, or null to cancel any previous setting
-
getCloneFunction
Returns a previously registered clone function for the specified type or null if there is no custom clone function for the type.- Type Parameters:
T
- the type of object to be cloned- Parameters:
type
- the type of object to be cloned- Returns:
- the registered function, or null if none
-
setClonedValue
public <T> void setClonedValue(T original, T clone) 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.- Type Parameters:
T
- the type of object to be detected and returned- Parameters:
original
- the instance to be detected (alias created)clone
- the instance to be returned (alias created)
-
isCloned
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.- Parameters:
o
- the object to be tested- Returns:
- true if the object has been cloned, otherwise false
-
clearIndex
public void clearIndex()Clears the object index allowing the cloner to be reused for a brand-new cloning operation. -
javaClone
Performs a raw shallow Java clone using reflection. This call does NOT check against the clone index and so will return new objects every time it is called. That's because these are shallow clones and have not (and may not ever, depending on the caller) get resolved.This method is provided as a convenient way for CloneFunctions to call clone() and objects without necessarily knowing their real type.
- Type Parameters:
T
- the type of object to be cloned- Parameters:
object
- the object to be cloned (may be null)- Returns:
- a new instance or null
- Throws:
CloneNotSupportedException
- if the object has no public clone method
-
arrayClone
protected <T> T arrayClone(T object) Clones a primitive array by coping it and clones an object array by coping it and then running each of its values through Cloner.clone().- Type Parameters:
T
- the type of array to be cloned- Parameters:
object
- the array to be cloned- Returns:
- a new array
-