diff --git a/jme3-core/src/main/java/com/jme3/util/clone/Cloner.java b/jme3-core/src/main/java/com/jme3/util/clone/Cloner.java index d5ffa10de..cc046b28a 100644 --- a/jme3-core/src/main/java/com/jme3/util/clone/Cloner.java +++ b/jme3-core/src/main/java/com/jme3/util/clone/Cloner.java @@ -221,7 +221,17 @@ public class Cloner { clone = arrayClone(object); // Array clone already indexes the clone + } else if( object instanceof JmeCloneable ) { + // Use the two-step cloning semantics + clone = ((JmeCloneable)object).jmeClone(); + + // Store the object in the identity map so that any circular references + // are resolvable + index.put(object, clone); + + ((JmeCloneable)clone).cloneFields(this, object); } else if( object instanceof Cloneable ) { + // Perform a regular Java shallow clone try { clone = javaClone(object); @@ -236,12 +246,6 @@ public class Cloner { throw new IllegalArgumentException("Object is not cloneable, type:" + type); } - // Finally, check to see if the object implements special field cloning - // behavior. - if( clone instanceof JmeCloneable ) { - ((JmeCloneable)clone).cloneFields(this, object); - } - return type.cast(clone); } diff --git a/jme3-core/src/main/java/com/jme3/util/clone/JmeCloneable.java b/jme3-core/src/main/java/com/jme3/util/clone/JmeCloneable.java index 28049e0f1..6b278b222 100644 --- a/jme3-core/src/main/java/com/jme3/util/clone/JmeCloneable.java +++ b/jme3-core/src/main/java/com/jme3/util/clone/JmeCloneable.java @@ -61,9 +61,21 @@ package com.jme3.util.clone; public interface JmeCloneable extends Cloneable { /** - * Performs a shallow clone of the object. + * Performs a regular shallow clone of the object. Some fields + * may also be cloned but generally only if they will never be + * shared with other objects. (For example, local Vector3fs and so on.) + * + *
This method is separate from the regular clone() method + * so that objects might still maintain their own regular java clone() + * semantics (perhaps even using Cloner for those methods). However, + * because Java's clone() has specific features in the sense of Object's + * clone() implementation, it's usually best to have some path for + * subclasses to bypass the public clone() method that might be cloning + * fields and instead get at the superclass protected clone() methods. + * For example, through super.jmeClone() or another protected clone + * method that some base class eventually calls super.clone() in.
*/ - public Object clone(); + public Object jmeClone(); /** * Implemented to perform deep cloning for this object, resolving diff --git a/jme3-examples/src/main/java/jme3test/app/TestCloner.java b/jme3-examples/src/main/java/jme3test/app/TestCloner.java index b28647a23..4ae105603 100644 --- a/jme3-examples/src/main/java/jme3test/app/TestCloner.java +++ b/jme3-examples/src/main/java/jme3test/app/TestCloner.java @@ -170,6 +170,12 @@ public class TestCloner { throw new RuntimeException(e); } } + + public Parent jmeClone() { + // Ok to delegate to clone() in this case because no deep + // cloning is done there. + return clone(); + } public void cloneFields( Cloner cloner, Object original ) { this.ro = cloner.clone(ro); @@ -224,7 +230,7 @@ public class TestCloner { return links; } - public GraphNode clone() { + public GraphNode jmeClone() { try { return (GraphNode)super.clone(); } catch( CloneNotSupportedException e ) { @@ -267,7 +273,7 @@ public class TestCloner { } } - public ArrayHolder clone() { + public ArrayHolder jmeClone() { try { return (ArrayHolder)super.clone(); } catch( CloneNotSupportedException e ) {