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:
guyben 2017-12-15 15:41:05 -08:00 committed by Ben McIlwain
parent d538dca2e0
commit 8157928a35
53 changed files with 424 additions and 399 deletions

View file

@ -21,7 +21,6 @@ import static google.registry.util.DateTimeUtils.isAtOrAfter;
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
import static google.registry.util.DateTimeUtils.latestOf;
import com.google.common.base.Function;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Result;
import com.googlecode.objectify.cmd.Query;
@ -44,6 +43,7 @@ import google.registry.util.FormattingLogger;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Function;
import javax.annotation.Nullable;
import org.joda.time.DateTime;
import org.joda.time.Interval;

View file

@ -14,15 +14,14 @@
package google.registry.model;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Maps.transformValues;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toList;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.Maps;
import com.googlecode.objectify.Key;
@ -34,6 +33,7 @@ import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableMap;
@ -125,8 +125,7 @@ public abstract class ImmutableObject implements Cloneable {
Field field = entry.getKey();
Object value = entry.getValue();
sortedFields.put(
field.getName(),
field.isAnnotationPresent(DoNotHydrate.class) ? value : HYDRATOR.apply(value));
field.getName(), field.isAnnotationPresent(DoNotHydrate.class) ? value : hydrate(value));
}
return toStringHelper(sortedFields);
}
@ -141,54 +140,60 @@ public abstract class ImmutableObject implements Cloneable {
}
/** Helper function to recursively hydrate an ImmutableObject. */
private static final Function<Object, Object> HYDRATOR =
new Function<Object, Object>() {
@Override
public Object apply(Object value) {
if (value instanceof Key) {
return apply(ofy().load().key((Key<?>) value).now());
} else if (value instanceof Map) {
return transformValues((Map<?, ?>) value, this);
} else if (value instanceof Collection) {
return transform((Collection<?>) value, this);
} else if (value instanceof ImmutableObject) {
return ((ImmutableObject) value).toHydratedString();
}
return value;
}};
private static final Object hydrate(Object value) {
if (value instanceof Key) {
return hydrate(ofy().load().key((Key<?>) value).now());
} else if (value instanceof Map) {
return transformValues((Map<?, ?>) value, ImmutableObject::hydrate);
} else if (value instanceof Collection) {
return transform((Collection<?>) value, ImmutableObject::hydrate);
} else if (value instanceof ImmutableObject) {
return ((ImmutableObject) value).toHydratedString();
}
return value;
}
/** Helper function to recursively convert a ImmutableObject to a Map of generic objects. */
private static final Function<Object, Object> TO_MAP_HELPER =
new Function<Object, Object>() {
@Override
public Object apply(Object o) {
if (o == null) {
return null;
} else if (o instanceof ImmutableObject) {
// LinkedHashMap to preserve field ordering and because ImmutableMap forbids null
// values.
Map<String, Object> result = new LinkedHashMap<>();
for (Entry<Field, Object> entry : ModelUtils.getFieldValues(o).entrySet()) {
result.put(entry.getKey().getName(), apply(entry.getValue()));
}
return result;
} else if (o instanceof Map) {
return Maps.transformValues((Map<?, ?>) o, this);
} else if (o instanceof Set) {
return ((Set<?>) o).stream().map(this).collect(toImmutableSet());
} else if (o instanceof Collection) {
return ((Collection<?>) o).stream().map(this).collect(toImmutableList());
} else if (o instanceof Number || o instanceof Boolean) {
return o;
} else {
return o.toString();
}
}
};
private static Object toMapRecursive(Object o) {
if (o == null) {
return null;
} else if (o instanceof ImmutableObject) {
// LinkedHashMap to preserve field ordering and because ImmutableMap forbids null
// values.
Map<String, Object> result = new LinkedHashMap<>();
for (Entry<Field, Object> entry : ModelUtils.getFieldValues(o).entrySet()) {
result.put(entry.getKey().getName(), toMapRecursive(entry.getValue()));
}
return result;
} else if (o instanceof Map) {
return Maps.transformValues((Map<?, ?>) o, ImmutableObject::toMapRecursive);
} else if (o instanceof Set) {
return ((Set<?>) o)
.stream()
.map(ImmutableObject::toMapRecursive)
// We can't use toImmutableSet here, because values can be null (especially since the
// original ImmutableObject might have been the result of a cloneEmptyToNull call).
//
// We can't use toSet either, because we want to preserve order. So we use LinkedHashSet
// instead.
.collect(toCollection(LinkedHashSet::new));
} else if (o instanceof Collection) {
return ((Collection<?>) o)
.stream()
.map(ImmutableObject::toMapRecursive)
// We can't use toImmutableList here, because values can be null (especially since the
// original ImmutableObject might have been the result of a cloneEmptyToNull call).
.collect(toList());
} else if (o instanceof Number || o instanceof Boolean) {
return o;
} else {
return o.toString();
}
}
/** Returns a map of all object fields (including sensitive data) that's used to produce diffs. */
@SuppressWarnings("unchecked")
public Map<String, Object> toDiffableFieldMap() {
return (Map<String, Object>) TO_MAP_HELPER.apply(this);
return (Map<String, Object>) toMapRecursive(this);
}
}

View file

@ -16,7 +16,6 @@ package google.registry.model;
import static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.common.base.Functions;
import com.google.common.collect.Streams;
import java.util.Collections;
import java.util.LinkedHashMap;
@ -67,7 +66,7 @@ public final class JsonMapBuilder {
name,
value == null
? Collections.EMPTY_LIST
: Streams.stream(value).map(Functions.toStringFunction()).collect(toImmutableList()));
: Streams.stream(value).map(Object::toString).collect(toImmutableList()));
return this;
}

View file

@ -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

View file

@ -15,10 +15,9 @@
package google.registry.model.ofy;
import static com.googlecode.objectify.ObjectifyService.ofy;
import static google.registry.util.ObjectifyUtils.OBJECTS_TO_KEYS;
import com.google.common.base.Functions;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Result;
@ -38,13 +37,13 @@ abstract class AugmentedDeleter implements Deleter {
@Override
public Result<Void> entities(Iterable<?> entities) {
handleDeletion(Iterables.transform(entities, OBJECTS_TO_KEYS));
handleDeletion(Iterables.transform(entities, Key::create));
return delegate.entities(entities);
}
@Override
public Result<Void> entities(Object... entities) {
handleDeletion(FluentIterable.from(entities).transform(OBJECTS_TO_KEYS));
handleDeletion(FluentIterable.from(entities).transform(Key::create));
return delegate.entities(entities);
}
@ -65,11 +64,8 @@ abstract class AugmentedDeleter implements Deleter {
// Magic to convert the type Iterable<? extends Key<?>> (a family of types which allows for
// homogeneous iterables of a fixed Key<T> type, e.g. List<Key<Lock>>, and is convenient for
// callers) into the type Iterable<Key<?>> (a concrete type of heterogeneous keys, which is
// convenient for users). We do this by passing each key through the identity function
// parameterized for Key<?>, which erases any homogeneous typing on the iterable.
// See: http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ104
Iterable<Key<?>> retypedKeys = Iterables.transform(keys, Functions.<Key<?>>identity());
handleDeletion(retypedKeys);
// convenient for users).
handleDeletion(ImmutableList.<Key<?>>copyOf(keys));
return delegate.keys(keys);
}

View file

@ -25,7 +25,6 @@ import static google.registry.model.ofy.CommitLogBucket.loadBucket;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
@ -153,9 +152,7 @@ class CommitLoggedWork<R> extends VoidWork {
mutations =
union(info.getSaves(), untouchedRootsWithTouchedChildren)
.stream()
.map(
(Function<Object, ImmutableObject>)
(Object saveEntity) -> CommitLogMutation.create(manifestKey, saveEntity))
.map(entity -> (ImmutableObject) CommitLogMutation.create(manifestKey, entity))
.collect(toImmutableSet());
ofy().saveWithoutBackup()
.entities(new ImmutableSet.Builder<>()

View file

@ -21,7 +21,6 @@ import static com.google.common.collect.Maps.uniqueIndex;
import static com.googlecode.objectify.ObjectifyService.ofy;
import static google.registry.config.RegistryConfig.getBaseOfyRetryDuration;
import static google.registry.util.CollectionUtils.union;
import static google.registry.util.ObjectifyUtils.OBJECTS_TO_KEYS;
import com.google.appengine.api.datastore.DatastoreFailureException;
import com.google.appengine.api.datastore.DatastoreTimeoutException;
@ -170,7 +169,7 @@ public class Ofy {
assertInTransaction();
checkState(Streams.stream(entities).allMatch(notNull()), "Can't save a null entity.");
checkProhibitedAnnotations(entities, NotBackedUp.class, VirtualEntity.class);
ImmutableMap<Key<?>, ?> keysToEntities = uniqueIndex(entities, OBJECTS_TO_KEYS);
ImmutableMap<Key<?>, ?> keysToEntities = uniqueIndex(entities, Key::create);
TRANSACTION_INFO.get().putSaves(keysToEntities);
}
};

View file

@ -14,7 +14,6 @@
package google.registry.model.ofy;
import static com.google.common.base.Functions.constant;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Maps.filterValues;
@ -76,7 +75,7 @@ class TransactionInfo {
void putDeletes(Iterable<Key<?>> keys) {
assertNotReadOnly();
changesBuilder.putAll(toMap(keys, constant(TransactionInfo.Delete.SENTINEL)));
changesBuilder.putAll(toMap(keys, k -> TransactionInfo.Delete.SENTINEL));
}
ImmutableSet<Key<?>> getTouchedKeys() {

View file

@ -14,13 +14,11 @@
package google.registry.model.registrar;
import static com.google.common.base.Functions.toStringFunction;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Sets.difference;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableSortedCopy;
import static google.registry.util.ObjectifyUtils.OBJECTS_TO_KEYS;
import static java.util.stream.Collectors.joining;
import com.google.common.base.Enums;
@ -171,7 +169,7 @@ public class RegistrarContact extends ImmutableObject implements Jsonifiable {
.type(RegistrarContact.class)
.ancestor(registrar)
.keys()),
contacts.stream().map(OBJECTS_TO_KEYS).collect(toImmutableSet())));
contacts.stream().map(Key::create).collect(toImmutableSet())));
ofy().save().entities(contacts);
}
});
@ -272,7 +270,7 @@ public class RegistrarContact extends ImmutableObject implements Jsonifiable {
.put("emailAddress", emailAddress)
.put("phoneNumber", phoneNumber)
.put("faxNumber", faxNumber)
.put("types", getTypes().stream().map(toStringFunction()).collect(joining(",")))
.put("types", getTypes().stream().map(Object::toString).collect(joining(",")))
.put("visibleInWhoisAsAdmin", visibleInWhoisAsAdmin)
.put("visibleInWhoisAsTech", visibleInWhoisAsTech)
.put("visibleInDomainWhoisAsAbuse", visibleInDomainWhoisAsAbuse)