View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  package org.apache.bcel.generic;
18  
19  import java.io.ByteArrayInputStream;
20  import java.io.ByteArrayOutputStream;
21  import java.io.DataInput;
22  import java.io.DataInputStream;
23  import java.io.DataOutputStream;
24  import java.io.IOException;
25  import java.util.ArrayList;
26  import java.util.List;
27  
28  import org.apache.bcel.classfile.AnnotationEntry;
29  import org.apache.bcel.classfile.Attribute;
30  import org.apache.bcel.classfile.ConstantUtf8;
31  import org.apache.bcel.classfile.ElementValuePair;
32  import org.apache.bcel.classfile.RuntimeInvisibleAnnotations;
33  import org.apache.bcel.classfile.RuntimeInvisibleParameterAnnotations;
34  import org.apache.bcel.classfile.RuntimeVisibleAnnotations;
35  import org.apache.bcel.classfile.RuntimeVisibleParameterAnnotations;
36  
37  /**
38   * @since 6.0
39   */
40  public class AnnotationEntryGen {
41  
42      static final AnnotationEntryGen[] EMPTY_ARRAY = {};
43  
44      /**
45       * Converts a list of AnnotationGen objects into a set of attributes that can be attached to the class file.
46       *
47       * @param cp The constant pool gen where we can create the necessary name refs
48       * @param annotationEntryGens An array of AnnotationGen objects
49       */
50      static Attribute[] getAnnotationAttributes(final ConstantPoolGen cp, final AnnotationEntryGen[] annotationEntryGens) {
51          if (annotationEntryGens.length == 0) {
52              return Attribute.EMPTY_ARRAY;
53          }
54  
55          try {
56              int countVisible = 0;
57              int countInvisible = 0;
58  
59              // put the annotations in the right output stream
60              for (final AnnotationEntryGen a : annotationEntryGens) {
61                  if (a.isRuntimeVisible()) {
62                      countVisible++;
63                  } else {
64                      countInvisible++;
65                  }
66              }
67  
68              final ByteArrayOutputStream rvaBytes = new ByteArrayOutputStream();
69              final ByteArrayOutputStream riaBytes = new ByteArrayOutputStream();
70              try (DataOutputStream rvaDos = new DataOutputStream(rvaBytes); DataOutputStream riaDos = new DataOutputStream(riaBytes)) {
71  
72                  rvaDos.writeShort(countVisible);
73                  riaDos.writeShort(countInvisible);
74  
75                  // put the annotations in the right output stream
76                  for (final AnnotationEntryGen a : annotationEntryGens) {
77                      if (a.isRuntimeVisible()) {
78                          a.dump(rvaDos);
79                      } else {
80                          a.dump(riaDos);
81                      }
82                  }
83              }
84  
85              final byte[] rvaData = rvaBytes.toByteArray();
86              final byte[] riaData = riaBytes.toByteArray();
87  
88              int rvaIndex = -1;
89              int riaIndex = -1;
90  
91              if (rvaData.length > 2) {
92                  rvaIndex = cp.addUtf8("RuntimeVisibleAnnotations");
93              }
94              if (riaData.length > 2) {
95                  riaIndex = cp.addUtf8("RuntimeInvisibleAnnotations");
96              }
97  
98              final List<Attribute> newAttributes = new ArrayList<>();
99              if (rvaData.length > 2) {
100                 newAttributes
101                     .add(new RuntimeVisibleAnnotations(rvaIndex, rvaData.length, new DataInputStream(new ByteArrayInputStream(rvaData)), cp.getConstantPool()));
102             }
103             if (riaData.length > 2) {
104                 newAttributes.add(
105                     new RuntimeInvisibleAnnotations(riaIndex, riaData.length, new DataInputStream(new ByteArrayInputStream(riaData)), cp.getConstantPool()));
106             }
107 
108             return newAttributes.toArray(Attribute.EMPTY_ARRAY);
109         } catch (final IOException e) {
110             System.err.println("IOException whilst processing annotations");
111             e.printStackTrace();
112         }
113         return null;
114     }
115 
116     /**
117      * Annotations against a class are stored in one of four attribute kinds: - RuntimeVisibleParameterAnnotations -
118      * RuntimeInvisibleParameterAnnotations
119      */
120     static Attribute[] getParameterAnnotationAttributes(final ConstantPoolGen cp,
121         final List<AnnotationEntryGen>[] /* Array of lists, array size depends on #params */ vec) {
122         final int[] visCount = new int[vec.length];
123         int totalVisCount = 0;
124         final int[] invisCount = new int[vec.length];
125         int totalInvisCount = 0;
126         try {
127             for (int i = 0; i < vec.length; i++) {
128                 if (vec[i] != null) {
129                     for (final AnnotationEntryGen element : vec[i]) {
130                         if (element.isRuntimeVisible()) {
131                             visCount[i]++;
132                             totalVisCount++;
133                         } else {
134                             invisCount[i]++;
135                             totalInvisCount++;
136                         }
137                     }
138                 }
139             }
140             // Lets do the visible ones
141             final ByteArrayOutputStream rvaBytes = new ByteArrayOutputStream();
142             try (DataOutputStream rvaDos = new DataOutputStream(rvaBytes)) {
143                 rvaDos.writeByte(vec.length); // First goes number of parameters
144                 for (int i = 0; i < vec.length; i++) {
145                     rvaDos.writeShort(visCount[i]);
146                     if (visCount[i] > 0) {
147                         for (final AnnotationEntryGen element : vec[i]) {
148                             if (element.isRuntimeVisible()) {
149                                 element.dump(rvaDos);
150                             }
151                         }
152                     }
153                 }
154             }
155             // Lets do the invisible ones
156             final ByteArrayOutputStream riaBytes = new ByteArrayOutputStream();
157             try (DataOutputStream riaDos = new DataOutputStream(riaBytes)) {
158                 riaDos.writeByte(vec.length); // First goes number of parameters
159                 for (int i = 0; i < vec.length; i++) {
160                     riaDos.writeShort(invisCount[i]);
161                     if (invisCount[i] > 0) {
162                         for (final AnnotationEntryGen element : vec[i]) {
163                             if (!element.isRuntimeVisible()) {
164                                 element.dump(riaDos);
165                             }
166                         }
167                     }
168                 }
169             }
170             final byte[] rvaData = rvaBytes.toByteArray();
171             final byte[] riaData = riaBytes.toByteArray();
172             int rvaIndex = -1;
173             int riaIndex = -1;
174             if (totalVisCount > 0) {
175                 rvaIndex = cp.addUtf8("RuntimeVisibleParameterAnnotations");
176             }
177             if (totalInvisCount > 0) {
178                 riaIndex = cp.addUtf8("RuntimeInvisibleParameterAnnotations");
179             }
180             final List<Attribute> newAttributes = new ArrayList<>();
181             if (totalVisCount > 0) {
182                 newAttributes.add(new RuntimeVisibleParameterAnnotations(rvaIndex, rvaData.length, new DataInputStream(new ByteArrayInputStream(rvaData)),
183                     cp.getConstantPool()));
184             }
185             if (totalInvisCount > 0) {
186                 newAttributes.add(new RuntimeInvisibleParameterAnnotations(riaIndex, riaData.length, new DataInputStream(new ByteArrayInputStream(riaData)),
187                     cp.getConstantPool()));
188             }
189             return newAttributes.toArray(Attribute.EMPTY_ARRAY);
190         } catch (final IOException e) {
191             System.err.println("IOException whilst processing parameter annotations");
192             e.printStackTrace();
193         }
194         return null;
195     }
196 
197     public static AnnotationEntryGen read(final DataInput dis, final ConstantPoolGen cpool, final boolean b) throws IOException {
198         final AnnotationEntryGen a = new AnnotationEntryGen(cpool);
199         a.typeIndex = dis.readUnsignedShort();
200         final int elemValuePairCount = dis.readUnsignedShort();
201         for (int i = 0; i < elemValuePairCount; i++) {
202             final int nidx = dis.readUnsignedShort();
203             a.addElementNameValuePair(new ElementValuePairGen(nidx, ElementValueGen.readElementValue(dis, cpool), cpool));
204         }
205         a.isRuntimeVisible(b);
206         return a;
207     }
208 
209     private int typeIndex;
210 
211     private List<ElementValuePairGen> evs;
212 
213     private final ConstantPoolGen cpool;
214 
215     private boolean isRuntimeVisible;
216 
217     /**
218      * Here we are taking a fixed annotation of type Annotation and building a modifiable AnnotationGen object. If the pool
219      * passed in is for a different class file, then copyPoolEntries should have been passed as true as that will force us
220      * to do a deep copy of the annotation and move the cpool entries across. We need to copy the type and the element name
221      * value pairs and the visibility.
222      */
223     public AnnotationEntryGen(final AnnotationEntry a, final ConstantPoolGen cpool, final boolean copyPoolEntries) {
224         this.cpool = cpool;
225         if (copyPoolEntries) {
226             typeIndex = cpool.addUtf8(a.getAnnotationType());
227         } else {
228             typeIndex = a.getAnnotationTypeIndex();
229         }
230         isRuntimeVisible = a.isRuntimeVisible();
231         evs = copyValues(a.getElementValuePairs(), cpool, copyPoolEntries);
232     }
233 
234     private AnnotationEntryGen(final ConstantPoolGen cpool) {
235         this.cpool = cpool;
236     }
237 
238     public AnnotationEntryGen(final ObjectType type, final List<ElementValuePairGen> elements, final boolean vis, final ConstantPoolGen cpool) {
239         this.cpool = cpool;
240         this.typeIndex = cpool.addUtf8(type.getSignature());
241         evs = elements;
242         isRuntimeVisible = vis;
243     }
244 
245     public void addElementNameValuePair(final ElementValuePairGen evp) {
246         if (evs == null) {
247             evs = new ArrayList<>();
248         }
249         evs.add(evp);
250     }
251 
252     private List<ElementValuePairGen> copyValues(final ElementValuePair[] in, final ConstantPoolGen cpool, final boolean copyPoolEntries) {
253         final List<ElementValuePairGen> out = new ArrayList<>();
254         for (final ElementValuePair nvp : in) {
255             out.add(new ElementValuePairGen(nvp, cpool, copyPoolEntries));
256         }
257         return out;
258     }
259 
260     public void dump(final DataOutputStream dos) throws IOException {
261         dos.writeShort(typeIndex); // u2 index of type name in cpool
262         dos.writeShort(evs.size()); // u2 element_value pair count
263         for (final ElementValuePairGen envp : evs) {
264             envp.dump(dos);
265         }
266     }
267 
268     /**
269      * Retrieve an immutable version of this AnnotationGen
270      */
271     public AnnotationEntry getAnnotation() {
272         final AnnotationEntry a = new AnnotationEntry(typeIndex, cpool.getConstantPool(), isRuntimeVisible);
273         for (final ElementValuePairGen element : evs) {
274             a.addElementNameValuePair(element.getElementNameValuePair());
275         }
276         return a;
277     }
278 
279     public int getTypeIndex() {
280         return typeIndex;
281     }
282 
283     public final String getTypeName() {
284         return getTypeSignature();// BCELBUG: Should I use this instead?
285         // Utility.signatureToString(getTypeSignature());
286     }
287 
288     public final String getTypeSignature() {
289         // ConstantClass c = (ConstantClass)cpool.getConstant(typeIndex);
290         final ConstantUtf8 utf8 = (ConstantUtf8) cpool.getConstant(typeIndex/* c.getNameIndex() */);
291         return utf8.getBytes();
292     }
293 
294     /**
295      * Returns list of ElementNameValuePair objects.
296      *
297      * @return list of ElementNameValuePair objects.
298      */
299     public List<ElementValuePairGen> getValues() {
300         return evs;
301     }
302 
303     public boolean isRuntimeVisible() {
304         return isRuntimeVisible;
305     }
306 
307     private void isRuntimeVisible(final boolean b) {
308         isRuntimeVisible = b;
309     }
310 
311     public String toShortString() {
312         final StringBuilder s = new StringBuilder();
313         s.append("@").append(getTypeName()).append("(");
314         for (int i = 0; i < evs.size(); i++) {
315             s.append(evs.get(i));
316             if (i + 1 < evs.size()) {
317                 s.append(",");
318             }
319         }
320         s.append(")");
321         return s.toString();
322     }
323 
324     @Override
325     public String toString() {
326         final StringBuilder s = new StringBuilder(32); // CHECKSTYLE IGNORE MagicNumber
327         s.append("AnnotationGen:[").append(getTypeName()).append(" #").append(evs.size()).append(" {");
328         for (int i = 0; i < evs.size(); i++) {
329             s.append(evs.get(i));
330             if (i + 1 < evs.size()) {
331                 s.append(",");
332             }
333         }
334         s.append("}]");
335         return s.toString();
336     }
337 
338 }