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.bcel.util.Args; 029import org.apache.commons.lang3.stream.Streams; 030 031/** 032 * Represents one annotation in the annotation table 033 * 034 * @since 6.0 035 */ 036public class AnnotationEntry implements Node { 037 038 /** 039 * Empty array of AnnotationEntry objects. 040 */ 041 public static final AnnotationEntry[] EMPTY_ARRAY = {}; 042 043 /** 044 * Creates annotation entries from attributes. 045 * 046 * @param attributes The attributes. 047 * @return The annotation entries. 048 */ 049 public static AnnotationEntry[] createAnnotationEntries(final Attribute[] attributes) { 050 // Find attributes that contain annotation data 051 return Streams.of(attributes).filter(Annotations.class::isInstance).flatMap(e -> Stream.of(((Annotations) e).getAnnotationEntries())) 052 .toArray(AnnotationEntry[]::new); 053 } 054 055 /** 056 * Factory method to create an AnnotionEntry from a DataInput. 057 * 058 * @param input The input stream. 059 * @param constantPool The constant pool. 060 * @param isRuntimeVisible whether the annotation is runtime visible. 061 * @return The entry. 062 * @throws IOException Thrown if an I/O error occurs. 063 */ 064 public static AnnotationEntry read(final DataInput input, final ConstantPool constantPool, final boolean isRuntimeVisible) throws IOException { 065 return read(input, constantPool, isRuntimeVisible, 0); 066 } 067 068 /** 069 * Factory method to create an AnnotionEntry from a DataInput, carrying the nesting depth of the enclosing element values so that 070 * {@link ElementValue#readElementValue(DataInput, ConstantPool, boolean, int)} can bound the combined annotation/array nesting depth. 071 * 072 * @param input The input stream. 073 * @param constantPool The constant pool. 074 * @param isRuntimeVisible whether the annotation is runtime visible. 075 * @param nesting the current element value nesting level. 076 * @return The entry. 077 * @throws IOException Thrown if an I/O error occurs. 078 */ 079 static AnnotationEntry read(final DataInput input, final ConstantPool constantPool, final boolean isRuntimeVisible, final int nesting) 080 throws IOException { 081 final AnnotationEntry annotationEntry = new AnnotationEntry(input.readUnsignedShort(), constantPool, isRuntimeVisible); 082 final int numElementValuePairs = input.readUnsignedShort(); 083 for (int i = 0; i < numElementValuePairs; i++) { 084 annotationEntry.elementValuePairs.add( 085 new ElementValuePair(input.readUnsignedShort(), ElementValue.readElementValue(input, constantPool, isRuntimeVisible, nesting), constantPool)); 086 } 087 return annotationEntry; 088 } 089 090 private final int typeIndex; 091 092 private final ConstantPool constantPool; 093 094 private final boolean isRuntimeVisible; 095 096 private final List<ElementValuePair> elementValuePairs; 097 098 /** 099 * Constructs an AnnotationEntry. 100 * 101 * @param typeIndex The type index. 102 * @param constantPool The constant pool. 103 * @param isRuntimeVisible whether the annotation is runtime visible. 104 */ 105 public AnnotationEntry(final int typeIndex, final ConstantPool constantPool, final boolean isRuntimeVisible) { 106 this.typeIndex = typeIndex; 107 this.constantPool = constantPool; 108 this.isRuntimeVisible = isRuntimeVisible; 109 this.elementValuePairs = new ArrayList<>(); 110 } 111 112 /** 113 * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. 114 * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. 115 * 116 * @param v Visitor object. 117 */ 118 @Override 119 public void accept(final Visitor v) { 120 v.visitAnnotationEntry(this); 121 } 122 123 /** 124 * Adds an element name value pair. 125 * 126 * @param elementNameValuePair The element name value pair. 127 */ 128 public void addElementNameValuePair(final ElementValuePair elementNameValuePair) { 129 elementValuePairs.add(elementNameValuePair); 130 } 131 132 /** 133 * Dumps this annotation entry to a DataOutputStream. 134 * 135 * @param dos The output stream. 136 * @throws IOException Thrown if an I/O error occurs. 137 */ 138 public void dump(final DataOutputStream dos) throws IOException { 139 dos.writeShort(typeIndex); // u2 index of type name in cpool 140 dos.writeShort(Args.requireU2(elementValuePairs.size(), "elementValuePairs.size()")); // u2 element_value pair 141 // count 142 for (final ElementValuePair envp : elementValuePairs) { 143 envp.dump(dos); 144 } 145 } 146 147 /** 148 * Gets the annotation type name. 149 * 150 * @return The annotation type name. 151 */ 152 public String getAnnotationType() { 153 return constantPool.getConstantUtf8(typeIndex).getBytes(); 154 } 155 156 /** 157 * Gets the annotation type index. 158 * 159 * @return The annotation type index. 160 */ 161 public int getAnnotationTypeIndex() { 162 return typeIndex; 163 } 164 165 /** 166 * Gets the constant pool. 167 * 168 * @return The constant pool. 169 */ 170 public ConstantPool getConstantPool() { 171 return constantPool; 172 } 173 174 /** 175 * Gets the element value pairs in this annotation entry. 176 * 177 * @return The element value pairs in this annotation entry. 178 */ 179 public ElementValuePair[] getElementValuePairs() { 180 // TODO return List 181 return elementValuePairs.toArray(ElementValuePair.EMPTY_ARRAY); 182 } 183 184 /** 185 * Gets the number of element value pairs in this annotation entry. 186 * 187 * @return The number of element value pairs in this annotation entry. 188 */ 189 public final int getNumElementValuePairs() { 190 return elementValuePairs.size(); 191 } 192 193 /** 194 * Gets the type index. 195 * 196 * @return The type index. 197 */ 198 public int getTypeIndex() { 199 return typeIndex; 200 } 201 202 /** 203 * Gets whether this annotation is runtime visible. 204 * 205 * @return true if this annotation is runtime visible. 206 */ 207 public boolean isRuntimeVisible() { 208 return isRuntimeVisible; 209 } 210 211 /** 212 * Gets a short string representation of this annotation. 213 * 214 * @return A short string representation of this annotation. 215 */ 216 public String toShortString() { 217 final StringBuilder result = new StringBuilder(); 218 result.append("@"); 219 result.append(getAnnotationType()); 220 final ElementValuePair[] evPairs = getElementValuePairs(); 221 if (evPairs.length > 0) { 222 result.append("("); 223 for (final ElementValuePair element : evPairs) { 224 result.append(element.toShortString()); 225 result.append(", "); 226 } 227 // remove last ", " 228 result.setLength(result.length() - 2); 229 result.append(")"); 230 } 231 return result.toString(); 232 } 233 234 @Override 235 public String toString() { 236 return toShortString(); 237 } 238}