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