001/* 002 * Copyright 2002-2020 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.core.annotation; 018 019import java.lang.annotation.Annotation; 020import java.lang.annotation.Inherited; 021import java.lang.reflect.AnnotatedElement; 022import java.lang.reflect.Proxy; 023import java.util.EnumSet; 024import java.util.List; 025import java.util.Map; 026import java.util.NoSuchElementException; 027import java.util.Optional; 028import java.util.Set; 029import java.util.function.Function; 030import java.util.function.Predicate; 031 032import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; 033import org.springframework.lang.Nullable; 034 035/** 036 * A single merged annotation returned from a {@link MergedAnnotations} 037 * collection. Presents a view onto an annotation where attribute values may 038 * have been "merged" from different source values. 039 * 040 * <p>Attribute values may be accessed using the various {@code get} methods. 041 * For example, to access an {@code int} attribute the {@link #getInt(String)} 042 * method would be used. 043 * 044 * <p>Note that attribute values are <b>not</b> converted when accessed. 045 * For example, it is not possible to call {@link #getString(String)} if the 046 * underlying attribute is an {@code int}. The only exception to this rule is 047 * {@code Class} and {@code Class[]} values which may be accessed as 048 * {@code String} and {@code String[]} respectively to prevent potential early 049 * class initialization. 050 * 051 * <p>If necessary, a {@code MergedAnnotation} can be {@linkplain #synthesize() 052 * synthesized} back into an actual {@link java.lang.annotation.Annotation}. 053 * 054 * @author Phillip Webb 055 * @author Juergen Hoeller 056 * @author Sam Brannen 057 * @since 5.2 058 * @param <A> the annotation type 059 * @see MergedAnnotations 060 * @see MergedAnnotationPredicates 061 */ 062public interface MergedAnnotation<A extends Annotation> { 063 064 /** 065 * The attribute name for annotations with a single element. 066 */ 067 String VALUE = "value"; 068 069 070 /** 071 * Get the {@code Class} reference for the actual annotation type. 072 * @return the annotation type 073 */ 074 Class<A> getType(); 075 076 /** 077 * Determine if the annotation is present on the source. Considers 078 * {@linkplain #isDirectlyPresent() directly present} and 079 * {@linkplain #isMetaPresent() meta-present} annotations within the context 080 * of the {@link SearchStrategy} used. 081 * @return {@code true} if the annotation is present 082 */ 083 boolean isPresent(); 084 085 /** 086 * Determine if the annotation is directly present on the source. 087 * <p>A directly present annotation is one that the user has explicitly 088 * declared and not one that is {@linkplain #isMetaPresent() meta-present} 089 * or {@link Inherited @Inherited}. 090 * @return {@code true} if the annotation is directly present 091 */ 092 boolean isDirectlyPresent(); 093 094 /** 095 * Determine if the annotation is meta-present on the source. 096 * <p>A meta-present annotation is an annotation that the user hasn't 097 * explicitly declared, but has been used as a meta-annotation somewhere in 098 * the annotation hierarchy. 099 * @return {@code true} if the annotation is meta-present 100 */ 101 boolean isMetaPresent(); 102 103 /** 104 * Get the distance of this annotation related to its use as a 105 * meta-annotation. 106 * <p>A directly declared annotation has a distance of {@code 0}, a 107 * meta-annotation has a distance of {@code 1}, a meta-annotation on a 108 * meta-annotation has a distance of {@code 2}, etc. A {@linkplain #missing() 109 * missing} annotation will always return a distance of {@code -1}. 110 * @return the annotation distance or {@code -1} if the annotation is missing 111 */ 112 int getDistance(); 113 114 /** 115 * Get the index of the aggregate collection containing this annotation. 116 * <p>Can be used to reorder a stream of annotations, for example, to give a 117 * higher priority to annotations declared on a superclass or interface. A 118 * {@linkplain #missing() missing} annotation will always return an aggregate 119 * index of {@code -1}. 120 * @return the aggregate index (starting at {@code 0}) or {@code -1} if the 121 * annotation is missing 122 */ 123 int getAggregateIndex(); 124 125 /** 126 * Get the source that ultimately declared the root annotation, or 127 * {@code null} if the source is not known. 128 * <p>If this merged annotation was created 129 * {@link MergedAnnotations#from(AnnotatedElement) from} an 130 * {@link AnnotatedElement} then this source will be an element of the same 131 * type. If the annotation was loaded without using reflection, the source 132 * can be of any type, but should have a sensible {@code toString()}. 133 * Meta-annotations will always return the same source as the 134 * {@link #getRoot() root}. 135 * @return the source, or {@code null} 136 */ 137 @Nullable 138 Object getSource(); 139 140 /** 141 * Get the source of the meta-annotation, or {@code null} if the 142 * annotation is not {@linkplain #isMetaPresent() meta-present}. 143 * <p>The meta-source is the annotation that was meta-annotated with this 144 * annotation. 145 * @return the meta-annotation source or {@code null} 146 * @see #getRoot() 147 */ 148 @Nullable 149 MergedAnnotation<?> getMetaSource(); 150 151 /** 152 * Get the root annotation, i.e. the {@link #getDistance() distance} {@code 0} 153 * annotation as directly declared on the source. 154 * @return the root annotation 155 * @see #getMetaSource() 156 */ 157 MergedAnnotation<?> getRoot(); 158 159 /** 160 * Get the complete list of annotation types within the annotation hierarchy 161 * from this annotation to the {@link #getRoot() root}. 162 * <p>Provides a useful way to uniquely identify a merged annotation instance. 163 * @return the meta types for the annotation 164 * @see MergedAnnotationPredicates#unique(Function) 165 * @see #getRoot() 166 * @see #getMetaSource() 167 */ 168 List<Class<? extends Annotation>> getMetaTypes(); 169 170 171 /** 172 * Determine if the specified attribute name has a non-default value when 173 * compared to the annotation declaration. 174 * @param attributeName the attribute name 175 * @return {@code true} if the attribute value is different from the default 176 * value 177 */ 178 boolean hasNonDefaultValue(String attributeName); 179 180 /** 181 * Determine if the specified attribute name has a default value when compared 182 * to the annotation declaration. 183 * @param attributeName the attribute name 184 * @return {@code true} if the attribute value is the same as the default 185 * value 186 */ 187 boolean hasDefaultValue(String attributeName) throws NoSuchElementException; 188 189 /** 190 * Get a required byte attribute value from the annotation. 191 * @param attributeName the attribute name 192 * @return the value as a byte 193 * @throws NoSuchElementException if there is no matching attribute 194 */ 195 byte getByte(String attributeName) throws NoSuchElementException; 196 197 /** 198 * Get a required byte array attribute value from the annotation. 199 * @param attributeName the attribute name 200 * @return the value as a byte array 201 * @throws NoSuchElementException if there is no matching attribute 202 */ 203 byte[] getByteArray(String attributeName) throws NoSuchElementException; 204 205 /** 206 * Get a required boolean attribute value from the annotation. 207 * @param attributeName the attribute name 208 * @return the value as a boolean 209 * @throws NoSuchElementException if there is no matching attribute 210 */ 211 boolean getBoolean(String attributeName) throws NoSuchElementException; 212 213 /** 214 * Get a required boolean array attribute value from the annotation. 215 * @param attributeName the attribute name 216 * @return the value as a boolean array 217 * @throws NoSuchElementException if there is no matching attribute 218 */ 219 boolean[] getBooleanArray(String attributeName) throws NoSuchElementException; 220 221 /** 222 * Get a required char attribute value from the annotation. 223 * @param attributeName the attribute name 224 * @return the value as a char 225 * @throws NoSuchElementException if there is no matching attribute 226 */ 227 char getChar(String attributeName) throws NoSuchElementException; 228 229 /** 230 * Get a required char array attribute value from the annotation. 231 * @param attributeName the attribute name 232 * @return the value as a char array 233 * @throws NoSuchElementException if there is no matching attribute 234 */ 235 char[] getCharArray(String attributeName) throws NoSuchElementException; 236 237 /** 238 * Get a required short attribute value from the annotation. 239 * @param attributeName the attribute name 240 * @return the value as a short 241 * @throws NoSuchElementException if there is no matching attribute 242 */ 243 short getShort(String attributeName) throws NoSuchElementException; 244 245 /** 246 * Get a required short array attribute value from the annotation. 247 * @param attributeName the attribute name 248 * @return the value as a short array 249 * @throws NoSuchElementException if there is no matching attribute 250 */ 251 short[] getShortArray(String attributeName) throws NoSuchElementException; 252 253 /** 254 * Get a required int attribute value from the annotation. 255 * @param attributeName the attribute name 256 * @return the value as an int 257 * @throws NoSuchElementException if there is no matching attribute 258 */ 259 int getInt(String attributeName) throws NoSuchElementException; 260 261 /** 262 * Get a required int array attribute value from the annotation. 263 * @param attributeName the attribute name 264 * @return the value as an int array 265 * @throws NoSuchElementException if there is no matching attribute 266 */ 267 int[] getIntArray(String attributeName) throws NoSuchElementException; 268 269 /** 270 * Get a required long attribute value from the annotation. 271 * @param attributeName the attribute name 272 * @return the value as a long 273 * @throws NoSuchElementException if there is no matching attribute 274 */ 275 long getLong(String attributeName) throws NoSuchElementException; 276 277 /** 278 * Get a required long array attribute value from the annotation. 279 * @param attributeName the attribute name 280 * @return the value as a long array 281 * @throws NoSuchElementException if there is no matching attribute 282 */ 283 long[] getLongArray(String attributeName) throws NoSuchElementException; 284 285 /** 286 * Get a required double attribute value from the annotation. 287 * @param attributeName the attribute name 288 * @return the value as a double 289 * @throws NoSuchElementException if there is no matching attribute 290 */ 291 double getDouble(String attributeName) throws NoSuchElementException; 292 293 /** 294 * Get a required double array attribute value from the annotation. 295 * @param attributeName the attribute name 296 * @return the value as a double array 297 * @throws NoSuchElementException if there is no matching attribute 298 */ 299 double[] getDoubleArray(String attributeName) throws NoSuchElementException; 300 301 /** 302 * Get a required float attribute value from the annotation. 303 * @param attributeName the attribute name 304 * @return the value as a float 305 * @throws NoSuchElementException if there is no matching attribute 306 */ 307 float getFloat(String attributeName) throws NoSuchElementException; 308 309 /** 310 * Get a required float array attribute value from the annotation. 311 * @param attributeName the attribute name 312 * @return the value as a float array 313 * @throws NoSuchElementException if there is no matching attribute 314 */ 315 float[] getFloatArray(String attributeName) throws NoSuchElementException; 316 317 /** 318 * Get a required string attribute value from the annotation. 319 * @param attributeName the attribute name 320 * @return the value as a string 321 * @throws NoSuchElementException if there is no matching attribute 322 */ 323 String getString(String attributeName) throws NoSuchElementException; 324 325 /** 326 * Get a required string array attribute value from the annotation. 327 * @param attributeName the attribute name 328 * @return the value as a string array 329 * @throws NoSuchElementException if there is no matching attribute 330 */ 331 String[] getStringArray(String attributeName) throws NoSuchElementException; 332 333 /** 334 * Get a required class attribute value from the annotation. 335 * @param attributeName the attribute name 336 * @return the value as a class 337 * @throws NoSuchElementException if there is no matching attribute 338 */ 339 Class<?> getClass(String attributeName) throws NoSuchElementException; 340 341 /** 342 * Get a required class array attribute value from the annotation. 343 * @param attributeName the attribute name 344 * @return the value as a class array 345 * @throws NoSuchElementException if there is no matching attribute 346 */ 347 Class<?>[] getClassArray(String attributeName) throws NoSuchElementException; 348 349 /** 350 * Get a required enum attribute value from the annotation. 351 * @param attributeName the attribute name 352 * @param type the enum type 353 * @return the value as a enum 354 * @throws NoSuchElementException if there is no matching attribute 355 */ 356 <E extends Enum<E>> E getEnum(String attributeName, Class<E> type) throws NoSuchElementException; 357 358 /** 359 * Get a required enum array attribute value from the annotation. 360 * @param attributeName the attribute name 361 * @param type the enum type 362 * @return the value as a enum array 363 * @throws NoSuchElementException if there is no matching attribute 364 */ 365 <E extends Enum<E>> E[] getEnumArray(String attributeName, Class<E> type) throws NoSuchElementException; 366 367 /** 368 * Get a required annotation attribute value from the annotation. 369 * @param attributeName the attribute name 370 * @param type the annotation type 371 * @return the value as a {@link MergedAnnotation} 372 * @throws NoSuchElementException if there is no matching attribute 373 */ 374 <T extends Annotation> MergedAnnotation<T> getAnnotation(String attributeName, Class<T> type) 375 throws NoSuchElementException; 376 377 /** 378 * Get a required annotation array attribute value from the annotation. 379 * @param attributeName the attribute name 380 * @param type the annotation type 381 * @return the value as a {@link MergedAnnotation} array 382 * @throws NoSuchElementException if there is no matching attribute 383 */ 384 <T extends Annotation> MergedAnnotation<T>[] getAnnotationArray(String attributeName, Class<T> type) 385 throws NoSuchElementException; 386 387 /** 388 * Get an optional attribute value from the annotation. 389 * @param attributeName the attribute name 390 * @return an optional value or {@link Optional#empty()} if there is no 391 * matching attribute 392 */ 393 Optional<Object> getValue(String attributeName); 394 395 /** 396 * Get an optional attribute value from the annotation. 397 * @param attributeName the attribute name 398 * @param type the attribute type. Must be compatible with the underlying 399 * attribute type or {@code Object.class}. 400 * @return an optional value or {@link Optional#empty()} if there is no 401 * matching attribute 402 */ 403 <T> Optional<T> getValue(String attributeName, Class<T> type); 404 405 /** 406 * Get the default attribute value from the annotation as specified in 407 * the annotation declaration. 408 * @param attributeName the attribute name 409 * @return an optional of the default value or {@link Optional#empty()} if 410 * there is no matching attribute or no defined default 411 */ 412 Optional<Object> getDefaultValue(String attributeName); 413 414 /** 415 * Get the default attribute value from the annotation as specified in 416 * the annotation declaration. 417 * @param attributeName the attribute name 418 * @param type the attribute type. Must be compatible with the underlying 419 * attribute type or {@code Object.class}. 420 * @return an optional of the default value or {@link Optional#empty()} if 421 * there is no matching attribute or no defined default 422 */ 423 <T> Optional<T> getDefaultValue(String attributeName, Class<T> type); 424 425 /** 426 * Create a new view of the annotation with all attributes that have default 427 * values removed. 428 * @return a filtered view of the annotation without any attributes that 429 * have a default value 430 * @see #filterAttributes(Predicate) 431 */ 432 MergedAnnotation<A> filterDefaultValues(); 433 434 /** 435 * Create a new view of the annotation with only attributes that match the 436 * given predicate. 437 * @param predicate a predicate used to filter attribute names 438 * @return a filtered view of the annotation 439 * @see #filterDefaultValues() 440 * @see MergedAnnotationPredicates 441 */ 442 MergedAnnotation<A> filterAttributes(Predicate<String> predicate); 443 444 /** 445 * Create a new view of the annotation that exposes non-merged attribute values. 446 * <p>Methods from this view will return attribute values with only alias mirroring 447 * rules applied. Aliases to {@link #getMetaSource() meta-source} attributes will 448 * not be applied. 449 * @return a non-merged view of the annotation 450 */ 451 MergedAnnotation<A> withNonMergedAttributes(); 452 453 /** 454 * Create a new mutable {@link AnnotationAttributes} instance from this 455 * merged annotation. 456 * <p>The {@link Adapt adaptations} may be used to change the way that values 457 * are added. 458 * @param adaptations the adaptations that should be applied to the annotation values 459 * @return an immutable map containing the attributes and values 460 */ 461 AnnotationAttributes asAnnotationAttributes(Adapt... adaptations); 462 463 /** 464 * Get an immutable {@link Map} that contains all the annotation attributes. 465 * <p>The {@link Adapt adaptations} may be used to change the way that values are added. 466 * @param adaptations the adaptations that should be applied to the annotation values 467 * @return an immutable map containing the attributes and values 468 */ 469 Map<String, Object> asMap(Adapt... adaptations); 470 471 /** 472 * Create a new {@link Map} instance of the given type that contains all the annotation 473 * attributes. 474 * <p>The {@link Adapt adaptations} may be used to change the way that values are added. 475 * @param factory a map factory 476 * @param adaptations the adaptations that should be applied to the annotation values 477 * @return a map containing the attributes and values 478 */ 479 <T extends Map<String, Object>> T asMap(Function<MergedAnnotation<?>, T> factory, Adapt... adaptations); 480 481 /** 482 * Create a type-safe synthesized version of this merged annotation that can 483 * be used directly in code. 484 * <p>The result is synthesized using a JDK {@link Proxy} and as a result may 485 * incur a computational cost when first invoked. 486 * <p>If this merged annotation was created {@linkplain #from(Annotation) from} 487 * an annotation instance, that annotation will be returned unmodified if it is 488 * not <em>synthesizable</em>. An annotation is considered synthesizable if 489 * one of the following is true. 490 * <ul> 491 * <li>The annotation declares attributes annotated with {@link AliasFor @AliasFor}.</li> 492 * <li>The annotation is a composed annotation that relies on convention-based 493 * annotation attribute overrides in meta-annotations.</li> 494 * <li>The annotation declares attributes that are annotations or arrays of 495 * annotations that are themselves synthesizable.</li> 496 * </ul> 497 * @return a synthesized version of the annotation or the original annotation 498 * unmodified 499 * @throws NoSuchElementException on a missing annotation 500 */ 501 A synthesize() throws NoSuchElementException; 502 503 /** 504 * Optionally create a type-safe synthesized version of this annotation based 505 * on a condition predicate. 506 * <p>The result is synthesized using a JDK {@link Proxy} and as a result may 507 * incur a computational cost when first invoked. 508 * <p>Consult the documentation for {@link #synthesize()} for an explanation 509 * of what is considered synthesizable. 510 * @param condition the test to determine if the annotation can be synthesized 511 * @return an optional containing the synthesized version of the annotation or 512 * an empty optional if the condition doesn't match 513 * @throws NoSuchElementException on a missing annotation 514 * @see MergedAnnotationPredicates 515 */ 516 Optional<A> synthesize(Predicate<? super MergedAnnotation<A>> condition) throws NoSuchElementException; 517 518 519 /** 520 * Create a {@link MergedAnnotation} that represents a missing annotation 521 * (i.e. one that is not present). 522 * @return an instance representing a missing annotation 523 */ 524 static <A extends Annotation> MergedAnnotation<A> missing() { 525 return MissingMergedAnnotation.getInstance(); 526 } 527 528 /** 529 * Create a new {@link MergedAnnotation} instance from the specified 530 * annotation. 531 * @param annotation the annotation to include 532 * @return a {@link MergedAnnotation} instance containing the annotation 533 */ 534 static <A extends Annotation> MergedAnnotation<A> from(A annotation) { 535 return from(null, annotation); 536 } 537 538 /** 539 * Create a new {@link MergedAnnotation} instance from the specified 540 * annotation. 541 * @param source the source for the annotation. This source is used only for 542 * information and logging. It does not need to <em>actually</em> contain 543 * the specified annotations, and it will not be searched. 544 * @param annotation the annotation to include 545 * @return a {@link MergedAnnotation} instance for the annotation 546 */ 547 static <A extends Annotation> MergedAnnotation<A> from(@Nullable Object source, A annotation) { 548 return TypeMappedAnnotation.from(source, annotation); 549 } 550 551 /** 552 * Create a new {@link MergedAnnotation} instance of the specified 553 * annotation type. The resulting annotation will not have any attribute 554 * values but may still be used to query default values. 555 * @param annotationType the annotation type 556 * @return a {@link MergedAnnotation} instance for the annotation 557 */ 558 static <A extends Annotation> MergedAnnotation<A> of(Class<A> annotationType) { 559 return of(null, annotationType, null); 560 } 561 562 /** 563 * Create a new {@link MergedAnnotation} instance of the specified 564 * annotation type with attribute values supplied by a map. 565 * @param annotationType the annotation type 566 * @param attributes the annotation attributes or {@code null} if just default 567 * values should be used 568 * @return a {@link MergedAnnotation} instance for the annotation and attributes 569 * @see #of(AnnotatedElement, Class, Map) 570 */ 571 static <A extends Annotation> MergedAnnotation<A> of( 572 Class<A> annotationType, @Nullable Map<String, ?> attributes) { 573 574 return of(null, annotationType, attributes); 575 } 576 577 /** 578 * Create a new {@link MergedAnnotation} instance of the specified 579 * annotation type with attribute values supplied by a map. 580 * @param source the source for the annotation. This source is used only for 581 * information and logging. It does not need to <em>actually</em> contain 582 * the specified annotations and it will not be searched. 583 * @param annotationType the annotation type 584 * @param attributes the annotation attributes or {@code null} if just default 585 * values should be used 586 * @return a {@link MergedAnnotation} instance for the annotation and attributes 587 */ 588 static <A extends Annotation> MergedAnnotation<A> of( 589 @Nullable AnnotatedElement source, Class<A> annotationType, @Nullable Map<String, ?> attributes) { 590 591 return of(null, source, annotationType, attributes); 592 } 593 594 /** 595 * Create a new {@link MergedAnnotation} instance of the specified 596 * annotation type with attribute values supplied by a map. 597 * @param classLoader the class loader used to resolve class attributes 598 * @param source the source for the annotation. This source is used only for 599 * information and logging. It does not need to <em>actually</em> contain 600 * the specified annotations and it will not be searched. 601 * @param annotationType the annotation type 602 * @param attributes the annotation attributes or {@code null} if just default 603 * values should be used 604 * @return a {@link MergedAnnotation} instance for the annotation and attributes 605 */ 606 static <A extends Annotation> MergedAnnotation<A> of( 607 @Nullable ClassLoader classLoader, @Nullable Object source, 608 Class<A> annotationType, @Nullable Map<String, ?> attributes) { 609 610 return TypeMappedAnnotation.of(classLoader, source, annotationType, attributes); 611 } 612 613 614 /** 615 * Adaptations that can be applied to attribute values when creating 616 * {@linkplain MergedAnnotation#asMap(Adapt...) Maps} or 617 * {@link MergedAnnotation#asAnnotationAttributes(Adapt...) AnnotationAttributes}. 618 */ 619 enum Adapt { 620 621 /** 622 * Adapt class or class array attributes to strings. 623 */ 624 CLASS_TO_STRING, 625 626 /** 627 * Adapt nested annotation or annotation arrays to maps rather 628 * than synthesizing the values. 629 */ 630 ANNOTATION_TO_MAP; 631 632 protected final boolean isIn(Adapt... adaptations) { 633 for (Adapt candidate : adaptations) { 634 if (candidate == this) { 635 return true; 636 } 637 } 638 return false; 639 } 640 641 /** 642 * Factory method to create an {@link Adapt} array from a set of boolean flags. 643 * @param classToString if {@link Adapt#CLASS_TO_STRING} is included 644 * @param annotationsToMap if {@link Adapt#ANNOTATION_TO_MAP} is included 645 * @return a new {@link Adapt} array 646 */ 647 public static Adapt[] values(boolean classToString, boolean annotationsToMap) { 648 EnumSet<Adapt> result = EnumSet.noneOf(Adapt.class); 649 addIfTrue(result, Adapt.CLASS_TO_STRING, classToString); 650 addIfTrue(result, Adapt.ANNOTATION_TO_MAP, annotationsToMap); 651 return result.toArray(new Adapt[0]); 652 } 653 654 private static <T> void addIfTrue(Set<T> result, T value, boolean test) { 655 if (test) { 656 result.add(value); 657 } 658 } 659 } 660 661}