1 /*
2 * Copyright 2014 - 2020 Rafael Winterhalter
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package net.bytebuddy.description.annotation;
17
18 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19 import net.bytebuddy.ClassFileVersion;
20 import net.bytebuddy.build.CachedReturnPlugin;
21 import net.bytebuddy.build.HashCodeAndEqualsPlugin;
22 import net.bytebuddy.description.enumeration.EnumerationDescription;
23 import net.bytebuddy.description.method.MethodDescription;
24 import net.bytebuddy.description.method.MethodList;
25 import net.bytebuddy.description.type.TypeDescription;
26 import net.bytebuddy.description.type.TypeList;
27 import net.bytebuddy.utility.privilege.SetAccessibleAction;
28
29 import java.lang.annotation.*;
30 import java.lang.reflect.InvocationHandler;
31 import java.lang.reflect.InvocationTargetException;
32 import java.lang.reflect.Method;
33 import java.lang.reflect.Proxy;
34 import java.security.AccessController;
35 import java.util.*;
36
37 import static net.bytebuddy.matcher.ElementMatchers.named;
38
39 /**
40 * An annotation description describes {@link java.lang.annotation.Annotation} meta data of a class without this class
41 * being required to be loaded. All values of an annotation are therefore represented in unloaded state:
42 * <ul>
43 * <li>{@link java.lang.Class} instances are represented as {@link TypeDescription}s.</li>
44 * <li>{@link java.lang.Enum} instances are represented as
45 * {@link net.bytebuddy.description.enumeration.EnumerationDescription}s.</li>
46 * <li>{@link java.lang.annotation.Annotation}s are described as
47 * {@link AnnotationDescription}s.</li>
48 * <li>All primitive types are represented as their wrapper types.</li>
49 * </ul>
50 * An annotation can however be loaded in order to access unwrapped values. This will cause a loading of the classes
51 * of these values.
52 */
53 public interface AnnotationDescription {
54
55 /**
56 * Indicates a nonexistent annotation in a type-safe manner.
57 */
58 AnnotationDescription.Loadable<?> UNDEFINED = null;
59
60 /**
61 * Returns the value of this annotation.
62 *
63 * @param property The property being accessed.
64 * @return The value for the supplied property.
65 */
66 AnnotationValue<?, ?> getValue(MethodDescription.InDefinedShape property);
67
68 /**
69 * Returns a description of the annotation type of this annotation.
70 *
71 * @return A description of the annotation type of this annotation.
72 */
73 TypeDescription getAnnotationType();
74
75 /**
76 * Links this annotation description to a given annotation type such that it can be loaded. This does not cause
77 * the values of this annotation to be loaded.
78 *
79 * @param annotationType The loaded annotation type of this annotation description.
80 * @param <T> The type of the annotation.
81 * @return A loadable version of this annotation description.
82 */
83 <T extends Annotation> Loadable<T> prepare(Class<T> annotationType);
84
85 /**
86 * Returns this annotation's retention policy.
87 *
88 * @return This annotation's retention policy.
89 */
90 RetentionPolicy getRetention();
91
92 /**
93 * Returns a set of all {@link ElementType}s that can declare this annotation.
94 *
95 * @return A set of all element types that can declare this annotation.
96 */
97 Set<ElementType> getElementTypes();
98
99 /**
100 * Checks if this annotation is inherited.
101 *
102 * @return {@code true} if this annotation is inherited.
103 * @see Inherited
104 */
105 boolean isInherited();
106
107 /**
108 * Checks if this annotation is documented.
109 *
110 * @return {@code true} if this annotation is documented.
111 * @see Documented
112 */
113 boolean isDocumented();
114
115 /**
116 * An annotation description that is linked to a given loaded annotation type which allows its representation
117 * as a fully loaded instance.
118 *
119 * @param <S> The annotation type.
120 */
121 interface Loadable<S extends Annotation> extends AnnotationDescription {
122
123 /**
124 * Loads this annotation description. This causes all classes referenced by the annotation value to be loaded.
125 * Without specifying a class loader, the annotation's class loader which was used to prepare this instance
126 * is used.
127 *
128 * @return A loaded version of this annotation description.
129 */
130 S load();
131 }
132
133 /**
134 * A rendering dispatcher is responsible for resolving annotation descriptions to {@link String} representations.
135 */
136 enum RenderingDispatcher {
137
138 /**
139 * A rendering dispatcher for any VM previous to Java 14.
140 */
141 LEGACY_VM,
142
143 /**
144 * A rendering dispatcher for Java 14 onward.
145 */
146 JAVA_14_CAPABLE_VM {
147 @Override
148 public void appendPrefix(StringBuilder toString, String key, int count) {
149 if (count > 1 || !key.equals("value")) {
150 super.appendPrefix(toString, key, count);
151 }
152 }
153 };
154
155 /**
156 * The rendering dispatcher for the current VM.
157 */
158 public static final RenderingDispatcher CURRENT;
159
160 /*
161 * Initializes the rendering dispatcher.
162 */
163 static {
164 ClassFileVersion classFileVersion = ClassFileVersion.ofThisVm(ClassFileVersion.JAVA_V6);
165 if (classFileVersion.isAtLeast(ClassFileVersion.JAVA_V14)) {
166 CURRENT = RenderingDispatcher.JAVA_14_CAPABLE_VM;
167 } else {
168 CURRENT = RenderingDispatcher.LEGACY_VM;
169 }
170 }
171
172 /**
173 * Appends the key property prefix to a string builder representing an annotation's string representation.
174 *
175 * @param toString The {@link Object#toString()} representation of the annotation being handled.
176 * @param key The key's name.
177 * @param count The property count.
178 */
179 public void appendPrefix(StringBuilder toString, String key, int count) {
180 toString.append(key).append('=');
181 }
182 }
183
184 /**
185 * An {@link java.lang.reflect.InvocationHandler} for implementing annotations.
186 *
187 * @param <T> The type of the handled annotation.
188 */
189 class AnnotationInvocationHandler<T extends Annotation> implements InvocationHandler {
190
191 /**
192 * The name of the {@link Object#hashCode()} method.
193 */
194 private static final String HASH_CODE = "hashCode";
195
196 /**
197 * The name of the {@link Object#equals(Object)} method.
198 */
199 private static final String EQUALS = "equals";
200
201 /**
202 * The name of the {@link Object#toString()} method.
203 */
204 private static final String TO_STRING = "toString";
205
206 /**
207 * An empty array that can be used to indicate no arguments to avoid an allocation on a reflective call.
208 */
209 private static final Object[] NO_ARGUMENTS = new Object[0];
210
211 /**
212 * The loaded annotation type.
213 */
214 private final Class<? extends Annotation> annotationType;
215
216 /**
217 * A sorted list of values of this annotation.
218 */
219 private final LinkedHashMap<Method, AnnotationValue.Loaded<?>> values;
220
221 /**
222 * Creates a new invocation handler.
223 *
224 * @param annotationType The loaded annotation type.
225 * @param values A sorted list of values of this annotation.
226 */
227 protected AnnotationInvocationHandler(Class<T> annotationType, LinkedHashMap<Method, AnnotationValue.Loaded<?>> values) {
228 this.annotationType = annotationType;
229 this.values = values;
230 }
231
232 /**
233 * Creates a proxy instance for the supplied annotation type and values.
234 *
235 * @param classLoader The class loader that should be used for loading the annotation's values.
236 * @param annotationType The annotation's type.
237 * @param values The values that the annotation contains.
238 * @param <S> The type of the handled annotation.
239 * @return A proxy for the annotation type and values.
240 */
241 @SuppressWarnings("unchecked")
242 public static <S extends Annotation> S of(ClassLoader classLoader,
243 Class<S> annotationType,
244 Map<String, ? extends AnnotationValue<?, ?>> values) {
245 LinkedHashMap<Method, AnnotationValue.Loaded<?>> loadedValues = new LinkedHashMap<Method, AnnotationValue.Loaded<?>>();
246 for (Method method : annotationType.getDeclaredMethods()) {
247 AnnotationValue<?, ?> annotationValue = values.get(method.getName());
248 if (annotationValue == null) {
249 Object defaultValue = method.getDefaultValue();
250 loadedValues.put(method, (defaultValue == null
251 ? new AnnotationValue.ForMissingValue<Void, Void>(new TypeDescription.ForLoadedType(method.getDeclaringClass()), method.getName())
252 : AnnotationDescription.ForLoadedAnnotation.asValue(defaultValue, method.getReturnType())).load(classLoader));
253 } else {
254 loadedValues.put(method, annotationValue.filter(new MethodDescription.ForLoadedMethod(method)).load(classLoader));
255 }
256 }
257 return (S) Proxy.newProxyInstance(classLoader, new Class<?>[]{annotationType}, new AnnotationInvocationHandler<S>(annotationType, loadedValues));
258 }
259
260 /**
261 * {@inheritDoc}
262 */
263 public Object invoke(Object proxy, Method method, Object[] argument) {
264 if (method.getDeclaringClass() != annotationType) {
265 if (method.getName().equals(HASH_CODE)) {
266 return hashCodeRepresentation();
267 } else if (method.getName().equals(EQUALS) && method.getParameterTypes().length == 1) {
268 return equalsRepresentation(proxy, argument[0]);
269 } else if (method.getName().equals(TO_STRING)) {
270 return toStringRepresentation();
271 } else /* if (method.getName().equals("annotationType")) */ {
272 return annotationType;
273 }
274 }
275 return values.get(method).resolve();
276 }
277
278 /**
279 * Returns the string representation of the represented annotation.
280 *
281 * @return The string representation of the represented annotation.
282 */
283 protected String toStringRepresentation() {
284 StringBuilder toString = new StringBuilder();
285 toString.append('@');
286 toString.append(annotationType.getName());
287 toString.append('(');
288 boolean firstMember = true;
289 for (Map.Entry<Method, AnnotationValue.Loaded<?>> entry : values.entrySet()) {
290 if (!entry.getValue().getState().isDefined()) {
291 continue;
292 }
293 if (firstMember) {
294 firstMember = false;
295 } else {
296 toString.append(", ");
297 }
298 RenderingDispatcher.CURRENT.appendPrefix(toString, entry.getKey().getName(), values.entrySet().size());
299 toString.append(entry.getValue().toString());
300 }
301 toString.append(')');
302 return toString.toString();
303 }
304
305 /**
306 * Returns the hash code of the represented annotation.
307 *
308 * @return The hash code of the represented annotation.
309 */
310 private int hashCodeRepresentation() {
311 int hashCode = 0;
312 for (Map.Entry<Method, AnnotationValue.Loaded<?>> entry : values.entrySet()) {
313 if (!entry.getValue().getState().isDefined()) {
314 continue;
315 }
316 hashCode += (127 * entry.getKey().getName().hashCode()) ^ entry.getValue().hashCode();
317 }
318 return hashCode;
319 }
320
321 /**
322 * Checks if another instance is equal to this instance.
323 *
324 * @param self The annotation proxy instance.
325 * @param other The instance to be examined for equality to the represented instance.
326 * @return {@code true} if the given instance is equal to the represented instance.
327 */
328 private boolean equalsRepresentation(Object self, Object other) {
329 if (self == other) {
330 return true;
331 } else if (!annotationType.isInstance(other)) {
332 return false;
333 } else if (Proxy.isProxyClass(other.getClass())) {
334 InvocationHandler invocationHandler = Proxy.getInvocationHandler(other);
335 if (invocationHandler instanceof AnnotationInvocationHandler) {
336 return invocationHandler.equals(this);
337 }
338 }
339 try {
340 for (Map.Entry<Method, AnnotationValue.Loaded<?>> entry : values.entrySet()) {
341 try {
342 if (!entry.getValue().represents(entry.getKey().invoke(other, NO_ARGUMENTS))) {
343 return false;
344 }
345 } catch (RuntimeException exception) {
346 return false; // Incomplete annotations are not equal to one another.
347 }
348 }
349 return true;
350 } catch (InvocationTargetException ignored) {
351 return false;
352 } catch (IllegalAccessException exception) {
353 throw new IllegalStateException("Could not access annotation property", exception);
354 }
355 }
356
357 @Override
358 @CachedReturnPlugin.Enhance
359 public int hashCode() {
360 int result = annotationType.hashCode();
361 result = 31 * result + values.hashCode();
362 for (Map.Entry<Method, ?> entry : values.entrySet()) {
363 result = 31 * result + entry.getValue().hashCode();
364 }
365 return result;
366 }
367
368 @Override
369 public boolean equals(Object other) {
370 if (this == other) {
371 return true;
372 } else if (!(other instanceof AnnotationInvocationHandler)) {
373 return false;
374 }
375 AnnotationInvocationHandler that = (AnnotationInvocationHandler) other;
376 if (!annotationType.equals(that.annotationType)) {
377 return false;
378 }
379 for (Map.Entry<Method, AnnotationValue.Loaded<?>> entry : values.entrySet()) {
380 if (!entry.getValue().equals(that.values.get(entry.getKey()))) {
381 return false;
382 }
383 }
384 return true;
385 }
386 }
387
388 /**
389 * An adapter implementation of an annotation.
390 */
391 abstract class AbstractBase implements AnnotationDescription {
392
393 /**
394 * An array containing all element types that are a legal annotation target when such a target
395 * is not specified explicitly.
396 */
397 private static final ElementType[] DEFAULT_TARGET = new ElementType[]{ElementType.ANNOTATION_TYPE,
398 ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD,
399 ElementType.PACKAGE, ElementType.PARAMETER, ElementType.TYPE};
400
401 /**
402 * {@inheritDoc}
403 */
404 public RetentionPolicy getRetention() {
405 AnnotationDescription.Loadable<Retention> retention = getAnnotationType().getDeclaredAnnotations().ofType(Retention.class);
406 return retention == null
407 ? RetentionPolicy.CLASS
408 : retention.load().value();
409 }
410
411 /**
412 * {@inheritDoc}
413 */
414 public Set<ElementType> getElementTypes() {
415 AnnotationDescription.Loadable<Target> target = getAnnotationType().getDeclaredAnnotations().ofType(Target.class);
416 return new HashSet<ElementType>(Arrays.asList(target == null
417 ? DEFAULT_TARGET
418 : target.load().value()));
419 }
420
421 /**
422 * {@inheritDoc}
423 */
424 public boolean isInherited() {
425 return getAnnotationType().getDeclaredAnnotations().isAnnotationPresent(Inherited.class);
426 }
427
428 /**
429 * {@inheritDoc}
430 */
431 public boolean isDocumented() {
432 return getAnnotationType().getDeclaredAnnotations().isAnnotationPresent(Documented.class);
433 }
434
435 @Override
436 @CachedReturnPlugin.Enhance
437 public int hashCode() {
438 int hashCode = 0;
439 for (MethodDescription.InDefinedShape methodDescription : getAnnotationType().getDeclaredMethods()) {
440 hashCode += 31 * getValue(methodDescription).hashCode();
441 }
442 return hashCode;
443 }
444
445 @Override
446 public boolean equals(Object other) {
447 if (this == other) {
448 return true;
449 } else if (!(other instanceof AnnotationDescription)) {
450 return false;
451 }
452 AnnotationDescription annotationDescription = ((AnnotationDescription) other);
453 TypeDescription annotationType = getAnnotationType();
454 if (!annotationDescription.getAnnotationType().equals(annotationType)) {
455 return false;
456 }
457 for (MethodDescription.InDefinedShape methodDescription : annotationType.getDeclaredMethods()) {
458 if (!getValue(methodDescription).equals(annotationDescription.getValue(methodDescription))) {
459 return false;
460 }
461 }
462 return true;
463 }
464
465 @Override
466 public String toString() {
467 TypeDescription annotationType = getAnnotationType();
468 StringBuilder toString = new StringBuilder().append('@').append(annotationType.getName()).append('(');
469 boolean firstMember = true;
470 for (MethodDescription.InDefinedShape methodDescription : annotationType.getDeclaredMethods()) {
471 AnnotationValue<?, ?> value = getValue(methodDescription);
472 if (value.getState() == AnnotationValue.State.UNDEFINED) {
473 continue;
474 } else if (firstMember) {
475 firstMember = false;
476 } else {
477 toString.append(", ");
478 }
479 RenderingDispatcher.CURRENT.appendPrefix(toString, methodDescription.getName(), annotationType.getDeclaredMethods().size());
480 toString.append(value);
481 }
482 return toString.append(')').toString();
483 }
484 }
485
486 /**
487 * A description of an already loaded annotation.
488 *
489 * @param <S> The type of the annotation.
490 */
491 class ForLoadedAnnotation<S extends Annotation> extends AbstractBase implements Loadable<S> {
492
493 /**
494 * The represented annotation value.
495 */
496 private final S annotation;
497
498 /**
499 * The annotation's loaded type which might be loaded by a different class loader than the value's
500 * annotation type but must be structurally equal to it.
501 */
502 private final Class<S> annotationType;
503
504 /**
505 * Creates a new annotation description for a loaded annotation.
506 *
507 * @param annotation The annotation to represent.
508 */
509 @SuppressWarnings("unchecked")
510 protected ForLoadedAnnotation(S annotation) {
511 this(annotation, (Class<S>) annotation.annotationType());
512 }
513
514 /**
515 * Creates a new annotation description for a loaded annotation.
516 *
517 * @param annotation The annotation to represent.
518 * @param annotationType The annotation's loaded type which might be loaded by a different class loader than the value's
519 * annotation type but must be structurally equal to it.
520 */
521 private ForLoadedAnnotation(S annotation, Class<S> annotationType) {
522 this.annotation = annotation;
523 this.annotationType = annotationType;
524 }
525
526 /**
527 * Creates a description of the given annotation.
528 *
529 * @param annotation The annotation to be described.
530 * @param <U> The type of the annotation.
531 * @return A description of the given annotation.
532 */
533 public static <U extends Annotation> Loadable<U> of(U annotation) {
534 return new ForLoadedAnnotation<U>(annotation);
535 }
536
537 /**
538 * {@inheritDoc}
539 */
540 public S load() {
541 return annotationType == annotation.annotationType()
542 ? annotation
543 : AnnotationInvocationHandler.of(annotationType.getClassLoader(), annotationType, asValue(annotation));
544 }
545
546 /**
547 * Extracts the annotation values of an annotation into a property map.
548 *
549 * @param annotation The annotation to convert.
550 * @return A mapping of property names to their annotation value.
551 */
552 @SuppressWarnings("unchecked")
553 private static Map<String, AnnotationValue<?, ?>> asValue(Annotation annotation) {
554 Map<String, AnnotationValue<?, ?>> annotationValues = new HashMap<String, AnnotationValue<?, ?>>();
555 for (Method property : annotation.annotationType().getDeclaredMethods()) {
556 try {
557 annotationValues.put(property.getName(), asValue(property.invoke(annotation), property.getReturnType()));
558 } catch (InvocationTargetException exception) {
559 Throwable cause = exception.getCause();
560 if (cause instanceof TypeNotPresentException) {
561 annotationValues.put(property.getName(), new AnnotationValue.ForMissingType<Void, Void>(((TypeNotPresentException) cause).typeName()));
562 } else if (cause instanceof EnumConstantNotPresentException) {
563 annotationValues.put(property.getName(), new AnnotationValue.ForEnumerationDescription.WithUnknownConstant(
564 new TypeDescription.ForLoadedType(((EnumConstantNotPresentException) cause).enumType()),
565 ((EnumConstantNotPresentException) cause).constantName()));
566 } else if (cause instanceof AnnotationTypeMismatchException) {
567 annotationValues.put(property.getName(), new AnnotationValue.ForMismatchedType<Void, Void>(
568 new MethodDescription.ForLoadedMethod(((AnnotationTypeMismatchException) cause).element()),
569 ((AnnotationTypeMismatchException) cause).foundType()));
570 } else if (!(cause instanceof IncompleteAnnotationException)) {
571 throw new IllegalStateException("Cannot read " + property, exception.getCause());
572 }
573 } catch (IllegalAccessException exception) {
574 throw new IllegalStateException("Cannot access " + property, exception);
575 }
576 }
577 return annotationValues;
578 }
579
580 /**
581 * Transforms an annotation property to an annotation value.
582 *
583 * @param type The annotation's type.
584 * @param value The annotations value.
585 * @return An annotation value representation.
586 */
587 @SuppressWarnings("unchecked")
588 public static AnnotationValue<?, ?> asValue(Object value, Class<?> type) {
589 // Because enums can implement annotation interfaces, the enum property needs to be checked first.
590 if (Enum.class.isAssignableFrom(type)) {
591 return AnnotationValue.ForEnumerationDescription.<Enum>of(new EnumerationDescription.ForLoadedEnumeration((Enum) value));
592 } else if (Enum[].class.isAssignableFrom(type)) {
593 Enum<?>[] element = (Enum<?>[]) value;
594 EnumerationDescription[] enumerationDescription = new EnumerationDescription[element.length];
595 int index = 0;
596 for (Enum<?> anElement : element) {
597 enumerationDescription[index++] = new EnumerationDescription.ForLoadedEnumeration(anElement);
598 }
599 return AnnotationValue.ForDescriptionArray.<Enum>of(TypeDescription.ForLoadedType.of(type.getComponentType()), enumerationDescription);
600 } else if (Annotation.class.isAssignableFrom(type)) {
601 return AnnotationValue.ForAnnotationDescription.<Annotation>of(TypeDescription.ForLoadedType.of(type), asValue((Annotation) value));
602 } else if (Annotation[].class.isAssignableFrom(type)) {
603 Annotation[] element = (Annotation[]) value;
604 AnnotationDescription[] annotationDescription = new AnnotationDescription[element.length];
605 int index = 0;
606 for (Annotation anElement : element) {
607 annotationDescription[index++] = new AnnotationDescription.Latent(TypeDescription.ForLoadedType.of(type.getComponentType()), asValue(anElement));
608 }
609 return AnnotationValue.ForDescriptionArray.of(TypeDescription.ForLoadedType.of(type.getComponentType()), annotationDescription);
610 } else if (Class.class.isAssignableFrom(type)) {
611 return AnnotationValue.ForTypeDescription.<Class>of(TypeDescription.ForLoadedType.of((Class<?>) value));
612 } else if (Class[].class.isAssignableFrom(type)) {
613 Class<?>[] element = (Class<?>[]) value;
614 TypeDescription[] typeDescription = new TypeDescription[element.length];
615 int index = 0;
616 for (Class<?> anElement : element) {
617 typeDescription[index++] = TypeDescription.ForLoadedType.of(anElement);
618 }
619 return AnnotationValue.ForDescriptionArray.of(typeDescription);
620 } else {
621 return AnnotationValue.ForConstant.of(value);
622 }
623 }
624
625 /**
626 * {@inheritDoc}
627 */
628 @SuppressWarnings("deprecation")
629 @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should always be wrapped for clarity")
630 public AnnotationValue<?, ?> getValue(MethodDescription.InDefinedShape property) {
631 if (!property.getDeclaringType().represents(annotation.annotationType())) {
632 throw new IllegalArgumentException(property + " does not represent " + annotation.annotationType());
633 }
634 try {
635 boolean accessible = property.getDeclaringType().isPublic(); // method is required to be public
636 Method method = property instanceof MethodDescription.ForLoadedMethod
637 ? ((MethodDescription.ForLoadedMethod) property).getLoadedMethod()
638 : null;
639 if (method == null || method.getDeclaringClass() != annotation.annotationType() || (!accessible && !method.isAccessible())) {
640 method = annotation.annotationType().getMethod(property.getName());
641 if (!accessible) {
642 AccessController.doPrivileged(new SetAccessibleAction<Method>(method));
643 }
644 }
645 return asValue(method.invoke(annotation), method.getReturnType()).filter(property);
646 } catch (InvocationTargetException exception) {
647 Throwable cause = exception.getCause();
648 if (cause instanceof TypeNotPresentException) {
649 return new AnnotationValue.ForMissingType<Void, Void>(((TypeNotPresentException) cause).typeName());
650 } else if (cause instanceof EnumConstantNotPresentException) {
651 return new AnnotationValue.ForEnumerationDescription.WithUnknownConstant(
652 new TypeDescription.ForLoadedType(((EnumConstantNotPresentException) cause).enumType()),
653 ((EnumConstantNotPresentException) cause).constantName());
654 } else if (cause instanceof AnnotationTypeMismatchException) {
655 return new AnnotationValue.ForMismatchedType<Void, Void>(
656 new MethodDescription.ForLoadedMethod(((AnnotationTypeMismatchException) cause).element()),
657 ((AnnotationTypeMismatchException) cause).foundType());
658 } else if (cause instanceof IncompleteAnnotationException) {
659 return new AnnotationValue.ForMissingValue<Void, Void>(
660 new TypeDescription.ForLoadedType(((IncompleteAnnotationException) cause).annotationType()),
661 ((IncompleteAnnotationException) cause).elementName());
662 } else {
663 throw new IllegalStateException("Error reading annotation property " + property, cause);
664 }
665 } catch (Exception exception) {
666 throw new IllegalStateException("Cannot access annotation property " + property, exception);
667 }
668 }
669
670 /**
671 * {@inheritDoc}
672 */
673 @SuppressWarnings("unchecked")
674 public <T extends Annotation> Loadable<T> prepare(Class<T> annotationType) {
675 if (!annotation.annotationType().getName().equals(annotationType.getName())) {
676 throw new IllegalArgumentException(annotationType + " does not represent " + annotation.annotationType());
677 }
678 return annotationType == annotation.annotationType()
679 ? (Loadable<T>) this
680 : new ForLoadedAnnotation<T>((T) annotation, annotationType);
681 }
682
683 /**
684 * {@inheritDoc}
685 */
686 public TypeDescription getAnnotationType() {
687 return TypeDescription.ForLoadedType.of(annotation.annotationType());
688 }
689 }
690
691 /**
692 * A latent description of an annotation value that is defined explicitly.
693 */
694 class Latent extends AbstractBase {
695
696 /**
697 * The type of the annotation.
698 */
699 private final TypeDescription annotationType;
700
701 /**
702 * The values of the annotation mapped by their property name.
703 */
704 private final Map<String, ? extends AnnotationValue<?, ?>> annotationValues;
705
706 /**
707 * Creates a new latent annotation description.
708 *
709 * @param annotationType The type of the annotation.
710 * @param annotationValues The values of the annotation mapped by their property name.
711 */
712 protected Latent(TypeDescription annotationType, Map<String, ? extends AnnotationValue<?, ?>> annotationValues) {
713 this.annotationType = annotationType;
714 this.annotationValues = annotationValues;
715 }
716
717 /**
718 * {@inheritDoc}
719 */
720 public AnnotationValue<?, ?> getValue(MethodDescription.InDefinedShape property) {
721 if (!property.getDeclaringType().equals(annotationType)) {
722 throw new IllegalArgumentException("Not a property of " + annotationType + ": " + property);
723 }
724 AnnotationValue<?, ?> value = annotationValues.get(property.getName());
725 if (value != null) {
726 return value.filter(property);
727 }
728 AnnotationValue<?, ?> defaultValue = property.getDefaultValue();
729 return defaultValue == null
730 ? new AnnotationValue.ForMissingValue<Void, Void>(annotationType, property.getName())
731 : defaultValue;
732 }
733
734 /**
735 * {@inheritDoc}
736 */
737 public TypeDescription getAnnotationType() {
738 return annotationType;
739 }
740
741 /**
742 * {@inheritDoc}
743 */
744 public <T extends Annotation> Loadable<T> prepare(Class<T> annotationType) {
745 if (!this.annotationType.represents(annotationType)) {
746 throw new IllegalArgumentException(annotationType + " does not represent " + this.annotationType);
747 }
748 return new Loadable<T>(annotationType);
749 }
750
751 /**
752 * A loadable annotation description of a latent annotation description.
753 *
754 * @param <S> The annotation type.
755 */
756 protected class Loadable<S extends Annotation> extends AbstractBase implements AnnotationDescription.Loadable<S> {
757
758 /**
759 * The annotation type.
760 */
761 private final Class<S> annotationType;
762
763 /**
764 * Creates a loadable version of a latent annotation description.
765 *
766 * @param annotationType The annotation type.
767 */
768 protected Loadable(Class<S> annotationType) {
769 this.annotationType = annotationType;
770 }
771
772 /**
773 * {@inheritDoc}
774 */
775 public S load() {
776 return AnnotationDescription.AnnotationInvocationHandler.of(annotationType.getClassLoader(), annotationType, annotationValues);
777 }
778
779 /**
780 * {@inheritDoc}
781 */
782 public AnnotationValue<?, ?> getValue(MethodDescription.InDefinedShape property) {
783 return Latent.this.getValue(property);
784 }
785
786 /**
787 * {@inheritDoc}
788 */
789 public TypeDescription getAnnotationType() {
790 return TypeDescription.ForLoadedType.of(annotationType);
791 }
792
793 /**
794 * {@inheritDoc}
795 */
796 public <T extends Annotation> Loadable<T> prepare(Class<T> annotationType) {
797 return Latent.this.prepare(annotationType);
798 }
799 }
800 }
801
802 /**
803 * A builder for pragmatically creating {@link net.bytebuddy.description.annotation.AnnotationDescription}.
804 */
805 @HashCodeAndEqualsPlugin.Enhance
806 class Builder {
807
808 /**
809 * The annotation type.
810 */
811 private final TypeDescription annotationType;
812
813 /**
814 * A mapping of annotation properties to their annotation values.
815 */
816 private final Map<String, AnnotationValue<?, ?>> annotationValues;
817
818 /**
819 * Creates a builder for an annotation description.
820 *
821 * @param annotationType The annotation type.
822 * @param annotationValues A mapping of annotation properties to their annotation values.
823 */
824 protected Builder(TypeDescription annotationType, Map<String, AnnotationValue<?, ?>> annotationValues) {
825 this.annotationType = annotationType;
826 this.annotationValues = annotationValues;
827 }
828
829 /**
830 * Creates a builder for creating an annotation of the given type.
831 *
832 * @param annotationType The annotation type.
833 * @return A builder for creating an annotation of the given type.
834 */
835 public static Builder ofType(Class<? extends Annotation> annotationType) {
836 return ofType(TypeDescription.ForLoadedType.of(annotationType));
837 }
838
839 /**
840 * Creates a builder for creating an annotation of the given type.
841 *
842 * @param annotationType A description of the annotation type.
843 * @return A builder for creating an annotation of the given type.
844 */
845 public static Builder ofType(TypeDescription annotationType) {
846 if (!annotationType.isAnnotation()) {
847 throw new IllegalArgumentException("Not an annotation type: " + annotationType);
848 }
849 return new Builder(annotationType, Collections.<String, AnnotationValue<?, ?>>emptyMap());
850 }
851
852 /**
853 * Returns a builder with the additional, given property.
854 *
855 * @param property The name of the property to define.
856 * @param value An explicit description of the annotation value.
857 * @return A builder with the additional, given property.
858 */
859 public Builder define(String property, AnnotationValue<?, ?> value) {
860 MethodList<MethodDescription.InDefinedShape> methodDescriptions = annotationType.getDeclaredMethods().filter(named(property));
861 if (methodDescriptions.isEmpty()) {
862 throw new IllegalArgumentException(annotationType + " does not define a property named " + property);
863 }
864 Map<String, AnnotationValue<?, ?>> annotationValues = new HashMap<String, AnnotationValue<?, ?>>(this.annotationValues);
865 if (annotationValues.put(methodDescriptions.getOnly().getName(), value) != null) {
866 throw new IllegalArgumentException("Property already defined: " + property);
867 }
868 return new Builder(annotationType, annotationValues);
869 }
870
871 /**
872 * Returns a builder with the additional enumeration property.
873 *
874 * @param property The name of the property to define.
875 * @param value The enumeration value to define.
876 * @return A builder with the additional enumeration property.
877 */
878 public Builder define(String property, Enum<?> value) {
879 return define(property, new EnumerationDescription.ForLoadedEnumeration(value));
880 }
881
882 /**
883 * Returns a builder with the additional enumeration property.
884 *
885 * @param property The name of the property to define.
886 * @param enumerationType The type of the enumeration.
887 * @param value The enumeration value to define.
888 * @return A builder with the additional enumeration property.
889 */
890 public Builder define(String property, TypeDescription enumerationType, String value) {
891 return define(property, new EnumerationDescription.Latent(enumerationType, value));
892 }
893
894 /**
895 * Returns a builder with the additional enumeration property.
896 *
897 * @param property The name of the property to define.
898 * @param value A description of the enumeration value to define.
899 * @return A builder with the additional enumeration property.
900 */
901 @SuppressWarnings("unchecked")
902 public Builder define(String property, EnumerationDescription value) {
903 return define(property, AnnotationValue.ForEnumerationDescription.<Enum>of(value));
904 }
905
906 /**
907 * Returns a builder with the additional annotation property.
908 *
909 * @param property The name of the property to define.
910 * @param annotation The annotation value to define.
911 * @return A builder with the additional annotation property.
912 */
913 public Builder define(String property, Annotation annotation) {
914 return define(property, new ForLoadedAnnotation<Annotation>(annotation));
915 }
916
917 /**
918 * Returns a builder with the additional annotation property.
919 *
920 * @param property The name of the property to define.
921 * @param annotationDescription A description of the annotation value to define.
922 * @return A builder with the additional annotation property.
923 */
924 public Builder define(String property, AnnotationDescription annotationDescription) {
925 return define(property, new AnnotationValue.ForAnnotationDescription<Annotation>(annotationDescription));
926 }
927
928 /**
929 * Returns a builder with the additional class property.
930 *
931 * @param property The name of the property to define.
932 * @param type The class value to define.
933 * @return A builder with the additional class property.
934 */
935 public Builder define(String property, Class<?> type) {
936 return define(property, TypeDescription.ForLoadedType.of(type));
937 }
938
939 /**
940 * Returns a builder with the additional class property.
941 *
942 * @param property The name of the property to define.
943 * @param typeDescription A description of the type to define as a property value.
944 * @return A builder with the additional class property.
945 */
946 @SuppressWarnings("unchecked")
947 public Builder define(String property, TypeDescription typeDescription) {
948 return define(property, AnnotationValue.ForTypeDescription.<Class>of(typeDescription));
949 }
950
951 /**
952 * Returns a builder with the additional enumeration array property.
953 *
954 * @param property The name of the property to define.
955 * @param enumerationType The type of the enumeration, i.e. the component type of the enumeration array.
956 * @param value The enumeration values to be contained by the array.
957 * @param <T> The enumeration type.
958 * @return A builder with the additional class property.
959 */
960 @SuppressWarnings("unchecked")
961 public <T extends Enum<?>> Builder defineEnumerationArray(String property, Class<T> enumerationType, T... value) {
962 EnumerationDescription[] enumerationDescription = new EnumerationDescription[value.length];
963 int index = 0;
964 for (T aValue : value) {
965 enumerationDescription[index++] = new EnumerationDescription.ForLoadedEnumeration(aValue);
966 }
967 return defineEnumerationArray(property, TypeDescription.ForLoadedType.of(enumerationType), enumerationDescription);
968 }
969
970 /**
971 * Returns a builder with the additional enumeration array property.
972 *
973 * @param property The name of the property to define.
974 * @param enumerationType The type of the enumerations, i.e. is the component type of the enumeration array.
975 * @param value The enumeration values to be contained by the array.
976 * @return A builder with the additional enumeration property.
977 */
978 public Builder defineEnumerationArray(String property, TypeDescription enumerationType, String... value) {
979 if (!enumerationType.isEnum()) {
980 throw new IllegalArgumentException("Not an enumeration type: " + enumerationType);
981 }
982 EnumerationDescription[] enumerationDescription = new EnumerationDescription[value.length];
983 for (int i = 0; i < value.length; i++) {
984 enumerationDescription[i] = new EnumerationDescription.Latent(enumerationType, value[i]);
985 }
986 return defineEnumerationArray(property, enumerationType, enumerationDescription);
987 }
988
989 /**
990 * Returns a builder with the additional enumeration array property.
991 *
992 * @param property The name of the property to define.
993 * @param enumerationType The type of the enumerations, i.e. the component type of the enumeration array.
994 * @param value Descriptions of the enumerations to be contained by the array.
995 * @return A builder with the additional enumeration property.
996 */
997 @SuppressWarnings("unchecked")
998 public Builder defineEnumerationArray(String property, TypeDescription enumerationType, EnumerationDescription... value) {
999 return define(property, AnnotationValue.ForDescriptionArray.<Enum>of(enumerationType, value));
1000 }
1001
1002 /**
1003 * Returns a builder with the additional annotation array property.
1004 *
1005 * @param property The name of the property to define.
1006 * @param annotationType The type of the annotations, i.e. the component type of the enumeration array.
1007 * @param annotation The annotation values to be contained by the array.
1008 * @param <T> The annotation type.
1009 * @return A builder with the additional annotation property.
1010 */
1011 @SuppressWarnings("unchecked")
1012 public <T extends Annotation> Builder defineAnnotationArray(String property, Class<T> annotationType, T... annotation) {
1013 return defineAnnotationArray(property,
1014 TypeDescription.ForLoadedType.of(annotationType),
1015 new AnnotationList.ForLoadedAnnotations(annotation).toArray(new AnnotationDescription[0]));
1016 }
1017
1018 /**
1019 * Returns a builder with the additional annotation array property.
1020 *
1021 * @param property The name of the property to define.
1022 * @param annotationType The type of the annotations, i.e. the component type of the enumeration array.
1023 * @param annotationDescription Descriptions of the annotation values to be contained by the array.
1024 * @return A builder with the additional annotation property.
1025 */
1026 public Builder defineAnnotationArray(String property, TypeDescription annotationType, AnnotationDescription... annotationDescription) {
1027 return define(property, AnnotationValue.ForDescriptionArray.of(annotationType, annotationDescription));
1028 }
1029
1030 /**
1031 * Returns a builder with the additional type array property.
1032 *
1033 * @param property The name of the property to define.
1034 * @param type The types that should be contained by the array.
1035 * @return A builder with the additional type array property.
1036 */
1037 public Builder defineTypeArray(String property, Class<?>... type) {
1038 return defineTypeArray(property, new TypeList.ForLoadedTypes(type).toArray(new TypeDescription[0]));
1039 }
1040
1041 /**
1042 * Returns a builder with the additional type array property.
1043 *
1044 * @param property The name of the property to define.
1045 * @param typeDescription Descriptions of the types that should be contained by the array.
1046 * @return A builder with the additional type array property.
1047 */
1048 public Builder defineTypeArray(String property, TypeDescription... typeDescription) {
1049 return define(property, AnnotationValue.ForDescriptionArray.of(typeDescription));
1050 }
1051
1052 /**
1053 * Returns a builder with the additional {@code boolean} property.
1054 *
1055 * @param property The name of the property to define.
1056 * @param value The {@code boolean} value to define for the property.
1057 * @return A builder with the additional {@code boolean} property.
1058 */
1059 public Builder define(String property, boolean value) {
1060 return define(property, AnnotationValue.ForConstant.of(value));
1061 }
1062
1063 /**
1064 * Returns a builder with the additional {@code byte} property.
1065 *
1066 * @param property The name of the property to define.
1067 * @param value The {@code byte} value to define for the property.
1068 * @return A builder with the additional {@code byte} property.
1069 */
1070 public Builder define(String property, byte value) {
1071 return define(property, AnnotationValue.ForConstant.of(value));
1072 }
1073
1074 /**
1075 * Returns a builder with the additional {@code char} property.
1076 *
1077 * @param property The name of the property to define.
1078 * @param value The {@code char} value to define for the property.
1079 * @return A builder with the additional {@code char} property.
1080 */
1081 public Builder define(String property, char value) {
1082 return define(property, AnnotationValue.ForConstant.of(value));
1083 }
1084
1085 /**
1086 * Returns a builder with the additional {@code short} property.
1087 *
1088 * @param property The name of the property to define.
1089 * @param value The {@code short} value to define for the property.
1090 * @return A builder with the additional {@code short} property.
1091 */
1092 public Builder define(String property, short value) {
1093 return define(property, AnnotationValue.ForConstant.of(value));
1094 }
1095
1096 /**
1097 * Returns a builder with the additional {@code int} property.
1098 *
1099 * @param property The name of the property to define.
1100 * @param value The {@code int} value to define for the property.
1101 * @return A builder with the additional {@code int} property.
1102 */
1103 public Builder define(String property, int value) {
1104 return define(property, AnnotationValue.ForConstant.of(value));
1105 }
1106
1107 /**
1108 * Returns a builder with the additional {@code long} property.
1109 *
1110 * @param property The name of the property to define.
1111 * @param value The {@code long} value to define for the property.
1112 * @return A builder with the additional {@code long} property.
1113 */
1114 public Builder define(String property, long value) {
1115 return define(property, AnnotationValue.ForConstant.of(value));
1116 }
1117
1118 /**
1119 * Returns a builder with the additional {@code float} property.
1120 *
1121 * @param property The name of the property to define.
1122 * @param value The {@code float} value to define for the property.
1123 * @return A builder with the additional {@code float} property.
1124 */
1125 public Builder define(String property, float value) {
1126 return define(property, AnnotationValue.ForConstant.of(value));
1127 }
1128
1129 /**
1130 * Returns a builder with the additional {@code double} property.
1131 *
1132 * @param property The name of the property to define.
1133 * @param value The {@code double} value to define for the property.
1134 * @return A builder with the additional {@code double} property.
1135 */
1136 public Builder define(String property, double value) {
1137 return define(property, AnnotationValue.ForConstant.of(value));
1138 }
1139
1140 /**
1141 * Returns a builder with the additional {@link java.lang.String} property.
1142 *
1143 * @param property The name of the property to define.
1144 * @param value The {@link java.lang.String} value to define for the property.
1145 * @return A builder with the additional {@link java.lang.String} property.
1146 */
1147 public Builder define(String property, String value) {
1148 return define(property, AnnotationValue.ForConstant.of(value));
1149 }
1150
1151 /**
1152 * Returns a builder with the additional {@code boolean} array property.
1153 *
1154 * @param property The name of the property to define.
1155 * @param value The {@code boolean} values to define for the property.
1156 * @return A builder with the additional {@code boolean} array property.
1157 */
1158 public Builder defineArray(String property, boolean... value) {
1159 return define(property, AnnotationValue.ForConstant.of(value));
1160 }
1161
1162 /**
1163 * Returns a builder with the additional {@code byte} array property.
1164 *
1165 * @param property The name of the property to define.
1166 * @param value The {@code byte} values to define for the property.
1167 * @return A builder with the additional {@code byte} array property.
1168 */
1169 public Builder defineArray(String property, byte... value) {
1170 return define(property, AnnotationValue.ForConstant.of(value));
1171 }
1172
1173 /**
1174 * Returns a builder with the additional {@code char} array property.
1175 *
1176 * @param property The name of the property to define.
1177 * @param value The {@code char} values to define for the property.
1178 * @return A builder with the additional {@code char} array property.
1179 */
1180 public Builder defineArray(String property, char... value) {
1181 return define(property, AnnotationValue.ForConstant.of(value));
1182 }
1183
1184 /**
1185 * Returns a builder with the additional {@code short} array property.
1186 *
1187 * @param property The name of the property to define.
1188 * @param value The {@code short} values to define for the property.
1189 * @return A builder with the additional {@code short} array property.
1190 */
1191 public Builder defineArray(String property, short... value) {
1192 return define(property, AnnotationValue.ForConstant.of(value));
1193 }
1194
1195 /**
1196 * Returns a builder with the additional {@code int} array property.
1197 *
1198 * @param property The name of the property to define.
1199 * @param value The {@code int} values to define for the property.
1200 * @return A builder with the additional {@code int} array property.
1201 */
1202 public Builder defineArray(String property, int... value) {
1203 return define(property, AnnotationValue.ForConstant.of(value));
1204 }
1205
1206 /**
1207 * Returns a builder with the additional {@code long} array property.
1208 *
1209 * @param property The name of the property to define.
1210 * @param value The {@code long} values to define for the property.
1211 * @return A builder with the additional {@code long} array property.
1212 */
1213 public Builder defineArray(String property, long... value) {
1214 return define(property, AnnotationValue.ForConstant.of(value));
1215 }
1216
1217 /**
1218 * Returns a builder with the additional {@code float} array property.
1219 *
1220 * @param property The name of the property to define.
1221 * @param value The {@code float} values to define for the property.
1222 * @return A builder with the additional {@code float} array property.
1223 */
1224 public Builder defineArray(String property, float... value) {
1225 return define(property, AnnotationValue.ForConstant.of(value));
1226 }
1227
1228 /**
1229 * Returns a builder with the additional {@code double} array property.
1230 *
1231 * @param property The name of the property to define.
1232 * @param value The {@code double} values to define for the property.
1233 * @return A builder with the additional {@code double} array property.
1234 */
1235 public Builder defineArray(String property, double... value) {
1236 return define(property, AnnotationValue.ForConstant.of(value));
1237 }
1238
1239 /**
1240 * Returns a builder with the additional {@link java.lang.String} array property.
1241 *
1242 * @param property The name of the property to define.
1243 * @param value The {@link java.lang.String} array value to define for the property.
1244 * @return A builder with the additional {@link java.lang.String} array property.
1245 */
1246 public Builder defineArray(String property, String... value) {
1247 return define(property, AnnotationValue.ForConstant.of(value));
1248 }
1249
1250 /**
1251 * Creates an annotation description for the values that were defined for this builder. It is validated that all
1252 * properties are defined if no default value is set for an annotation property.
1253 *
1254 * @return An appropriate annotation description.
1255 */
1256 public AnnotationDescription build() {
1257 for (MethodDescription.InDefinedShape methodDescription : annotationType.getDeclaredMethods()) {
1258 AnnotationValue<?, ?> annotationValue = annotationValues.get(methodDescription.getName());
1259 if (annotationValue == null && methodDescription.getDefaultValue() == null) {
1260 throw new IllegalStateException("No value or default value defined for " + methodDescription.getName());
1261 } else if (annotationValue != null && annotationValue.filter(methodDescription).getState() != AnnotationValue.State.RESOLVED) {
1262 throw new IllegalStateException("Illegal annotation value for " + methodDescription + ": " + annotationValue);
1263 }
1264 }
1265 return new Latent(annotationType, annotationValues);
1266 }
1267
1268 /**
1269 * Creates an annotation description for the values that were defined for this builder.
1270 *
1271 * @param validated {@code true} if the annotation description should be validated for having included all values.
1272 * @return An appropriate annotation description.
1273 */
1274 public AnnotationDescription build(boolean validated) {
1275 return validated
1276 ? build()
1277 : new Latent(annotationType, annotationValues);
1278 }
1279 }
1280 }
1281