• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

raphw / byte-buddy / #815

03 Nov 2025 02:07PM UTC coverage: 82.717% (-1.0%) from 83.677%
#815

push

raphw
Fix up test runs.

29472 of 35630 relevant lines covered (82.72%)

0.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

57.1
/byte-buddy-dep/src/main/java/net/bytebuddy/description/annotation/AnnotationDescription.java
1
/*
2
 * Copyright 2014 - Present 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.AccessControllerPlugin;
21
import net.bytebuddy.build.CachedReturnPlugin;
22
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
23
import net.bytebuddy.description.enumeration.EnumerationDescription;
24
import net.bytebuddy.description.method.MethodDescription;
25
import net.bytebuddy.description.method.MethodList;
26
import net.bytebuddy.description.type.TypeDescription;
27
import net.bytebuddy.description.type.TypeList;
28
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
29
import net.bytebuddy.utility.nullability.AlwaysNull;
30
import net.bytebuddy.utility.nullability.MaybeNull;
31
import net.bytebuddy.utility.privilege.SetAccessibleAction;
32

33
import java.lang.annotation.Annotation;
34
import java.lang.annotation.AnnotationTypeMismatchException;
35
import java.lang.annotation.Documented;
36
import java.lang.annotation.ElementType;
37
import java.lang.annotation.IncompleteAnnotationException;
38
import java.lang.annotation.Inherited;
39
import java.lang.annotation.Retention;
40
import java.lang.annotation.RetentionPolicy;
41
import java.lang.annotation.Target;
42
import java.lang.reflect.InvocationHandler;
43
import java.lang.reflect.InvocationTargetException;
44
import java.lang.reflect.Method;
45
import java.lang.reflect.Proxy;
46
import java.security.PrivilegedAction;
47
import java.util.Arrays;
48
import java.util.Collections;
49
import java.util.HashMap;
50
import java.util.HashSet;
51
import java.util.LinkedHashMap;
52
import java.util.Map;
53
import java.util.Set;
54

55
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
56
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
57
import static net.bytebuddy.matcher.ElementMatchers.named;
58
import static net.bytebuddy.matcher.ElementMatchers.not;
59
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
60

61
/**
62
 * An annotation description describes {@link java.lang.annotation.Annotation} meta data of a class without this class
63
 * being required to be loaded. All values of an annotation are therefore represented in unloaded state:
64
 * <ul>
65
 * <li>{@link java.lang.Class} instances are represented as {@link TypeDescription}s.</li>
66
 * <li>{@link java.lang.Enum} instances are represented as
67
 * {@link net.bytebuddy.description.enumeration.EnumerationDescription}s.</li>
68
 * <li>{@link java.lang.annotation.Annotation}s are described as
69
 * {@link AnnotationDescription}s.</li>
70
 * <li>All primitive types are represented as their wrapper types.</li>
71
 * </ul>
72
 * An annotation can however be loaded in order to access unwrapped values. This will cause a loading of the classes
73
 * of these values.
74
 */
75
public interface AnnotationDescription {
76

77
    /**
78
     * Indicates a nonexistent annotation in a type-safe manner.
79
     */
80
    @AlwaysNull
81
    AnnotationDescription.Loadable<?> UNDEFINED = null;
1✔
82

83
    /**
84
     * Returns a value of this annotation.
85
     *
86
     * @param property The name of the property being accessed.
87
     * @return The value for the supplied property.
88
     */
89
    AnnotationValue<?, ?> getValue(String property);
90

91
    /**
92
     * Returns a value of this annotation.
93
     *
94
     * @param property The property being accessed.
95
     * @return The value for the supplied property.
96
     */
97
    AnnotationValue<?, ?> getValue(MethodDescription.InDefinedShape property);
98

99
    /**
100
     * Returns a description of the annotation type of this annotation.
101
     *
102
     * @return A description of the annotation type of this annotation.
103
     */
104
    TypeDescription getAnnotationType();
105

106
    /**
107
     * Links this annotation description to a given annotation type such that it can be loaded. This does not cause
108
     * the values of this annotation to be loaded.
109
     *
110
     * @param annotationType The loaded annotation type of this annotation description.
111
     * @param <T>            The type of the annotation.
112
     * @return A loadable version of this annotation description.
113
     */
114
    <T extends Annotation> Loadable<T> prepare(Class<T> annotationType);
115

116
    /**
117
     * Returns this annotation's retention policy.
118
     *
119
     * @return This annotation's retention policy.
120
     */
121
    RetentionPolicy getRetention();
122

123
    /**
124
     * Returns a set of all {@link ElementType}s that can declare this annotation.
125
     *
126
     * @return A set of all element types that can declare this annotation.
127
     */
128
    Set<ElementType> getElementTypes();
129

130
    /**
131
     * Checks if this annotation is supported on the supplied element type.
132
     *
133
     * @param elementType The element type to check.
134
     * @return {@code true} if the supplied element type is supported by this annotation.
135
     */
136
    boolean isSupportedOn(ElementType elementType);
137

138
    /**
139
     * Checks if this annotation is supported on the supplied element type.
140
     *
141
     * @param elementType The element type to check.
142
     * @return {@code true} if the supplied element type is supported by this annotation.
143
     */
144
    boolean isSupportedOn(String elementType);
145

146
    /**
147
     * Checks if this annotation is inherited.
148
     *
149
     * @return {@code true} if this annotation is inherited.
150
     * @see Inherited
151
     */
152
    boolean isInherited();
153

154
    /**
155
     * Checks if this annotation is documented.
156
     *
157
     * @return {@code true} if this annotation is documented.
158
     * @see Documented
159
     */
160
    boolean isDocumented();
161

162
    /**
163
     * An annotation description that is linked to a given loaded annotation type which allows its representation
164
     * as a fully loaded instance.
165
     *
166
     * @param <S> The annotation type.
167
     */
168
    interface Loadable<S extends Annotation> extends AnnotationDescription {
169

170
        /**
171
         * Loads this annotation description. This causes all classes referenced by the annotation value to be loaded.
172
         * Without specifying a class loader, the annotation's class loader which was used to prepare this instance
173
         * is used.
174
         *
175
         * @return A loaded version of this annotation description.
176
         */
177
        S load();
178
    }
179

180
    /**
181
     * A rendering dispatcher is responsible for resolving annotation descriptions to {@link String} representations.
182
     */
183
    enum RenderingDispatcher {
1✔
184

185
        /**
186
         * A rendering dispatcher for any VM previous to Java 14.
187
         */
188
        LEGACY_VM,
1✔
189

190
        /**
191
         * A rendering dispatcher for Java 14 until Java 18.
192
         */
193
        JAVA_14_CAPABLE_VM {
1✔
194
            @Override
195
            public void appendPrefix(StringBuilder toString, String key, int count) {
196
                if (count > 1 || !key.equals("value")) {
×
197
                    super.appendPrefix(toString, key, count);
×
198
                }
199
            }
×
200
        },
201

202
        /**
203
         * A rendering dispatcher for Java 19 onward.
204
         */
205
        JAVA_19_CAPABLE_VM {
1✔
206
            @Override
207
            public void appendPrefix(StringBuilder toString, String key, int count) {
208
                if (count > 1 || !key.equals("value")) {
×
209
                    super.appendPrefix(toString, key, count);
×
210
                }
211
            }
×
212

213
            @Override
214
            public void appendType(StringBuilder toString, TypeDescription typeDescription) {
215
                toString.append(typeDescription.getCanonicalName());
×
216
            }
×
217
        };
218

219
        /**
220
         * The rendering dispatcher for the current VM.
221
         */
222
        public static final RenderingDispatcher CURRENT;
223

224
        /*
225
         * Initializes the rendering dispatcher.
226
         */
227
        static {
228
            ClassFileVersion classFileVersion = ClassFileVersion.ofThisVm(ClassFileVersion.JAVA_V5);
1✔
229
            if (classFileVersion.isAtLeast(ClassFileVersion.JAVA_V19)) {
1✔
230
                CURRENT = RenderingDispatcher.JAVA_19_CAPABLE_VM;
×
231
            } else if (classFileVersion.isAtLeast(ClassFileVersion.JAVA_V14)) {
1✔
232
                CURRENT = RenderingDispatcher.JAVA_14_CAPABLE_VM;
×
233
            } else {
234
                CURRENT = RenderingDispatcher.LEGACY_VM;
1✔
235
            }
236
        }
1✔
237

238
        /**
239
         * Appends the key property prefix to a string builder representing an annotation's string representation.
240
         *
241
         * @param toString The string builder that creates the string representation.
242
         * @param key      The key's name.
243
         * @param count    The property count.
244
         */
245
        public void appendPrefix(StringBuilder toString, String key, int count) {
246
            toString.append(key).append('=');
×
247
        }
×
248

249
        /**
250
         * Appends the type name of the annotation being rendered.
251
         *
252
         * @param toString        The string builder that creates the string representation.
253
         * @param typeDescription The annotation type being rendered.
254
         */
255
        public void appendType(StringBuilder toString, TypeDescription typeDescription) {
256
            toString.append(typeDescription.getName());
1✔
257
        }
1✔
258
    }
259

260
    /**
261
     * An {@link java.lang.reflect.InvocationHandler} for implementing annotations.
262
     *
263
     * @param <T> The type of the handled annotation.
264
     */
265
    class AnnotationInvocationHandler<T extends Annotation> implements InvocationHandler {
266

267
        /**
268
         * The name of the {@link Object#hashCode()} method.
269
         */
270
        private static final String HASH_CODE = "hashCode";
271

272
        /**
273
         * The name of the {@link Object#equals(Object)} method.
274
         */
275
        private static final String EQUALS = "equals";
276

277
        /**
278
         * The name of the {@link Object#toString()} method.
279
         */
280
        private static final String TO_STRING = "toString";
281

282
        /**
283
         * The name of the {@link Annotation#annotationType()} method.
284
         */
285
        private static final String ANNOTATION_TYPE = "annotationType";
286

287
        /**
288
         * An empty array that can be used to indicate no arguments to avoid an allocation on a reflective call.
289
         */
290
        private static final Object[] NO_ARGUMENT = new Object[0];
1✔
291

292
        /**
293
         * The loaded annotation type.
294
         */
295
        private final Class<? extends Annotation> annotationType;
296

297
        /**
298
         * A sorted list of values of this annotation.
299
         */
300
        private final LinkedHashMap<Method, AnnotationValue.Loaded<?>> values;
301

302
        /**
303
         * Creates a new invocation handler.
304
         *
305
         * @param annotationType The loaded annotation type.
306
         * @param values         A sorted list of values of this annotation.
307
         */
308
        protected AnnotationInvocationHandler(Class<T> annotationType, LinkedHashMap<Method, AnnotationValue.Loaded<?>> values) {
1✔
309
            this.annotationType = annotationType;
1✔
310
            this.values = values;
1✔
311
        }
1✔
312

313
        /**
314
         * Creates a proxy instance for the supplied annotation type and values.
315
         *
316
         * @param classLoader    The class loader that should be used for loading the annotation's values.
317
         * @param annotationType The annotation's type.
318
         * @param values         The values that the annotation contains.
319
         * @param <S>            The type of the handled annotation.
320
         * @return A proxy for the annotation type and values.
321
         */
322
        @SuppressWarnings("unchecked")
323
        public static <S extends Annotation> S of(@MaybeNull ClassLoader classLoader,
324
                                                  Class<S> annotationType,
325
                                                  Map<String, ? extends AnnotationValue<?, ?>> values) {
326
            LinkedHashMap<Method, AnnotationValue.Loaded<?>> loadedValues = new LinkedHashMap<Method, AnnotationValue.Loaded<?>>();
1✔
327
            for (Method method : annotationType.getDeclaredMethods()) {
1✔
328
                AnnotationValue<?, ?> annotationValue = values.get(method.getName());
1✔
329
                if (annotationValue == null) {
1✔
330
                    Object defaultValue = method.getDefaultValue();
×
331
                    loadedValues.put(method, (defaultValue == null
×
332
                            ? new AnnotationValue.ForMissingValue<Void, Void>(new TypeDescription.ForLoadedType(method.getDeclaringClass()), method.getName())
×
333
                            : AnnotationDescription.ForLoadedAnnotation.asValue(defaultValue, method.getReturnType())).load(classLoader));
×
334
                } else {
×
335
                    loadedValues.put(method, annotationValue.filter(new MethodDescription.ForLoadedMethod(method)).load(classLoader));
1✔
336
                }
337
            }
338
            return (S) Proxy.newProxyInstance(classLoader, new Class<?>[]{annotationType}, new AnnotationInvocationHandler<S>(annotationType, loadedValues));
1✔
339
        }
340

341
        /**
342
         * {@inheritDoc}
343
         */
344
        public Object invoke(Object proxy, Method method, @MaybeNull Object[] argument) {
345
            if (method.getDeclaringClass() != annotationType) {
1✔
346
                if (method.getName().equals(HASH_CODE)) {
1✔
347
                    return hashCodeRepresentation();
×
348
                } else if (method.getName().equals(EQUALS) && method.getParameterTypes().length == 1) {
1✔
349
                    return equalsRepresentation(proxy, argument[0]);
1✔
350
                } else if (method.getName().equals(TO_STRING)) {
×
351
                    return toStringRepresentation();
×
352
                } else if (method.getName().equals(ANNOTATION_TYPE)) {
×
353
                    return annotationType;
×
354
                } else {
355
                    throw new IllegalStateException("Unexpected method: " + method);
×
356
                }
357
            }
358
            return values.get(method).resolve();
×
359
        }
360

361
        /**
362
         * Returns the string representation of the represented annotation.
363
         *
364
         * @return The string representation of the represented annotation.
365
         */
366
        protected String toStringRepresentation() {
367
            StringBuilder toString = new StringBuilder();
×
368
            toString.append('@');
×
369
            RenderingDispatcher.CURRENT.appendType(toString, TypeDescription.ForLoadedType.of(annotationType));
×
370
            toString.append('(');
×
371
            boolean firstMember = true;
×
372
            for (Map.Entry<Method, AnnotationValue.Loaded<?>> entry : values.entrySet()) {
×
373
                if (!entry.getValue().getState().isDefined()) {
×
374
                    continue;
×
375
                }
376
                if (firstMember) {
×
377
                    firstMember = false;
×
378
                } else {
379
                    toString.append(", ");
×
380
                }
381
                RenderingDispatcher.CURRENT.appendPrefix(toString, entry.getKey().getName(), values.entrySet().size());
×
382
                toString.append(entry.getValue().toString());
×
383
            }
×
384
            toString.append(')');
×
385
            return toString.toString();
×
386
        }
387

388
        /**
389
         * Returns the hash code of the represented annotation.
390
         *
391
         * @return The hash code of the represented annotation.
392
         */
393
        private int hashCodeRepresentation() {
394
            int hashCode = 0;
×
395
            for (Map.Entry<Method, AnnotationValue.Loaded<?>> entry : values.entrySet()) {
×
396
                if (!entry.getValue().getState().isDefined()) {
×
397
                    continue;
×
398
                }
399
                hashCode += (127 * entry.getKey().getName().hashCode()) ^ entry.getValue().hashCode();
×
400
            }
×
401
            return hashCode;
×
402
        }
403

404
        /**
405
         * Checks if another instance is equal to this instance.
406
         *
407
         * @param self  The annotation proxy instance.
408
         * @param other The instance to be examined for equality to the represented instance.
409
         * @return {@code true} if the given instance is equal to the represented instance.
410
         */
411
        private boolean equalsRepresentation(Object self, Object other) {
412
            if (self == other) {
1✔
413
                return true;
×
414
            } else if (!annotationType.isInstance(other)) {
1✔
415
                return false;
1✔
416
            } else if (Proxy.isProxyClass(other.getClass())) {
1✔
417
                InvocationHandler invocationHandler = Proxy.getInvocationHandler(other);
1✔
418
                if (invocationHandler instanceof AnnotationInvocationHandler) {
1✔
419
                    return invocationHandler.equals(this);
1✔
420
                }
421
            }
422
            try {
423
                for (Map.Entry<Method, AnnotationValue.Loaded<?>> entry : values.entrySet()) {
1✔
424
                    try {
425
                        if (!entry.getValue().represents(entry.getKey().invoke(other, NO_ARGUMENT))) {
1✔
426
                            return false;
1✔
427
                        }
428
                    } catch (RuntimeException exception) {
×
429
                        return false; // Incomplete annotations are not equal to one another.
×
430
                    }
1✔
431
                }
1✔
432
                return true;
1✔
433
            } catch (InvocationTargetException ignored) {
1✔
434
                return false;
1✔
435
            } catch (IllegalAccessException exception) {
×
436
                throw new IllegalStateException("Could not access annotation property", exception);
×
437
            }
438
        }
439

440
        @Override
441
        @CachedReturnPlugin.Enhance("hashCode")
442
        public int hashCode() {
443
            int result = annotationType.hashCode();
×
444
            result = 31 * result + values.hashCode();
×
445
            for (Map.Entry<Method, ?> entry : values.entrySet()) {
×
446
                result = 31 * result + entry.getValue().hashCode();
×
447
            }
×
448
            return result;
×
449
        }
450

451
        @Override
452
        public boolean equals(@MaybeNull Object other) {
453
            if (this == other) {
1✔
454
                return true;
×
455
            } else if (!(other instanceof AnnotationInvocationHandler)) {
1✔
456
                return false;
×
457
            }
458
            AnnotationInvocationHandler<?> that = (AnnotationInvocationHandler<?>) other;
1✔
459
            if (!annotationType.equals(that.annotationType)) {
1✔
460
                return false;
×
461
            }
462
            for (Map.Entry<Method, AnnotationValue.Loaded<?>> entry : values.entrySet()) {
1✔
463
                if (!entry.getValue().equals(that.values.get(entry.getKey()))) {
1✔
464
                    return false;
1✔
465
                }
466
            }
1✔
467
            return true;
1✔
468
        }
469
    }
470

471
    /**
472
     * An adapter implementation of an annotation.
473
     */
474
    abstract class AbstractBase implements AnnotationDescription {
1✔
475

476
        /**
477
         * An array containing all element types that are a legal annotation target when such a target
478
         * is not specified explicitly.
479
         */
480
        private static final Set<ElementType> DEFAULT_TARGET;
481

482
        /**
483
         * A description of the {@link Retention#value()} method.
484
         */
485
        private static final MethodDescription.InDefinedShape RETENTION_VALUE;
486

487
        /**
488
         * A description of the {@link Target#value()} method.
489
         */
490
        private static final MethodDescription.InDefinedShape TARGET_VALUE;
491

492
        /*
493
         * Resolves common annotation properties.
494
         */
495
        static {
496
            DEFAULT_TARGET = new HashSet<ElementType>();
1✔
497
            for (ElementType elementType : ElementType.values()) {
1✔
498
                if (!elementType.name().equals("TYPE_PARAMETER")) {
1✔
499
                    DEFAULT_TARGET.add(elementType);
1✔
500
                }
501
            }
502
            RETENTION_VALUE = TypeDescription.ForLoadedType.of(Retention.class)
1✔
503
                    .getDeclaredMethods()
1✔
504
                    .filter(named("value"))
1✔
505
                    .getOnly();
1✔
506
            TARGET_VALUE = TypeDescription.ForLoadedType.of(Target.class)
1✔
507
                    .getDeclaredMethods()
1✔
508
                    .filter(named("value"))
1✔
509
                    .getOnly();
1✔
510
        }
1✔
511

512
        /**
513
         * {@inheritDoc}
514
         */
515
        public AnnotationValue<?, ?> getValue(String property) {
516
            MethodList<MethodDescription.InDefinedShape> candidates = getAnnotationType().getDeclaredMethods().filter(named(property)
×
517
                    .and(takesArguments(0))
×
518
                    .and(isPublic())
×
519
                    .and(not(isStatic())));
×
520
            if (candidates.size() == 1) {
×
521
                return getValue(candidates.getOnly());
×
522
            } else {
523
                throw new IllegalArgumentException("Unknown property of " + getAnnotationType() + ": " + property);
×
524
            }
525
        }
526

527
        /**
528
         * {@inheritDoc}
529
         */
530
        public RetentionPolicy getRetention() {
531
            AnnotationDescription.Loadable<Retention> retention = getAnnotationType().getDeclaredAnnotations().ofType(Retention.class);
1✔
532
            return retention == null
1✔
533
                    ? RetentionPolicy.CLASS
534
                    : retention.getValue(RETENTION_VALUE).load(ClassLoadingStrategy.BOOTSTRAP_LOADER).resolve(RetentionPolicy.class);
1✔
535
        }
536

537
        /**
538
         * {@inheritDoc}
539
         */
540
        public Set<ElementType> getElementTypes() {
541
            AnnotationDescription.Loadable<Target> target = getAnnotationType().getDeclaredAnnotations().ofType(Target.class);
×
542
            return target == null
×
543
                    ? Collections.unmodifiableSet(DEFAULT_TARGET)
×
544
                    : new HashSet<ElementType>(Arrays.asList(target.getValue(TARGET_VALUE).load(ClassLoadingStrategy.BOOTSTRAP_LOADER).resolve(ElementType[].class)));
×
545
        }
546

547
        /**
548
         * {@inheritDoc}
549
         */
550
        public boolean isSupportedOn(ElementType elementType) {
551
            return isSupportedOn(elementType.name());
1✔
552
        }
553

554
        /**
555
         * {@inheritDoc}
556
         */
557
        public boolean isSupportedOn(String elementType) {
558
            AnnotationDescription.Loadable<Target> target = getAnnotationType().getDeclaredAnnotations().ofType(Target.class);
1✔
559
            if (target == null) {
1✔
560
                if (elementType.equals("TYPE_USE")) {
1✔
561
                    return true;
×
562
                }
563
                for (ElementType candidate : DEFAULT_TARGET) {
1✔
564
                    if (candidate.name().equals(elementType)) {
1✔
565
                        return true;
1✔
566
                    }
567
                }
1✔
568
            } else {
569
                for (EnumerationDescription enumerationDescription : target.getValue(TARGET_VALUE).resolve(EnumerationDescription[].class)) {
1✔
570
                    if (enumerationDescription.getValue().equals(elementType)) {
1✔
571
                        return true;
1✔
572
                    }
573
                }
574
            }
575
            return false;
1✔
576
        }
577

578
        /**
579
         * {@inheritDoc}
580
         */
581
        public boolean isInherited() {
582
            return getAnnotationType().getDeclaredAnnotations().isAnnotationPresent(Inherited.class);
1✔
583
        }
584

585
        /**
586
         * {@inheritDoc}
587
         */
588
        public boolean isDocumented() {
589
            return getAnnotationType().getDeclaredAnnotations().isAnnotationPresent(Documented.class);
×
590
        }
591

592
        @Override
593
        @CachedReturnPlugin.Enhance("hashCode")
594
        public int hashCode() {
595
            int hashCode = 0;
1✔
596
            for (MethodDescription.InDefinedShape methodDescription : getAnnotationType().getDeclaredMethods()) {
1✔
597
                hashCode += 31 * getValue(methodDescription).hashCode();
1✔
598
            }
1✔
599
            return hashCode;
1✔
600
        }
601

602
        @Override
603
        public boolean equals(@MaybeNull Object other) {
604
            if (this == other) {
1✔
605
                return true;
×
606
            } else if (!(other instanceof AnnotationDescription)) {
1✔
607
                return false;
×
608
            }
609
            AnnotationDescription annotationDescription = ((AnnotationDescription) other);
1✔
610
            TypeDescription annotationType = getAnnotationType();
1✔
611
            if (!annotationDescription.getAnnotationType().equals(annotationType)) {
1✔
612
                return false;
1✔
613
            }
614
            for (MethodDescription.InDefinedShape methodDescription : annotationType.getDeclaredMethods()) {
1✔
615
                if (!getValue(methodDescription).equals(annotationDescription.getValue(methodDescription))) {
1✔
616
                    return false;
×
617
                }
618
            }
1✔
619
            return true;
1✔
620
        }
621

622
        @Override
623
        public String toString() {
624
            TypeDescription annotationType = getAnnotationType();
1✔
625
            StringBuilder toString = new StringBuilder().append('@');
1✔
626
            RenderingDispatcher.CURRENT.appendType(toString, annotationType);
1✔
627
            toString.append('(');
1✔
628
            boolean firstMember = true;
1✔
629
            for (MethodDescription.InDefinedShape methodDescription : annotationType.getDeclaredMethods()) {
1✔
630
                AnnotationValue<?, ?> value = getValue(methodDescription);
×
631
                if (value.getState() == AnnotationValue.State.UNDEFINED) {
×
632
                    continue;
×
633
                } else if (firstMember) {
×
634
                    firstMember = false;
×
635
                } else {
636
                    toString.append(", ");
×
637
                }
638
                RenderingDispatcher.CURRENT.appendPrefix(toString, methodDescription.getName(), annotationType.getDeclaredMethods().size());
×
639
                toString.append(value);
×
640
            }
×
641
            return toString.append(')').toString();
1✔
642
        }
643
    }
644

645
    /**
646
     * A description of an already loaded annotation.
647
     *
648
     * @param <S> The type of the annotation.
649
     */
650
    class ForLoadedAnnotation<S extends Annotation> extends AbstractBase implements Loadable<S> {
651

652
        /**
653
         * An empty array that can be used to indicate no arguments to avoid an allocation on a reflective call.
654
         */
655
        private static final Object[] NO_ARGUMENT = new Object[0];
1✔
656

657
        /**
658
         * The represented annotation value.
659
         */
660
        private final S annotation;
661

662
        /**
663
         * The annotation's loaded type which might be loaded by a different class loader than the value's
664
         * annotation type but must be structurally equal to it.
665
         */
666
        private final Class<S> annotationType;
667

668
        /**
669
         * Creates a new annotation description for a loaded annotation.
670
         *
671
         * @param annotation The annotation to represent.
672
         */
673
        @SuppressWarnings("unchecked")
674
        protected ForLoadedAnnotation(S annotation) {
675
            this(annotation, (Class<S>) annotation.annotationType());
1✔
676
        }
1✔
677

678
        /**
679
         * Creates a new annotation description for a loaded annotation.
680
         *
681
         * @param annotation     The annotation to represent.
682
         * @param annotationType The annotation's loaded type which might be loaded by a different class loader than the value's
683
         *                       annotation type but must be structurally equal to it.
684
         */
685
        private ForLoadedAnnotation(S annotation, Class<S> annotationType) {
1✔
686
            this.annotation = annotation;
1✔
687
            this.annotationType = annotationType;
1✔
688
        }
1✔
689

690
        /**
691
         * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
692
         *
693
         * @param action The action to execute from a privileged context.
694
         * @param <T>    The type of the action's resolved value.
695
         * @return The action's resolved value.
696
         */
697
        @AccessControllerPlugin.Enhance
698
        private static <T> T doPrivileged(PrivilegedAction<T> action) {
699
            return action.run();
×
700
        }
701

702
        /**
703
         * Creates a description of the given annotation.
704
         *
705
         * @param annotation The annotation to be described.
706
         * @param <U>        The type of the annotation.
707
         * @return A description of the given annotation.
708
         */
709
        public static <U extends Annotation> Loadable<U> of(U annotation) {
710
            return new ForLoadedAnnotation<U>(annotation);
1✔
711
        }
712

713
        /**
714
         * {@inheritDoc}
715
         */
716
        public S load() {
717
            return annotationType == annotation.annotationType()
1✔
718
                    ? annotation
719
                    : AnnotationInvocationHandler.of(annotationType.getClassLoader(), annotationType, asValue(annotation));
×
720
        }
721

722
        /**
723
         * Extracts the annotation values of an annotation into a property map.
724
         *
725
         * @param annotation The annotation to convert.
726
         * @return A mapping of property names to their annotation value.
727
         */
728
        @SuppressWarnings("rawtypes")
729
        private static Map<String, AnnotationValue<?, ?>> asValue(Annotation annotation) {
730
            Map<String, AnnotationValue<?, ?>> annotationValues = new HashMap<String, AnnotationValue<?, ?>>();
1✔
731
            for (Method property : annotation.annotationType().getDeclaredMethods()) {
1✔
732
                try {
733
                    annotationValues.put(property.getName(), asValue(property.invoke(annotation, NO_ARGUMENT), property.getReturnType()));
1✔
734
                } catch (InvocationTargetException exception) {
×
735
                    Throwable cause = exception.getTargetException();
×
736
                    if (cause instanceof TypeNotPresentException) {
×
737
                        annotationValues.put(property.getName(), new AnnotationValue.ForMissingType<Void, Void>(((TypeNotPresentException) cause).typeName()));
×
738
                    } else if (cause instanceof EnumConstantNotPresentException) {
×
739
                        annotationValues.put(property.getName(), new AnnotationValue.ForEnumerationDescription.WithUnknownConstant(
×
740
                                new TypeDescription.ForLoadedType(((EnumConstantNotPresentException) cause).enumType()),
×
741
                                ((EnumConstantNotPresentException) cause).constantName()));
×
742
                    } else if (cause instanceof AnnotationTypeMismatchException) {
×
743
                        annotationValues.put(property.getName(), new AnnotationValue.ForMismatchedType<Void, Void>(
×
744
                                new MethodDescription.ForLoadedMethod(((AnnotationTypeMismatchException) cause).element()),
×
745
                                ((AnnotationTypeMismatchException) cause).foundType()));
×
746
                    } else if (!(cause instanceof IncompleteAnnotationException)) {
×
747
                        throw new IllegalStateException("Cannot read " + property, cause);
×
748
                    }
749
                } catch (IllegalAccessException exception) {
×
750
                    throw new IllegalStateException("Cannot access " + property, exception);
×
751
                }
1✔
752
            }
753
            return annotationValues;
1✔
754
        }
755

756
        /**
757
         * Transforms an annotation property to an annotation value.
758
         *
759
         * @param type  The annotation's type.
760
         * @param value The annotations value.
761
         * @return An annotation value representation.
762
         */
763
        @SuppressWarnings({"unchecked", "rawtypes"})
764
        public static AnnotationValue<?, ?> asValue(Object value, Class<?> type) {
765
            // Because enums can implement annotation interfaces, the enum property needs to be checked first.
766
            if (Enum.class.isAssignableFrom(type)) {
1✔
767
                return AnnotationValue.ForEnumerationDescription.<Enum>of(new EnumerationDescription.ForLoadedEnumeration((Enum) value));
1✔
768
            } else if (Enum[].class.isAssignableFrom(type)) {
1✔
769
                Enum<?>[] element = (Enum<?>[]) value;
1✔
770
                EnumerationDescription[] enumerationDescription = new EnumerationDescription[element.length];
1✔
771
                int index = 0;
1✔
772
                for (Enum<?> anElement : element) {
1✔
773
                    enumerationDescription[index++] = new EnumerationDescription.ForLoadedEnumeration(anElement);
1✔
774
                }
775
                return AnnotationValue.ForDescriptionArray.<Enum>of(TypeDescription.ForLoadedType.of(type.getComponentType()), enumerationDescription);
1✔
776
            } else if (Annotation.class.isAssignableFrom(type)) {
1✔
777
                return AnnotationValue.ForAnnotationDescription.<Annotation>of(TypeDescription.ForLoadedType.of(type), asValue((Annotation) value));
1✔
778
            } else if (Annotation[].class.isAssignableFrom(type)) {
1✔
779
                Annotation[] element = (Annotation[]) value;
1✔
780
                AnnotationDescription[] annotationDescription = new AnnotationDescription[element.length];
1✔
781
                int index = 0;
1✔
782
                for (Annotation anElement : element) {
1✔
783
                    annotationDescription[index++] = new AnnotationDescription.Latent(TypeDescription.ForLoadedType.of(type.getComponentType()), asValue(anElement));
1✔
784
                }
785
                return AnnotationValue.ForDescriptionArray.of(TypeDescription.ForLoadedType.of(type.getComponentType()), annotationDescription);
1✔
786
            } else if (Class.class.isAssignableFrom(type)) {
1✔
787
                return AnnotationValue.ForTypeDescription.<Class>of(TypeDescription.ForLoadedType.of((Class<?>) value));
1✔
788
            } else if (Class[].class.isAssignableFrom(type)) {
1✔
789
                Class<?>[] element = (Class<?>[]) value;
1✔
790
                TypeDescription[] typeDescription = new TypeDescription[element.length];
1✔
791
                int index = 0;
1✔
792
                for (Class<?> anElement : element) {
1✔
793
                    typeDescription[index++] = TypeDescription.ForLoadedType.of(anElement);
1✔
794
                }
795
                return AnnotationValue.ForDescriptionArray.of(typeDescription);
1✔
796
            } else {
797
                return AnnotationValue.ForConstant.of(value);
1✔
798
            }
799
        }
800

801
        /**
802
         * {@inheritDoc}
803
         */
804
        @SuppressWarnings({"deprecation", "rawtypes"})
805
        @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should always be wrapped for clarity.")
806
        public AnnotationValue<?, ?> getValue(MethodDescription.InDefinedShape property) {
807
            if (!property.getDeclaringType().represents(annotation.annotationType())) {
1✔
808
                throw new IllegalArgumentException(property + " does not represent " + annotation.annotationType());
×
809
            }
810
            try {
811
                boolean accessible = property.getDeclaringType().isPublic(); // method is required to be public
1✔
812
                Method method = property instanceof MethodDescription.ForLoadedMethod
1✔
813
                        ? ((MethodDescription.ForLoadedMethod) property).getLoadedMethod()
1✔
814
                        : null;
815
                if (method == null || method.getDeclaringClass() != annotation.annotationType() || (!accessible && !method.isAccessible())) {
1✔
816
                    method = annotation.annotationType().getMethod(property.getName());
1✔
817
                    if (!accessible) {
1✔
818
                        doPrivileged(new SetAccessibleAction<Method>(method));
1✔
819
                    }
820
                }
821
                return asValue(method.invoke(annotation, NO_ARGUMENT), method.getReturnType()).filter(property);
1✔
822
            } catch (InvocationTargetException exception) {
×
823
                Throwable cause = exception.getTargetException();
×
824
                if (cause instanceof TypeNotPresentException) {
×
825
                    return new AnnotationValue.ForMissingType<Void, Void>(((TypeNotPresentException) cause).typeName());
×
826
                } else if (cause instanceof EnumConstantNotPresentException) {
×
827
                    return new AnnotationValue.ForEnumerationDescription.WithUnknownConstant(
×
828
                            new TypeDescription.ForLoadedType(((EnumConstantNotPresentException) cause).enumType()),
×
829
                            ((EnumConstantNotPresentException) cause).constantName());
×
830
                } else if (cause instanceof AnnotationTypeMismatchException) {
×
831
                    return new AnnotationValue.ForMismatchedType<Void, Void>(
×
832
                            new MethodDescription.ForLoadedMethod(((AnnotationTypeMismatchException) cause).element()),
×
833
                            ((AnnotationTypeMismatchException) cause).foundType());
×
834
                } else if (cause instanceof IncompleteAnnotationException) {
×
835
                    return new AnnotationValue.ForMissingValue<Void, Void>(
×
836
                            new TypeDescription.ForLoadedType(((IncompleteAnnotationException) cause).annotationType()),
×
837
                            ((IncompleteAnnotationException) cause).elementName());
×
838
                } else {
839
                    throw new IllegalStateException("Error reading annotation property " + property, cause);
×
840
                }
841
            } catch (Exception exception) {
×
842
                throw new IllegalStateException("Cannot access annotation property " + property, exception);
×
843
            }
844
        }
845

846
        /**
847
         * {@inheritDoc}
848
         */
849
        @SuppressWarnings("unchecked")
850
        public <T extends Annotation> Loadable<T> prepare(Class<T> annotationType) {
851
            if (!annotation.annotationType().getName().equals(annotationType.getName())) {
1✔
852
                throw new IllegalArgumentException(annotationType + " does not represent " + annotation.annotationType());
×
853
            }
854
            return annotationType == annotation.annotationType()
1✔
855
                    ? (Loadable<T>) this
856
                    : new ForLoadedAnnotation<T>((T) annotation, annotationType);
857
        }
858

859
        /**
860
         * {@inheritDoc}
861
         */
862
        public TypeDescription getAnnotationType() {
863
            return TypeDescription.ForLoadedType.of(annotation.annotationType());
1✔
864
        }
865
    }
866

867
    /**
868
     * A latent description of an annotation value that is defined explicitly.
869
     */
870
    class Latent extends AbstractBase {
871

872
        /**
873
         * The type of the annotation.
874
         */
875
        private final TypeDescription annotationType;
876

877
        /**
878
         * The values of the annotation mapped by their property name.
879
         */
880
        private final Map<String, ? extends AnnotationValue<?, ?>> annotationValues;
881

882
        /**
883
         * Creates a new latent annotation description.
884
         *
885
         * @param annotationType   The type of the annotation.
886
         * @param annotationValues The values of the annotation mapped by their property name.
887
         */
888
        protected Latent(TypeDescription annotationType, Map<String, ? extends AnnotationValue<?, ?>> annotationValues) {
1✔
889
            this.annotationType = annotationType;
1✔
890
            this.annotationValues = annotationValues;
1✔
891
        }
1✔
892

893
        /**
894
         * {@inheritDoc}
895
         */
896
        public AnnotationValue<?, ?> getValue(MethodDescription.InDefinedShape property) {
897
            if (!property.getDeclaringType().equals(annotationType)) {
1✔
898
                throw new IllegalArgumentException("Not a property of " + annotationType + ": " + property);
×
899
            }
900
            AnnotationValue<?, ?> value = annotationValues.get(property.getName());
1✔
901
            if (value != null) {
1✔
902
                return value.filter(property);
1✔
903
            }
904
            AnnotationValue<?, ?> defaultValue = property.getDefaultValue();
1✔
905
            return defaultValue == null
1✔
906
                    ? new AnnotationValue.ForMissingValue<Void, Void>(annotationType, property.getName())
1✔
907
                    : defaultValue;
908
        }
909

910
        /**
911
         * {@inheritDoc}
912
         */
913
        public TypeDescription getAnnotationType() {
914
            return annotationType;
1✔
915
        }
916

917
        /**
918
         * {@inheritDoc}
919
         */
920
        public <T extends Annotation> Loadable<T> prepare(Class<T> annotationType) {
921
            if (!this.annotationType.represents(annotationType)) {
1✔
922
                throw new IllegalArgumentException(annotationType + " does not represent " + this.annotationType);
×
923
            }
924
            return new Loadable<T>(annotationType);
1✔
925
        }
926

927
        /**
928
         * A loadable annotation description of a latent annotation description.
929
         *
930
         * @param <S> The annotation type.
931
         */
932
        protected class Loadable<S extends Annotation> extends AbstractBase implements AnnotationDescription.Loadable<S> {
933

934
            /**
935
             * The annotation type.
936
             */
937
            private final Class<S> annotationType;
938

939
            /**
940
             * Creates a loadable version of a latent annotation description.
941
             *
942
             * @param annotationType The annotation type.
943
             */
944
            protected Loadable(Class<S> annotationType) {
1✔
945
                this.annotationType = annotationType;
1✔
946
            }
1✔
947

948
            /**
949
             * {@inheritDoc}
950
             */
951
            public S load() {
952
                return AnnotationDescription.AnnotationInvocationHandler.of(annotationType.getClassLoader(), annotationType, annotationValues);
×
953
            }
954

955
            /**
956
             * {@inheritDoc}
957
             */
958
            public AnnotationValue<?, ?> getValue(MethodDescription.InDefinedShape property) {
959
                return Latent.this.getValue(property);
1✔
960
            }
961

962
            /**
963
             * {@inheritDoc}
964
             */
965
            public TypeDescription getAnnotationType() {
966
                return TypeDescription.ForLoadedType.of(annotationType);
×
967
            }
968

969
            /**
970
             * {@inheritDoc}
971
             */
972
            public <T extends Annotation> Loadable<T> prepare(Class<T> annotationType) {
973
                return Latent.this.prepare(annotationType);
×
974
            }
975
        }
976
    }
977

978
    /**
979
     * A builder for pragmatically creating {@link net.bytebuddy.description.annotation.AnnotationDescription}.
980
     */
981
    @HashCodeAndEqualsPlugin.Enhance
982
    class Builder {
983

984
        /**
985
         * The annotation type.
986
         */
987
        private final TypeDescription annotationType;
988

989
        /**
990
         * A mapping of annotation properties to their annotation values.
991
         */
992
        private final Map<String, AnnotationValue<?, ?>> annotationValues;
993

994
        /**
995
         * Creates a builder for an annotation description.
996
         *
997
         * @param annotationType   The annotation type.
998
         * @param annotationValues A mapping of annotation properties to their annotation values.
999
         */
1000
        protected Builder(TypeDescription annotationType, Map<String, AnnotationValue<?, ?>> annotationValues) {
1✔
1001
            this.annotationType = annotationType;
1✔
1002
            this.annotationValues = annotationValues;
1✔
1003
        }
1✔
1004

1005
        /**
1006
         * Creates a builder for creating an annotation of the given type.
1007
         *
1008
         * @param annotationType The annotation type.
1009
         * @return A builder for creating an annotation of the given type.
1010
         */
1011
        public static Builder ofType(Class<? extends Annotation> annotationType) {
1012
            return ofType(TypeDescription.ForLoadedType.of(annotationType));
1✔
1013
        }
1014

1015
        /**
1016
         * Creates a builder for creating an annotation of the given type.
1017
         *
1018
         * @param annotationType A description of the annotation type.
1019
         * @return A builder for creating an annotation of the given type.
1020
         */
1021
        public static Builder ofType(TypeDescription annotationType) {
1022
            if (!annotationType.isAnnotation()) {
1✔
1023
                throw new IllegalArgumentException("Not an annotation type: " + annotationType);
1✔
1024
            }
1025
            return new Builder(annotationType, Collections.<String, AnnotationValue<?, ?>>emptyMap());
1✔
1026
        }
1027

1028
        /**
1029
         * Returns a builder with the additional, given property.
1030
         *
1031
         * @param property The name of the property to define.
1032
         * @param value    An explicit description of the annotation value.
1033
         * @return A builder with the additional, given property.
1034
         */
1035
        public Builder define(String property, AnnotationValue<?, ?> value) {
1036
            MethodList<MethodDescription.InDefinedShape> methodDescriptions = annotationType.getDeclaredMethods().filter(named(property));
1✔
1037
            if (methodDescriptions.isEmpty()) {
1✔
1038
                throw new IllegalArgumentException(annotationType + " does not define a property named " + property);
1✔
1039
            }
1040
            Map<String, AnnotationValue<?, ?>> annotationValues = new HashMap<String, AnnotationValue<?, ?>>(this.annotationValues);
1✔
1041
            if (annotationValues.put(methodDescriptions.getOnly().getName(), value) != null) {
1✔
1042
                throw new IllegalArgumentException("Property already defined: " + property);
1✔
1043
            }
1044
            return new Builder(annotationType, annotationValues);
1✔
1045
        }
1046

1047
        /**
1048
         * Returns a builder with the additional enumeration property.
1049
         *
1050
         * @param property The name of the property to define.
1051
         * @param value    The enumeration value to define.
1052
         * @return A builder with the additional enumeration property.
1053
         */
1054
        public Builder define(String property, Enum<?> value) {
1055
            return define(property, new EnumerationDescription.ForLoadedEnumeration(value));
1✔
1056
        }
1057

1058
        /**
1059
         * Returns a builder with the additional enumeration property.
1060
         *
1061
         * @param property        The name of the property to define.
1062
         * @param enumerationType The type of the enumeration.
1063
         * @param value           The enumeration value to define.
1064
         * @return A builder with the additional enumeration property.
1065
         */
1066
        public Builder define(String property, TypeDescription enumerationType, String value) {
1067
            return define(property, new EnumerationDescription.Latent(enumerationType, value));
×
1068
        }
1069

1070
        /**
1071
         * Returns a builder with the additional enumeration property.
1072
         *
1073
         * @param property The name of the property to define.
1074
         * @param value    A description of the enumeration value to define.
1075
         * @return A builder with the additional enumeration property.
1076
         */
1077
        @SuppressWarnings({"unchecked", "rawtypes"})
1078
        public Builder define(String property, EnumerationDescription value) {
1079
            return define(property, AnnotationValue.ForEnumerationDescription.<Enum>of(value));
1✔
1080
        }
1081

1082
        /**
1083
         * Returns a builder with the additional annotation property.
1084
         *
1085
         * @param property   The name of the property to define.
1086
         * @param annotation The annotation value to define.
1087
         * @return A builder with the additional annotation property.
1088
         */
1089
        public Builder define(String property, Annotation annotation) {
1090
            return define(property, new ForLoadedAnnotation<Annotation>(annotation));
×
1091
        }
1092

1093
        /**
1094
         * Returns a builder with the additional annotation property.
1095
         *
1096
         * @param property              The name of the property to define.
1097
         * @param annotationDescription A description of the annotation value to define.
1098
         * @return A builder with the additional annotation property.
1099
         */
1100
        public Builder define(String property, AnnotationDescription annotationDescription) {
1101
            return define(property, new AnnotationValue.ForAnnotationDescription<Annotation>(annotationDescription));
×
1102
        }
1103

1104
        /**
1105
         * Returns a builder with the additional class property.
1106
         *
1107
         * @param property The name of the property to define.
1108
         * @param type     The class value to define.
1109
         * @return A builder with the additional class property.
1110
         */
1111
        public Builder define(String property, Class<?> type) {
1112
            return define(property, TypeDescription.ForLoadedType.of(type));
1✔
1113
        }
1114

1115
        /**
1116
         * Returns a builder with the additional class property.
1117
         *
1118
         * @param property        The name of the property to define.
1119
         * @param typeDescription A description of the type to define as a property value.
1120
         * @return A builder with the additional class property.
1121
         */
1122
        @SuppressWarnings({"unchecked", "rawtypes"})
1123
        public Builder define(String property, TypeDescription typeDescription) {
1124
            return define(property, AnnotationValue.ForTypeDescription.<Class>of(typeDescription));
1✔
1125
        }
1126

1127
        /**
1128
         * Returns a builder with the additional enumeration array property.
1129
         *
1130
         * @param property        The name of the property to define.
1131
         * @param enumerationType The type of the enumeration, i.e. the component type of the enumeration array.
1132
         * @param value           The enumeration values to be contained by the array.
1133
         * @param <T>             The enumeration type.
1134
         * @return A builder with the additional class property.
1135
         */
1136
        @SuppressWarnings("unchecked")
1137
        public <T extends Enum<?>> Builder defineEnumerationArray(String property, Class<T> enumerationType, T... value) {
1138
            EnumerationDescription[] enumerationDescription = new EnumerationDescription[value.length];
1✔
1139
            int index = 0;
1✔
1140
            for (T aValue : value) {
1✔
1141
                enumerationDescription[index++] = new EnumerationDescription.ForLoadedEnumeration(aValue);
1✔
1142
            }
1143
            return defineEnumerationArray(property, TypeDescription.ForLoadedType.of(enumerationType), enumerationDescription);
×
1144
        }
1145

1146
        /**
1147
         * Returns a builder with the additional enumeration array property.
1148
         *
1149
         * @param property        The name of the property to define.
1150
         * @param enumerationType The type of the enumerations, i.e. is the component type of the enumeration array.
1151
         * @param value           The enumeration values to be contained by the array.
1152
         * @return A builder with the additional enumeration property.
1153
         */
1154
        public Builder defineEnumerationArray(String property, TypeDescription enumerationType, String... value) {
1155
            if (!enumerationType.isEnum()) {
×
1156
                throw new IllegalArgumentException("Not an enumeration type: " + enumerationType);
×
1157
            }
1158
            EnumerationDescription[] enumerationDescription = new EnumerationDescription[value.length];
×
1159
            for (int i = 0; i < value.length; i++) {
×
1160
                enumerationDescription[i] = new EnumerationDescription.Latent(enumerationType, value[i]);
×
1161
            }
1162
            return defineEnumerationArray(property, enumerationType, enumerationDescription);
×
1163
        }
1164

1165
        /**
1166
         * Returns a builder with the additional enumeration array property.
1167
         *
1168
         * @param property        The name of the property to define.
1169
         * @param enumerationType The type of the enumerations, i.e. the component type of the enumeration array.
1170
         * @param value           Descriptions of the enumerations to be contained by the array.
1171
         * @return A builder with the additional enumeration property.
1172
         */
1173
        @SuppressWarnings({"unchecked", "rawtypes"})
1174
        public Builder defineEnumerationArray(String property, TypeDescription enumerationType, EnumerationDescription... value) {
1175
            return define(property, AnnotationValue.ForDescriptionArray.<Enum>of(enumerationType, value));
×
1176
        }
1177

1178
        /**
1179
         * Returns a builder with the additional annotation array property.
1180
         *
1181
         * @param property       The name of the property to define.
1182
         * @param annotationType The type of the annotations, i.e. the component type of the enumeration array.
1183
         * @param annotation     The annotation values to be contained by the array.
1184
         * @param <T>            The annotation type.
1185
         * @return A builder with the additional annotation property.
1186
         */
1187
        @SuppressWarnings("unchecked")
1188
        public <T extends Annotation> Builder defineAnnotationArray(String property, Class<T> annotationType, T... annotation) {
1189
            return defineAnnotationArray(property,
1✔
1190
                    TypeDescription.ForLoadedType.of(annotationType),
1✔
1191
                    new AnnotationList.ForLoadedAnnotations(annotation).toArray(new AnnotationDescription[0]));
1✔
1192
        }
1193

1194
        /**
1195
         * Returns a builder with the additional annotation array property.
1196
         *
1197
         * @param property              The name of the property to define.
1198
         * @param annotationType        The type of the annotations, i.e. the component type of the enumeration array.
1199
         * @param annotationDescription Descriptions of the annotation values to be contained by the array.
1200
         * @return A builder with the additional annotation property.
1201
         */
1202
        public Builder defineAnnotationArray(String property, TypeDescription annotationType, AnnotationDescription... annotationDescription) {
1203
            return define(property, AnnotationValue.ForDescriptionArray.of(annotationType, annotationDescription));
×
1204
        }
1205

1206
        /**
1207
         * Returns a builder with the additional type array property.
1208
         *
1209
         * @param property The name of the property to define.
1210
         * @param type     The types that should be contained by the array.
1211
         * @return A builder with the additional type array property.
1212
         */
1213
        public Builder defineTypeArray(String property, Class<?>... type) {
1214
            return defineTypeArray(property, new TypeList.ForLoadedTypes(type).toArray(new TypeDescription[0]));
×
1215
        }
1216

1217
        /**
1218
         * Returns a builder with the additional type array property.
1219
         *
1220
         * @param property        The name of the property to define.
1221
         * @param typeDescription Descriptions of the types that should be contained by the array.
1222
         * @return A builder with the additional type array property.
1223
         */
1224
        public Builder defineTypeArray(String property, TypeDescription... typeDescription) {
1225
            return define(property, AnnotationValue.ForDescriptionArray.of(typeDescription));
×
1226
        }
1227

1228
        /**
1229
         * Returns a builder with the additional {@code boolean} property.
1230
         *
1231
         * @param property The name of the property to define.
1232
         * @param value    The {@code boolean} value to define for the property.
1233
         * @return A builder with the additional {@code boolean} property.
1234
         */
1235
        public Builder define(String property, boolean value) {
1236
            return define(property, AnnotationValue.ForConstant.of(value));
1✔
1237
        }
1238

1239
        /**
1240
         * Returns a builder with the additional {@code byte} property.
1241
         *
1242
         * @param property The name of the property to define.
1243
         * @param value    The {@code byte} value to define for the property.
1244
         * @return A builder with the additional {@code byte} property.
1245
         */
1246
        public Builder define(String property, byte value) {
1247
            return define(property, AnnotationValue.ForConstant.of(value));
×
1248
        }
1249

1250
        /**
1251
         * Returns a builder with the additional {@code char} property.
1252
         *
1253
         * @param property The name of the property to define.
1254
         * @param value    The {@code char} value to define for the property.
1255
         * @return A builder with the additional {@code char} property.
1256
         */
1257
        public Builder define(String property, char value) {
1258
            return define(property, AnnotationValue.ForConstant.of(value));
×
1259
        }
1260

1261
        /**
1262
         * Returns a builder with the additional {@code short} property.
1263
         *
1264
         * @param property The name of the property to define.
1265
         * @param value    The {@code short} value to define for the property.
1266
         * @return A builder with the additional {@code short} property.
1267
         */
1268
        public Builder define(String property, short value) {
1269
            return define(property, AnnotationValue.ForConstant.of(value));
×
1270
        }
1271

1272
        /**
1273
         * Returns a builder with the additional {@code int} property.
1274
         *
1275
         * @param property The name of the property to define.
1276
         * @param value    The {@code int} value to define for the property.
1277
         * @return A builder with the additional {@code int} property.
1278
         */
1279
        public Builder define(String property, int value) {
1280
            return define(property, AnnotationValue.ForConstant.of(value));
1✔
1281
        }
1282

1283
        /**
1284
         * Returns a builder with the additional {@code long} property.
1285
         *
1286
         * @param property The name of the property to define.
1287
         * @param value    The {@code long} value to define for the property.
1288
         * @return A builder with the additional {@code long} property.
1289
         */
1290
        public Builder define(String property, long value) {
1291
            return define(property, AnnotationValue.ForConstant.of(value));
×
1292
        }
1293

1294
        /**
1295
         * Returns a builder with the additional {@code float} property.
1296
         *
1297
         * @param property The name of the property to define.
1298
         * @param value    The {@code float} value to define for the property.
1299
         * @return A builder with the additional {@code float} property.
1300
         */
1301
        public Builder define(String property, float value) {
1302
            return define(property, AnnotationValue.ForConstant.of(value));
×
1303
        }
1304

1305
        /**
1306
         * Returns a builder with the additional {@code double} property.
1307
         *
1308
         * @param property The name of the property to define.
1309
         * @param value    The {@code double} value to define for the property.
1310
         * @return A builder with the additional {@code double} property.
1311
         */
1312
        public Builder define(String property, double value) {
1313
            return define(property, AnnotationValue.ForConstant.of(value));
×
1314
        }
1315

1316
        /**
1317
         * Returns a builder with the additional {@link java.lang.String} property.
1318
         *
1319
         * @param property The name of the property to define.
1320
         * @param value    The {@link java.lang.String} value to define for the property.
1321
         * @return A builder with the additional {@link java.lang.String} property.
1322
         */
1323
        public Builder define(String property, String value) {
1324
            return define(property, AnnotationValue.ForConstant.of(value));
1✔
1325
        }
1326

1327
        /**
1328
         * Returns a builder with the additional {@code boolean} array property.
1329
         *
1330
         * @param property The name of the property to define.
1331
         * @param value    The {@code boolean} values to define for the property.
1332
         * @return A builder with the additional {@code boolean} array property.
1333
         */
1334
        public Builder defineArray(String property, boolean... value) {
1335
            return define(property, AnnotationValue.ForConstant.of(value));
×
1336
        }
1337

1338
        /**
1339
         * Returns a builder with the additional {@code byte} array property.
1340
         *
1341
         * @param property The name of the property to define.
1342
         * @param value    The {@code byte} values to define for the property.
1343
         * @return A builder with the additional {@code byte} array property.
1344
         */
1345
        public Builder defineArray(String property, byte... value) {
1346
            return define(property, AnnotationValue.ForConstant.of(value));
×
1347
        }
1348

1349
        /**
1350
         * Returns a builder with the additional {@code char} array property.
1351
         *
1352
         * @param property The name of the property to define.
1353
         * @param value    The {@code char} values to define for the property.
1354
         * @return A builder with the additional {@code char} array property.
1355
         */
1356
        public Builder defineArray(String property, char... value) {
1357
            return define(property, AnnotationValue.ForConstant.of(value));
×
1358
        }
1359

1360
        /**
1361
         * Returns a builder with the additional {@code short} array property.
1362
         *
1363
         * @param property The name of the property to define.
1364
         * @param value    The {@code short} values to define for the property.
1365
         * @return A builder with the additional {@code short} array property.
1366
         */
1367
        public Builder defineArray(String property, short... value) {
1368
            return define(property, AnnotationValue.ForConstant.of(value));
×
1369
        }
1370

1371
        /**
1372
         * Returns a builder with the additional {@code int} array property.
1373
         *
1374
         * @param property The name of the property to define.
1375
         * @param value    The {@code int} values to define for the property.
1376
         * @return A builder with the additional {@code int} array property.
1377
         */
1378
        public Builder defineArray(String property, int... value) {
1379
            return define(property, AnnotationValue.ForConstant.of(value));
×
1380
        }
1381

1382
        /**
1383
         * Returns a builder with the additional {@code long} array property.
1384
         *
1385
         * @param property The name of the property to define.
1386
         * @param value    The {@code long} values to define for the property.
1387
         * @return A builder with the additional {@code long} array property.
1388
         */
1389
        public Builder defineArray(String property, long... value) {
1390
            return define(property, AnnotationValue.ForConstant.of(value));
×
1391
        }
1392

1393
        /**
1394
         * Returns a builder with the additional {@code float} array property.
1395
         *
1396
         * @param property The name of the property to define.
1397
         * @param value    The {@code float} values to define for the property.
1398
         * @return A builder with the additional {@code float} array property.
1399
         */
1400
        public Builder defineArray(String property, float... value) {
1401
            return define(property, AnnotationValue.ForConstant.of(value));
×
1402
        }
1403

1404
        /**
1405
         * Returns a builder with the additional {@code double} array property.
1406
         *
1407
         * @param property The name of the property to define.
1408
         * @param value    The {@code double} values to define for the property.
1409
         * @return A builder with the additional {@code double} array property.
1410
         */
1411
        public Builder defineArray(String property, double... value) {
1412
            return define(property, AnnotationValue.ForConstant.of(value));
×
1413
        }
1414

1415
        /**
1416
         * Returns a builder with the additional {@link java.lang.String} array property.
1417
         *
1418
         * @param property The name of the property to define.
1419
         * @param value    The {@link java.lang.String} array value to define for the property.
1420
         * @return A builder with the additional {@link java.lang.String} array property.
1421
         */
1422
        public Builder defineArray(String property, String... value) {
1423
            return define(property, AnnotationValue.ForConstant.of(value));
×
1424
        }
1425

1426
        /**
1427
         * Creates an annotation description for the values that were defined for this builder. It is validated that all
1428
         * properties are defined if no default value is set for an annotation property.
1429
         *
1430
         * @return An appropriate annotation description.
1431
         */
1432
        public AnnotationDescription build() {
1433
            for (MethodDescription.InDefinedShape methodDescription : annotationType.getDeclaredMethods()) {
1✔
1434
                AnnotationValue<?, ?> annotationValue = annotationValues.get(methodDescription.getName());
1✔
1435
                if (annotationValue == null && methodDescription.getDefaultValue() == null) {
1✔
1436
                    throw new IllegalStateException("No value or default value defined for " + methodDescription.getName());
1✔
1437
                } else if (annotationValue != null && annotationValue.filter(methodDescription).getState() != AnnotationValue.State.RESOLVED) {
1✔
1438
                    throw new IllegalStateException("Illegal annotation value for " + methodDescription + ": " + annotationValue);
×
1439
                }
1440
            }
1✔
1441
            return new Latent(annotationType, annotationValues);
1✔
1442
        }
1443

1444
        /**
1445
         * Creates an annotation description for the values that were defined for this builder.
1446
         *
1447
         * @param validated {@code true} if the annotation description should be validated for having included all values.
1448
         * @return An appropriate annotation description.
1449
         */
1450
        public AnnotationDescription build(boolean validated) {
1451
            return validated
1✔
1452
                    ? build()
1✔
1453
                    : new Latent(annotationType, annotationValues);
1454
        }
1455
    }
1456
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc