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}