public <T> T clone(T object, boolean useFunctions) {

        if (object == null) {
            return null;
        }

        if (log.isLoggable(Level.FINER)) {
            log.finer("cloning:" + object.getClass() + "@" + System.identityHashCode(object));
        }

        Class<T> type = objectClass(object);

        // Check the index to see if we already have it
        Object clone = index.get(object);
        if (clone != null || index.containsKey(object)) {
            if (log.isLoggable(Level.FINER)) {
                log.finer("cloned:" + object.getClass() + "@" + System.identityHashCode(object)
                            + " as cached:" + (clone == null ? "null" : (clone.getClass() + "@" + System.identityHashCode(clone))));
            }
            return type.cast(clone);
        }

        // See if there is a custom function... that trumps everything.
        CloneFunction<T> f = getCloneFunction(type);
        if (f != null) {
            T result = f.cloneObject(this, object);

            // Store the object in the identity map so that any circular references
            // are resolvable.
            index.put(object, result);

            // Now call the function again to deep clone the fields
            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;
        }

        if (object.getClass().isArray()) {
            // Perform an array clone
            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);
            } catch (CloneNotSupportedException e) {
                throw new IllegalArgumentException("Object is not cloneable, type:" + type, e);
            }

            // Store the object in the identity map so that any circular references
            // are resolvable
            index.put(object, clone);
        } else {
            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);
    }
Deeps clones the specified object, reusing previous clones when possible.

 <p>Object cloning priority works as follows:</p>
 <ul>
 <li>If the object has already been cloned then its clone is returned.
 <li>If useFunctions is true and there is a custom CloneFunction then it is
     called to clone the object.
 <li>If the object implements Cloneable then its clone() method is called, arrays are
     deep cloned with entries passing through clone().
 <li>If the object implements JmeCloneable then its cloneFields() method is called on the
     clone.
 <li>Else an IllegalArgumentException is thrown.
 </ul>

 <p>The ability to selectively use clone functions is useful when
 being called from a clone function.</p>

 Note: objects returned by this method may not have yet had their cloneField()
 method called.

@param <T> the type of object to be cloned
@param object the object to be cloned (may be null)
@param useFunctions true&rarr;use custom clone functions,
false&rarr;don't use
@return a new instance, or a cached value, or null
jme3-core/src/main/java/com/jme3/util/clone/Cloner.java