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}