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}