001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 */
017package org.apache.bcel.generic;
018
019import java.io.ByteArrayInputStream;
020import java.io.ByteArrayOutputStream;
021import java.io.DataInput;
022import java.io.DataInputStream;
023import java.io.DataOutputStream;
024import java.io.IOException;
025import java.util.ArrayList;
026import java.util.List;
027
028import org.apache.bcel.classfile.AnnotationEntry;
029import org.apache.bcel.classfile.Attribute;
030import org.apache.bcel.classfile.ConstantUtf8;
031import org.apache.bcel.classfile.ElementValuePair;
032import org.apache.bcel.classfile.RuntimeInvisibleAnnotations;
033import org.apache.bcel.classfile.RuntimeInvisibleParameterAnnotations;
034import org.apache.bcel.classfile.RuntimeVisibleAnnotations;
035import org.apache.bcel.classfile.RuntimeVisibleParameterAnnotations;
036
037/**
038 * @since 6.0
039 */
040public class AnnotationEntryGen {
041
042    static final AnnotationEntryGen[] EMPTY_ARRAY = {};
043
044    /**
045     * Converts a list of AnnotationGen objects into a set of attributes that can be attached to the class file.
046     *
047     * @param cp The constant pool gen where we can create the necessary name refs
048     * @param annotationEntryGens An array of AnnotationGen objects
049     */
050    static Attribute[] getAnnotationAttributes(final ConstantPoolGen cp, final AnnotationEntryGen[] annotationEntryGens) {
051        if (annotationEntryGens.length == 0) {
052            return Attribute.EMPTY_ARRAY;
053        }
054
055        try {
056            int countVisible = 0;
057            int countInvisible = 0;
058
059            // put the annotations in the right output stream
060            for (final AnnotationEntryGen a : annotationEntryGens) {
061                if (a.isRuntimeVisible()) {
062                    countVisible++;
063                } else {
064                    countInvisible++;
065                }
066            }
067
068            final ByteArrayOutputStream rvaBytes = new ByteArrayOutputStream();
069            final ByteArrayOutputStream riaBytes = new ByteArrayOutputStream();
070            try (DataOutputStream rvaDos = new DataOutputStream(rvaBytes); DataOutputStream riaDos = new DataOutputStream(riaBytes)) {
071
072                rvaDos.writeShort(countVisible);
073                riaDos.writeShort(countInvisible);
074
075                // put the annotations in the right output stream
076                for (final AnnotationEntryGen a : annotationEntryGens) {
077                    if (a.isRuntimeVisible()) {
078                        a.dump(rvaDos);
079                    } else {
080                        a.dump(riaDos);
081                    }
082                }
083            }
084
085            final byte[] rvaData = rvaBytes.toByteArray();
086            final byte[] riaData = riaBytes.toByteArray();
087
088            int rvaIndex = -1;
089            int riaIndex = -1;
090
091            if (rvaData.length > 2) {
092                rvaIndex = cp.addUtf8("RuntimeVisibleAnnotations");
093            }
094            if (riaData.length > 2) {
095                riaIndex = cp.addUtf8("RuntimeInvisibleAnnotations");
096            }
097
098            final List<Attribute> newAttributes = new ArrayList<>();
099            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}