mirror of
https://github.com/google/nomulus.git
synced 2025-05-13 07:57:13 +02:00
Change @DoNotHydrate to work on fields, not types.
There was a circular reference when hydrating a domain with a subordinate host, since the host references the domain. To fix this, I redid @DoNotHydrate to be the way it should have been, rather than the hack I had originally submitted. I also beefed up the unit tests of the epp resource types to check for cycles. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=135792416
This commit is contained in:
parent
27ec47051e
commit
cb8320ff40
12 changed files with 151 additions and 118 deletions
|
@ -14,11 +14,10 @@
|
|||
|
||||
package google.registry.model;
|
||||
|
||||
import static com.google.common.base.Functions.identity;
|
||||
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.TYPE;
|
||||
import static java.lang.annotation.ElementType.FIELD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
@ -31,10 +30,16 @@ import google.registry.model.domain.ReferenceUnion;
|
|||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NavigableMap;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
|
||||
|
@ -43,10 +48,10 @@ import javax.xml.bind.annotation.XmlTransient;
|
|||
@XmlTransient
|
||||
public abstract class ImmutableObject implements Cloneable {
|
||||
|
||||
/** Marker to indicate that {@link #toHydratedString} should not hydrate a field of this type. */
|
||||
/** Marker to indicate that {@link #toHydratedString} should not hydrate a field. */
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@Target(TYPE)
|
||||
@Target(FIELD)
|
||||
public static @interface DoNotHydrate {}
|
||||
|
||||
@Ignore
|
||||
|
@ -105,47 +110,55 @@ public abstract class ImmutableObject implements Cloneable {
|
|||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return toStringHelper(identity());
|
||||
NavigableMap<String, Object> sortedFields = new TreeMap<>();
|
||||
for (Entry<Field, Object> entry : ModelUtils.getFieldValues(this).entrySet()) {
|
||||
sortedFields.put(entry.getKey().getName(), entry.getValue());
|
||||
}
|
||||
return toStringHelper(sortedFields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to toString(), with a full expansion of embedded ImmutableObjects,
|
||||
* collections, and referenced keys.
|
||||
*/
|
||||
/** Similar to toString(), with a full expansion of referenced keys, including in collections. */
|
||||
public String toHydratedString() {
|
||||
return toStringHelper(new Function<Object, Object>() {
|
||||
@Override
|
||||
public Object apply(Object input) {
|
||||
if (input instanceof ReferenceUnion) {
|
||||
return apply(((ReferenceUnion<?>) input).getLinked());
|
||||
} else if (input instanceof Key) {
|
||||
Object target = ofy().load().key((Key<?>) input).now();
|
||||
return target != null && target.getClass().isAnnotationPresent(DoNotHydrate.class)
|
||||
? input
|
||||
: apply(target);
|
||||
} else if (input instanceof Map) {
|
||||
return transformValues((Map<?, ?>) input, this);
|
||||
} else if (input instanceof Collection) {
|
||||
return transform((Collection<?>) input, this);
|
||||
} else if (input instanceof ImmutableObject) {
|
||||
return ((ImmutableObject) input).toHydratedString();
|
||||
}
|
||||
return input;
|
||||
}});
|
||||
// We can't use ImmutableSortedMap because we need to allow null values.
|
||||
NavigableMap<String, Object> sortedFields = new TreeMap<>();
|
||||
for (Entry<Field, Object> entry : ModelUtils.getFieldValues(this).entrySet()) {
|
||||
Field field = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
sortedFields.put(
|
||||
field.getName(),
|
||||
field.isAnnotationPresent(DoNotHydrate.class) ? value : HYDRATOR.apply(value));
|
||||
}
|
||||
return toStringHelper(sortedFields);
|
||||
}
|
||||
|
||||
public String toStringHelper(Function<Object, Object> transformation) {
|
||||
Map<String, Object> sortedFields = Maps.newTreeMap();
|
||||
sortedFields.putAll(
|
||||
transformValues(ModelUtils.getFieldValues(this), transformation));
|
||||
public String toStringHelper(SortedMap<String, Object> fields) {
|
||||
return String.format(
|
||||
"%s (@%s): {\n%s",
|
||||
getClass().getSimpleName(),
|
||||
System.identityHashCode(this),
|
||||
Joiner.on('\n').join(sortedFields.entrySet()))
|
||||
Joiner.on('\n').join(fields.entrySet()))
|
||||
.replaceAll("\n", "\n ") + "\n}";
|
||||
}
|
||||
|
||||
/** 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 ReferenceUnion) {
|
||||
return apply(((ReferenceUnion<?>) value).getLinked());
|
||||
} else 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;
|
||||
}};
|
||||
|
||||
/** 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
|
||||
|
@ -153,8 +166,11 @@ public abstract class ImmutableObject implements Cloneable {
|
|||
if (o == null) {
|
||||
return null;
|
||||
} else if (o instanceof ImmutableObject) {
|
||||
Map<String, Object> result =
|
||||
Maps.transformValues(ModelUtils.getFieldValues(o), this);
|
||||
// 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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue