mirror of
https://github.com/google/nomulus.git
synced 2025-05-12 22:38:16 +02:00
Replace com.google.common.base.Function with java.util.function.Function
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=179249159
This commit is contained in:
parent
d538dca2e0
commit
8157928a35
53 changed files with 424 additions and 399 deletions
|
@ -17,12 +17,11 @@ package google.registry.model;
|
|||
import static com.google.common.base.Predicates.instanceOf;
|
||||
import static com.google.common.base.Predicates.isNull;
|
||||
import static com.google.common.base.Predicates.or;
|
||||
import static com.google.common.collect.Lists.newArrayList;
|
||||
import static com.google.common.collect.Maps.transformValues;
|
||||
import static com.google.common.collect.Sets.newLinkedHashSet;
|
||||
import static java.util.stream.Collectors.toCollection;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
|
@ -44,42 +43,48 @@ import java.lang.reflect.Modifier;
|
|||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Collection;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/** A collection of static methods that deal with reflection on model classes. */
|
||||
public class ModelUtils {
|
||||
|
||||
/** Caches all instance fields on an object, including non-public and inherited fields. */
|
||||
private static final LoadingCache<Class<?>, ImmutableMap<String, Field>> ALL_FIELDS_CACHE =
|
||||
CacheBuilder.newBuilder().build(new CacheLoader<Class<?>, ImmutableMap<String, Field>>() {
|
||||
@Override
|
||||
public ImmutableMap<String, Field> load(Class<?> clazz) {
|
||||
Deque<Class<?>> hierarchy = new LinkedList<>();
|
||||
// Walk the hierarchy up to but not including ImmutableObject (to ignore hashCode).
|
||||
for (; clazz != ImmutableObject.class; clazz = clazz.getSuperclass()) {
|
||||
// Add to the front, so that shadowed fields show up later in the list.
|
||||
// This will mean that getFieldValues will show the most derived value.
|
||||
hierarchy.addFirst(clazz);
|
||||
}
|
||||
Map<String, Field> fields = new LinkedHashMap<>();
|
||||
for (Class<?> hierarchyClass : hierarchy) {
|
||||
// Don't use hierarchyClass.getFields() because it only picks up public fields.
|
||||
for (Field field : hierarchyClass.getDeclaredFields()) {
|
||||
if (!Modifier.isStatic(field.getModifiers())) {
|
||||
field.setAccessible(true);
|
||||
fields.put(field.getName(), field);
|
||||
CacheBuilder.newBuilder()
|
||||
.build(
|
||||
new CacheLoader<Class<?>, ImmutableMap<String, Field>>() {
|
||||
@Override
|
||||
public ImmutableMap<String, Field> load(Class<?> clazz) {
|
||||
Deque<Class<?>> hierarchy = new ArrayDeque<>();
|
||||
// Walk the hierarchy up to but not including ImmutableObject (to ignore
|
||||
// hashCode).
|
||||
for (; clazz != ImmutableObject.class; clazz = clazz.getSuperclass()) {
|
||||
// Add to the front, so that shadowed fields show up later in the list.
|
||||
// This will mean that getFieldValues will show the most derived value.
|
||||
hierarchy.addFirst(clazz);
|
||||
}
|
||||
Map<String, Field> fields = new LinkedHashMap<>();
|
||||
for (Class<?> hierarchyClass : hierarchy) {
|
||||
// Don't use hierarchyClass.getFields() because it only picks up public fields.
|
||||
for (Field field : hierarchyClass.getDeclaredFields()) {
|
||||
if (!Modifier.isStatic(field.getModifiers())) {
|
||||
field.setAccessible(true);
|
||||
fields.put(field.getName(), field);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ImmutableMap.copyOf(fields);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ImmutableMap.copyOf(fields);
|
||||
}});
|
||||
});
|
||||
|
||||
/** Lists all instance fields on an object, including non-public and inherited fields. */
|
||||
static Map<String, Field> getAllFields(Class<?> clazz) {
|
||||
|
@ -208,53 +213,55 @@ public class ModelUtils {
|
|||
}
|
||||
|
||||
/** Functional helper for {@link #cloneEmptyToNull}. */
|
||||
private static final Function<Object, ?> CLONE_EMPTY_TO_NULL =
|
||||
new Function<Object, Object>() {
|
||||
@Override
|
||||
public Object apply(Object obj) {
|
||||
if (obj instanceof ImmutableSortedMap) {
|
||||
// ImmutableSortedMapTranslatorFactory handles empty for us. If the object is null, then
|
||||
// its on-save hook can't run.
|
||||
return obj;
|
||||
}
|
||||
if ("".equals(obj)
|
||||
|| (obj instanceof Collection && ((Collection<?>) obj).isEmpty())
|
||||
|| (obj instanceof Map && ((Map<?, ?>) obj).isEmpty())
|
||||
|| (obj != null && obj.getClass().isArray() && Array.getLength(obj) == 0)) {
|
||||
return null;
|
||||
}
|
||||
Predicate<Object> immutableObjectOrNull = or(isNull(), instanceOf(ImmutableObject.class));
|
||||
if ((obj instanceof Set || obj instanceof List)
|
||||
&& Streams.stream((Iterable<?>) obj).allMatch(immutableObjectOrNull)) {
|
||||
// Recurse into sets and lists, but only if they contain ImmutableObjects.
|
||||
FluentIterable<?> fluent = FluentIterable.from((Iterable<?>) obj).transform(this);
|
||||
return (obj instanceof List) ? newArrayList(fluent) : newLinkedHashSet(fluent);
|
||||
}
|
||||
if (obj instanceof Map
|
||||
&& ((Map<?, ?>) obj).values().stream().allMatch(immutableObjectOrNull)) {
|
||||
// Recurse into maps with ImmutableObject values.
|
||||
return transformValues((Map<?, ?>) obj, this);
|
||||
}
|
||||
if (obj instanceof ImmutableObject) {
|
||||
// Recurse on the fields of an ImmutableObject.
|
||||
ImmutableObject copy = ImmutableObject.clone((ImmutableObject) obj);
|
||||
for (Field field : getAllFields(obj.getClass()).values()) {
|
||||
Object oldValue = getFieldValue(obj, field);
|
||||
Object newValue = apply(oldValue);
|
||||
if (!Objects.equals(oldValue, newValue)) {
|
||||
setFieldValue(copy, field, newValue);
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
return obj;
|
||||
private static Object cloneEmptyToNullRecursive(Object obj) {
|
||||
if (obj instanceof ImmutableSortedMap) {
|
||||
// ImmutableSortedMapTranslatorFactory handles empty for us. If the object is null, then
|
||||
// its on-save hook can't run.
|
||||
return obj;
|
||||
}
|
||||
if (obj == null
|
||||
|| obj.equals("")
|
||||
|| (obj instanceof Collection && ((Collection<?>) obj).isEmpty())
|
||||
|| (obj instanceof Map && ((Map<?, ?>) obj).isEmpty())
|
||||
|| (obj.getClass().isArray() && Array.getLength(obj) == 0)) {
|
||||
return null;
|
||||
}
|
||||
Predicate<Object> immutableObjectOrNull = or(isNull(), instanceOf(ImmutableObject.class));
|
||||
if ((obj instanceof Set || obj instanceof List)
|
||||
&& Streams.stream((Iterable<?>) obj).allMatch(immutableObjectOrNull)) {
|
||||
// Recurse into sets and lists, but only if they contain ImmutableObjects.
|
||||
Stream<?> stream =
|
||||
Streams.stream((Iterable<?>) obj).map(ModelUtils::cloneEmptyToNullRecursive);
|
||||
// We can't use toImmutable(List/Set) because the values can be null.
|
||||
// We can't use toSet because we have to preserve order in the Set.
|
||||
// So we use toList (accepts null) and LinkedHashSet (preserves order and accepts null)
|
||||
return (obj instanceof List)
|
||||
? stream.collect(toList())
|
||||
: stream.collect(toCollection(LinkedHashSet::new));
|
||||
}
|
||||
if (obj instanceof Map && ((Map<?, ?>) obj).values().stream().allMatch(immutableObjectOrNull)) {
|
||||
// Recurse into maps with ImmutableObject values.
|
||||
return transformValues((Map<?, ?>) obj, ModelUtils::cloneEmptyToNullRecursive);
|
||||
}
|
||||
if (obj instanceof ImmutableObject) {
|
||||
// Recurse on the fields of an ImmutableObject.
|
||||
ImmutableObject copy = ImmutableObject.clone((ImmutableObject) obj);
|
||||
for (Field field : getAllFields(obj.getClass()).values()) {
|
||||
Object oldValue = getFieldValue(obj, field);
|
||||
Object newValue = cloneEmptyToNullRecursive(oldValue);
|
||||
if (!Objects.equals(oldValue, newValue)) {
|
||||
setFieldValue(copy, field, newValue);
|
||||
}
|
||||
};
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/** Returns a clone of the object and sets empty collections, arrays, maps and strings to null. */
|
||||
@SuppressWarnings("unchecked")
|
||||
protected static <T extends ImmutableObject> T cloneEmptyToNull(T obj) {
|
||||
return (T) CLONE_EMPTY_TO_NULL.apply(obj);
|
||||
return (T) cloneEmptyToNullRecursive(obj);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue