MergeUtils.java
/*-
* ========================LICENSE_START=================================
* flyway-core
* ========================================================================
* Copyright (C) 2010 - 2025 Red Gate Software Ltd
* ========================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =========================LICENSE_END==================================
*/
package org.flywaydb.core.internal.util;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.CustomLog;
@CustomLog
public class MergeUtils {
public static <T> T merge(T a, T b) {
return b != null ? b : a;
}
public static <E, T extends Collection<E>> T merge(T a, T b) {
return a == null ? b : (b != null && !b.isEmpty() ? b : a);
}
public static <K, V> Map<K, V> merge(Map<K, V> primary, Map<K, V> overrides, BiFunction<V, V, V> mergeFn) {
if (primary == null) {
return overrides;
}
Map<K, V> result = new HashMap<>(primary);
if (overrides != null) {
for (K key : overrides.keySet()) {
if (primary.containsKey(key)) {
V mergedValue = mergeFn.apply(primary.get(key), overrides.get(key));
result.replace(key, mergedValue);
} else {
result.put(key, mergeFn.apply(overrides.get(key), overrides.get(key)));
}
}
}
return result;
}
public static Object mergeObjects(final Object primary, final Object override) {
if (primary instanceof Map && override instanceof Map) {
return mergeMaps((Map<?, ?>) primary, (Map<?, ?>) override);
}
return override != null ? override : primary;
}
private static Map<?, ?> mergeMaps(final Map<?, ?> primary, final Map<?, ?> overrides) {
if (primary == null) {
return overrides;
} else if (overrides == null) {
return primary;
}
final Map<Object, Object> result = new HashMap<>();
Stream.concat(primary.keySet().stream(), overrides.keySet().stream())
.distinct()
.forEach(key -> {
final Object primaryValue = primary.get(key);
final Object overrideValue = overrides.get(key);
if (primaryValue instanceof Map && overrideValue instanceof Map) {
result.put(key, mergeMaps((Map<?, ?>) primaryValue, (Map<?, ?>) overrideValue));
} else {
result.put(key, overrideValue != null ? overrideValue : primaryValue);
}
});
return result;
}
public static <T> void mergeModel(T source, T target) {
Class<?> clas = source.getClass();
Field[] fields = clas.getDeclaredFields();
try {
for (Field field : fields) {
if (!Modifier.isFinal(field.getModifiers())) {
field.setAccessible(true);
Object sourceValue = field.get(source);
Object targetValue = field.get(target);
Object value = (sourceValue != null) ? sourceValue : targetValue;
field.set(target, value);
}
}
} catch (Exception e) {
LOG.error("Failed to get value from field when merging model", e);
}
}
}