com.yahoo.processing.request.CloneHelper Maven / Gradle / Ivy
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.processing.request;
import com.yahoo.collections.MethodCache;
import com.yahoo.component.provider.FreezableClass;
import com.yahoo.lang.PublicCloneable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Objects;
import java.util.logging.Logger;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.HashMap;
/**
* Helps to deep clone complex objects
* The following classes and their subclasses does have a fastpath
* - com.yahoo.component.provider.FreezableClass
* - com.yahoo.processing.request.properties.PublicCloneable BTW, this is the one you should implement too
* if you want the fastpath.
* - java.util.LinkedList
* - java.util.ArrayList
* The rest has the slow path with reflection,
* though using a fast thread safe method cache for speedup.
*
* @author bratseth
* @author baldersheim
*/
public class CloneHelper {
private static final Logger log = Logger.getLogger(CloneHelper.class.getName());
private static final MethodCache cloneMethodCache = new MethodCache("clone");
/** Clones this object if it is clonable, and the clone is public. Returns null if not. */
public final Object clone(Object object) {
if (object == null) return null;
if ( ! (object instanceof Cloneable)) return null;
if (object.getClass().isArray())
return arrayClone(object);
else
return objectClone(object);
}
private Object arrayClone(Object array) {
if (array instanceof Object[])
return objectArrayClone((Object[]) array);
else if (array instanceof byte[])
return Arrays.copyOf((byte[])array, ((byte[])array).length);
else if (array instanceof char[])
return Arrays.copyOf((char[])array, ((char[])array).length);
else if (array instanceof short[])
return Arrays.copyOf((short[])array, ((short[])array).length);
else if (array instanceof int[])
return Arrays.copyOf((int[])array, ((int[])array).length);
else if (array instanceof long[])
return Arrays.copyOf((long[])array, ((long[])array).length);
else if (array instanceof float[])
return Arrays.copyOf((float[])array, ((float[])array).length);
else if (array instanceof double[])
return Arrays.copyOf((double[])array, ((double[])array).length);
else if (array instanceof boolean[])
return Arrays.copyOf((boolean[])array, ((boolean[])array).length);
else
return new IllegalArgumentException("Unexpected primitive array type " + array.getClass());
}
private Object objectArrayClone(Object[] object) {
Object[] arrayClone = Arrays.copyOf(object, object.length);
// deep clone
for (int i = 0; i < arrayClone.length; i++) {
Object elementClone = clone(arrayClone[i]);
if (elementClone != null)
arrayClone[i] = elementClone;
}
return arrayClone;
}
@SuppressWarnings({"removal"})
protected Object objectClone(Object object) {
// Fastpath for commonly used classes
if (object instanceof FreezableClass)
return ((FreezableClass)object).clone();
else if (object instanceof PublicCloneable)
return ((PublicCloneable>)object).clone();
else if (object instanceof com.yahoo.processing.request.properties.PublicCloneable)
return ((com.yahoo.processing.request.properties.PublicCloneable>)object).clone();
else if (object instanceof LinkedList)
return ((LinkedList>) object).clone();
else if (object instanceof ArrayList)
return ((ArrayList>) object).clone();
else if (object instanceof HashMap)
return ((HashMap, ?>) object).clone();
else if (object instanceof HashSet)
return ((HashSet>) object).clone();
return cloneByReflection(object);
}
private Object cloneByReflection(Object object) {
try {
Method cloneMethod = cloneMethodCache.get(object, name -> log.warning("Caching the clone method of '" + name + "'. Let it implement com.yahoo.lang.PublicCloneable instead"));
if (cloneMethod == null) {
log.warning("'" + object + "' of class " + object.getClass() +
" is Cloneable, but has no clone method - will use the same instance in all requests");
return null;
}
return cloneMethod.invoke(object);
} catch (IllegalAccessException e) {
log.warning("'" + object + "' of class " + object.getClass() +
" is Cloneable, but clone method cannot be accessed - will use the same instance in all requests");
return null;
} catch (InvocationTargetException e) {
throw new RuntimeException("Exception cloning '" + object + "'", e);
}
}
/** Clones a map by deep cloning each value which is cloneable and shallow copying all other values. */
public Map cloneMap(Map map) {
Map cloneMap = new HashMap<>((int)(map.size()/0.75) + 1);
for (Map.Entry entry : map.entrySet()) {
Object cloneValue = clone(entry.getValue());
if (cloneValue == null)
cloneValue = entry.getValue(); // Shallow copy objects which does not support cloning
cloneMap.put(entry.getKey(), cloneValue);
}
return cloneMap;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy