import com.raybow.model.annotation.Mergeable;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* Merger.
*/
public final class Merger {
private static final ConcurrentHashMap<Class, Field[]> FIELD_CACHE = new ConcurrentHashMap<>();
private Merger() {
// private ctor
}
public static <T> T merge(T source, T target) {
try {
if (source == null)
return target;
if (target == null)
return source;
// process collection merge
if (Collection.class.isAssignableFrom(source.getClass()))
return (T) mergeCollection((Collection) source, (Collection) target);
// process map merge
if (Map.class.isAssignableFrom(source.getClass()))
return (T) mergeMap((Map) source, (Map) target);
// process array merge
if (source.getClass().isArray())
return (T) mergeArray((T[]) source, (T[]) target);
// process single object merge
if (!(source instanceof Mergeable))
return source;
Field[] fields = FIELD_CACHE.get(source.getClass());
if (fields == null)
fields = FieldUtils.getAllFields(source.getClass());
for (Field field : fields) {
field.setAccessible(true);
Object sourceValue = field.get(source);
Object targetValue = field.get(target);
// TODO: process null value force override case
if (sourceValue == null) {
continue;
}
field.set(target, merge(sourceValue, targetValue));
}
return target;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static <T> Class<T> getCollectionElementType(Collection<T> list) {
if (list == null || list.isEmpty()) {
return null;
}
return (Class<T>) list.iterator().next().getClass();
}
private static <T> Collection mergeCollection(Collection<T> source, Collection<T> target) {
// source collection override target collection
// source collection element merge into target collection element
Class elementType = getCollectionElementType(source);
if (!(Mergeable.class.isAssignableFrom(elementType)))
return source;
Map<String, Object> targetMap = new HashMap<>();
Iterator targetIterator = target.iterator();
while (targetIterator.hasNext()) {
Object targetElement = targetIterator.next();
if (targetElement != null) {
String mergeKey = ((Mergeable) targetElement)._mergeKey();
if (!StringUtils.isEmpty(mergeKey))
targetMap.put(mergeKey, targetElement);
}
}
Iterator sourceIterator = source.iterator();
Set mergedSet = null;
if (List.class.isAssignableFrom(source.getClass())) {
sourceIterator = ((List) source).listIterator();
} else if (Set.class.isAssignableFrom(source.getClass())) {
mergedSet = new HashSet<>();
}
while (sourceIterator.hasNext()) {
Object sourceElement = sourceIterator.next();
if (sourceElement == null)
continue;
Object targetElement = targetMap.get(((Mergeable) sourceElement)._mergeKey());
if (targetElement == null)
continue;
Object merged = merge(sourceElement, targetElement);
if (List.class.isAssignableFrom(source.getClass())) {
((ListIterator) sourceIterator).set(merged);
} else if (Set.class.isAssignableFrom(source.getClass())) {
sourceIterator.remove();
mergedSet.add(merged);
} else {
throw new RuntimeException("Don't support merge for [" + source.getClass().getCanonicalName() + "] collection");
}
}
if (mergedSet != null)
source.addAll(mergedSet);
return source;
}
private static <K, V> Map mergeMap(Map<K, V> source, Map<K, V> target) {
for (Map.Entry<K, V> entry : source.entrySet()) {
K key = entry.getKey();
V value = entry.getValue();
source.put(key, merge(value, target.get(key)));
}
return source;
}
private static <T> T[] mergeArray(T[] source, T[] target) {
// source array override target collection
// source array element merge into target array element
Class elementType = source.getClass().getComponentType();
if (!(Mergeable.class.isAssignableFrom(elementType)))
return source;
Map<String, T> targetMap = new HashMap<>();
for (T targetElement : target) {
if (targetElement != null) {
String mergeKey = ((Mergeable) targetElement)._mergeKey();
if (!StringUtils.isEmpty(mergeKey))
targetMap.put(mergeKey, targetElement);
}
}
for (int i = 0; i < source.length; i++) {
T sourceElement = source[i];
if (sourceElement != null) {
source[i] = merge(sourceElement, targetMap.get(((Mergeable) source[i])._mergeKey()));
}
}
return source;
}
}
merge two objects
Be the first to comment
You can use [html][/html], [css][/css], [php][/php] and more to embed the code. Urls are automatically hyperlinked. Line breaks and paragraphs are automatically generated.