dnet-hadoop/dhp-common/src/main/java/eu/dnetlib/dhp/schema/oaf/utils/MergeBeanUtils.java

105 lines
3.2 KiB
Java

package eu.dnetlib.dhp.schema.oaf.utils;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.beanutils.BeanUtilsBean;
public class MergeBeanUtils {
/**
* Copies all properties from sources to destination, does not copy null values and any nested objects will attempted to be
* either cloned or copied into the existing object. This is recursive. Should not cause any infinite recursion.
* @param dest object to copy props into (will mutate)
* @param sources
* @param <T> dest
* @return
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
public static <T> T mergeIn(T dest, T... sources) {
// to keep from any chance infinite recursion lets limit each object to 1 instance at a time in the stack
final List<Object> lookingAt = new ArrayList<>();
BeanUtilsBean recursiveBeanUtils = new BeanUtilsBean() {
/**
* Check if the class name is an internal one
* @param name
* @return
*/
private boolean isInternal(String name) {
return name.startsWith("java.") || name.startsWith("javax.")
|| name.startsWith("com.sun.") || name.startsWith("javax.")
|| name.startsWith("oracle.");
}
/**
* Override to ensure that we dont end up in infinite recursion
* @param dest
* @param orig
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
@Override
public void copyProperties(Object dest, Object orig)
throws IllegalAccessException, InvocationTargetException {
try {
// if we have an object in our list, that means we hit some sort of recursion, stop here.
if (lookingAt.stream().anyMatch(o -> o == dest)) {
return; // recursion detected
}
lookingAt.add(dest);
super.copyProperties(dest, orig);
} finally {
lookingAt.remove(dest);
}
}
@Override
public void copyProperty(Object dest, String name, Object value)
throws IllegalAccessException, InvocationTargetException {
if ("resulttype".equals(name)) {
return;
} else if (value != null) {
// dont copy over null values
// attempt to check if the value is a pojo we can clone using nested calls
if (!value.getClass().isPrimitive() && !value.getClass().isSynthetic()
&& !isInternal(value.getClass().getName())) {
try {
Object prop = super.getPropertyUtils().getProperty(dest, name);
// get current value, if its null then clone the value and set that to the value
if (prop == null) {
super.setProperty(dest, name, super.cloneBean(value));
} else {
// get the destination value and then recursively call
copyProperties(prop, value);
}
} catch (NoSuchMethodException e) {
return;
} catch (InstantiationException e) {
throw new RuntimeException("Nested property could not be cloned.", e);
}
} else {
super.copyProperty(dest, name, value);
}
}
}
};
for (Object source : sources) {
try {
recursiveBeanUtils.copyProperties(dest, source);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
return dest;
}
}