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