DeepCopyHelper.java
package fr.inrae.agroclim.indicators.util;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* Helper methods to make deep copies using copy constructors, in order to avoid {@link Cloneable}.
*
* @author Olivier Maury
*/
public interface DeepCopyHelper {
/**
* Make a copy of an object using a copy constructor.
*
* @param <T> object class
* @param obj object to copy
* @param clazz object class
* @return copied object
*/
static <T> T copyObject(final T obj, final Class<T> clazz) {
if (obj == null) {
return null;
}
final Constructor<T> copyConstructor = getCopyConstructor(clazz);
if (copyConstructor == null) {
return obj;
}
try {
return copyConstructor.newInstance(obj);
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException ex) {
throw new UnsupportedOperationException("Cannot create copy of " + obj, ex);
}
}
/**
* Get the copy constructor of a class if exists or return null.
*
* @param <T> class
* @param clazz class
* @return copy constructor or null
*/
static <T> Constructor<T> getCopyConstructor(final Class<T> clazz) {
try {
return clazz.getConstructor(clazz);
} catch (NoSuchMethodException | SecurityException ex) {
return null;
}
}
/**
* Deep copy a list.
*
* @param <T> class of list items
* @param list list to copy
* @param clazz class of list items
* @return copied list
*/
static <T> List<T> deepCopy(final List<T> list, final Class<T> clazz) {
if (list == null) {
return null;
}
final List<T> copy;
if (list instanceof ArrayList) {
copy = new ArrayList<>();
} else {
throw new UnsupportedOperationException("Only ArrayList is supported.");
}
for (T obj : list) {
if (obj == null) {
copy.add(null);
} else if (obj instanceof Enum<?>) {
copy.add(obj);
} else {
copy.add(copyObject(obj, clazz));
}
}
return copy;
}
/**
* Deep copy a map.
*
* @param <T> class of key items
* @param <U> class of value items
* @param map map to copy
* @param keyClass class of key items
* @param valueClass class of value items
* @return copied map
*/
static <T, U> Map<T, U> deepCopy(final Map<T, U> map, final Class<T> keyClass, final Class<U> valueClass) {
if (map == null) {
return null;
}
final Map<T, U> copy;
if (map instanceof HashMap) {
copy = new HashMap<>();
} else {
throw new UnsupportedOperationException("Only HashMap is supported.");
}
for (final Entry<T, U> entry : map.entrySet()) {
T copiedKey = copyObject(entry.getKey(), keyClass);
U copiedValue = copyObject(entry.getValue(), valueClass);
copy.put(copiedKey, copiedValue);
}
return copy;
}
}