ElementKind} and {@linkplain Element#getSimpleName() bearing} the supplied {@code simpleName}
- * that the supplied {@code enclosingElement} {@linkplain Element#getEnclosedElements() encloses}, or {@code
- * null} if there is no such {@link VariableElement}.
+ * A convenience method that returns the first {@link VariableElement} {@linkplain Element#getSimpleName() bearing}
+ * the supplied {@code simpleName} that the supplied {@code enclosingElement} {@linkplain
+ * Element#getEnclosedElements() encloses}, or {@code null} if there is no such {@link
+ * VariableElement}.
*
* @param enclosingElement an {@link Element}; must not be {@code null}
*
@@ -1526,8 +1526,6 @@ public default TypeVariable typeVariable(Parameterizable p, final CharSequence n
*
* @see Element#getEnclosedElements()
*
- * @see ElementKind#isVariable()
- *
* @see Element#getSimpleName()
*
* @see VariableElement
@@ -1539,7 +1537,7 @@ public default VariableElement variableElement(final Element enclosingElement, f
case null -> throw new NullPointerException("enclosingElement");
case UniversalElement ue -> {
for (final UniversalElement ee : ue.getEnclosedElements()) {
- if (ee.getKind().isVariable() && ee.getSimpleName().contentEquals(simpleName)) {
+ if (ee.getSimpleName().contentEquals(simpleName)) {
yield ee;
}
}
@@ -1548,7 +1546,7 @@ public default VariableElement variableElement(final Element enclosingElement, f
default -> {
try (var lock = lock()) {
for (final Element ee : enclosingElement.getEnclosedElements()) {
- if (ee.getKind().isVariable() && ee.getSimpleName().contentEquals(simpleName)) {
+ if (ee.getSimpleName().contentEquals(simpleName)) {
yield (VariableElement)ee;
}
}
diff --git a/src/main/java/org/microbean/construct/UniversalConstruct.java b/src/main/java/org/microbean/construct/UniversalConstruct.java
index 49d6f46..adef09a 100644
--- a/src/main/java/org/microbean/construct/UniversalConstruct.java
+++ b/src/main/java/org/microbean/construct/UniversalConstruct.java
@@ -1,6 +1,6 @@
/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
*
- * Copyright © 2024–2025 microBean™.
+ * Copyright © 2024–2026 microBean™.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
@@ -20,22 +20,23 @@
import java.lang.constant.ConstantDesc;
import java.lang.constant.DynamicConstantDesc;
-import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import java.util.concurrent.CopyOnWriteArrayList;
+
import java.util.function.Supplier;
import javax.lang.model.AnnotatedConstruct;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
+import javax.lang.model.element.QualifiedNameable;
import javax.lang.model.type.TypeMirror;
import org.microbean.construct.constant.Constables;
-import org.microbean.construct.element.SyntheticAnnotationMirror;
import org.microbean.construct.element.UniversalAnnotation;
import org.microbean.construct.element.UniversalElement;
@@ -46,8 +47,6 @@
import static java.lang.constant.MethodHandleDesc.ofConstructor;
-import static java.util.Collections.unmodifiableList;
-
import static java.util.Objects.requireNonNull;
/**
@@ -56,8 +55,6 @@
*
* @param See the specification for the {@link AnnotatedConstruct#getAnnotation(Class)} method for important details.
+ * + *{@link UniversalConstruct} implementations deliberately permit modification of their {@linkplain + * #getAnnotationMirrors() annotations}. Consequently, this override first checks to see if there is at least one + * {@link AnnotationMirror} whose {@linkplain AnnotationMirror#getAnnotationType() annotation type} is declared by a + * {@link javax.lang.model.element.TypeElement} whose {@linkplain + * javax.lang.model.element.TypeElement#getQualifiedName() qualified name} is {@linkplain + * javax.lang.model.element.Name#contentEquals(CharSequence) equal to} the {@linkplain Class#getCanonicalName() + * canonical name} of the supplied {@link Class}. If there is, then the {@link AnnotatedConstruct#getAnnotation(Class) + * getAnnotation(Class)} method is invoked on the {@linkplain #delegate() delegate} and its result is + * returned. Otherwise, {@code null} is returned.
+ * + *There are circumstances where the {@link Annotation} returned by this method may not accurately reflect a + * synthetic annotation added to this {@link AnnotatedConstruct} implementation's {@linkplain #getAnnotationMirrors() + * annotations}.
+ * + *In general, the use of this method is discouraged.
+ * + * @param annotationType a {@link Class} that is an annotation interface; must not be {@code null} + * + * @return an appropriate {@link Annotation}, or {@code null} + * + * @exception NullPointerException if {@code annotationType} is {@code null} + * + * @see AnnotatedConstruct#getAnnotation(Class) + * + * @deprecated The use of this method is discouraged. + */ + @Deprecated @Override // AnnotatedConstruct @SuppressWarnings("try") public final A getAnnotation(final Class annotationType) { - // TODO: is this lock actually needed, given how delegateSupplier works? - try (var lock = this.domain().lock()) { - return this.delegate().getAnnotation(annotationType); + if (!annotationType.isAnnotation()) { + return null; } + final String canonicalName = annotationType.getCanonicalName(); + for (final AnnotationMirror a : this.getAnnotationMirrors()) { + if (((QualifiedNameable)a.getAnnotationType().asElement()).getQualifiedName().contentEquals(canonicalName)) { + // TODO: is this lock actually needed, given how delegateSupplier works? + try (var lock = this.domain().lock()) { + return this.delegate().getAnnotation(annotationType); + } + } + } + return null; + } + /** + * Makes a best effort to return an array of {@link Annotation}s of the appropriate type + * associated with this {@link UniversalConstruct} implementation. + * + *See the specification for the {@link AnnotatedConstruct#getAnnotationsByType(Class)} method for important + * details.
+ * + *{@link UniversalConstruct} implementations deliberately permit modification of their {@linkplain + * #getAnnotationMirrors() annotations}. Consequently, this override first checks to see if there is at least one + * {@link AnnotationMirror} whose {@linkplain AnnotationMirror#getAnnotationType() annotation type} is declared by a + * {@link javax.lang.model.element.TypeElement} whose {@linkplain + * javax.lang.model.element.TypeElement#getQualifiedName() qualified name} is {@linkplain + * javax.lang.model.element.Name#contentEquals(CharSequence) equal to} the {@linkplain Class#getCanonicalName() + * canonical name} of the supplied {@link Class}. If there is, then the {@link + * AnnotatedConstruct#getAnnotationsByType(Class) getAnnotationsByType(Class)} method is invoked on the {@linkplain + * #delegate() delegate} and its result is returned. Otherwise, an empty array is returned.
+ * + *There are circumstances where the {@link Annotation} array returned by this method may not accurately reflect + * synthetic annotations added to this {@link AnnotatedConstruct} implementation's {@linkplain #getAnnotationMirrors() + * annotations}.
+ * + *In general, the use of this method is discouraged.
+ * + * @param annotationType a {@link Class} that is an annotation interface; must not be {@code null} + * + * @return an appropriate {@link Annotation}, or {@code null} + * + * @exception NullPointerException if {@code annotationType} is {@code null} + * + * @see AnnotatedConstruct#getAnnotation(Class) + * + * @deprecated The use of this method is discouraged. + */ + @Deprecated @Override // AnnotatedConstruct - @SuppressWarnings("try") + @SuppressWarnings({"try", "unchecked"}) public final A[] getAnnotationsByType(final Class annotationType) { - // TODO: is this lock actually needed, given how delegateSupplier works? - try (var lock = this.domain().lock()) { - return this.delegate().getAnnotationsByType(annotationType); + if (!annotationType.isAnnotation()) { + return (A[])EMPTY_ANNOTATION_ARRAY; + } + final String canonicalName = annotationType.getCanonicalName(); + for (final AnnotationMirror a : this.getAnnotationMirrors()) { + if (((QualifiedNameable)a.getAnnotationType().asElement()).getQualifiedName().contentEquals(canonicalName)) { + // TODO: is this lock actually needed, given how delegateSupplier works? + try (var lock = this.domain().lock()) { + return this.delegate().getAnnotationsByType(annotationType); + } + } } + return (A[])EMPTY_ANNOTATION_ARRAY; } @Override // Object @@ -320,7 +374,6 @@ public final String toString() { try (var lock = this.domain().lock()) { s = this.s = this.delegate().toString(); // volatile write, read } - assert s != null; } return s; } @@ -345,7 +398,7 @@ public final String toString() { */ @SuppressWarnings("unchecked") public static finalThis method is a more capable, better-typed replacement of the {@link + * javax.lang.model.util.Elements#getAllAnnotationMirrors(Element)} method, and should be preferred.
+ * + * @param e an {@link Element}; must not be {@code null} + * + * @return a determinate, non-{@code null}, immutable {@link List} of {@link AnnotationMirror}s present on + * the supplied {@link Element} + * + * @exception NullPointerException if {@code e} is {@code null} + * + * @see javax.lang.model.util.Elements#getAllAnnotationMirrors(Element) + */ + public static final List extends AnnotationMirror> allAnnotationMirrors(Element e) { + final ListThe first and possibly only element in the {@link Stream} that is returned is guaranteed to be the supplied + * {@link AnnotationMirror}.
* * @param am an {@link AnnotationMirror}; must not be {@code null} * - * @param name a {@link CharSequence}; may be {@code null} in which case {@code null} will be returned + * @return a non-{@code null} {@link Stream} of {@link AnnotationMirror}s * - * @return the result of invoking {@link AnnotationValue#getValue() getValue()} on an {@link AnnotationValue}, or - * {@code null} + * @exception NullPointerException if {@code am} is {@code null} + * + * @see #streamBreadthFirst(AnnotatedConstruct) + */ + public static final StreamThe first and possibly only element in the {@link Stream} that is returned is guaranteed to be the supplied + * {@link AnnotationMirror}.
+ * + * @param am an {@link AnnotationMirror}; must not be {@code null} + * + * @return a non-{@code null} {@link Stream} of {@link AnnotationMirror}s * * @exception NullPointerException if {@code am} is {@code null} * - * @see AnnotationValue + * @see #streamDepthFirst(AnnotatedConstruct) + */ + // (Convenience.) + public static final Stream