Skip to content

Commit

Permalink
Introduces Attributes for ValueProviders
Browse files Browse the repository at this point in the history
  • Loading branch information
jqno committed Nov 22, 2024
1 parent 64cdebe commit b362d72
Show file tree
Hide file tree
Showing 39 changed files with 241 additions and 210 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
import static org.junit.jupiter.api.Assertions.assertNotSame;

import java.lang.reflect.Constructor;
import java.util.LinkedHashSet;
import nl.jqno.equalsverifier.internal.reflection.JavaApiPrefabValues;
import nl.jqno.equalsverifier.internal.reflection.TypeTag;
import nl.jqno.equalsverifier.internal.reflection.instantiation.CachedValueProvider;
import nl.jqno.equalsverifier.internal.reflection.instantiation.ValueProvider.Attributes;
import nl.jqno.equalsverifier.internal.reflection.instantiation.VintageValueProvider;
import nl.jqno.equalsverifier.internal.testhelpers.TestValueProviders;
import org.junit.jupiter.api.BeforeEach;
Expand All @@ -17,7 +17,7 @@

public class RecordObjectAccessorScramblingTest {

private static final LinkedHashSet<TypeTag> EMPTY_TYPE_STACK = new LinkedHashSet<>();
private static final Attributes EMPTY_ATTRIBUTES = Attributes.unlabeled();
private CachedValueProvider cache;
private FactoryCache factoryCache;
private VintageValueProvider valueProvider;
Expand Down Expand Up @@ -66,7 +66,7 @@ private <T> RecordObjectAccessor<T> create(T object) {
}

private ObjectAccessor<Object> doScramble(Object object) {
return create(object).scramble(valueProvider, TypeTag.NULL, EMPTY_TYPE_STACK);
return create(object).scramble(valueProvider, TypeTag.NULL, EMPTY_ATTRIBUTES);
}

record Point(int x, int y) {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.lang.reflect.Constructor;
import java.util.LinkedHashSet;
import java.util.Objects;
import nl.jqno.equalsverifier.internal.exceptions.ReflectionException;
import nl.jqno.equalsverifier.internal.reflection.Instantiator;
import nl.jqno.equalsverifier.internal.reflection.JavaApiPrefabValues;
import nl.jqno.equalsverifier.internal.reflection.TypeTag;
import nl.jqno.equalsverifier.internal.reflection.instantiation.CachedValueProvider;
import nl.jqno.equalsverifier.internal.reflection.instantiation.ValueProvider.Attributes;
import nl.jqno.equalsverifier.internal.reflection.instantiation.VintageValueProvider;
import nl.jqno.equalsverifier.internal.testhelpers.ExpectedException;
import nl.jqno.equalsverifier.internal.testhelpers.TestValueProviders;
Expand All @@ -21,7 +21,7 @@

public class RecordObjectAccessorTest {

private static final LinkedHashSet<TypeTag> EMPTY_TYPE_STACK = new LinkedHashSet<>();
private static final Attributes EMPTY_ATTRIBUTES = Attributes.unlabeled();
private Objenesis objenesis;
private Object recordInstance;

Expand Down Expand Up @@ -83,7 +83,7 @@ public void fail_whenConstructorThrowsOnSomethingElse() {
objenesis
);
ExpectedException
.when(() -> accessorFor(instance).scramble(vp, TypeTag.NULL, EMPTY_TYPE_STACK))
.when(() -> accessorFor(instance).scramble(vp, TypeTag.NULL, EMPTY_ATTRIBUTES))
.assertThrows(ReflectionException.class)
.assertMessageContains("Record:", "failed to run constructor", "prefab values");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import nl.jqno.equalsverifier.internal.reflection.TypeTag;
import nl.jqno.equalsverifier.internal.reflection.instantiation.SubjectCreator;
import nl.jqno.equalsverifier.internal.reflection.instantiation.ValueProvider;
import nl.jqno.equalsverifier.internal.reflection.instantiation.ValueProvider.Attributes;
import nl.jqno.equalsverifier.internal.util.*;

public class AbstractDelegationChecker<T> implements Checker {
Expand Down Expand Up @@ -74,7 +75,7 @@ private void checkAbstractDelegationInFields() {

private <U> Tuple<U> safelyGetTuple(TypeTag tag, String fieldName) {
try {
return valueProvider.provideOrThrow(tag, fieldName);
return valueProvider.provideOrThrow(tag, Attributes.labeled(fieldName));
} catch (Exception ignored) {
// If it fails for some reason, any reason, just return null so we can skip the test.
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.Map;
import java.util.Objects;
import nl.jqno.equalsverifier.internal.reflection.instantiation.ValueProvider;
import nl.jqno.equalsverifier.internal.reflection.instantiation.ValueProvider.Attributes;
import nl.jqno.equalsverifier.internal.util.Configuration;
import nl.jqno.equalsverifier.internal.util.Context;
import nl.jqno.equalsverifier.internal.util.Formatter;
Expand All @@ -23,7 +24,7 @@ public MapEntryHashCodeRequirementChecker(Context<T> context) {
public void check() {
if (Map.Entry.class.isAssignableFrom(config.getType())) {
Map.Entry<?, ?> e = valueProvider
.<Map.Entry<?, ?>>provideOrThrow(config.getTypeTag(), null)
.<Map.Entry<?, ?>>provideOrThrow(config.getTypeTag(), Attributes.unlabeled())
.getRed();

int expectedHashCode = Objects.hashCode(e.getKey()) ^ Objects.hashCode(e.getValue());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import nl.jqno.equalsverifier.internal.reflection.annotations.SupportedAnnotations;
import nl.jqno.equalsverifier.internal.reflection.instantiation.SubjectCreator;
import nl.jqno.equalsverifier.internal.reflection.instantiation.ValueProvider;
import nl.jqno.equalsverifier.internal.reflection.instantiation.ValueProvider.Attributes;
import nl.jqno.equalsverifier.internal.util.Configuration;
import nl.jqno.equalsverifier.internal.util.Context;
import nl.jqno.equalsverifier.internal.util.Formatter;
Expand Down Expand Up @@ -64,7 +65,7 @@ public void execute(FieldProbe fieldProbe) {
);

TypeTag sub = new TypeTag(throwingGetterCreator(getterName));
Tuple<T> tuple = valueProvider.provideOrThrow(sub, fieldName);
Tuple<T> tuple = valueProvider.provideOrThrow(sub, Attributes.labeled(fieldName));
T red1 = tuple.getRed();
T red2 = tuple.getRedCopy();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import nl.jqno.equalsverifier.internal.reflection.annotations.SupportedAnnotations;
import nl.jqno.equalsverifier.internal.reflection.instantiation.SubjectCreator;
import nl.jqno.equalsverifier.internal.reflection.instantiation.ValueProvider;
import nl.jqno.equalsverifier.internal.reflection.instantiation.ValueProvider.Attributes;
import nl.jqno.equalsverifier.internal.util.Configuration;
import nl.jqno.equalsverifier.internal.util.Context;
import nl.jqno.equalsverifier.internal.util.Formatter;
Expand Down Expand Up @@ -80,7 +81,7 @@ private void checkValueReflexivity(FieldProbe probe) {
String fieldName = field.getName();
TypeTag tag = TypeTag.of(field, typeTag);
Tuple<?> tuple = valueProvider
.provide(tag, fieldName)
.provide(tag, Attributes.labeled(fieldName))
.orElseThrow(() -> new NoValueException(tag, fieldName));

Object left = subjectCreator.withFieldSetTo(field, tuple.getRed());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import nl.jqno.equalsverifier.internal.reflection.TypeTag;
import nl.jqno.equalsverifier.internal.reflection.instantiation.SubjectCreator;
import nl.jqno.equalsverifier.internal.reflection.instantiation.ValueProvider;
import nl.jqno.equalsverifier.internal.reflection.instantiation.ValueProvider.Attributes;
import nl.jqno.equalsverifier.internal.util.CachedHashCodeInitializer;
import nl.jqno.equalsverifier.internal.util.Formatter;

Expand Down Expand Up @@ -42,7 +43,7 @@ public void execute(FieldProbe fieldProbe) {
if (String.class.equals(fieldProbe.getType()) && !fieldProbe.isStatic()) {
TypeTag string = new TypeTag(String.class);
String red = valueProvider
.<String>provideOrThrow(string, fieldProbe.getName())
.<String>provideOrThrow(string, Attributes.labeled(fieldProbe.getName()))
.getRed();

final T reference;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ public boolean contains(TypeTag tag, String label) {
*/
@SuppressWarnings("unchecked")
@Override
public <T> Optional<Tuple<T>> provide(TypeTag tag, String label) {
Key key = Key.of(tag, label);
public <T> Optional<Tuple<T>> provide(TypeTag tag, Attributes attributes) {
Key key = Key.of(tag, attributes.label);
if (cache.containsKey(key)) {
return Optional.of((Tuple<T>) cache.get(key));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ public void register(ValueProvider... valueProviders) {

/** {@inheritDoc} */
@Override
public <T> Optional<Tuple<T>> provide(TypeTag tag, String label) {
public <T> Optional<Tuple<T>> provide(TypeTag tag, Attributes attributes) {
Optional<Tuple<T>> result = providers
.stream()
.map(vp -> vp.<T>provide(tag, label))
.map(vp -> vp.<T>provide(tag, attributes))
.filter(Optional::isPresent)
.findFirst()
.orElse(Optional.empty());

result.ifPresent(r -> cache.put(tag, label, r));
result.ifPresent(r -> cache.put(tag, attributes.label, r));
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.Map;
import nl.jqno.equalsverifier.internal.exceptions.ModuleException;
import nl.jqno.equalsverifier.internal.reflection.*;
import nl.jqno.equalsverifier.internal.reflection.instantiation.ValueProvider.Attributes;
import nl.jqno.equalsverifier.internal.util.Configuration;
import nl.jqno.equalsverifier.internal.util.Rethrow;
import org.objenesis.Objenesis;
Expand Down Expand Up @@ -237,7 +238,10 @@ private FieldIterable nonSuperFields() {
private Tuple<?> valuesFor(Field f) {
try {
TypeTag fieldTag = TypeTag.of(f, typeTag);
Tuple<?> tuple = valueProvider.provideOrThrow(fieldTag, f.getName());
Tuple<?> tuple = valueProvider.provideOrThrow(
fieldTag,
Attributes.labeled(f.getName())
);
return tuple;
} catch (ModuleException e) {
throw new ModuleException(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package nl.jqno.equalsverifier.internal.reflection.instantiation;

import java.util.LinkedHashSet;
import java.util.Optional;
import nl.jqno.equalsverifier.internal.exceptions.NoValueException;
import nl.jqno.equalsverifier.internal.reflection.Tuple;
Expand All @@ -21,26 +22,78 @@ public interface ValueProvider {
*
* @param <T> The returned tuple will have this generic type.
* @param tag A description of the desired type, including generic parameters.
* @param label Returns only the value assigned to the given label, or if label is null,
* returns the value that's not assigned to any label.
* @param attributes Provides metadata needed to provide a value.
* @return A tuple of two different values of the given type, or an empty Optional if none
* could be found.
*/
<T> Optional<Tuple<T>> provide(TypeTag tag, String label);
<T> Optional<Tuple<T>> provide(TypeTag tag, Attributes attributes);

/**
* Returns a tuple of prefabricated values of the specified type, or, if none exists, throws a
* NoValueException.
*
* @param <T> The returned tuple will have this generic type.
* @param tag A description of the desired type, including generic parameters.
* @param label Returns only the value assigned to the given label, or if label is null,
* returns the value that's not assigned to any label.
* @param attributes Provides metadata needed to provide a value.
* @return A tuple of two different values of the given type, or an empty Optional if none
* could be found.
* @throws NoValueException if no value could be found for the given tag.
*/
default <T> Tuple<T> provideOrThrow(TypeTag tag, String label) {
return this.<T>provide(tag, label).orElseThrow(() -> new NoValueException(tag));
default <T> Tuple<T> provideOrThrow(TypeTag tag, Attributes attributes) {
return this.<T>provide(tag, attributes).orElseThrow(() -> new NoValueException(tag));
}

/**
* Container for metadata needed to provide values.
*/
public static final class Attributes {

/**
* Values can be assigned to a label; if one is specified, ValueProvider returns the value
* assigned to it (or falls back to the value assigned to a null label). If label is null,
* it immediately returns the value assigned to the null label.
*/
public final String label;
/**
* Keeps track of recursion.
*/
public final LinkedHashSet<TypeTag> typeStack;

/** Private constructor. Use the factories instead. */
private Attributes(String label, LinkedHashSet<TypeTag> typeStack) {
this.label = label;
this.typeStack = typeStack;
}

/**
* Don't use a label when providing a value.
*
* @return An Attributes object with no label.
*/
public static Attributes unlabeled() {
return new Attributes(null, new LinkedHashSet<>());
}

/**
* Use a label when providing a value.
*
* @param label The label to use.
* @return An Attributes object with the given label.
*/
public static Attributes labeled(String label) {
return new Attributes(label, new LinkedHashSet<>());
}

/**
* Clones the internal typeStack and adds a type to it.
*
* @param tag A type to add to the recursion checker.
* @return A new Attributes object with a type added to its typeStack.
*/
public Attributes cloneAndAdd(TypeTag tag) {
LinkedHashSet<TypeTag> clone = new LinkedHashSet<>(typeStack);
clone.add(tag);
return new Attributes(label, clone);
}
}
}
Loading

0 comments on commit b362d72

Please sign in to comment.