Skip to content

Commit

Permalink
Replaces PrefabValueProvider with FactoryCache for prefab values for …
Browse files Browse the repository at this point in the history
…fields
  • Loading branch information
jqno committed Nov 15, 2024
1 parent 66a7e3f commit 12bf102
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class SingleTypeEqualsVerifierApi<T> implements EqualsVerifierApi<T> {
private Set<String> allIncludedFields = new HashSet<>();
private Set<String> nonnullFields = new HashSet<>();
private Set<String> ignoredAnnotationClassNames = new HashSet<>();
private Set<String> prefabbedFieldNames = new HashSet<>();
private List<T> equalExamples = new ArrayList<>();
private List<T> unequalExamples = new ArrayList<>();
private final Objenesis objenesis;
Expand Down Expand Up @@ -160,13 +161,14 @@ public <S> SingleTypeEqualsVerifierApi<T> withPrefabValuesForField(
S blue
) {
PrefabValuesApi.addPrefabValuesForField(
prefabValueProvider,
factoryCache,
objenesis,
type,
fieldName,
red,
blue
);
prefabbedFieldNames.add(fieldName);
withNonnullFields(fieldName);
return this;
}
Expand Down Expand Up @@ -479,7 +481,7 @@ private Configuration<T> buildConfig() {
allExcludedFields,
allIncludedFields,
nonnullFields,
prefabValueProvider.getFieldNames(),
prefabbedFieldNames,
cachedHashCodeInitializer,
hasRedefinedSuperclass,
redefinedSubclass,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@
*/
public class VintageValueProvider implements ValueProvider {

// I'd like to remove this, but that affects recursion detection it a way I can't yet explain
private final Map<TypeTag, Tuple<?>> valueCache = new HashMap<>();
private final Map<Key, Tuple<?>> valueCache = new HashMap<>();

private final ValueProvider valueProvider;
private final FactoryCache factoryCache;
Expand All @@ -50,7 +49,7 @@ public VintageValueProvider(
/** {@inheritDoc} */
@Override
public <T> Optional<Tuple<T>> provide(TypeTag tag, String label) {
return Rethrow.rethrow(() -> Optional.of(giveTuple(tag)));
return Rethrow.rethrow(() -> Optional.of(giveTuple(tag, label)));
}

/**
Expand All @@ -63,7 +62,7 @@ public <T> Optional<Tuple<T>> provide(TypeTag tag, String label) {
* @return The "red" prefabricated value.
*/
public <T> T giveRed(TypeTag tag) {
return this.<T>giveTuple(tag).getRed();
return this.<T>giveTuple(tag, null).getRed();
}

/**
Expand All @@ -76,7 +75,7 @@ public <T> T giveRed(TypeTag tag) {
* @return The "blue" prefabricated value.
*/
public <T> T giveBlue(TypeTag tag) {
return this.<T>giveTuple(tag).getBlue();
return this.<T>giveTuple(tag, null).getBlue();
}

/**
Expand All @@ -89,7 +88,7 @@ public <T> T giveBlue(TypeTag tag) {
* @return A shallow copy of the "red" prefabricated value.
*/
public <T> T giveRedCopy(TypeTag tag) {
return this.<T>giveTuple(tag).getRedCopy();
return this.<T>giveTuple(tag, null).getRedCopy();
}

/**
Expand All @@ -113,7 +112,7 @@ public <T> T giveOther(TypeTag tag, T value, LinkedHashSet<TypeTag> typeStack) {
throw new ReflectionException("TypeTag does not match value.");
}

Tuple<T> tuple = giveTuple(tag, typeStack);
Tuple<T> tuple = giveTuple(tag, null, typeStack);
if (tuple.getRed() == null) {
return null;
}
Expand Down Expand Up @@ -153,23 +152,37 @@ private boolean arraysAreDeeplyEqual(Object x, Object y) {
* @param typeStack Keeps track of recursion in the type.
*/
public <T> void realizeCacheFor(TypeTag tag, LinkedHashSet<TypeTag> typeStack) {
if (!valueCache.containsKey(tag)) {
Tuple<T> tuple = createTuple(tag, typeStack);
valueCache.put(tag, tuple);
realizeCacheFor(tag, null, typeStack);
}

/**
* Makes sure that values for the specified type are present in the cache, but doesn't return
* them.
*
* @param <T> The desired type.
* @param tag A description of the desired type, including generic parameters.
* @param label Adds the value assigned to the given label, if a label is given.
* @param typeStack Keeps track of recursion in the type.
*/
public <T> void realizeCacheFor(TypeTag tag, String label, LinkedHashSet<TypeTag> typeStack) {
Key key = Key.of(tag, label);
if (!valueCache.containsKey(key)) {
Tuple<T> tuple = createTuple(tag, label, typeStack);
valueCache.put(key, tuple);
}
}

private <T> Tuple<T> giveTuple(TypeTag tag) {
return giveTuple(tag, new LinkedHashSet<>());
private <T> Tuple<T> giveTuple(TypeTag tag, String label) {
return giveTuple(tag, label, new LinkedHashSet<>());
}

@SuppressWarnings("unchecked")
private <T> Tuple<T> giveTuple(TypeTag tag, LinkedHashSet<TypeTag> typeStack) {
realizeCacheFor(tag, typeStack);
return (Tuple<T>) valueCache.get(tag);
private <T> Tuple<T> giveTuple(TypeTag tag, String label, LinkedHashSet<TypeTag> typeStack) {
realizeCacheFor(tag, label, typeStack);
return (Tuple<T>) valueCache.get(Key.of(tag, label));
}

private <T> Tuple<T> createTuple(TypeTag tag, LinkedHashSet<TypeTag> typeStack) {
private <T> Tuple<T> createTuple(TypeTag tag, String label, LinkedHashSet<TypeTag> typeStack) {
if (typeStack.contains(tag)) {
throw new RecursionException(typeStack);
}
Expand All @@ -180,6 +193,10 @@ private <T> Tuple<T> createTuple(TypeTag tag, LinkedHashSet<TypeTag> typeStack)
}

Class<T> type = tag.getType();
if (label != null && factoryCache.contains(type, label)) {
PrefabValueFactory<T> factory = factoryCache.get(type, label);
return factory.createValues(tag, this, typeStack);
}
if (factoryCache.contains(type)) {
PrefabValueFactory<T> factory = factoryCache.get(type);
return factory.createValues(tag, this, typeStack);
Expand All @@ -189,4 +206,38 @@ private <T> Tuple<T> createTuple(TypeTag tag, LinkedHashSet<TypeTag> typeStack)
Tuple<T> result = (Tuple<T>) fallbackFactory.createValues(tag, this, typeStack);
return result;
}

private static final class Key {

final TypeTag tag;
final String label;

private Key(TypeTag tag, String label) {
this.tag = tag;
this.label = label;
}

public static Key of(TypeTag tag, String label) {
return new Key(tag, label);
}

@Override
public boolean equals(Object obj) {
if (!(obj instanceof Key)) {
return false;
}
Key other = (Key) obj;
return Objects.equals(tag, other.tag) && Objects.equals(label, other.label);
}

@Override
public int hashCode() {
return Objects.hash(tag, label);
}

@Override
public String toString() {
return "Key: [" + tag + "/" + label + "]";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,15 @@ public boolean contains(Class<?> type) {
return cache.containsKey(Key.of(type.getName()));
}

/**
* @param type The type for which a factory is needed.
* @param label The label that the factory needs to be assigned to.
* @return Whether a factory is available for the given type.
*/
public boolean contains(Class<?> type, String label) {
return cache.containsKey(Key.of(type.getName(), label));
}

/**
* Returns a new {@code FactoryCache} instance containing the factories from {@code this} and
* from the {@code other} cache.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import nl.jqno.equalsverifier.Func.Func2;
import nl.jqno.equalsverifier.internal.reflection.SuperclassIterable;
import nl.jqno.equalsverifier.internal.reflection.instantiation.GenericPrefabValueProvider.GenericFactories;
import nl.jqno.equalsverifier.internal.reflection.instantiation.PrefabValueProvider;
import nl.jqno.equalsverifier.internal.reflection.vintage.FactoryCache;
import nl.jqno.equalsverifier.internal.reflection.vintage.ObjectAccessor;
import org.objenesis.Objenesis;
Expand Down Expand Up @@ -40,7 +39,7 @@ public static <T> void addPrefabValues(

@SuppressWarnings("unchecked")
public static <T> void addPrefabValuesForField(
PrefabValueProvider provider,
FactoryCache factoryCache,
Objenesis objenesis,
Class<?> enclosingType,
String fieldName,
Expand All @@ -54,14 +53,14 @@ public static <T> void addPrefabValuesForField(
Validations.validateFieldTypeMatches(field, red.getClass());

if (type.isArray()) {
provider.register(type, fieldName, red, blue, red);
factoryCache.put(type, fieldName, values(red, blue, red));
} else {
try {
T redCopy = ObjectAccessor.of(red).copy(objenesis);
provider.register(type, fieldName, red, blue, redCopy);
factoryCache.put(type, fieldName, values(red, blue, redCopy));
} catch (RuntimeException ignored) {
/* specifically, on Java 9+: InacessibleObjectException */
provider.register(type, fieldName, red, blue, red);
factoryCache.put(type, fieldName, values(red, blue, red));
}
}
}
Expand Down

0 comments on commit 12bf102

Please sign in to comment.