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.implementation.attribute;
17
18 import net.bytebuddy.build.HashCodeAndEqualsPlugin;
19 import net.bytebuddy.description.annotation.AnnotationDescription;
20 import net.bytebuddy.description.annotation.AnnotationList;
21 import net.bytebuddy.description.type.TypeDescription;
22 import net.bytebuddy.description.type.TypeList;
23 import net.bytebuddy.jar.asm.ClassVisitor;
24
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.List;
28
29 /**
30  * An appender that writes attributes or annotations to a given ASM {@link net.bytebuddy.jar.asm.ClassVisitor}.
31  */

32 public interface TypeAttributeAppender {
33
34     /**
35      * Applies this type attribute appender.
36      *
37      * @param classVisitor          The class visitor to which the annotations of this visitor should be written to.
38      * @param instrumentedType      A description of the instrumented type that is target of the ongoing instrumentation.
39      * @param annotationValueFilter The annotation value filter to apply when writing annotations.
40      */

41     void apply(ClassVisitor classVisitor, TypeDescription instrumentedType, AnnotationValueFilter annotationValueFilter);
42
43     /**
44      * A type attribute appender that does not append any attributes.
45      */

46     enum NoOp implements TypeAttributeAppender {
47
48         /**
49          * The singleton instance.
50          */

51         INSTANCE;
52
53         /**
54          * {@inheritDoc}
55          */

56         public void apply(ClassVisitor classVisitor, TypeDescription instrumentedType, AnnotationValueFilter annotationValueFilter) {
57             /* do nothing */
58         }
59     }
60
61     /**
62      * An attribute appender that writes all annotations that are found on a given target type to the
63      * instrumented type this type attribute appender is applied onto. The visibility for the annotation
64      * will be inferred from the annotations' {@link java.lang.annotation.RetentionPolicy}.
65      */

66     enum ForInstrumentedType implements TypeAttributeAppender {
67
68         /**
69          * The singleton instance.
70          */

71         INSTANCE;
72
73         /**
74          * {@inheritDoc}
75          */

76         public void apply(ClassVisitor classVisitor, TypeDescription instrumentedType, AnnotationValueFilter annotationValueFilter) {
77             AnnotationAppender annotationAppender = new AnnotationAppender.Default(new AnnotationAppender.Target.OnType(classVisitor));
78             annotationAppender = AnnotationAppender.ForTypeAnnotations.ofTypeVariable(annotationAppender,
79                     annotationValueFilter,
80                     AnnotationAppender.ForTypeAnnotations.VARIABLE_ON_TYPE,
81                     instrumentedType.getTypeVariables());
82             TypeDescription.Generic superClass = instrumentedType.getSuperClass();
83             if (superClass != null) {
84                 annotationAppender = superClass.accept(AnnotationAppender.ForTypeAnnotations.ofSuperClass(annotationAppender, annotationValueFilter));
85             }
86             int interfaceIndex = 0;
87             for (TypeDescription.Generic interfaceType : instrumentedType.getInterfaces()) {
88                 annotationAppender = interfaceType.accept(AnnotationAppender.ForTypeAnnotations.ofInterfaceType(annotationAppender,
89                         annotationValueFilter,
90                         interfaceIndex++));
91             }
92             for (AnnotationDescription annotation : instrumentedType.getDeclaredAnnotations()) {
93                 annotationAppender = annotationAppender.append(annotation, annotationValueFilter);
94             }
95         }
96
97         /**
98          * A type attribute appender that writes all annotations of the instrumented but excludes annotations up to
99          * a given index.
100          */

101         @HashCodeAndEqualsPlugin.Enhance
102         public static class Differentiating implements TypeAttributeAppender {
103
104             /**
105              * The index of the first annotations that should be directly written onto the type.
106              */

107             private final int annotationIndex;
108
109             /**
110              * The index of the first type variable for which type annotations should be directly written onto the type.
111              */

112             private final int typeVariableIndex;
113
114             /**
115              * The index of the first interface type for which type annotations should be directly written onto the type.
116              */

117             private final int interfaceTypeIndex;
118
119             /**
120              * Creates a new differentiating type attribute appender.
121              *
122              * @param typeDescription The type for which to resolve all exclusion indices.
123              */

124             public Differentiating(TypeDescription typeDescription) {
125                 this(typeDescription.getDeclaredAnnotations().size(), typeDescription.getTypeVariables().size(), typeDescription.getInterfaces().size());
126             }
127
128             /**
129              * Creates a new differentiating type attribute appender.
130              *
131              * @param annotationIndex    The index of the first annotations that should be directly written onto the type.
132              * @param typeVariableIndex  The index of the first interface type for which type annotations should be directly written onto the type.
133              * @param interfaceTypeIndex The index of the first interface type for which type annotations should be directly written onto the type.
134              */

135             protected Differentiating(int annotationIndex, int typeVariableIndex, int interfaceTypeIndex) {
136                 this.annotationIndex = annotationIndex;
137                 this.typeVariableIndex = typeVariableIndex;
138                 this.interfaceTypeIndex = interfaceTypeIndex;
139             }
140
141             /**
142              * {@inheritDoc}
143              */

144             public void apply(ClassVisitor classVisitor, TypeDescription instrumentedType, AnnotationValueFilter annotationValueFilter) {
145                 AnnotationAppender annotationAppender = new AnnotationAppender.Default(new AnnotationAppender.Target.OnType(classVisitor));
146                 AnnotationAppender.ForTypeAnnotations.ofTypeVariable(annotationAppender,
147                         annotationValueFilter,
148                         AnnotationAppender.ForTypeAnnotations.VARIABLE_ON_TYPE,
149                         typeVariableIndex,
150                         instrumentedType.getTypeVariables());
151                 TypeList.Generic interfaceTypes = instrumentedType.getInterfaces();
152                 int interfaceTypeIndex = this.interfaceTypeIndex;
153                 for (TypeDescription.Generic interfaceType : interfaceTypes.subList(this.interfaceTypeIndex, interfaceTypes.size())) {
154                     annotationAppender = interfaceType.accept(AnnotationAppender.ForTypeAnnotations.ofInterfaceType(annotationAppender,
155                             annotationValueFilter,
156                             interfaceTypeIndex++));
157                 }
158                 AnnotationList declaredAnnotations = instrumentedType.getDeclaredAnnotations();
159                 for (AnnotationDescription annotationDescription : declaredAnnotations.subList(annotationIndex, declaredAnnotations.size())) {
160                     annotationAppender = annotationAppender.append(annotationDescription, annotationValueFilter);
161                 }
162             }
163         }
164     }
165
166     /**
167      * An attribute appender that appends a single annotation to a given type. The visibility for the annotation
168      * will be inferred from the annotation's {@link java.lang.annotation.RetentionPolicy}.
169      */

170     @HashCodeAndEqualsPlugin.Enhance
171     class Explicit implements TypeAttributeAppender {
172
173         /**
174          * The annotations to write to the given type.
175          */

176         private final List<? extends AnnotationDescription> annotations;
177
178         /**
179          * Creates a new annotation attribute appender for explicit annotation values.
180          *
181          * @param annotations The annotations to write to the given type.
182          */

183         public Explicit(List<? extends AnnotationDescription> annotations) {
184             this.annotations = annotations;
185         }
186
187         /**
188          * {@inheritDoc}
189          */

190         public void apply(ClassVisitor classVisitor, TypeDescription instrumentedType, AnnotationValueFilter annotationValueFilter) {
191             AnnotationAppender appender = new AnnotationAppender.Default(new AnnotationAppender.Target.OnType(classVisitor));
192             for (AnnotationDescription annotation : annotations) {
193                 appender = appender.append(annotation, annotationValueFilter);
194             }
195         }
196     }
197
198     /**
199      * A compound type attribute appender that concatenates a number of other attribute appenders.
200      */

201     @HashCodeAndEqualsPlugin.Enhance
202     class Compound implements TypeAttributeAppender {
203
204         /**
205          * The type attribute appenders this compound appender represents in their application order.
206          */

207         private final List<TypeAttributeAppender> typeAttributeAppenders;
208
209         /**
210          * Creates a new compound attribute appender.
211          *
212          * @param typeAttributeAppender The type attribute appenders to concatenate in the order of their application.
213          */

214         public Compound(TypeAttributeAppender... typeAttributeAppender) {
215             this(Arrays.asList(typeAttributeAppender));
216         }
217
218         /**
219          * Creates a new compound attribute appender.
220          *
221          * @param typeAttributeAppenders The type attribute appenders to concatenate in the order of their application.
222          */

223         public Compound(List<? extends TypeAttributeAppender> typeAttributeAppenders) {
224             this.typeAttributeAppenders = new ArrayList<TypeAttributeAppender>();
225             for (TypeAttributeAppender typeAttributeAppender : typeAttributeAppenders) {
226                 if (typeAttributeAppender instanceof Compound) {
227                     this.typeAttributeAppenders.addAll(((Compound) typeAttributeAppender).typeAttributeAppenders);
228                 } else if (!(typeAttributeAppender instanceof NoOp)) {
229                     this.typeAttributeAppenders.add(typeAttributeAppender);
230                 }
231             }
232         }
233
234         /**
235          * {@inheritDoc}
236          */

237         public void apply(ClassVisitor classVisitor, TypeDescription instrumentedType, AnnotationValueFilter annotationValueFilter) {
238             for (TypeAttributeAppender typeAttributeAppender : typeAttributeAppenders) {
239                 typeAttributeAppender.apply(classVisitor, instrumentedType, annotationValueFilter);
240             }
241         }
242     }
243 }
244