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.Iterator;
025import java.util.stream.Stream;
026
027import org.apache.bcel.Const;
028import org.apache.bcel.util.Args;
029
030/**
031 * Base class for the type annotation attributes, {@link RuntimeVisibleTypeAnnotations} and {@link RuntimeInvisibleTypeAnnotations}: the annotations written on
032 * the types used in a declaration (such as {@code List<@Nullable String>}, the supertypes of a class, a {@code throws} clause, or the declaration and the
033 * bounds of a type parameter as in {@code <T extends @Nullable Object>}) or in the code of a method (such as a cast, an {@code instanceof}, a {@code new}
034 * expression or a local variable), which the attributes of the declaration annotations do not hold. They are attributes of a class, a field, a method, a
035 * record component or a {@link Code} attribute.
036 *
037 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se21/html/jvms-4.html#jvms-4.7.20">JVMS 4.7.20 The RuntimeVisibleTypeAnnotations attribute</a>
038 * @since 6.14.0
039 */
040public abstract class TypeAnnotations extends Attribute implements Iterable<TypeAnnotationEntry> {
041
042    private TypeAnnotationEntry[] typeAnnotationTable;
043    private final boolean isRuntimeVisible;
044
045    /**
046     * Constructs an instance.
047     *
048     * @param annotationType The subclass type of the attribute.
049     * @param nameIndex Index pointing to the name of the attribute.
050     * @param length Content length in bytes.
051     * @param input Input stream.
052     * @param constantPool Array of constants.
053     * @param isRuntimeVisible whether the annotations are visible at runtime.
054     * @throws IOException Thrown if an I/O error occurs.
055     */
056    TypeAnnotations(final byte annotationType, final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool,
057            final boolean isRuntimeVisible) throws IOException {
058        this(annotationType, nameIndex, length, (TypeAnnotationEntry[]) null, constantPool, isRuntimeVisible);
059        final int tableLength = input.readUnsignedShort();
060        typeAnnotationTable = new TypeAnnotationEntry[tableLength];
061        for (int i = 0; i < tableLength; i++) {
062            typeAnnotationTable[i] = TypeAnnotationEntry.read(input, constantPool, isRuntimeVisible);
063        }
064    }
065
066    /**
067     * Constructs an instance.
068     *
069     * @param annotationType The subclass type of the attribute.
070     * @param nameIndex Index pointing to the name of the attribute.
071     * @param length Content length in bytes.
072     * @param typeAnnotationTable The actual type annotations.
073     * @param constantPool Array of constants.
074     * @param isRuntimeVisible whether the annotations are visible at runtime.
075     */
076    public TypeAnnotations(final byte annotationType, final int nameIndex, final int length, final TypeAnnotationEntry[] typeAnnotationTable,
077            final ConstantPool constantPool, final boolean isRuntimeVisible) {
078        super(annotationType, nameIndex, length, constantPool);
079        setTypeAnnotationTable(typeAnnotationTable);
080        this.isRuntimeVisible = isRuntimeVisible;
081    }
082
083    @Override
084    public void accept(final Visitor v) {
085        v.visitTypeAnnotations(this);
086    }
087
088    /**
089     * Creates deep copies of the type annotations, which refer to the given constant pool.
090     *
091     * @param constantPool The constant pool of the copies.
092     * @return The copies.
093     */
094    protected final TypeAnnotationEntry[] copyTypeAnnotations(final ConstantPool constantPool) {
095        return Stream.of(typeAnnotationTable).map(e -> e.copy(constantPool)).toArray(TypeAnnotationEntry[]::new);
096    }
097
098    /**
099     * Gets the number of type annotations.
100     *
101     * @return The number of type annotations.
102     */
103    public final int getNumTypeAnnotations() {
104        return typeAnnotationTable.length;
105    }
106
107    /**
108     * Gets the type annotations.
109     *
110     * @return The type annotations.
111     */
112    public TypeAnnotationEntry[] getTypeAnnotationEntries() {
113        return typeAnnotationTable;
114    }
115
116    /**
117     * Tests whether the annotations are visible at runtime.
118     *
119     * @return true if the annotations are runtime visible.
120     */
121    public boolean isRuntimeVisible() {
122        return isRuntimeVisible;
123    }
124
125    @Override
126    public Iterator<TypeAnnotationEntry> iterator() {
127        return Stream.of(typeAnnotationTable).iterator();
128    }
129
130    /**
131     * Sets the type annotations.
132     *
133     * @param typeAnnotationTable The type annotations.
134     */
135    public final void setTypeAnnotationTable(final TypeAnnotationEntry[] typeAnnotationTable) {
136        this.typeAnnotationTable = typeAnnotationTable != null ? typeAnnotationTable : TypeAnnotationEntry.EMPTY_ARRAY;
137    }
138
139    /**
140     * Converts to a String representation.
141     *
142     * @return String representation.
143     */
144    @Override
145    public final String toString() {
146        final StringBuilder buf = new StringBuilder(Const.getAttributeName(getTag()));
147        buf.append(":\n");
148        for (int i = 0; i < typeAnnotationTable.length; i++) {
149            buf.append("  ").append(typeAnnotationTable[i]);
150            if (i < typeAnnotationTable.length - 1) {
151                buf.append('\n');
152            }
153        }
154        return buf.toString();
155    }
156
157    /**
158     * Writes the type annotations to a DataOutputStream.
159     *
160     * @param dos The data output stream.
161     * @throws IOException Thrown if an I/O error occurs.
162     */
163    protected void writeTypeAnnotations(final DataOutputStream dos) throws IOException {
164        dos.writeShort(Args.requireU2(typeAnnotationTable.length, "typeAnnotationTable.length"));
165        for (final TypeAnnotationEntry element : typeAnnotationTable) {
166            element.dump(dos);
167        }
168    }
169}