View Javadoc
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.commons.lang3.stream.Streams;
29  
30  /**
31   * Represents one annotation in the annotation table
32   *
33   * @since 6.0
34   */
35  public class AnnotationEntry implements Node {
36  
37      /**
38       * Empty array of AnnotationEntry objects.
39       */
40      public static final AnnotationEntry[] EMPTY_ARRAY = {};
41  
42      /**
43       * Creates annotation entries from attributes.
44       *
45       * @param attributes the attributes.
46       * @return the annotation entries.
47       */
48      public static AnnotationEntry[] createAnnotationEntries(final Attribute[] attributes) {
49          // Find attributes that contain annotation data
50          return Streams.of(attributes).filter(Annotations.class::isInstance).flatMap(e -> Stream.of(((Annotations) e).getAnnotationEntries()))
51                  .toArray(AnnotationEntry[]::new);
52      }
53  
54      /**
55       * Factory method to create an AnnotionEntry from a DataInput.
56       *
57       * @param input the input stream.
58       * @param constantPool the constant pool.
59       * @param isRuntimeVisible whether the annotation is runtime visible.
60       * @return the entry.
61       * @throws IOException if an I/O error occurs.
62       */
63      public static AnnotationEntry read(final DataInput input, final ConstantPool constantPool, final boolean isRuntimeVisible) throws IOException {
64          final AnnotationEntry annotationEntry = new AnnotationEntry(input.readUnsignedShort(), constantPool, isRuntimeVisible);
65          final int numElementValuePairs = input.readUnsignedShort();
66          for (int i = 0; i < numElementValuePairs; i++) {
67              annotationEntry.elementValuePairs
68                  .add(new ElementValuePair(input.readUnsignedShort(), ElementValue.readElementValue(input, constantPool), constantPool));
69          }
70          return annotationEntry;
71      }
72  
73      private final int typeIndex;
74  
75      private final ConstantPool constantPool;
76  
77      private final boolean isRuntimeVisible;
78  
79      private final List<ElementValuePair> elementValuePairs;
80  
81      /**
82       * Constructs an AnnotationEntry.
83       *
84       * @param typeIndex the type index.
85       * @param constantPool the constant pool.
86       * @param isRuntimeVisible whether the annotation is runtime visible.
87       */
88      public AnnotationEntry(final int typeIndex, final ConstantPool constantPool, final boolean isRuntimeVisible) {
89          this.typeIndex = typeIndex;
90          this.constantPool = constantPool;
91          this.isRuntimeVisible = isRuntimeVisible;
92          this.elementValuePairs = new ArrayList<>();
93      }
94  
95      /**
96       * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
97       * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
98       *
99       * @param v Visitor object.
100      */
101     @Override
102     public void accept(final Visitor v) {
103         v.visitAnnotationEntry(this);
104     }
105 
106     /**
107      * Adds an element name value pair.
108      *
109      * @param elementNameValuePair the element name value pair.
110      */
111     public void addElementNameValuePair(final ElementValuePair elementNameValuePair) {
112         elementValuePairs.add(elementNameValuePair);
113     }
114 
115     /**
116      * Dumps this annotation entry to a DataOutputStream.
117      *
118      * @param dos the output stream.
119      * @throws IOException if an I/O error occurs.
120      */
121     public void dump(final DataOutputStream dos) throws IOException {
122         dos.writeShort(typeIndex); // u2 index of type name in cpool
123         dos.writeShort(elementValuePairs.size()); // u2 element_value pair
124         // count
125         for (final ElementValuePair envp : elementValuePairs) {
126             envp.dump(dos);
127         }
128     }
129 
130     /**
131      * Gets the annotation type name.
132      *
133      * @return the annotation type name.
134      */
135     public String getAnnotationType() {
136         return constantPool.getConstantUtf8(typeIndex).getBytes();
137     }
138 
139     /**
140      * Gets the annotation type index.
141      *
142      * @return the annotation type index.
143      */
144     public int getAnnotationTypeIndex() {
145         return typeIndex;
146     }
147 
148     /**
149      * Gets the constant pool.
150      *
151      * @return the constant pool.
152      */
153     public ConstantPool getConstantPool() {
154         return constantPool;
155     }
156 
157     /**
158      * Gets the element value pairs in this annotation entry.
159      *
160      * @return the element value pairs in this annotation entry.
161      */
162     public ElementValuePair[] getElementValuePairs() {
163         // TODO return List
164         return elementValuePairs.toArray(ElementValuePair.EMPTY_ARRAY);
165     }
166 
167     /**
168      * Gets the number of element value pairs in this annotation entry.
169      *
170      * @return the number of element value pairs in this annotation entry.
171      */
172     public final int getNumElementValuePairs() {
173         return elementValuePairs.size();
174     }
175 
176     /**
177      * Gets the type index.
178      *
179      * @return the type index.
180      */
181     public int getTypeIndex() {
182         return typeIndex;
183     }
184 
185     /**
186      * Gets whether this annotation is runtime visible.
187      *
188      * @return true if this annotation is runtime visible.
189      */
190     public boolean isRuntimeVisible() {
191         return isRuntimeVisible;
192     }
193 
194     /**
195      * Gets a short string representation of this annotation.
196      *
197      * @return a short string representation of this annotation.
198      */
199     public String toShortString() {
200         final StringBuilder result = new StringBuilder();
201         result.append("@");
202         result.append(getAnnotationType());
203         final ElementValuePair[] evPairs = getElementValuePairs();
204         if (evPairs.length > 0) {
205             result.append("(");
206             for (final ElementValuePair element : evPairs) {
207                 result.append(element.toShortString());
208                 result.append(", ");
209             }
210             // remove last ", "
211             result.setLength(result.length() - 2);
212             result.append(")");
213         }
214         return result.toString();
215     }
216 
217     @Override
218     public String toString() {
219         return toShortString();
220     }
221 }