Use less confusing MergedAnnotation method names

Rename some `MergedAnnotation` methods to prevent using parent/child
terminology, specifically:

	`getDepth()` has been renamed `getDistance()`
	`getParent()` has been renamed `getMetaSource()`
	`getTypeHierarchy()` has been renamed `getMetaTypes()`

The parent child naming was particularly confusing given that the
parent/child relationships were inverted from the way that a lot of
users think about meta-annotations. For example, a `@RequestMapping`
having a parent of `@GetMapping` feels odd given that `@GetMapping`
is the thing declaring the meta-annotation relationship.

The new method names are designed to align more closely with existing
terms. For example, `getMetaSource` hints at the relationship with
`isMetaAnnotated` and `getSource`.

Closes gh-22946
This commit is contained in:
Phillip Webb
2019-05-26 13:15:59 -07:00
parent 4f4427f550
commit e386e53f92
18 changed files with 173 additions and 162 deletions

View File

@@ -41,12 +41,12 @@ abstract class AbstractMergedAnnotation<A extends Annotation> implements MergedA
@Override
public boolean isDirectlyPresent() {
return isPresent() && getDepth() == 0;
return isPresent() && getDistance() == 0;
}
@Override
public boolean isMetaPresent() {
return isPresent() && getDepth() > 0;
return isPresent() && getDistance() > 0;
}
@Override

View File

@@ -502,7 +502,7 @@ public abstract class AnnotatedElementUtils {
Adapt[] adaptations = Adapt.values(classValuesAsString, nestedAnnotationsAsMap);
return getAnnotations(element).stream(annotationName)
.filter(MergedAnnotationPredicates.unique(MergedAnnotation::getTypeHierarchy))
.filter(MergedAnnotationPredicates.unique(MergedAnnotation::getMetaTypes))
.map(MergedAnnotation::withNonMergedAttributes)
.collect(MergedAnnotationCollectors.toMultiValueMap(AnnotatedElementUtils::nullIfEmpty, adaptations));
}

View File

@@ -49,15 +49,15 @@ import org.springframework.util.StringUtils;
final class AnnotationTypeMapping {
@Nullable
private final AnnotationTypeMapping parent;
private final AnnotationTypeMapping source;
private final AnnotationTypeMapping root;
private final int depth;
private final int distance;
private final Class<? extends Annotation> annotationType;
private final List<Class<? extends Annotation>> annotationTypeHierarchy;
private final List<Class<? extends Annotation>> metaTypes;
@Nullable
private final Annotation annotation;
@@ -79,15 +79,15 @@ final class AnnotationTypeMapping {
private final Set<Method> claimedAliases = new HashSet<>();
AnnotationTypeMapping(@Nullable AnnotationTypeMapping parent,
AnnotationTypeMapping(@Nullable AnnotationTypeMapping source,
Class<? extends Annotation> annotationType, @Nullable Annotation annotation) {
this.parent = parent;
this.root = (parent != null ? parent.getRoot() : this);
this.depth = (parent == null ? 0 : parent.getDepth() + 1);
this.source = source;
this.root = (source != null ? source.getRoot() : this);
this.distance = (source == null ? 0 : source.getDistance() + 1);
this.annotationType = annotationType;
this.annotationTypeHierarchy = merge(
parent != null ? parent.getAnnotationTypeHierarchy() : null,
this.metaTypes = merge(
source != null ? source.getMetaTypes() : null,
annotationType);
this.annotation = annotation;
this.attributes = AttributeMethods.forAnnotationType(annotationType);
@@ -223,7 +223,7 @@ final class AnnotationTypeMapping {
aliases.addAll(additional);
}
}
mapping = mapping.parent;
mapping = mapping.source;
}
}
@@ -250,7 +250,7 @@ final class AnnotationTypeMapping {
}
}
}
mapping = mapping.parent;
mapping = mapping.source;
}
}
@@ -265,7 +265,7 @@ final class AnnotationTypeMapping {
}
private void addConventionMappings() {
if (this.depth == 0) {
if (this.distance == 0) {
return;
}
AttributeMethods rootAttributes = this.root.getAttributes();
@@ -290,13 +290,13 @@ final class AnnotationTypeMapping {
Method attribute = this.attributes.get(i);
boolean isValueAttribute = MergedAnnotation.VALUE.equals(attribute.getName());
AnnotationTypeMapping mapping = this;
while (mapping != null && mapping.depth > 0) {
while (mapping != null && mapping.distance > 0) {
int mapped = mapping.getAttributes().indexOf(attribute.getName());
if (mapped != -1 && isBetterConventionAnnotationValue(i, isValueAttribute, mapping)) {
this.annotationValueMappings[i] = mapped;
this.annotationValueSource[i] = mapping;
}
mapping = mapping.parent;
mapping = mapping.source;
}
}
}
@@ -306,8 +306,8 @@ final class AnnotationTypeMapping {
if (this.annotationValueMappings[index] == -1) {
return true;
}
int existingDepth = this.annotationValueSource[index].depth;
return !isValueAttribute && existingDepth > mapping.depth;
int existingDistance = this.annotationValueSource[index].distance;
return !isValueAttribute && existingDistance > mapping.distance;
}
/**
@@ -363,20 +363,20 @@ final class AnnotationTypeMapping {
}
/**
* Get the parent mapping or {@code null}.
* @return the parent mapping
* Get the source of the mapping or {@code null}.
* @return the source of the mapping
*/
@Nullable
AnnotationTypeMapping getParent() {
return this.parent;
AnnotationTypeMapping getSource() {
return this.source;
}
/**
* Get the depth of this mapping.
* @return the depth of the mapping
* Get the distance of this mapping.
* @return the distance of the mapping
*/
int getDepth() {
return this.depth;
int getDistance() {
return this.distance;
}
/**
@@ -387,8 +387,8 @@ final class AnnotationTypeMapping {
return this.annotationType;
}
List<Class<? extends Annotation>> getAnnotationTypeHierarchy() {
return this.annotationTypeHierarchy;
List<Class<? extends Annotation>> getMetaTypes() {
return this.metaTypes;
}
/**

View File

@@ -73,38 +73,40 @@ final class AnnotationTypeMappings {
}
}
private void addMetaAnnotationsToQueue(Deque<AnnotationTypeMapping> queue, AnnotationTypeMapping parent) {
private void addMetaAnnotationsToQueue(Deque<AnnotationTypeMapping> queue, AnnotationTypeMapping source) {
Annotation[] metaAnnotations =
AnnotationsScanner.getDeclaredAnnotations(parent.getAnnotationType(), false);
AnnotationsScanner.getDeclaredAnnotations(source.getAnnotationType(), false);
for (Annotation metaAnnotation : metaAnnotations) {
if (!isMappable(parent, metaAnnotation)) {
if (!isMappable(source, metaAnnotation)) {
continue;
}
Annotation[] repeatedAnnotations = RepeatableContainers.standardRepeatables()
.findRepeatedAnnotations(metaAnnotation);
if (repeatedAnnotations != null) {
for (Annotation repeatedAnnotation : repeatedAnnotations) {
if (!isMappable(parent, metaAnnotation)) {
if (!isMappable(source, metaAnnotation)) {
continue;
}
addIfPossible(queue, parent, repeatedAnnotation);
addIfPossible(queue, source, repeatedAnnotation);
}
}
else {
addIfPossible(queue, parent, metaAnnotation);
addIfPossible(queue, source, metaAnnotation);
}
}
}
private void addIfPossible(Deque<AnnotationTypeMapping> queue, AnnotationTypeMapping parent, Annotation ann) {
addIfPossible(queue, parent, ann.annotationType(), ann);
private void addIfPossible(Deque<AnnotationTypeMapping> queue,
AnnotationTypeMapping source, Annotation ann) {
addIfPossible(queue, source, ann.annotationType(), ann);
}
private void addIfPossible(Deque<AnnotationTypeMapping> queue, @Nullable AnnotationTypeMapping parent,
private void addIfPossible(Deque<AnnotationTypeMapping> queue, @Nullable AnnotationTypeMapping source,
Class<? extends Annotation> annotationType, @Nullable Annotation ann) {
try {
queue.addLast(new AnnotationTypeMapping(parent, annotationType, ann));
queue.addLast(new AnnotationTypeMapping(source, annotationType, ann));
}
catch (Exception ex) {
if (ex instanceof AnnotationConfigurationException) {
@@ -112,25 +114,25 @@ final class AnnotationTypeMappings {
}
if (failureLogger.isEnabled()) {
failureLogger.log("Failed to introspect meta-annotation " + annotationType.getName(),
(parent != null ? parent.getAnnotationType() : null), ex);
(source != null ? source.getAnnotationType() : null), ex);
}
}
}
private boolean isMappable(AnnotationTypeMapping parent, @Nullable Annotation metaAnnotation) {
private boolean isMappable(AnnotationTypeMapping source, @Nullable Annotation metaAnnotation) {
return (metaAnnotation != null && !this.filter.matches(metaAnnotation) &&
!AnnotationFilter.PLAIN.matches(parent.getAnnotationType()) &&
!isAlreadyMapped(parent, metaAnnotation));
!AnnotationFilter.PLAIN.matches(source.getAnnotationType()) &&
!isAlreadyMapped(source, metaAnnotation));
}
private boolean isAlreadyMapped(AnnotationTypeMapping parent, Annotation metaAnnotation) {
private boolean isAlreadyMapped(AnnotationTypeMapping source, Annotation metaAnnotation) {
Class<? extends Annotation> annotationType = metaAnnotation.annotationType();
AnnotationTypeMapping mapping = parent;
AnnotationTypeMapping mapping = source;
while (mapping != null) {
if (mapping.getAnnotationType() == annotationType) {
return true;
}
mapping = mapping.getParent();
mapping = mapping.getSource();
}
return false;
}

View File

@@ -229,8 +229,8 @@ public abstract class AnnotationUtils {
}
private static <A extends Annotation> boolean isSingleLevelPresent(MergedAnnotation<A> mergedAnnotation) {
int depth = mergedAnnotation.getDepth();
return (depth == 0 || depth == 1);
int distance = mergedAnnotation.getDistance();
return (distance == 0 || distance == 1);
}
/**

View File

@@ -73,15 +73,6 @@ public interface MergedAnnotation<A extends Annotation> {
*/
Class<A> getType();
/**
* Return a complete type hierarchy from this annotation to the
* {@link #getRoot() root}. Provides a useful way to uniquely identify a
* merged annotation instance.
* @return the type hierarchy for the annotation
* @see MergedAnnotationPredicates#unique(Function)
*/
List<Class<? extends Annotation>> getTypeHierarchy();
/**
* Determine if the annotation is present on the source. Considers
* {@linkplain #isDirectlyPresent() directly present} and
@@ -109,14 +100,14 @@ public interface MergedAnnotation<A extends Annotation> {
boolean isMetaPresent();
/**
* Get the depth of this annotation related to its use as a
* meta-annotation. A directly declared annotation has a depth of {@code 0},
* a meta-annotation has a depth of {@code 1}, a meta-annotation on a
* meta-annotation has a depth of {@code 2}, etc. A {@linkplain #missing()
* missing} annotation will always return a depth of {@code -1}.
* @return the annotation depth or {@code -1} if the annotation is missing
* Get the distance of this annotation related to its use as a
* meta-annotation. A directly declared annotation has a distance of {@code 0},
* a meta-annotation has a distance of {@code 1}, a meta-annotation on a
* meta-annotation has a distance of {@code 2}, etc. A {@linkplain #missing()
* missing} annotation will always return a distance of {@code -1}.
* @return the annotation distance or {@code -1} if the annotation is missing
*/
int getDepth();
int getDistance();
/**
* Get the index of the aggregate collection containing this annotation.
@@ -130,33 +121,50 @@ public interface MergedAnnotation<A extends Annotation> {
int getAggregateIndex();
/**
* Get the source that ultimately declared the annotation, or
* Get the source that ultimately declared the root annotation, or
* {@code null} if the source is not known. If this merged annotation was
* created {@link MergedAnnotations#from(java.lang.reflect.AnnotatedElement)
* from} an {@link AnnotatedElement} then this source will be an element of
* the same type. If the annotation was loaded without using reflection, the
* source can be of any type, but should have a sensible {@code toString()}.
* Meta-annotations will return the same source as the {@link #getParent()}.
* Meta-annotations will always return the same source as the
* {@link #getRoot() root}.
* @return the source, or {@code null}
*/
@Nullable
Object getSource();
/**
* Get the parent of the meta-annotation, or {@code null} if the
* annotation is not {@linkplain #isMetaPresent() meta-present}.
* @return the parent annotation or {@code null}
* Get the source of the meta-annotation, or {@code null} if the
* annotation is not {@linkplain #isMetaPresent() meta-present}. The
* meta-source is the annotation that was meta-annotated with this
* annotation.
* @return the meta-annotation source or {@code null}
* @see #getRoot()
*/
@Nullable
MergedAnnotation<?> getParent();
MergedAnnotation<?> getMetaSource();
/**
* Get the root annotation, i.e. the {@link #getDepth() depth} {@code 0}
* Get the root annotation, i.e. the {@link #getDistance() distance} {@code 0}
* annotation as directly declared on the source.
* @return the root annotation
* @see #getMetaSource()
*/
MergedAnnotation<?> getRoot();
/**
* Return the complete list of annotation types from this annotation to the
* {@link #getRoot() root}. Provides a useful way to uniquely identify a
* merged annotation instance.
* @return the meta types for the annotation
* @see MergedAnnotationPredicates#unique(Function)
* @see #getRoot()
* @see #getMetaSource()
*/
List<Class<? extends Annotation>> getMetaTypes();
/**
* Determine if the specified attribute name has a non-default value when
* compared to the annotation declaration.
@@ -433,7 +441,8 @@ public interface MergedAnnotation<A extends Annotation> {
/**
* Create a new view of the annotation that exposes non-merged attribute values.
* <p>Methods from this view will return attribute values with only alias mirroring
* rules applied. Aliases to parent attributes will not be applied.
* rules applied. Aliases to {@link #getMetaSource() meta-source} attributes will
* not be applied.
* @return a non-merged view of the annotation
*/
MergedAnnotation<A> withNonMergedAttributes();

View File

@@ -81,8 +81,8 @@ public abstract class MergedAnnotationPredicates {
/**
* Create a new stateful, single use {@link Predicate} that matches only
* the first run of an extracted value. For example,
* {@code MergedAnnotationPredicates.firstRunOf(MergedAnnotation::depth)}
* will return the first annotation and a subsequent run of the same depth.
* {@code MergedAnnotationPredicates.firstRunOf(MergedAnnotation::distance)}
* will return the first annotation and a subsequent run of the same distance.
* <p>NOTE: This predicate only matches the first first run. Once the extracted
* value changes, the predicate always returns {@code false}.
* @param valueExtractor function used to extract the value to check

View File

@@ -40,8 +40,8 @@ public abstract class MergedAnnotationSelectors {
/**
* Select the nearest annotation, i.e. the one with the lowest depth.
* @return a selector that picks the annotation with the lowest depth
* Select the nearest annotation, i.e. the one with the lowest distance.
* @return a selector that picks the annotation with the lowest distance
*/
@SuppressWarnings("unchecked")
public static <A extends Annotation> MergedAnnotationSelector<A> nearest() {
@@ -50,7 +50,7 @@ public abstract class MergedAnnotationSelectors {
/**
* Select the first directly declared annotation when possible. If no direct
* annotations are declared then the earliest annotation is selected.
* annotations are declared then the nearest annotation is selected.
* @return a selector that picks the first directly declared annotation whenever possible
*/
@SuppressWarnings("unchecked")
@@ -66,14 +66,14 @@ public abstract class MergedAnnotationSelectors {
@Override
public boolean isBestCandidate(MergedAnnotation<Annotation> annotation) {
return annotation.getDepth() == 0;
return annotation.getDistance() == 0;
}
@Override
public MergedAnnotation<Annotation> select(
MergedAnnotation<Annotation> existing, MergedAnnotation<Annotation> candidate) {
if (candidate.getDepth() < existing.getDepth()) {
if (candidate.getDistance() < existing.getDistance()) {
return candidate;
}
return existing;
@@ -90,14 +90,14 @@ public abstract class MergedAnnotationSelectors {
@Override
public boolean isBestCandidate(MergedAnnotation<Annotation> annotation) {
return annotation.getDepth() == 0;
return annotation.getDistance() == 0;
}
@Override
public MergedAnnotation<Annotation> select(
MergedAnnotation<Annotation> existing, MergedAnnotation<Annotation> candidate) {
if (existing.getDepth() > 0 && candidate.getDepth() == 0) {
if (existing.getDistance() > 0 && candidate.getDistance() == 0) {
return candidate;
}
return existing;

View File

@@ -269,7 +269,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
* Stream all annotations and meta-annotations contained in this collection.
* The resulting stream is ordered first by the
* {@linkplain MergedAnnotation#getAggregateIndex() aggregate index} and then
* by the annotation depth (with the closest annotations first). This ordering
* by the annotation distance (with the closest annotations first). This ordering
* means that, for most use-cases, the most suitable annotations appear
* earliest in the stream.
* @return a stream of annotations

View File

@@ -233,15 +233,15 @@ final class MergedAnnotationsCollection implements MergedAnnotations {
@Override
public boolean tryAdvance(Consumer<? super MergedAnnotation<A>> action) {
int lowestDepth = Integer.MAX_VALUE;
int lowestDistance = Integer.MAX_VALUE;
int annotationResult = -1;
for (int annotationIndex = 0; annotationIndex < annotations.length; annotationIndex++) {
AnnotationTypeMapping mapping = getNextSuitableMapping(annotationIndex);
if (mapping != null && mapping.getDepth() < lowestDepth) {
if (mapping != null && mapping.getDistance() < lowestDistance) {
annotationResult = annotationIndex;
lowestDepth = mapping.getDepth();
lowestDistance = mapping.getDistance();
}
if (lowestDepth == 0) {
if (lowestDistance == 0) {
break;
}
}

View File

@@ -50,11 +50,6 @@ final class MissingMergedAnnotation<A extends Annotation> extends AbstractMerged
throw new NoSuchElementException("Unable to get type for missing annotation");
}
@Override
public List<Class<? extends Annotation>> getTypeHierarchy() {
return Collections.emptyList();
}
@Override
public boolean isPresent() {
return false;
@@ -68,7 +63,7 @@ final class MissingMergedAnnotation<A extends Annotation> extends AbstractMerged
@Override
@Nullable
public MergedAnnotation<?> getParent() {
public MergedAnnotation<?> getMetaSource() {
return null;
}
@@ -78,7 +73,12 @@ final class MissingMergedAnnotation<A extends Annotation> extends AbstractMerged
}
@Override
public int getDepth() {
public List<Class<? extends Annotation>> getMetaTypes() {
return Collections.emptyList();
}
@Override
public int getDistance() {
return -1;
}

View File

@@ -135,7 +135,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
this.attributeFilter = null;
this.resolvedRootMirrors = (resolvedRootMirrors != null ? resolvedRootMirrors :
mapping.getRoot().getMirrorSets().resolve(source, rootAttributes, this.valueExtractor));
this.resolvedMirrors = (getDepth() == 0 ? this.resolvedRootMirrors :
this.resolvedMirrors = (getDistance() == 0 ? this.resolvedRootMirrors :
mapping.getMirrorSets().resolve(source, this, this::getValueForMirrorResolution));
}
@@ -165,8 +165,8 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
}
@Override
public List<Class<? extends Annotation>> getTypeHierarchy() {
return this.mapping.getAnnotationTypeHierarchy();
public List<Class<? extends Annotation>> getMetaTypes() {
return this.mapping.getMetaTypes();
}
@Override
@@ -175,8 +175,8 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
}
@Override
public int getDepth() {
return this.mapping.getDepth();
public int getDistance() {
return this.mapping.getDistance();
}
@Override
@@ -192,23 +192,23 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
@Override
@Nullable
public MergedAnnotation<?> getParent() {
AnnotationTypeMapping parentMapping = this.mapping.getParent();
if (parentMapping == null) {
public MergedAnnotation<?> getMetaSource() {
AnnotationTypeMapping metaSourceMapping = this.mapping.getSource();
if (metaSourceMapping == null) {
return null;
}
return new TypeMappedAnnotation<>(parentMapping, this.classLoader, this.source, this.rootAttributes,
this.valueExtractor, this.aggregateIndex, this.resolvedRootMirrors);
return new TypeMappedAnnotation<>(metaSourceMapping, this.classLoader, this.source,
this.rootAttributes, this.valueExtractor, this.aggregateIndex, this.resolvedRootMirrors);
}
@Override
public MergedAnnotation<?> getRoot() {
if (getDepth() == 0) {
if (getDistance() == 0) {
return this;
}
AnnotationTypeMapping rootMapping = this.mapping.getRoot();
return new TypeMappedAnnotation<>(rootMapping, this.classLoader, this.source, this.rootAttributes,
this.valueExtractor, this.aggregateIndex, this.resolvedRootMirrors);
return new TypeMappedAnnotation<>(rootMapping, this.classLoader, this.source,
this.rootAttributes, this.valueExtractor, this.aggregateIndex, this.resolvedRootMirrors);
}
@Override
@@ -416,14 +416,14 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
}
}
if (!forMirrorResolution) {
attributeIndex = (mapping.getDepth() != 0 ?
attributeIndex = (mapping.getDistance() != 0 ?
this.resolvedMirrors :
this.resolvedRootMirrors)[attributeIndex];
}
if (attributeIndex == -1) {
return null;
}
if (mapping.getDepth() == 0) {
if (mapping.getDistance() == 0) {
Method attribute = mapping.getAttributes().get(attributeIndex);
Object result = this.valueExtractor.apply(attribute, this.rootAttributes);
return (result != null) ? result : attribute.getDefaultValue();
@@ -672,7 +672,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
}
if (logger.isEnabled()) {
String type = mapping.getAnnotationType().getName();
String item = (mapping.getDepth() == 0 ? "annotation " + type :
String item = (mapping.getDistance() == 0 ? "annotation " + type :
"meta-annotation " + type + " from " + mapping.getRoot().getAnnotationType().getName());
logger.log("Failed to introspect " + item, source, ex);
}

View File

@@ -542,7 +542,7 @@ final class TypeMappedAnnotations implements MergedAnnotations {
/**
* {@link Spliterator} used to consume merged annotations from the
* aggregates in depth fist order.
* aggregates in distance fist order.
*/
private class AggregatesSpliterator<A extends Annotation> implements Spliterator<MergedAnnotation<A>> {
@@ -578,15 +578,15 @@ final class TypeMappedAnnotations implements MergedAnnotations {
if (this.mappingCursors == null) {
this.mappingCursors = new int[aggregate.size()];
}
int lowestDepth = Integer.MAX_VALUE;
int lowestDistance = Integer.MAX_VALUE;
int annotationResult = -1;
for (int annotationIndex = 0; annotationIndex < aggregate.size(); annotationIndex++) {
AnnotationTypeMapping mapping = getNextSuitableMapping(aggregate, annotationIndex);
if (mapping != null && mapping.getDepth() < lowestDepth) {
if (mapping != null && mapping.getDistance() < lowestDistance) {
annotationResult = annotationIndex;
lowestDepth = mapping.getDepth();
lowestDistance = mapping.getDistance();
}
if (lowestDepth == 0) {
if (lowestDistance == 0) {
break;
}
}

View File

@@ -139,7 +139,7 @@ public interface AnnotatedTypeMetadata {
Adapt[] adaptations = Adapt.values(classValuesAsString, true);
return getAnnotations().stream(annotationName)
.filter(MergedAnnotationPredicates.unique(MergedAnnotation::getTypeHierarchy))
.filter(MergedAnnotationPredicates.unique(MergedAnnotation::getMetaTypes))
.map(MergedAnnotation::withNonMergedAttributes)
.collect(MergedAnnotationCollectors.toMultiValueMap(map ->
map.isEmpty() ? null : map, adaptations));