001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2026 microBean™.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
006 * the License. You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
011 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
012 * specific language governing permissions and limitations under the License.
013 */
014package org.microbean.construct.element;
015
016import java.lang.annotation.ElementType;
017import java.lang.annotation.RetentionPolicy;
018
019import java.util.ArrayDeque;
020import java.util.ArrayList;
021import java.util.Collection;
022import java.util.EnumSet;
023import java.util.Iterator;
024import java.util.LinkedList;
025import java.util.List;
026import java.util.Map;
027import java.util.Map.Entry;
028import java.util.Objects;
029import java.util.Queue;
030import java.util.SequencedMap;
031import java.util.Set;
032
033import java.util.function.Predicate;
034
035import java.util.stream.Stream;
036
037import javax.lang.model.AnnotatedConstruct;
038
039import javax.lang.model.element.AnnotationMirror;
040import javax.lang.model.element.AnnotationValue;
041import javax.lang.model.element.Element;
042import javax.lang.model.element.ExecutableElement;
043import javax.lang.model.element.Name;
044import javax.lang.model.element.QualifiedNameable;
045import javax.lang.model.element.TypeElement;
046import javax.lang.model.element.VariableElement;
047
048import javax.lang.model.type.ArrayType;
049import javax.lang.model.type.DeclaredType;
050import javax.lang.model.type.PrimitiveType;
051import javax.lang.model.type.TypeMirror;
052
053import static java.util.Collections.unmodifiableList;
054import static java.util.Collections.unmodifiableSequencedMap;
055import static java.util.Collections.unmodifiableSet;
056
057import static java.util.LinkedHashMap.newLinkedHashMap;
058
059import static java.util.HashSet.newHashSet;
060
061import static java.util.function.Function.identity;
062
063import static java.util.stream.Stream.concat;
064import static java.util.stream.Stream.empty;
065import static java.util.stream.Stream.iterate;
066
067import static javax.lang.model.element.ElementKind.ANNOTATION_TYPE;
068import static javax.lang.model.element.ElementKind.CLASS;
069import static javax.lang.model.element.ElementKind.METHOD;
070
071import static javax.lang.model.element.Modifier.ABSTRACT;
072import static javax.lang.model.element.Modifier.PUBLIC;
073
074import static javax.lang.model.type.TypeKind.ARRAY;
075import static javax.lang.model.type.TypeKind.DECLARED;
076
077import static javax.lang.model.util.ElementFilter.methodsIn;
078
079/**
080 * A utility class for working with annotations as represented by {@link AnnotationMirror}s, {@link ExecutableElement}s,
081 * and {@link AnnotationValue}s.
082 *
083 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
084 */
085public final class AnnotationMirrors {
086
087  private static final Set<ElementType> EMPTY_ELEMENT_TYPES = unmodifiableSet(EnumSet.noneOf(ElementType.class));
088
089  private static final SequencedMap<?, ?> EMPTY_MAP = unmodifiableSequencedMap(newLinkedHashMap(0));
090
091  private static final SameAnnotationValueVisitor sameAnnotationValueVisitor = new SameAnnotationValueVisitor();
092
093  private AnnotationMirrors() {
094    super();
095  }
096
097  /**
098   * Returns a determinate, non-{@code null}, immutable {@link List} of {@link AnnotationMirror}s <dfn>present</dfn> on
099   * the supplied {@link Element}.
100   *
101   * <p>This method is a more capable, better-typed replacement of the {@link
102   * javax.lang.model.util.Elements#getAllAnnotationMirrors(Element)} method, and should be preferred.</p>
103   *
104   * @param e an {@link Element}; must not be {@code null}
105   *
106   * @return a determinate, non-{@code null}, immutable {@link List} of {@link AnnotationMirror}s <dfn>present</dfn> on
107   * the supplied {@link Element}
108   *
109   * @exception NullPointerException if {@code e} is {@code null}
110   *
111   * @see javax.lang.model.util.Elements#getAllAnnotationMirrors(Element)
112   */
113  public static final List<? extends AnnotationMirror> allAnnotationMirrors(Element e) {
114    final List<AnnotationMirror> allAnnotations = new LinkedList<>(e.getAnnotationMirrors());
115    WHILE_LOOP:
116    while (e.getKind() == CLASS && e instanceof TypeElement te) {
117      final TypeMirror sct = te.getSuperclass();
118      if (sct.getKind() != DECLARED || !(sct instanceof DeclaredType)) {
119        break;
120      }
121      e = ((DeclaredType)sct).asElement();
122      final List<? extends AnnotationMirror> superclassAnnotations = e.getAnnotationMirrors();
123      if (!superclassAnnotations.isEmpty()) {
124        int added = 0;
125        for (final AnnotationMirror superclassAnnotation : superclassAnnotations) {
126          if (inherited(superclassAnnotation)) {
127            for (final AnnotationMirror a : allAnnotations.subList(added, allAnnotations.size())) {
128              if (((QualifiedNameable)superclassAnnotation.getAnnotationType().asElement()).getQualifiedName().contentEquals(((QualifiedNameable)a.getAnnotationType().asElement()).getQualifiedName())) {
129                continue WHILE_LOOP;
130              }
131            }
132            // javac prepends superclass annotations, resulting in a strage order; we duplicate it
133            allAnnotations.addFirst(superclassAnnotation);
134            ++added;
135          }
136        }
137      }
138    }
139    return allAnnotations.isEmpty() ? List.of() : unmodifiableList(allAnnotations);
140  }
141
142  /**
143   * For the supplied {@link AnnotationMirror}, returns an immutable, determinate {@link Map} of {@link AnnotationValue}
144   * instances indexed by its {@link ExecutableElement}s to which they apply.
145   *
146   * <p>Each {@link ExecutableElement} represents an <a
147   * href="https://docs.oracle.com/javase/specs/jls/se25/html/jls-9.html#jls-9.6.1">annotation interface element</a> and
148   * meets the requirements of such an element.</p>
149   *
150   * <p>Each {@link AnnotationValue} represents the value of an annotation interface element and meets the requirements
151   * for annotation values.</p>
152   *
153   * <p>This method is a more capable, better-typed replacement of the {@link
154   * javax.lang.model.util.Elements#getElementValuesWithDefaults(AnnotationMirror)} method, and should be preferred.</p>
155   *
156   * @param a an {@link AnnotationMirror}; may be {@code null} in which case an empty, immutable, determinate {@link
157   * Map} will be returned
158   *
159   * @return an immutable, determinate {@link Map} of {@link AnnotationValue} instances indexed by {@link
160   * ExecutableElement}s
161   *
162   * @spec https://docs.oracle.com/javase/specs/jls/se25/html/jls-9.html#jls-9.6.1 Java Language Specification, section
163   * 9.6.1
164   *
165   * @see javax.lang.model.util.Elements#getElementValuesWithDefaults(AnnotationMirror)
166   */
167  public static final SequencedMap<ExecutableElement, AnnotationValue> allAnnotationValues(final AnnotationMirror a) {
168    if (a == null) {
169      return emptySequencedMap();
170    }
171    final Collection<? extends ExecutableElement> elements = methodsIn(a.getAnnotationType().asElement().getEnclosedElements());
172    if (elements.isEmpty()) {
173      return emptySequencedMap();
174    }
175    final SequencedMap<ExecutableElement, AnnotationValue> m = newLinkedHashMap(elements.size());
176    final Map<? extends ExecutableElement, ? extends AnnotationValue> explicitValues = a.getElementValues();
177    for (final ExecutableElement ee : elements) {
178      // You're going to want to use getOrDefault() here. Go ahead and try but you'll run into typing issues.
179      m.put(ee, explicitValues.containsKey(ee) ? explicitValues.get(ee) : ee.getDefaultValue());
180    }
181    return m.isEmpty() ? emptySequencedMap() : unmodifiableSequencedMap(m);
182  }
183
184  /**
185   * Returns the (determinate) {@link AnnotationMirror} <em>directly present</em> on the supplied {@link
186   * AnnotatedConstruct} whose {@linkplain AnnotationMirror#getAnnotationType() annotation type}'s {@link
187   * javax.lang.model.type.DeclaredType#asElement() TypeElement} {@linkplain TypeElement#getQualifiedName() bears} the
188   * supplied {@code fullyQualifiedName}, or {@code null} if no such {@link AnnotationMirror} exists.
189   *
190   * @param ac an {@link AnnotatedConstruct}; must not be {@code null}
191   *
192   * @param name a {@link CharSequence}; may be {@code null} in which case {@code null} will be returned
193   *
194   * @return an {@link AnnotationMirror}, or {@code null}
195   *
196   * @exception NullPointerException if {@code ac} is {@code null}
197   *
198   * @see #streamBreadthFirst(AnnotatedConstruct)
199   *
200   * @see #streamDepthFirst(AnnotatedConstruct)
201   *
202   * @see AnnotationMirror#getAnnotationType()
203   *
204   * @see javax.lang.model.type.DeclaredType#asElement()
205   *
206   * @see TypeElement#getQualifiedName()
207   *
208   * @see javax.lang.model.element.Name#contentEquals(CharSequence)
209   */
210  public static final AnnotationMirror get(final AnnotatedConstruct ac, final CharSequence name) {
211    if (name == null) {
212      return null;
213    }
214    for (final AnnotationMirror am : ac.getAnnotationMirrors()) {
215      final TypeElement e = (TypeElement)am.getAnnotationType().asElement();
216      if (e.getQualifiedName().contentEquals(name)) {
217        return am;
218      }
219    }
220    return null;
221  }
222
223  /**
224   * A convenience method that returns a value for an annotation interface element {@linkplain
225   * ExecutableElement#getSimpleName() named} by the supplied {@link CharSequence} and logically owned by the supplied
226   * {@link AnnotationMirror}, or {@code null} if no such value exists.
227   *
228   * @param am an {@link AnnotationMirror}; must not be {@code null}
229   *
230   * @param name a {@link CharSequence}; may be {@code null} in which case {@code null} will be returned
231   *
232   * @return the result of invoking {@link AnnotationValue#getValue() getValue()} on an {@link AnnotationValue}, or
233   * {@code null}
234   *
235   * @exception NullPointerException if {@code am} is {@code null}
236   *
237   * @see AnnotationValue
238   *
239   * @see #allAnnotationValues(AnnotationMirror)
240   *
241   * @see #get(Map, CharSequence)
242   */
243  public static final Object get(final AnnotationMirror am, final CharSequence name) {
244    return name == null ? null : get(allAnnotationValues(am), name);
245  }
246
247  /**
248   * A convenience method that returns a value for an annotation interface element {@linkplain
249   * ExecutableElement#getSimpleName() named} by the supplied {@link CharSequence} and found in the supplied {@link
250   * Map}, or {@code null} if no such value exists.
251   *
252   * @param values a {@link Map} as returned by the {@link #allAnnotationValues(AnnotationMirror)} method; must not be
253   * {@code null}
254   *
255   * @param name a {@link CharSequence}; may be {@code null} in which case {@code null} will be returned
256   *
257   * @return the result of invoking {@link AnnotationValue#getValue() getValue()} on a suitable {@link AnnotationValue}
258   * found in the supplied {@link Map}, or {@code null}
259   *
260   * @exception NullPointerException if {@code values} is {@code null}
261   *
262   * @see AnnotationValue
263   *
264   * @see #allAnnotationValues(AnnotationMirror)
265   */
266  public static final Object get(final Map<? extends ExecutableElement, ? extends AnnotationValue> values,
267                                 final CharSequence name) {
268    if (name == null) {
269      return null;
270    }
271    for (final Entry<? extends Element, ? extends AnnotationValue> e : values.entrySet()) {
272      if (e.getKey().getSimpleName().contentEquals(name)) {
273        final AnnotationValue av = e.getValue();
274        return av == null ? null : av.getValue();
275      }
276    }
277    return null;
278  }
279
280  /**
281   * Returns {@code true} if and only if the supplied {@link AnnotationMirror}'s {@linkplain
282   * AnnotationMirror#getAnnotationType() annotation type} is {@linkplain javax.lang.model.type.DeclaredType#asElement()
283   * declared by} an element that has been (meta-) {@linkplain Element#getAnnotationMirrors() annotated} with {@link
284   * java.lang.annotation.Inherited}.
285   *
286   * @param a an {@link AnnotationMirror}; must not be {@code null}
287   *
288   * @return {@code true} if and only if the supplied {@link AnnotationMirror}'s {@linkplain
289   * AnnotationMirror#getAnnotationType() annotation type} is {@linkplain javax.lang.model.type.DeclaredType#asElement()
290   * declared by} an element that has been (meta-) {@linkplain Element#getAnnotationMirrors() annotated} with {@link
291   * java.lang.annotation.Inherited}
292   *
293   * @exception NullPointerException if {@code a} is {@code null}
294   *
295   * @see #inherited(TypeElement)
296   */
297  public static final boolean inherited(final AnnotationMirror a) {
298    return inherited((TypeElement)a.getAnnotationType().asElement());
299  }
300
301  /**
302   * Returns {@code true} if and only if the supplied {@link TypeElement} represents an {@linkplain
303   * javax.lang.model.element.ElementKind#ANNOTATION_TYPE annotation interface} and if it has been (meta-) annotated
304   * with {@link java.lang.annotation.Inherited}.
305   *
306   * @param annotationInterface a {@link TypeElement} representing an {@linkplain
307   * javax.lang.model.element.ElementKind#ANNOTATION_TYPE annotation interface}; must not be {@code null}
308   *
309   * @return {@code true} if and only if the supplied {@link TypeElement} represents an {@linkplain
310   * javax.lang.model.element.ElementKind#ANNOTATION_TYPE annotation interface} and if it has been (meta-) annotated
311   * with {@link java.lang.annotation.Inherited}
312   *
313   * @exception NullPointerException if {@code annotationInterface} is {@code null}
314   *
315   * @spec https://docs.oracle.com/javase/specs/jls/se25/html/jls-9.html#jls-9.6.4.3 Java Language Specification,
316   * section 9.6.4.3
317   */
318  public static final boolean inherited(final TypeElement annotationInterface) {
319    if (annotationInterface.getKind() == ANNOTATION_TYPE) {
320      for (final AnnotationMirror ma : annotationInterface.getAnnotationMirrors()) {
321        if (((QualifiedNameable)ma.getAnnotationType().asElement()).getQualifiedName().contentEquals("java.lang.annotation.Inherited")) {
322          return true;
323        }
324      }
325    }
326    return false;
327  }
328
329  /**
330   * Returns a {@link RetentionPolicy} for the supplied {@link AnnotationMirror}, or {@link RetentionPolicy#CLASS} if,
331   * for any reason, a retention policy cannot be found or computed.
332   *
333   * @param a an {@link AnnotationMirror}; must not be {@code null}
334   *
335   * @return the {@link RetentionPolicy} for the supplied {@link AnnotationMirror}; never {@code null}
336   *
337   * @exception NullPointerException if {@code a} is {@code null}
338   *
339   * @see #retentionPolicy(TypeElement)
340   */
341  public static final RetentionPolicy retentionPolicy(final AnnotationMirror a) {
342    return retentionPolicy((TypeElement)a.getAnnotationType().asElement());
343  }
344
345  /**
346   * Returns a {@link RetentionPolicy} for the supplied {@link TypeElement} representing an {@linkplain
347   * javax.lang.model.element.ElementKind#ANNOTATION_TYPE annotation interface}, or {@link RetentionPolicy#CLASS} if,
348   * for any reason, a retention policy cannot be found or computed.
349   *
350   * @param annotationInterface a {@link TypeElement}; must be non-{@code null} and should represent an {@linkplain
351   * javax.lang.model.element.ElementKind#ANNOTATION_TYPE annotation interface}
352   *
353   * @return the {@link RetentionPolicy} for the supplied {@link TypeElement}; never {@code null}
354   *
355   * @exception NullPointerException if {@code annotationInterface} is {@code null}
356   *
357   * @see RetentionPolicy
358   *
359   * @spec https://docs.oracle.com/javase/specs/jls/se25/html/jls-9.html#jls-9.6.4.2 Java Language Specification,
360   * section 9.6.4.2
361   */
362  public static final RetentionPolicy retentionPolicy(final TypeElement annotationInterface) {
363    if (annotationInterface.getKind() == ANNOTATION_TYPE) {
364      for (final AnnotationMirror ma : annotationInterface.getAnnotationMirrors()) {
365        if (((QualifiedNameable)ma.getAnnotationType().asElement()).getQualifiedName().contentEquals("java.lang.annotation.Retention")) {
366          return RetentionPolicy.valueOf(((VariableElement)get(ma, "value")).getSimpleName().toString());
367        }
368      }
369    }
370    return RetentionPolicy.CLASS;
371  }
372
373  /**
374   * Determines whether the two {@link AnnotationMirror}s represent the same (otherwise opaque) annotation.
375   *
376   * @param am0 an {@link AnnotationMirror}; may be {@code null}
377   *
378   * @param am1 an {@link AnnotationMirror}; may be {@code null}
379   *
380   * @return {@code true} if the supplied {@link AnnotationMirror}s represent the same (otherwise opaque) annotation;
381   * {@code false} otherwise
382   *
383   * @see #sameAnnotation(AnnotationMirror, AnnotationMirror)
384   */
385  public static final boolean sameAnnotation(final AnnotationMirror am0, final AnnotationMirror am1) {
386    return sameAnnotation(am0, am1, x -> true);
387  }
388
389  /**
390   * Determines whether the two {@link AnnotationMirror}s represent the same (underlying, otherwise opaque) annotation.
391   *
392   * @param am0 an {@link AnnotationMirror}; may be {@code null}
393   *
394   * @param am1 an {@link AnnotationMirror}; may be {@code null}
395   *
396   * @param p a {@link Predicate} that returns {@code true} if a given {@link ExecutableElement}, representing an
397   * annotation interface element, is to be included in the computation; may be {@code null} in which case it is as if
398   * {@code ()-> true} were supplied instead
399   *
400   * @return {@code true} if the supplied {@link AnnotationMirror}s represent the same (underlying, otherwise opaque)
401   * annotation; {@code false} otherwise
402   *
403   * @see SameAnnotationValueVisitor
404   *
405   * @see #allAnnotationValues(AnnotationMirror)
406   */
407  public static final boolean sameAnnotation(final AnnotationMirror am0,
408                                             final AnnotationMirror am1,
409                                             Predicate<? super ExecutableElement> p) {
410    if (am0 == am1) {
411      return true;
412    } else if (am0 == null || am1 == null) {
413      return false;
414    }
415    final QualifiedNameable qn0 = (QualifiedNameable)am0.getAnnotationType().asElement();
416    final QualifiedNameable qn1 = (QualifiedNameable)am1.getAnnotationType().asElement();
417    if (qn0 != qn1 && !qn0.getQualifiedName().contentEquals(qn1.getQualifiedName())) {
418      return false;
419    }
420    final SequencedMap<ExecutableElement, AnnotationValue> m0 = allAnnotationValues(am0);
421    final SequencedMap<ExecutableElement, AnnotationValue> m1 = allAnnotationValues(am1);
422    if (m0 == m1) {
423      // Unlikely, but hey
424      return true;
425    } else if (m0.size() != m1.size()) {
426      return false;
427    }
428    if (p == null) {
429      p = AnnotationMirrors::returnTrue;
430    }
431    final Iterator<Entry<ExecutableElement, AnnotationValue>> i0 = m0.entrySet().iterator();
432    final Iterator<Entry<ExecutableElement, AnnotationValue>> i1 = m1.entrySet().iterator();
433    while (i0.hasNext()) {
434      final Entry<ExecutableElement, AnnotationValue> e0 = i0.next();
435      final Entry<ExecutableElement, AnnotationValue> e1 = i1.next();
436      final ExecutableElement ee0 = e0.getKey();
437      if (p.test(ee0) &&
438          (!e0.getKey().getSimpleName().contentEquals(e1.getKey().getSimpleName()) ||
439           !sameAnnotationValueVisitor.visit(e0.getValue(), e1.getValue().getValue()))) {
440        return false;
441      }
442    }
443    return !i1.hasNext();
444  }
445
446  /**
447   * Returns a non-{@code null}, sequential, {@link Stream} that traverses the supplied {@link AnnotatedConstruct}'s
448   * {@linkplain AnnotatedConstruct#getAnnotationMirrors() annotations}, and their (meta-) annotations, in breadth-first
449   * order.
450   *
451   * <p>Cycles are avoided via usage of the {@link #sameAnnotation(AnnotationMirror, AnnotationMirror)} method.</p>
452   *
453   * @param ac an {@link AnnotatedConstruct}; must not be {@code null}
454   *
455   * @return a non-{@code null} {@link Stream} of {@link AnnotationMirror}s
456   *
457   * @exception NullPointerException if {@code ac} is {@code null}
458   *
459   * @see #sameAnnotation(AnnotationMirror, AnnotationMirror)
460   */
461  public static final Stream<AnnotationMirror> streamBreadthFirst(final AnnotatedConstruct ac) {
462    return streamBreadthFirst(ac, AnnotationMirrors::returnTrue);
463  }
464
465  /**
466   * Returns a non-{@code null}, sequential, {@link Stream} that traverses the supplied {@link AnnotatedConstruct}'s
467   * {@linkplain AnnotatedConstruct#getAnnotationMirrors() annotations}, and their (meta-) annotations, in breadth-first
468   * order.
469   *
470   * <p>Cycles and duplicates are avoided via usage of the {@link #sameAnnotation(AnnotationMirror, AnnotationMirror,
471   * Predicate)} method.</p>
472   *
473   * @param ac an {@link AnnotatedConstruct}; must not be {@code null}
474   *
475   * @param p a {@link Predicate} that returns {@code true} if a given {@link ExecutableElement}, representing an
476   * annotation interface element, is to be included in comparison operations; may be {@code null} in which case it is
477   * as if {@code ()-> true} were supplied instead
478   *
479   * @return a non-{@code null} {@link Stream} of {@link AnnotationMirror}s
480   *
481   * @exception NullPointerException if {@code ac} is {@code null}
482   *
483   * @see #streamBreadthFirst(Collection, Predicate)
484   *
485   * @see #sameAnnotation(AnnotationMirror, AnnotationMirror)
486   */
487  public static final Stream<AnnotationMirror> streamBreadthFirst(final AnnotatedConstruct ac,
488                                                                  final Predicate<? super ExecutableElement> p) {
489    return streamBreadthFirst(ac.getAnnotationMirrors(), p);
490  }
491
492  /**
493   * Returns a non-{@code null}, sequential, {@link Stream} that traverses the supplied {@link AnnotationMirror}s, and
494   * their (meta-) annotations, in breadth-first order.
495   *
496   * <p>Cycles and duplicates are avoided via usage of the {@link #sameAnnotation(AnnotationMirror, AnnotationMirror)}
497   * method. Consequently the returned {@link Stream} yields only semantically distinct elements.</p>
498   *
499   * @param ams a {@link Collection} of {@link AnnotationMirror}s; must not be {@code null}
500   *
501   * @return a non-{@code null} {@link Stream} of (distinct) {@link AnnotationMirror}s
502   *
503   * @exception NullPointerException if {@code ams} is {@code null}
504   *
505   * @see #sameAnnotation(AnnotationMirror, AnnotationMirror)
506   */
507  public static final Stream<AnnotationMirror> streamBreadthFirst(final Collection<? extends AnnotationMirror> ams) {
508    return streamBreadthFirst(ams, AnnotationMirrors::returnTrue);
509  }
510
511  /**
512   * Returns a non-{@code null}, sequential, {@link Stream} that traverses the supplied {@link AnnotationMirror}s, and
513   * their (meta-) annotations, in breadth-first order.
514   *
515   * <p>Cycles and duplicates are avoided via usage of the {@link #sameAnnotation(AnnotationMirror, AnnotationMirror,
516   * Predicate)} method. Consequently the returned {@link Stream} yields only semantically distinct elements.</p>
517   *
518   * @param ams a {@link Collection} of {@link AnnotationMirror}s; must not be {@code null}
519   *
520   * @param p a {@link Predicate} that returns {@code true} if a given {@link ExecutableElement}, representing an
521   * annotation element, is to be included in comparison operations; may be {@code null} in which case it is as if
522   * {@code ()-> true} were supplied instead
523   *
524   * @return a non-{@code null} {@link Stream} of (distinct) {@link AnnotationMirror}s
525   *
526   * @exception NullPointerException if {@code ams} is {@code null}
527   *
528   * @see #sameAnnotation(AnnotationMirror, AnnotationMirror, Predicate)
529   */
530  public static final Stream<AnnotationMirror> streamBreadthFirst(final Collection<? extends AnnotationMirror> ams,
531                                                                  final Predicate<? super ExecutableElement> p) {
532    if (ams.isEmpty()) {
533      return empty();
534    }
535    final Collection<AnnotationMirror> seen = new ArrayList<>();
536    final Queue<AnnotationMirror> q = new ArrayDeque<>();
537    DEDUP_LOOP_0:
538    for (final AnnotationMirror a0 : ams) {
539      for (final AnnotationMirror a1 : seen) {
540        if (sameAnnotation(a0, a1, p)) {
541          continue DEDUP_LOOP_0;
542        }
543      }
544      q.add(a0);
545      seen.add(a0);
546    }
547    return
548      iterate(q.poll(),
549              Objects::nonNull,
550              a0 -> {
551                DEDUP_LOOP_1:
552                for (final AnnotationMirror a1 : a0.getAnnotationType().asElement().getAnnotationMirrors()) {
553                  for (final AnnotationMirror a2 : seen) {
554                    if (sameAnnotation(a1, a2, p)) {
555                      continue DEDUP_LOOP_1;
556                    }
557                  }
558                  q.add(a1);
559                  seen.add(a1);
560                }
561                return q.poll();
562              });
563  }
564
565  /**
566   * Returns a non-{@code null}, sequential {@link Stream} that traverses the supplied {@link AnnotatedConstruct}'s
567   * {@linkplain AnnotatedConstruct#getAnnotationMirrors() annotations}, and their (meta-) annotations, in depth-first
568   * order.
569   *
570   * <p>Cycles are avoided via usage of the {@link #sameAnnotation(AnnotationMirror, AnnotationMirror)} method.</p>
571   *
572   * @param ac an {@link AnnotatedConstruct}; must not be {@code null}
573   *
574   * @return a non-{@code null} {@link Stream} of {@link AnnotationMirror}s
575   *
576   * @exception NullPointerException if {@code ac} is {@code null}
577   *
578   * @see #streamDepthFirst(AnnotatedConstruct, Predicate)
579   *
580   * @see #sameAnnotation(AnnotationMirror, AnnotationMirror)
581   */
582  public static final Stream<AnnotationMirror> streamDepthFirst(final AnnotatedConstruct ac) {
583    return streamDepthFirst(ac, AnnotationMirrors::returnTrue); // 17 == arbitrary
584  }
585
586  /**
587   * Returns a non-{@code null}, sequential {@link Stream} that traverses the supplied {@link AnnotatedConstruct}'s
588   * {@linkplain AnnotatedConstruct#getAnnotationMirrors() annotations}, and their (meta-) annotations, in depth-first
589   * order.
590   *
591   * <p>Cycles and duplicates are avoided via usage of the {@link #sameAnnotation(AnnotationMirror, AnnotationMirror, Predicate)}
592   * method.</p>
593   *
594   * @param ac an {@link AnnotatedConstruct}; must not be {@code null}
595   *
596   * @param p a {@link Predicate} that returns {@code true} if a given {@link ExecutableElement}, representing an
597   * annotation element, is to be included in comparison operations; may be {@code null} in which case it is as if
598   * {@code ()-> true} were supplied instead
599   *
600   * @return a non-{@code null} {@link Stream} of {@link AnnotationMirror}s
601   *
602   * @exception NullPointerException if {@code ac} is {@code null}
603   *
604   * @see #sameAnnotation(AnnotationMirror, AnnotationMirror)
605   */
606  public static final Stream<AnnotationMirror> streamDepthFirst(final AnnotatedConstruct ac,
607                                                                final Predicate<? super ExecutableElement> p) {
608    return streamDepthFirst(ac, new ArrayList<>(17), p); // 17 == arbitrary
609  }
610
611  /**
612   * Returns a non-{@code null}, sequential {@link Stream} that traverses the supplied {@link AnnotationMirror}s, and
613   * their (meta-) annotations, in depth-first order.
614   *
615   * <p>Cycles and duplicates are avoided via usage of the {@link #sameAnnotation(AnnotationMirror, AnnotationMirror)}
616   * method.</p>
617   *
618   * @param ams a non-{@code null} {@link Collection} of {@link AnnotationMirror}s
619   *
620   * @return a non-{@code null} {@link Stream} of {@link AnnotationMirror}s
621   *
622   * @exception NullPointerException if {@code ams} is {@code null}
623   *
624   * @see #streamDepthFirst(Collection, Predicate)
625   *
626   * @see #sameAnnotation(AnnotationMirror, AnnotationMirror)
627   */
628  public static final Stream<AnnotationMirror> streamDepthFirst(final Collection<? extends AnnotationMirror> ams) {
629    return streamDepthFirst(ams, AnnotationMirrors::returnTrue); // 17 == arbitrary
630  }
631
632  /**
633   * Returns a non-{@code null}, sequential {@link Stream} that traverses the supplied {@link AnnotationMirror}s, and
634   * their (meta-) annotations, in depth-first order.
635   *
636   * <p>Cycles and duplicates are avoided via usage of the {@link #sameAnnotation(AnnotationMirror, AnnotationMirror,
637   * Predicate)} method.</p>
638   *
639   * @param ams a non-{@code null} {@link Collection} of {@link AnnotationMirror}s
640   *
641   * @param p a {@link Predicate} that returns {@code true} if a given {@link ExecutableElement}, representing an
642   * annotation element, is to be included in comparison operations; may be {@code null} in which case it is as if
643   * {@code ()-> true} were supplied instead
644   *
645   * @return a non-{@code null} {@link Stream} of {@link AnnotationMirror}s
646   *
647   * @exception NullPointerException if {@code ams} is {@code null}
648   *
649   * @see #sameAnnotation(AnnotationMirror, AnnotationMirror)
650   */
651  public static final Stream<AnnotationMirror> streamDepthFirst(final Collection<? extends AnnotationMirror> ams,
652                                                                final Predicate<? super ExecutableElement> p) {
653    return ams.isEmpty() ? empty() : streamDepthFirst(ams, new ArrayList<>(17), p); // 17 == arbitrary
654  }
655
656
657  /**
658   * Returns a non-{@code null}, determinate, immutable {@link Set} of {@link ElementType}s describing restrictions
659   * concerning where the annotation interface {@linkplain AnnotationMirror#getAnnotationType() represented} by the
660   * supplied {@link AnnotationMirror} may be applied.
661   *
662   * @param a an {@link AnnotationMirror}; must not be {@code null}
663   *
664   * @return a non-{@code null}, determinate, immutable {@link Set} of {@link ElementType}s describing restrictions
665   * concerning where the annotation interface {@linkplain AnnotationMirror#getAnnotationType() represented} by the
666   * supplied {@link AnnotationMirror} may be applied
667   *
668   * @exception NullPointerException if {@code a} is {@code null}
669   */
670  public static final Set<ElementType> targetElementTypes(final AnnotationMirror a) {
671    return targetElementTypes((TypeElement)a.getAnnotationType().asElement());
672  }
673
674  /**
675   * Returns a non-{@code null}, determinate, immutable {@link Set} of {@link ElementType}s describing restrictions
676   * concerning where the supplied annotation interface may be applied.
677   *
678   * @param annotationInterface a {@link TypeElement} representing an {@linkplain
679   * javax.lang.model.element.ElementKind#ANNOTATION_TYPE annotation interface}; must not be {@code null}
680   *
681   * @return a non-{@code null}, determinate, immutable {@link Set} of {@link ElementType}s describing restrictions
682   * concerning where the supplied annotation interface may be applied
683   *
684   * @exception NullPointerException if {@code annotationInterface} is {@code null}
685   *
686   * @see java.lang.annotation.ElementType
687   *
688   * @see java.lang.annotation.Target
689   *
690   * @spec https://docs.oracle.com/javase/specs/jls/se25/html/jls-9.html#jls-9.6.4.1 Java Language Specification,
691   * section 9.6.4.1
692   */
693  public static final Set<ElementType> targetElementTypes(final TypeElement annotationInterface) {
694    if (annotationInterface.getKind() == ANNOTATION_TYPE) {
695      for (final AnnotationMirror ma : annotationInterface.getAnnotationMirrors()) {
696        if (((QualifiedNameable)ma.getAnnotationType().asElement()).getQualifiedName().contentEquals("java.lang.annotation.Target")) {
697          @SuppressWarnings("unchecked")
698          final List<? extends AnnotationValue> elementTypes = (List<? extends AnnotationValue>)get(ma, "value");
699          if (elementTypes.isEmpty()) {
700            break;
701          }
702          final Set<ElementType> s = EnumSet.noneOf(ElementType.class);
703          for (final AnnotationValue av : elementTypes) {
704            s.add(ElementType.valueOf(((VariableElement)av.getValue()).getSimpleName().toString()));
705          }
706          return unmodifiableSet(s);
707        }
708      }
709    }
710    return EMPTY_ELEMENT_TYPES;
711  }
712
713  /**
714   * Returns {@code true} if and only if the supplied {@link ExecutableElement} represents a valid <dfn>annotation
715   * interface element</dfn> as defined by <a
716   * href="https://docs.oracle.com/javase/specs/jls/se25/html/jls-9.html#jls-9.6.1">the Java Language Specification</a>.
717   *
718   * @param e an {@link ExecutableElement}; must not be {@code null}
719   *
720   * @return {@code true} if and only if the supplied {@link ExecutableElement} represents a valid <dfn>annotation
721   * interface element</dfn> as defined by <a
722   * href="https://docs.oracle.com/javase/specs/jls/se25/html/jls-9.html#jls-9.6.1">the Java Language Specification</a>
723   *
724   * @exception NullPointerException if {@code e} is {@code null}
725   *
726   * @spec https://docs.oracle.com/javase/specs/jls/se25/html/jls-9.html#jls-9.6.1 Java Language Specification, section
727   * 9.6.1
728   */
729  public static final boolean validAnnotationInterfaceElement(final ExecutableElement e) {
730    return
731      e.getKind() == METHOD &&
732      e.getEnclosingElement().getKind() == ANNOTATION_TYPE &&
733      e.getTypeParameters().isEmpty() &&
734      e.getParameters().isEmpty() &&
735      e.getThrownTypes().isEmpty() &&
736      validAnnotationInterfaceElementType(e.getReturnType()) &&
737      (e.getModifiers().isEmpty() ||
738       e.getModifiers().equals(EnumSet.of(ABSTRACT)) ||
739       e.getModifiers().equals(EnumSet.of(PUBLIC)));
740  }
741
742  /**
743   * Returns {@code true} if and only if the supplied {@link TypeMirror} is a valid type for an <dfn>annotation
744   * interface element</dfn> as defined by <a
745   * href="https://docs.oracle.com/javase/specs/jls/se25/html/jls-9.html#jls-9.6.1">the Java Language Specification</a>.
746   *
747   * @param t a {@link TypeMirror}; must not be {@code null}
748   *
749   * @return {@code true} if and only if the supplied {@link TypeMirror} is a valid type for an <dfn>annotation
750   * interface element</dfn> as defined by <a
751   * href="https://docs.oracle.com/javase/specs/jls/se25/html/jls-9.html#jls-9.6.1">the Java Language Specification</a>
752   *
753   * @exception NullPointerException if {@code t} is {@code null}
754   *
755   * @spec https://docs.oracle.com/javase/specs/jls/se25/html/jls-9.html#jls-9.6.1 Java Language Specification, section
756   * 9.6.1
757   */
758  public static final boolean validAnnotationInterfaceElementType(final TypeMirror t) {
759    return switch (t) {
760    case null -> throw new NullPointerException("t");
761    case ArrayType at when at.getKind() == ARRAY -> validAnnotationInterfaceElementScalarType(at.getComponentType());
762    default -> validAnnotationInterfaceElementScalarType(t);
763    };
764  }
765
766  static final boolean validAnnotationInterfaceElementScalarType(final TypeMirror t) {
767    return switch (t) {
768    case null -> throw new NullPointerException("t");
769    case PrimitiveType pt when pt.getKind().isPrimitive() -> true;
770    case DeclaredType dt when dt.getKind() == DECLARED -> {
771      final TypeElement te = (TypeElement)dt.asElement();
772      yield switch (te.getKind()) {
773      case ANNOTATION_TYPE, ENUM -> true;
774      case CLASS -> {
775        final Name fqn = te.getQualifiedName();
776        yield fqn.contentEquals("java.lang.Class") || fqn.contentEquals("java.lang.String");
777      }
778      default -> false;
779      };
780    }
781    default -> false;
782    };
783  }
784
785  @SuppressWarnings("unchecked")
786  private static final <K, V> SequencedMap<K, V> emptySequencedMap() {
787    return (SequencedMap<K, V>)EMPTY_MAP;
788  }
789
790  private static final <X> boolean returnTrue(final X ignored) {
791    return true;
792  }
793
794  private static final Stream<AnnotationMirror> streamDepthFirst(final AnnotatedConstruct ac,
795                                                                 final Collection<AnnotationMirror> seen,
796                                                                 final Predicate<? super ExecutableElement> p) {
797    return streamDepthFirst(ac.getAnnotationMirrors(), seen, p);
798  }
799
800  private static final Stream<AnnotationMirror> streamDepthFirst(final Collection<? extends AnnotationMirror> ams,
801                                                                 final Collection<AnnotationMirror> seen,
802                                                                 final Predicate<? super ExecutableElement> p) {
803    if (ams.isEmpty()) {
804      return empty();
805    }
806    // See https://www.techempower.com/blog/2016/10/19/efficient-multiple-stream-concatenation-in-java/
807    return
808      Stream.of(ams
809                .stream()
810                .sequential()
811                .flatMap(a0 -> {
812                    for (final AnnotationMirror a1 : seen) {
813                      if (sameAnnotation(a0, a1, p)) {
814                        return empty();
815                      }
816                    }
817                    seen.add(a0);
818                    return streamDepthFirst(a0.getAnnotationType().asElement().getAnnotationMirrors(), seen, p);
819                  }))
820      .flatMap(identity());
821  }
822
823}