1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * https://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.bcel.classfile;
20
21 import java.io.DataInput;
22 import java.io.DataOutputStream;
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.stream.Stream;
27
28 import org.apache.bcel.util.Args;
29 import org.apache.commons.lang3.stream.Streams;
30
31 /**
32 * Represents one annotation in the annotation table
33 *
34 * @since 6.0
35 */
36 public class AnnotationEntry implements Node {
37
38 /**
39 * Empty array of AnnotationEntry objects.
40 */
41 public static final AnnotationEntry[] EMPTY_ARRAY = {};
42
43 /**
44 * Creates annotation entries from attributes.
45 *
46 * @param attributes The attributes.
47 * @return The annotation entries.
48 */
49 public static AnnotationEntry[] createAnnotationEntries(final Attribute[] attributes) {
50 // Find attributes that contain annotation data
51 return Streams.of(attributes).filter(Annotations.class::isInstance).flatMap(e -> Stream.of(((Annotations) e).getAnnotationEntries()))
52 .toArray(AnnotationEntry[]::new);
53 }
54
55 /**
56 * Factory method to create an AnnotionEntry from a DataInput.
57 *
58 * @param input The input stream.
59 * @param constantPool The constant pool.
60 * @param isRuntimeVisible whether the annotation is runtime visible.
61 * @return The entry.
62 * @throws IOException Thrown if an I/O error occurs.
63 */
64 public static AnnotationEntry read(final DataInput input, final ConstantPool constantPool, final boolean isRuntimeVisible) throws IOException {
65 return read(input, constantPool, isRuntimeVisible, 0);
66 }
67
68 /**
69 * Factory method to create an AnnotionEntry from a DataInput, carrying the nesting depth of the enclosing element values so that
70 * {@link ElementValue#readElementValue(DataInput, ConstantPool, boolean, int)} can bound the combined annotation/array nesting depth.
71 *
72 * @param input The input stream.
73 * @param constantPool The constant pool.
74 * @param isRuntimeVisible whether the annotation is runtime visible.
75 * @param nesting the current element value nesting level.
76 * @return The entry.
77 * @throws IOException Thrown if an I/O error occurs.
78 */
79 static AnnotationEntry read(final DataInput input, final ConstantPool constantPool, final boolean isRuntimeVisible, final int nesting)
80 throws IOException {
81 final AnnotationEntry annotationEntry = new AnnotationEntry(input.readUnsignedShort(), constantPool, isRuntimeVisible);
82 final int numElementValuePairs = input.readUnsignedShort();
83 for (int i = 0; i < numElementValuePairs; i++) {
84 annotationEntry.elementValuePairs.add(
85 new ElementValuePair(input.readUnsignedShort(), ElementValue.readElementValue(input, constantPool, isRuntimeVisible, nesting), constantPool));
86 }
87 return annotationEntry;
88 }
89
90 private final int typeIndex;
91
92 private final ConstantPool constantPool;
93
94 private final boolean isRuntimeVisible;
95
96 private final List<ElementValuePair> elementValuePairs;
97
98 /**
99 * Constructs an AnnotationEntry.
100 *
101 * @param typeIndex The type index.
102 * @param constantPool The constant pool.
103 * @param isRuntimeVisible whether the annotation is runtime visible.
104 */
105 public AnnotationEntry(final int typeIndex, final ConstantPool constantPool, final boolean isRuntimeVisible) {
106 this.typeIndex = typeIndex;
107 this.constantPool = constantPool;
108 this.isRuntimeVisible = isRuntimeVisible;
109 this.elementValuePairs = new ArrayList<>();
110 }
111
112 /**
113 * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
114 * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
115 *
116 * @param v Visitor object.
117 */
118 @Override
119 public void accept(final Visitor v) {
120 v.visitAnnotationEntry(this);
121 }
122
123 /**
124 * Adds an element name value pair.
125 *
126 * @param elementNameValuePair The element name value pair.
127 */
128 public void addElementNameValuePair(final ElementValuePair elementNameValuePair) {
129 elementValuePairs.add(elementNameValuePair);
130 }
131
132 /**
133 * Dumps this annotation entry to a DataOutputStream.
134 *
135 * @param dos The output stream.
136 * @throws IOException Thrown if an I/O error occurs.
137 */
138 public void dump(final DataOutputStream dos) throws IOException {
139 dos.writeShort(typeIndex); // u2 index of type name in cpool
140 dos.writeShort(Args.requireU2(elementValuePairs.size(), "elementValuePairs.size()")); // u2 element_value pair
141 // count
142 for (final ElementValuePair envp : elementValuePairs) {
143 envp.dump(dos);
144 }
145 }
146
147 /**
148 * Gets the annotation type name.
149 *
150 * @return The annotation type name.
151 */
152 public String getAnnotationType() {
153 return constantPool.getConstantUtf8(typeIndex).getBytes();
154 }
155
156 /**
157 * Gets the annotation type index.
158 *
159 * @return The annotation type index.
160 */
161 public int getAnnotationTypeIndex() {
162 return typeIndex;
163 }
164
165 /**
166 * Gets the constant pool.
167 *
168 * @return The constant pool.
169 */
170 public ConstantPool getConstantPool() {
171 return constantPool;
172 }
173
174 /**
175 * Gets the element value pairs in this annotation entry.
176 *
177 * @return The element value pairs in this annotation entry.
178 */
179 public ElementValuePair[] getElementValuePairs() {
180 // TODO return List
181 return elementValuePairs.toArray(ElementValuePair.EMPTY_ARRAY);
182 }
183
184 /**
185 * Gets the number of element value pairs in this annotation entry.
186 *
187 * @return The number of element value pairs in this annotation entry.
188 */
189 public final int getNumElementValuePairs() {
190 return elementValuePairs.size();
191 }
192
193 /**
194 * Gets the type index.
195 *
196 * @return The type index.
197 */
198 public int getTypeIndex() {
199 return typeIndex;
200 }
201
202 /**
203 * Gets whether this annotation is runtime visible.
204 *
205 * @return true if this annotation is runtime visible.
206 */
207 public boolean isRuntimeVisible() {
208 return isRuntimeVisible;
209 }
210
211 /**
212 * Gets a short string representation of this annotation.
213 *
214 * @return A short string representation of this annotation.
215 */
216 public String toShortString() {
217 final StringBuilder result = new StringBuilder();
218 result.append("@");
219 result.append(getAnnotationType());
220 final ElementValuePair[] evPairs = getElementValuePairs();
221 if (evPairs.length > 0) {
222 result.append("(");
223 for (final ElementValuePair element : evPairs) {
224 result.append(element.toShortString());
225 result.append(", ");
226 }
227 // remove last ", "
228 result.setLength(result.length() - 2);
229 result.append(")");
230 }
231 return result.toString();
232 }
233
234 @Override
235 public String toString() {
236 return toShortString();
237 }
238 }