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.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 }