if ((cached = delegate.clone(replaced, this, classCloner)) != null) {
clones.put(replaced, cached);
return cached;
}
final Class<?> objClass = replaced.getClass();
final SerializableClass info = registry.lookup(objClass);
if (replace) {
if (info.hasWriteReplace()) {
replaced = info.callWriteReplace(replaced);
}
replaced = objectResolver.writeReplace(replaced);
if (replaced != orig) {
Object clone = clone(replaced, false);
clones.put(orig, clone);
return clone;
}
}
final Class<?> clonedClass = (Class<?>) clone(objClass);
final boolean sameClass = objClass == clonedClass;
if (orig instanceof Enum) {
if (sameClass) {
// same class means same enum constants
return orig;
} else {
final Class<? extends Enum> cloneEnumClass;
//the actual object class may be a sub class of the enum class
final Class<?> enumClass = ((Enum<?>) orig).getDeclaringClass();
if(enumClass == objClass) {
cloneEnumClass = clonedClass.asSubclass(Enum.class);
} else{
cloneEnumClass = ((Class<?>)clone(enumClass)).asSubclass(Enum.class);
}
return Enum.valueOf(cloneEnumClass, ((Enum<?>) orig).name());
}
}
if (Proxy.isProxyClass(objClass)) {
return Proxy.newProxyInstance(clonedClass.getClassLoader(), clonedClass.getInterfaces(), (InvocationHandler) clone(getInvocationHandler(orig)));
}
if (UNCLONED.contains(objClass)) {
return orig;
}
if (objClass.isArray()) {
Object simpleClone = simpleClone(orig, objClass);
if (simpleClone != null) return simpleClone;
// must be an object array
final Object[] origArray = (Object[]) orig;
final int len = origArray.length;
if (sameClass && len == 0) {
clones.put(orig, orig);
return orig;
}
if (UNCLONED.contains(objClass.getComponentType())) {
final Object[] clone = origArray.clone();
clones.put(orig, clone);
return clone;
}
final Object[] clone;
if (sameClass) {
clone = origArray.clone();
} else {
clone = (Object[])Array.newInstance(clonedClass.getComponentType(), len);
}
// deep clone
clones.put(orig, clone);
for (int i = 0; i < len; i++) {
clone[i] = clone(origArray[i]);
}
return clone;
}
final SerializableClass cloneInfo = sameClass ? info : registry.lookup(clonedClass);
// Now check the serializable types
final Object clone;
if (orig instanceof Externalizable) {
final Externalizable externalizable = (Externalizable) orig;
clone = cloneInfo.callNoArgConstructor();
clones.put(orig, clone);
final Queue<Step> steps = new ArrayDeque<Step>();
final StepObjectOutput soo = new StepObjectOutput(steps);
externalizable.writeExternal(soo);
soo.doFinish();
((Externalizable) clone).readExternal(new StepObjectInput(steps));
} else if (serializabilityChecker.isSerializable(objClass)) {
Class<?> nonSerializable;
for (nonSerializable = objClass.getSuperclass(); serializabilityChecker.isSerializable(nonSerializable); nonSerializable = nonSerializable.getSuperclass()) {
if (nonSerializable == Object.class) break;
}
clone = cloneInfo.callNonInitConstructor(nonSerializable);
final Class<?> cloneClass = clone.getClass();
if (! (serializabilityChecker.isSerializable(cloneClass))) {
throw new NotSerializableException(cloneClass.getName());
}
clones.put(orig, clone);
initSerializableClone(orig, info, clone, cloneInfo);
} else {
throw new NotSerializableException(objClass.getName());
}
replaced = clone;
if (cloneInfo.hasReadResolve()) {
replaced = cloneInfo.callReadResolve(replaced);
}
replaced = objectPreResolver.readResolve(objectResolver.readResolve(replaced));
if (replaced != clone) clones.put(orig, replaced);
return replaced;
}