    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    public static final AnnotationEntry[] EMPTY_ARRAY = {};
    038
    039    public static AnnotationEntry[] createAnnotationEntries(final Attribute[] attributes) {
    040        // Find attributes that contain annotation data
    041        return Streams.of(attributes).filter(Annotations.class::isInstance).flatMap(e -> Stream.of(((Annotations) e).getAnnotationEntries()))
    042                .toArray(AnnotationEntry[]::new);
    043    }
    044
    045    /**
    046     * Factory method to create an AnnotionEntry from a DataInput
    047     *
    048     * @param input
    049     * @param constantPool
    050     * @param isRuntimeVisible
    051     * @return the entry
    052     * @throws IOException if an I/O error occurs.
    053     */
    054    public static AnnotationEntry read(final DataInput input, final ConstantPool constantPool, final boolean isRuntimeVisible) throws IOException {
    055        final AnnotationEntry annotationEntry = new AnnotationEntry(input.readUnsignedShort(), constantPool, isRuntimeVisible);
    056        final int numElementValuePairs = input.readUnsignedShort();
    057        for (int i = 0; i < numElementValuePairs; i++) {
    058            annotationEntry.elementValuePairs
    059                .add(new ElementValuePair(input.readUnsignedShort(), ElementValue.readElementValue(input, constantPool), constantPool));
    060        }
    061        return annotationEntry;
    062    }
    063
    064    private final int typeIndex;
    065
    066    private final ConstantPool constantPool;
    067
    068    private final boolean isRuntimeVisible;
    069
    070    private final List<ElementValuePair> elementValuePairs;
    071
    072    public AnnotationEntry(final int typeIndex, final ConstantPool constantPool, final boolean isRuntimeVisible) {
    073        this.typeIndex = typeIndex;
    074        this.constantPool = constantPool;
    075        this.isRuntimeVisible = isRuntimeVisible;
    076        this.elementValuePairs = new ArrayList<>();
    077    }
    078
    079    /**
    080     * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
    081     * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
    082     *
    083     * @param v Visitor object
    084     */
    085    @Override
    086    public void accept(final Visitor v) {
    087        v.visitAnnotationEntry(this);
    088    }
    089
    090    public void addElementNameValuePair(final ElementValuePair elementNameValuePair) {
    091        elementValuePairs.add(elementNameValuePair);
    092    }
    093
    094    public void dump(final DataOutputStream dos) throws IOException {
    095        dos.writeShort(typeIndex); // u2 index of type name in cpool
    096        dos.writeShort(elementValuePairs.size()); // u2 element_value pair
    097        // count
    098        for (final ElementValuePair envp : elementValuePairs) {
    099            envp.dump(dos);
    100        }
    101    }
    102
    103    /**
    104     * @return the annotation type name
    105     */
    106    public String getAnnotationType() {
    107        return constantPool.getConstantUtf8(typeIndex).getBytes();
    108    }
    109
    110    /**
    111     * @return the annotation type index
    112     */
    113    public int getAnnotationTypeIndex() {
    114        return typeIndex;
    115    }
    116
    117    public ConstantPool getConstantPool() {
    118        return constantPool;
    119    }
    120
    121    /**
    122     * @return the element value pairs in this annotation entry
    123     */
    124    public ElementValuePair[] getElementValuePairs() {
    125        // TODO return List
    126        return elementValuePairs.toArray(ElementValuePair.EMPTY_ARRAY);
    127    }
    128
    129    /**
    130     * @return the number of element value pairs in this annotation entry
    131     */
    132    public final int getNumElementValuePairs() {
    133        return elementValuePairs.size();
    134    }
    135
    136    public int getTypeIndex() {
    137        return typeIndex;
    138    }
    139
    140    public boolean isRuntimeVisible() {
    141        return isRuntimeVisible;
    142    }
    143
    144    public String toShortString() {
    145        final StringBuilder result = new StringBuilder();
    146        result.append("@");
    147        result.append(getAnnotationType());
    148        final ElementValuePair[] evPairs = getElementValuePairs();
    149        if (evPairs.length > 0) {
    150            result.append("(");
    151            for (final ElementValuePair element : evPairs) {
    152                result.append(element.toShortString());
    153                result.append(", ");
    154            }
    155            // remove last ", "
    156            result.setLength(result.length() - 2);
    157            result.append(")");
    158        }
    159        return result.toString();
    160    }
    161
    162    @Override
    163    public String toString() {
    164        return toShortString();
    165    }
    166}