    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 *
    017 */
    018package org.apache.bcel.generic;
    019
    020import java.io.ByteArrayInputStream;
    021import java.io.ByteArrayOutputStream;
    022import java.io.DataInput;
    023import java.io.DataInputStream;
    024import java.io.DataOutputStream;
    025import java.io.IOException;
    026import java.util.ArrayList;
    027import java.util.List;
    028
    029import org.apache.bcel.classfile.AnnotationEntry;
    030import org.apache.bcel.classfile.Attribute;
    031import org.apache.bcel.classfile.ConstantUtf8;
    032import org.apache.bcel.classfile.ElementValuePair;
    033import org.apache.bcel.classfile.RuntimeInvisibleAnnotations;
    034import org.apache.bcel.classfile.RuntimeInvisibleParameterAnnotations;
    035import org.apache.bcel.classfile.RuntimeVisibleAnnotations;
    036import org.apache.bcel.classfile.RuntimeVisibleParameterAnnotations;
    037
    038/**
    039 * @since 6.0
    040 */
    041public class AnnotationEntryGen {
    042    private int typeIndex;
    043
    044    private List<ElementValuePairGen> evs;
    045
    046    private final ConstantPoolGen cpool;
    047
    048    private boolean isRuntimeVisible = false;
    049
    050    /**
    051     * Here we are taking a fixed annotation of type Annotation and building a
    052     * modifiable AnnotationGen object. If the pool passed in is for a different
    053     * class file, then copyPoolEntries should have been passed as true as that
    054     * will force us to do a deep copy of the annotation and move the cpool
    055     * entries across. We need to copy the type and the element name value pairs
    056     * and the visibility.
    057     */
    058    public AnnotationEntryGen(final AnnotationEntry a, final ConstantPoolGen cpool,
    059                              final boolean copyPoolEntries) {
    060        this.cpool = cpool;
    061        if (copyPoolEntries) {
    062            typeIndex = cpool.addUtf8(a.getAnnotationType());
    063        } else {
    064            typeIndex = a.getAnnotationTypeIndex();
    065        }
    066        isRuntimeVisible = a.isRuntimeVisible();
    067        evs = copyValues(a.getElementValuePairs(), cpool, copyPoolEntries);
    068    }
    069
    070    private List<ElementValuePairGen> copyValues(final ElementValuePair[] in, final ConstantPoolGen cpool,
    071                                                 final boolean copyPoolEntries) {
    072        final List<ElementValuePairGen> out = new ArrayList<>();
    073        for (final ElementValuePair nvp : in) {
    074            out.add(new ElementValuePairGen(nvp, cpool, copyPoolEntries));
    075        }
    076        return out;
    077    }
    078
    079    private AnnotationEntryGen(final ConstantPoolGen cpool) {
    080        this.cpool = cpool;
    081    }
    082
    083    /**
    084     * Retrieve an immutable version of this AnnotationGen
    085     */
    086    public AnnotationEntry getAnnotation() {
    087        final AnnotationEntry a = new AnnotationEntry(typeIndex, cpool.getConstantPool(),
    088                isRuntimeVisible);
    089        for (final ElementValuePairGen element : evs) {
    090            a.addElementNameValuePair(element.getElementNameValuePair());
    091        }
    092        return a;
    093    }
    094
    095    public AnnotationEntryGen(final ObjectType type,
    096                              final List<ElementValuePairGen> elements, final boolean vis,
    097                              final ConstantPoolGen cpool) {
    098        this.cpool = cpool;
    099        this.typeIndex = cpool.addUtf8(type.getSignature());
    100        evs = elements;
    101        isRuntimeVisible = vis;
    102    }
    103
    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
    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
    126    public void addElementNameValuePair(final ElementValuePairGen evp) {
    127        if (evs == null) {
    128            evs = new ArrayList<>();
    129        }
    130        evs.add(evp);
    131    }
    132
    133    public int getTypeIndex() {
    134        return typeIndex;
    135    }
    136
    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
    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     */
    152    public List<ElementValuePairGen> getValues() {
    153        return evs;
    154    }
    155
    156    @Override
    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
    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
    183    private void isRuntimeVisible(final boolean b) {
    184        isRuntimeVisible = b;
    185    }
    186
    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     */
    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     */
    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}