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}