From 12bf1023fa31e2d969d27d902b1a8f6d68bfc91c Mon Sep 17 00:00:00 2001 From: Jan Ouwens Date: Fri, 15 Nov 2024 19:25:00 +0100 Subject: [PATCH] Replaces PrefabValueProvider with FactoryCache for prefab values for fields --- .../api/SingleTypeEqualsVerifierApi.java | 6 +- .../instantiation/VintageValueProvider.java | 83 +++++++++++++++---- .../reflection/vintage/FactoryCache.java | 9 ++ .../internal/util/PrefabValuesApi.java | 9 +- 4 files changed, 84 insertions(+), 23 deletions(-) diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/api/SingleTypeEqualsVerifierApi.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/api/SingleTypeEqualsVerifierApi.java index 83946706d..409816552 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/api/SingleTypeEqualsVerifierApi.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/api/SingleTypeEqualsVerifierApi.java @@ -42,6 +42,7 @@ public class SingleTypeEqualsVerifierApi implements EqualsVerifierApi { private Set allIncludedFields = new HashSet<>(); private Set nonnullFields = new HashSet<>(); private Set ignoredAnnotationClassNames = new HashSet<>(); + private Set prefabbedFieldNames = new HashSet<>(); private List equalExamples = new ArrayList<>(); private List unequalExamples = new ArrayList<>(); private final Objenesis objenesis; @@ -160,13 +161,14 @@ public SingleTypeEqualsVerifierApi withPrefabValuesForField( S blue ) { PrefabValuesApi.addPrefabValuesForField( - prefabValueProvider, + factoryCache, objenesis, type, fieldName, red, blue ); + prefabbedFieldNames.add(fieldName); withNonnullFields(fieldName); return this; } @@ -479,7 +481,7 @@ private Configuration buildConfig() { allExcludedFields, allIncludedFields, nonnullFields, - prefabValueProvider.getFieldNames(), + prefabbedFieldNames, cachedHashCodeInitializer, hasRedefinedSuperclass, redefinedSubclass, diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java index 3b6740e16..aab525544 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/instantiation/VintageValueProvider.java @@ -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> valueCache = new HashMap<>(); + private final Map> valueCache = new HashMap<>(); private final ValueProvider valueProvider; private final FactoryCache factoryCache; @@ -50,7 +49,7 @@ public VintageValueProvider( /** {@inheritDoc} */ @Override public Optional> provide(TypeTag tag, String label) { - return Rethrow.rethrow(() -> Optional.of(giveTuple(tag))); + return Rethrow.rethrow(() -> Optional.of(giveTuple(tag, label))); } /** @@ -63,7 +62,7 @@ public Optional> provide(TypeTag tag, String label) { * @return The "red" prefabricated value. */ public T giveRed(TypeTag tag) { - return this.giveTuple(tag).getRed(); + return this.giveTuple(tag, null).getRed(); } /** @@ -76,7 +75,7 @@ public T giveRed(TypeTag tag) { * @return The "blue" prefabricated value. */ public T giveBlue(TypeTag tag) { - return this.giveTuple(tag).getBlue(); + return this.giveTuple(tag, null).getBlue(); } /** @@ -89,7 +88,7 @@ public T giveBlue(TypeTag tag) { * @return A shallow copy of the "red" prefabricated value. */ public T giveRedCopy(TypeTag tag) { - return this.giveTuple(tag).getRedCopy(); + return this.giveTuple(tag, null).getRedCopy(); } /** @@ -113,7 +112,7 @@ public T giveOther(TypeTag tag, T value, LinkedHashSet typeStack) { throw new ReflectionException("TypeTag does not match value."); } - Tuple tuple = giveTuple(tag, typeStack); + Tuple tuple = giveTuple(tag, null, typeStack); if (tuple.getRed() == null) { return null; } @@ -153,23 +152,37 @@ private boolean arraysAreDeeplyEqual(Object x, Object y) { * @param typeStack Keeps track of recursion in the type. */ public void realizeCacheFor(TypeTag tag, LinkedHashSet typeStack) { - if (!valueCache.containsKey(tag)) { - Tuple 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 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 void realizeCacheFor(TypeTag tag, String label, LinkedHashSet typeStack) { + Key key = Key.of(tag, label); + if (!valueCache.containsKey(key)) { + Tuple tuple = createTuple(tag, label, typeStack); + valueCache.put(key, tuple); } } - private Tuple giveTuple(TypeTag tag) { - return giveTuple(tag, new LinkedHashSet<>()); + private Tuple giveTuple(TypeTag tag, String label) { + return giveTuple(tag, label, new LinkedHashSet<>()); } @SuppressWarnings("unchecked") - private Tuple giveTuple(TypeTag tag, LinkedHashSet typeStack) { - realizeCacheFor(tag, typeStack); - return (Tuple) valueCache.get(tag); + private Tuple giveTuple(TypeTag tag, String label, LinkedHashSet typeStack) { + realizeCacheFor(tag, label, typeStack); + return (Tuple) valueCache.get(Key.of(tag, label)); } - private Tuple createTuple(TypeTag tag, LinkedHashSet typeStack) { + private Tuple createTuple(TypeTag tag, String label, LinkedHashSet typeStack) { if (typeStack.contains(tag)) { throw new RecursionException(typeStack); } @@ -180,6 +193,10 @@ private Tuple createTuple(TypeTag tag, LinkedHashSet typeStack) } Class type = tag.getType(); + if (label != null && factoryCache.contains(type, label)) { + PrefabValueFactory factory = factoryCache.get(type, label); + return factory.createValues(tag, this, typeStack); + } if (factoryCache.contains(type)) { PrefabValueFactory factory = factoryCache.get(type); return factory.createValues(tag, this, typeStack); @@ -189,4 +206,38 @@ private Tuple createTuple(TypeTag tag, LinkedHashSet typeStack) Tuple result = (Tuple) 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 + "]"; + } + } } diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/FactoryCache.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/FactoryCache.java index ef22d4bd1..2be23890b 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/FactoryCache.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/reflection/vintage/FactoryCache.java @@ -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. diff --git a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java index 3d94233ae..10a247e6e 100644 --- a/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java +++ b/equalsverifier-core/src/main/java/nl/jqno/equalsverifier/internal/util/PrefabValuesApi.java @@ -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; @@ -40,7 +39,7 @@ public static void addPrefabValues( @SuppressWarnings("unchecked") public static void addPrefabValuesForField( - PrefabValueProvider provider, + FactoryCache factoryCache, Objenesis objenesis, Class enclosingType, String fieldName, @@ -54,14 +53,14 @@ public static 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)); } } }