    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one
    003 * or more contributor license agreements.  See the NOTICE file
    004 * distributed with this work for additional information
    005 * regarding copyright ownership.  The ASF licenses this file
    006 * to you under the Apache License, Version 2.0 (the
    007 * "License"); you may not use this file except in compliance
    008 * with the License.  You may obtain a copy of the License at
    009 *
    010 *   https://www.apache.org/licenses/LICENSE-2.0
    011 *
    012 * Unless required by applicable law or agreed to in writing,
    013 * software distributed under the License is distributed on an
    014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015 * KIND, either express or implied.  See the License for the
    016 * specific language governing permissions and limitations
    017 * under the License.
    018 */
    019package org.apache.bcel.classfile;
    020
    021import java.io.DataInput;
    022import java.io.DataOutputStream;
    023import java.io.IOException;
    024import java.util.ArrayList;
    025import java.util.List;
    026import java.util.stream.Stream;
    027
    028import org.apache.commons.lang3.stream.Streams;
    029
    030/**
    031 * Represents one annotation in the annotation table
    032 *
    033 * @since 6.0
    034 */
    035public class AnnotationEntry implements Node {
    036
    037    /**
    038     * Empty array of AnnotationEntry objects.
    039     */
    040    public static final AnnotationEntry[] EMPTY_ARRAY = {};
    041
    042    /**
    043     * Creates annotation entries from attributes.
    044     *
    045     * @param attributes the attributes.
    046     * @return the annotation entries.
    047     */
    048    public static AnnotationEntry[] createAnnotationEntries(final Attribute[] attributes) {
    049        // Find attributes that contain annotation data
    050        return Streams.of(attributes).filter(Annotations.class::isInstance).flatMap(e -> Stream.of(((Annotations) e).getAnnotationEntries()))
    051                .toArray(AnnotationEntry[]::new);
    052    }
    053
    054    /**
    055     * Factory method to create an AnnotionEntry from a DataInput.
    056     *
    057     * @param input the input stream.
    058     * @param constantPool the constant pool.
    059     * @param isRuntimeVisible whether the annotation is runtime visible.
    060     * @return the entry.
    061     * @throws IOException if an I/O error occurs.
    062     */
    063    public static AnnotationEntry read(final DataInput input, final ConstantPool constantPool, final boolean isRuntimeVisible) throws IOException {
    064        final AnnotationEntry annotationEntry = new AnnotationEntry(input.readUnsignedShort(), constantPool, isRuntimeVisible);
    065        final int numElementValuePairs = input.readUnsignedShort();
    066        for (int i = 0; i < numElementValuePairs; i++) {
    067            annotationEntry.elementValuePairs
    068                .add(new ElementValuePair(input.readUnsignedShort(), ElementValue.readElementValue(input, constantPool), constantPool));
    069        }
    070        return annotationEntry;
    071    }
    072
    073    private final int typeIndex;
    074
    075    private final ConstantPool constantPool;
    076
    077    private final boolean isRuntimeVisible;
    078
    079    private final List<ElementValuePair> elementValuePairs;
    080
    081    /**
    082     * Constructs an AnnotationEntry.
    083     *
    084     * @param typeIndex the type index.
    085     * @param constantPool the constant pool.
    086     * @param isRuntimeVisible whether the annotation is runtime visible.
    087     */
    088    public AnnotationEntry(final int typeIndex, final ConstantPool constantPool, final boolean isRuntimeVisible) {
    089        this.typeIndex = typeIndex;
    090        this.constantPool = constantPool;
    091        this.isRuntimeVisible = isRuntimeVisible;
    092        this.elementValuePairs = new ArrayList<>();
    093    }
    094
    095    /**
    096     * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
    097     * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
    098     *
    099     * @param v Visitor object.
    100     */
    101    @Override
    102    public void accept(final Visitor v) {
    103        v.visitAnnotationEntry(this);
    104    }
    105
    106    /**
    107     * Adds an element name value pair.
    108     *
    109     * @param elementNameValuePair the element name value pair.
    110     */
    111    public void addElementNameValuePair(final ElementValuePair elementNameValuePair) {
    112        elementValuePairs.add(elementNameValuePair);
    113    }
    114
    115    /**
    116     * Dumps this annotation entry to a DataOutputStream.
    117     *
    118     * @param dos the output stream.
    119     * @throws IOException if an I/O error occurs.
    120     */
    121    public void dump(final DataOutputStream dos) throws IOException {
    122        dos.writeShort(typeIndex); // u2 index of type name in cpool
    123        dos.writeShort(elementValuePairs.size()); // u2 element_value pair
    124        // count
    125        for (final ElementValuePair envp : elementValuePairs) {
    126            envp.dump(dos);
    127        }
    128    }
    129
    130    /**
    131     * Gets the annotation type name.
    132     *
    133     * @return the annotation type name.
    134     */
    135    public String getAnnotationType() {
    136        return constantPool.getConstantUtf8(typeIndex).getBytes();
    137    }
    138
    139    /**
    140     * Gets the annotation type index.
    141     *
    142     * @return the annotation type index.
    143     */
    144    public int getAnnotationTypeIndex() {
    145        return typeIndex;
    146    }
    147
    148    /**
    149     * Gets the constant pool.
    150     *
    151     * @return the constant pool.
    152     */
    153    public ConstantPool getConstantPool() {
    154        return constantPool;
    155    }
    156
    157    /**
    158     * Gets the element value pairs in this annotation entry.
    159     *
    160     * @return the element value pairs in this annotation entry.
    161     */
    162    public ElementValuePair[] getElementValuePairs() {
    163        // TODO return List
    164        return elementValuePairs.toArray(ElementValuePair.EMPTY_ARRAY);
    165    }
    166
    167    /**
    168     * Gets the number of element value pairs in this annotation entry.
    169     *
    170     * @return the number of element value pairs in this annotation entry.
    171     */
    172    public final int getNumElementValuePairs() {
    173        return elementValuePairs.size();
    174    }
    175
    176    /**
    177     * Gets the type index.
    178     *
    179     * @return the type index.
    180     */
    181    public int getTypeIndex() {
    182        return typeIndex;
    183    }
    184
    185    /**
    186     * Gets whether this annotation is runtime visible.
    187     *
    188     * @return true if this annotation is runtime visible.
    189     */
    190    public boolean isRuntimeVisible() {
    191        return isRuntimeVisible;
    192    }
    193
    194    /**
    195     * Gets a short string representation of this annotation.
    196     *
    197     * @return a short string representation of this annotation.
    198     */
    199    public String toShortString() {
    200        final StringBuilder result = new StringBuilder();
    201        result.append("@");
    202        result.append(getAnnotationType());
    203        final ElementValuePair[] evPairs = getElementValuePairs();
    204        if (evPairs.length > 0) {
    205            result.append("(");
    206            for (final ElementValuePair element : evPairs) {
    207                result.append(element.toShortString());
    208                result.append(", ");
    209            }
    210            // remove last ", "
    211            result.setLength(result.length() - 2);
    212            result.append(")");
    213        }
    214        return result.toString();
    215    }
    216
    217    @Override
    218    public String toString() {
    219        return toShortString();
    220    }
    221}