001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.bcel.generic; 018 019import java.io.ByteArrayInputStream; 020import java.io.ByteArrayOutputStream; 021import java.io.DataInput; 022import java.io.DataInputStream; 023import java.io.DataOutputStream; 024import java.io.IOException; 025import java.util.ArrayList; 026import java.util.List; 027 028import org.apache.bcel.classfile.AnnotationEntry; 029import org.apache.bcel.classfile.Attribute; 030import org.apache.bcel.classfile.ConstantUtf8; 031import org.apache.bcel.classfile.ElementValuePair; 032import org.apache.bcel.classfile.RuntimeInvisibleAnnotations; 033import org.apache.bcel.classfile.RuntimeInvisibleParameterAnnotations; 034import org.apache.bcel.classfile.RuntimeVisibleAnnotations; 035import org.apache.bcel.classfile.RuntimeVisibleParameterAnnotations; 036 037/** 038 * @since 6.0 039 */ 040public class AnnotationEntryGen { 041 042 static final AnnotationEntryGen[] EMPTY_ARRAY = {}; 043 044 /** 045 * Converts a list of AnnotationGen objects into a set of attributes that can be attached to the class file. 046 * 047 * @param cp The constant pool gen where we can create the necessary name refs 048 * @param annotationEntryGens An array of AnnotationGen objects 049 */ 050 static Attribute[] getAnnotationAttributes(final ConstantPoolGen cp, final AnnotationEntryGen[] annotationEntryGens) { 051 if (annotationEntryGens.length == 0) { 052 return Attribute.EMPTY_ARRAY; 053 } 054 055 try { 056 int countVisible = 0; 057 int countInvisible = 0; 058 059 // put the annotations in the right output stream 060 for (final AnnotationEntryGen a : annotationEntryGens) { 061 if (a.isRuntimeVisible()) { 062 countVisible++; 063 } else { 064 countInvisible++; 065 } 066 } 067 068 final ByteArrayOutputStream rvaBytes = new ByteArrayOutputStream(); 069 final ByteArrayOutputStream riaBytes = new ByteArrayOutputStream(); 070 try (DataOutputStream rvaDos = new DataOutputStream(rvaBytes); DataOutputStream riaDos = new DataOutputStream(riaBytes)) { 071 072 rvaDos.writeShort(countVisible); 073 riaDos.writeShort(countInvisible); 074 075 // put the annotations in the right output stream 076 for (final AnnotationEntryGen a : annotationEntryGens) { 077 if (a.isRuntimeVisible()) { 078 a.dump(rvaDos); 079 } else { 080 a.dump(riaDos); 081 } 082 } 083 } 084 085 final byte[] rvaData = rvaBytes.toByteArray(); 086 final byte[] riaData = riaBytes.toByteArray(); 087 088 int rvaIndex = -1; 089 int riaIndex = -1; 090 091 if (rvaData.length > 2) { 092 rvaIndex = cp.addUtf8("RuntimeVisibleAnnotations"); 093 } 094 if (riaData.length > 2) { 095 riaIndex = cp.addUtf8("RuntimeInvisibleAnnotations"); 096 } 097 098 final List<Attribute> newAttributes = new ArrayList<>(); 099 if (rvaData.length > 2) { 100 newAttributes 101 .add(new RuntimeVisibleAnnotations(rvaIndex, rvaData.length, new DataInputStream(new ByteArrayInputStream(rvaData)), cp.getConstantPool())); 102 } 103 if (riaData.length > 2) { 104 newAttributes.add( 105 new RuntimeInvisibleAnnotations(riaIndex, riaData.length, new DataInputStream(new ByteArrayInputStream(riaData)), cp.getConstantPool())); 106 } 107 108 return newAttributes.toArray(Attribute.EMPTY_ARRAY); 109 } catch (final IOException e) { 110 System.err.println("IOException whilst processing annotations"); 111 e.printStackTrace(); 112 } 113 return null; 114 } 115 116 /** 117 * Annotations against a class are stored in one of four attribute kinds: - RuntimeVisibleParameterAnnotations - 118 * RuntimeInvisibleParameterAnnotations 119 */ 120 static Attribute[] getParameterAnnotationAttributes(final ConstantPoolGen cp, 121 final List<AnnotationEntryGen>[] /* Array of lists, array size depends on #params */ vec) { 122 final int[] visCount = new int[vec.length]; 123 int totalVisCount = 0; 124 final int[] invisCount = new int[vec.length]; 125 int totalInvisCount = 0; 126 try { 127 for (int i = 0; i < vec.length; i++) { 128 if (vec[i] != null) { 129 for (final AnnotationEntryGen element : vec[i]) { 130 if (element.isRuntimeVisible()) { 131 visCount[i]++; 132 totalVisCount++; 133 } else { 134 invisCount[i]++; 135 totalInvisCount++; 136 } 137 } 138 } 139 } 140 // Lets do the visible ones 141 final ByteArrayOutputStream rvaBytes = new ByteArrayOutputStream(); 142 try (DataOutputStream rvaDos = new DataOutputStream(rvaBytes)) { 143 rvaDos.writeByte(vec.length); // First goes number of parameters 144 for (int i = 0; i < vec.length; i++) { 145 rvaDos.writeShort(visCount[i]); 146 if (visCount[i] > 0) { 147 for (final AnnotationEntryGen element : vec[i]) { 148 if (element.isRuntimeVisible()) { 149 element.dump(rvaDos); 150 } 151 } 152 } 153 } 154 } 155 // Lets do the invisible ones 156 final ByteArrayOutputStream riaBytes = new ByteArrayOutputStream(); 157 try (DataOutputStream riaDos = new DataOutputStream(riaBytes)) { 158 riaDos.writeByte(vec.length); // First goes number of parameters 159 for (int i = 0; i < vec.length; i++) { 160 riaDos.writeShort(invisCount[i]); 161 if (invisCount[i] > 0) { 162 for (final AnnotationEntryGen element : vec[i]) { 163 if (!element.isRuntimeVisible()) { 164 element.dump(riaDos); 165 } 166 } 167 } 168 } 169 } 170 final byte[] rvaData = rvaBytes.toByteArray(); 171 final byte[] riaData = riaBytes.toByteArray(); 172 int rvaIndex = -1; 173 int riaIndex = -1; 174 if (totalVisCount > 0) { 175 rvaIndex = cp.addUtf8("RuntimeVisibleParameterAnnotations"); 176 } 177 if (totalInvisCount > 0) { 178 riaIndex = cp.addUtf8("RuntimeInvisibleParameterAnnotations"); 179 } 180 final List<Attribute> newAttributes = new ArrayList<>(); 181 if (totalVisCount > 0) { 182 newAttributes.add(new RuntimeVisibleParameterAnnotations(rvaIndex, rvaData.length, new DataInputStream(new ByteArrayInputStream(rvaData)), 183 cp.getConstantPool())); 184 } 185 if (totalInvisCount > 0) { 186 newAttributes.add(new RuntimeInvisibleParameterAnnotations(riaIndex, riaData.length, new DataInputStream(new ByteArrayInputStream(riaData)), 187 cp.getConstantPool())); 188 } 189 return newAttributes.toArray(Attribute.EMPTY_ARRAY); 190 } catch (final IOException e) { 191 System.err.println("IOException whilst processing parameter annotations"); 192 e.printStackTrace(); 193 } 194 return null; 195 } 196 197 public static AnnotationEntryGen read(final DataInput dis, final ConstantPoolGen cpool, final boolean b) throws IOException { 198 final AnnotationEntryGen a = new AnnotationEntryGen(cpool); 199 a.typeIndex = dis.readUnsignedShort(); 200 final int elemValuePairCount = dis.readUnsignedShort(); 201 for (int i = 0; i < elemValuePairCount; i++) { 202 final int nidx = dis.readUnsignedShort(); 203 a.addElementNameValuePair(new ElementValuePairGen(nidx, ElementValueGen.readElementValue(dis, cpool), cpool)); 204 } 205 a.isRuntimeVisible(b); 206 return a; 207 } 208 209 private int typeIndex; 210 211 private List<ElementValuePairGen> evs; 212 213 private final ConstantPoolGen cpool; 214 215 private boolean isRuntimeVisible; 216 217 /** 218 * Here we are taking a fixed annotation of type Annotation and building a modifiable AnnotationGen object. If the pool 219 * passed in is for a different class file, then copyPoolEntries should have been passed as true as that will force us 220 * to do a deep copy of the annotation and move the cpool entries across. We need to copy the type and the element name 221 * value pairs and the visibility. 222 */ 223 public AnnotationEntryGen(final AnnotationEntry a, final ConstantPoolGen cpool, final boolean copyPoolEntries) { 224 this.cpool = cpool; 225 if (copyPoolEntries) { 226 typeIndex = cpool.addUtf8(a.getAnnotationType()); 227 } else { 228 typeIndex = a.getAnnotationTypeIndex(); 229 } 230 isRuntimeVisible = a.isRuntimeVisible(); 231 evs = copyValues(a.getElementValuePairs(), cpool, copyPoolEntries); 232 } 233 234 private AnnotationEntryGen(final ConstantPoolGen cpool) { 235 this.cpool = cpool; 236 } 237 238 public AnnotationEntryGen(final ObjectType type, final List<ElementValuePairGen> elements, final boolean vis, final ConstantPoolGen cpool) { 239 this.cpool = cpool; 240 this.typeIndex = cpool.addUtf8(type.getSignature()); 241 evs = elements; 242 isRuntimeVisible = vis; 243 } 244 245 public void addElementNameValuePair(final ElementValuePairGen evp) { 246 if (evs == null) { 247 evs = new ArrayList<>(); 248 } 249 evs.add(evp); 250 } 251 252 private List<ElementValuePairGen> copyValues(final ElementValuePair[] in, final ConstantPoolGen cpool, final boolean copyPoolEntries) { 253 final List<ElementValuePairGen> out = new ArrayList<>(); 254 for (final ElementValuePair nvp : in) { 255 out.add(new ElementValuePairGen(nvp, cpool, copyPoolEntries)); 256 } 257 return out; 258 } 259 260 public void dump(final DataOutputStream dos) throws IOException { 261 dos.writeShort(typeIndex); // u2 index of type name in cpool 262 dos.writeShort(evs.size()); // u2 element_value pair count 263 for (final ElementValuePairGen envp : evs) { 264 envp.dump(dos); 265 } 266 } 267 268 /** 269 * Retrieve an immutable version of this AnnotationGen 270 */ 271 public AnnotationEntry getAnnotation() { 272 final AnnotationEntry a = new AnnotationEntry(typeIndex, cpool.getConstantPool(), isRuntimeVisible); 273 for (final ElementValuePairGen element : evs) { 274 a.addElementNameValuePair(element.getElementNameValuePair()); 275 } 276 return a; 277 } 278 279 public int getTypeIndex() { 280 return typeIndex; 281 } 282 283 public final String getTypeName() { 284 return getTypeSignature();// BCELBUG: Should I use this instead? 285 // Utility.signatureToString(getTypeSignature()); 286 } 287 288 public final String getTypeSignature() { 289 // ConstantClass c = (ConstantClass)cpool.getConstant(typeIndex); 290 final ConstantUtf8 utf8 = (ConstantUtf8) cpool.getConstant(typeIndex/* c.getNameIndex() */); 291 return utf8.getBytes(); 292 } 293 294 /** 295 * Returns list of ElementNameValuePair objects. 296 * 297 * @return list of ElementNameValuePair objects. 298 */ 299 public List<ElementValuePairGen> getValues() { 300 return evs; 301 } 302 303 public boolean isRuntimeVisible() { 304 return isRuntimeVisible; 305 } 306 307 private void isRuntimeVisible(final boolean b) { 308 isRuntimeVisible = b; 309 } 310 311 public String toShortString() { 312 final StringBuilder s = new StringBuilder(); 313 s.append("@").append(getTypeName()).append("("); 314 for (int i = 0; i < evs.size(); i++) { 315 s.append(evs.get(i)); 316 if (i + 1 < evs.size()) { 317 s.append(","); 318 } 319 } 320 s.append(")"); 321 return s.toString(); 322 } 323 324 @Override 325 public String toString() { 326 final StringBuilder s = new StringBuilder(32); // CHECKSTYLE IGNORE MagicNumber 327 s.append("AnnotationGen:[").append(getTypeName()).append(" #").append(evs.size()).append(" {"); 328 for (int i = 0; i < evs.size(); i++) { 329 s.append(evs.get(i)); 330 if (i + 1 < evs.size()) { 331 s.append(","); 332 } 333 } 334 s.append("}]"); 335 return s.toString(); 336 } 337 338}