1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 package org.apache.bcel.generic; 19 20 import java.io.ByteArrayInputStream; 21 import java.io.ByteArrayOutputStream; 22 import java.io.DataInput; 23 import java.io.DataInputStream; 24 import java.io.DataOutputStream; 25 import java.io.IOException; 26 import java.util.ArrayList; 27 import java.util.List; 28 29 import org.apache.bcel.classfile.AnnotationEntry; 30 import org.apache.bcel.classfile.Attribute; 31 import org.apache.bcel.classfile.ConstantUtf8; 32 import org.apache.bcel.classfile.ElementValuePair; 33 import org.apache.bcel.classfile.RuntimeInvisibleAnnotations; 34 import org.apache.bcel.classfile.RuntimeInvisibleParameterAnnotations; 35 import org.apache.bcel.classfile.RuntimeVisibleAnnotations; 36 import org.apache.bcel.classfile.RuntimeVisibleParameterAnnotations; 37 38 /** 39 * @since 6.0 40 */ 41 public class AnnotationEntryGen { 42 private int typeIndex; 43 44 private List<ElementValuePairGen> evs; 45 46 private final ConstantPoolGen cpool; 47 48 private boolean isRuntimeVisible = false; 49 50 /** 51 * Here we are taking a fixed annotation of type Annotation and building a 52 * modifiable AnnotationGen object. If the pool passed in is for a different 53 * class file, then copyPoolEntries should have been passed as true as that 54 * will force us to do a deep copy of the annotation and move the cpool 55 * entries across. We need to copy the type and the element name value pairs 56 * and the visibility. 57 */ AnnotationEntryGen(final AnnotationEntry a, final ConstantPoolGen cpool, final boolean copyPoolEntries)58 public AnnotationEntryGen(final AnnotationEntry a, final ConstantPoolGen cpool, 59 final boolean copyPoolEntries) { 60 this.cpool = cpool; 61 if (copyPoolEntries) { 62 typeIndex = cpool.addUtf8(a.getAnnotationType()); 63 } else { 64 typeIndex = a.getAnnotationTypeIndex(); 65 } 66 isRuntimeVisible = a.isRuntimeVisible(); 67 evs = copyValues(a.getElementValuePairs(), cpool, copyPoolEntries); 68 } 69 copyValues(final ElementValuePair[] in, final ConstantPoolGen cpool, final boolean copyPoolEntries)70 private List<ElementValuePairGen> copyValues(final ElementValuePair[] in, final ConstantPoolGen cpool, 71 final boolean copyPoolEntries) { 72 final List<ElementValuePairGen> out = new ArrayList<>(); 73 for (final ElementValuePair nvp : in) { 74 out.add(new ElementValuePairGen(nvp, cpool, copyPoolEntries)); 75 } 76 return out; 77 } 78 AnnotationEntryGen(final ConstantPoolGen cpool)79 private AnnotationEntryGen(final ConstantPoolGen cpool) { 80 this.cpool = cpool; 81 } 82 83 /** 84 * Retrieve an immutable version of this AnnotationGen 85 */ getAnnotation()86 public AnnotationEntry getAnnotation() { 87 final AnnotationEntry a = new AnnotationEntry(typeIndex, cpool.getConstantPool(), 88 isRuntimeVisible); 89 for (final ElementValuePairGen element : evs) { 90 a.addElementNameValuePair(element.getElementNameValuePair()); 91 } 92 return a; 93 } 94 AnnotationEntryGen(final ObjectType type, final List<ElementValuePairGen> elements, final boolean vis, final ConstantPoolGen cpool)95 public AnnotationEntryGen(final ObjectType type, 96 final List<ElementValuePairGen> elements, final boolean vis, 97 final ConstantPoolGen cpool) { 98 this.cpool = cpool; 99 this.typeIndex = cpool.addUtf8(type.getSignature()); 100 evs = elements; 101 isRuntimeVisible = vis; 102 } 103 read(final DataInput dis, final ConstantPoolGen cpool, final boolean b)104 public static AnnotationEntryGen read(final DataInput dis, 105 final ConstantPoolGen cpool, final boolean b) throws IOException { 106 final AnnotationEntryGen a = new AnnotationEntryGen(cpool); 107 a.typeIndex = dis.readUnsignedShort(); 108 final int elemValuePairCount = dis.readUnsignedShort(); 109 for (int i = 0; i < elemValuePairCount; i++) { 110 final int nidx = dis.readUnsignedShort(); 111 a.addElementNameValuePair(new ElementValuePairGen(nidx, 112 ElementValueGen.readElementValue(dis, cpool), cpool)); 113 } 114 a.isRuntimeVisible(b); 115 return a; 116 } 117 dump(final DataOutputStream dos)118 public void dump(final DataOutputStream dos) throws IOException { 119 dos.writeShort(typeIndex); // u2 index of type name in cpool 120 dos.writeShort(evs.size()); // u2 element_value pair count 121 for (final ElementValuePairGen envp : evs) { 122 envp.dump(dos); 123 } 124 } 125 addElementNameValuePair(final ElementValuePairGen evp)126 public void addElementNameValuePair(final ElementValuePairGen evp) { 127 if (evs == null) { 128 evs = new ArrayList<>(); 129 } 130 evs.add(evp); 131 } 132 getTypeIndex()133 public int getTypeIndex() { 134 return typeIndex; 135 } 136 getTypeSignature()137 public final String getTypeSignature() { 138 // ConstantClass c = (ConstantClass)cpool.getConstant(typeIndex); 139 final ConstantUtf8 utf8 = (ConstantUtf8) cpool 140 .getConstant(typeIndex/* c.getNameIndex() */); 141 return utf8.getBytes(); 142 } 143 getTypeName()144 public final String getTypeName() { 145 return getTypeSignature();// BCELBUG: Should I use this instead? 146 // Utility.signatureToString(getTypeSignature()); 147 } 148 149 /** 150 * Returns list of ElementNameValuePair objects 151 */ getValues()152 public List<ElementValuePairGen> getValues() { 153 return evs; 154 } 155 156 @Override toString()157 public String toString() { 158 final StringBuilder s = new StringBuilder(32); // CHECKSTYLE IGNORE MagicNumber 159 s.append("AnnotationGen:[").append(getTypeName()).append(" #").append(evs.size()).append(" {"); 160 for (int i = 0; i < evs.size(); i++) { 161 s.append(evs.get(i)); 162 if (i + 1 < evs.size()) { 163 s.append(","); 164 } 165 } 166 s.append("}]"); 167 return s.toString(); 168 } 169 toShortString()170 public String toShortString() { 171 final StringBuilder s = new StringBuilder(); 172 s.append("@").append(getTypeName()).append("("); 173 for (int i = 0; i < evs.size(); i++) { 174 s.append(evs.get(i)); 175 if (i + 1 < evs.size()) { 176 s.append(","); 177 } 178 } 179 s.append(")"); 180 return s.toString(); 181 } 182 isRuntimeVisible(final boolean b)183 private void isRuntimeVisible(final boolean b) { 184 isRuntimeVisible = b; 185 } 186 isRuntimeVisible()187 public boolean isRuntimeVisible() { 188 return isRuntimeVisible; 189 } 190 191 192 /** 193 * Converts a list of AnnotationGen objects into a set of attributes 194 * that can be attached to the class file. 195 * 196 * @param cp The constant pool gen where we can create the necessary name refs 197 * @param annotationEntryGens An array of AnnotationGen objects 198 */ getAnnotationAttributes(final ConstantPoolGen cp, final AnnotationEntryGen[] annotationEntryGens)199 static Attribute[] getAnnotationAttributes(final ConstantPoolGen cp, final AnnotationEntryGen[] annotationEntryGens) { 200 if (annotationEntryGens.length == 0) { 201 return new Attribute[0]; 202 } 203 204 try { 205 int countVisible = 0; 206 int countInvisible = 0; 207 208 // put the annotations in the right output stream 209 for (final AnnotationEntryGen a : annotationEntryGens) { 210 if (a.isRuntimeVisible()) { 211 countVisible++; 212 } else { 213 countInvisible++; 214 } 215 } 216 217 final ByteArrayOutputStream rvaBytes = new ByteArrayOutputStream(); 218 final ByteArrayOutputStream riaBytes = new ByteArrayOutputStream(); 219 try (DataOutputStream rvaDos = new DataOutputStream(rvaBytes); 220 DataOutputStream riaDos = new DataOutputStream(riaBytes)) { 221 222 rvaDos.writeShort(countVisible); 223 riaDos.writeShort(countInvisible); 224 225 // put the annotations in the right output stream 226 for (final AnnotationEntryGen a : annotationEntryGens) { 227 if (a.isRuntimeVisible()) { 228 a.dump(rvaDos); 229 } else { 230 a.dump(riaDos); 231 } 232 } 233 } 234 235 final byte[] rvaData = rvaBytes.toByteArray(); 236 final byte[] riaData = riaBytes.toByteArray(); 237 238 int rvaIndex = -1; 239 int riaIndex = -1; 240 241 if (rvaData.length > 2) { 242 rvaIndex = cp.addUtf8("RuntimeVisibleAnnotations"); 243 } 244 if (riaData.length > 2) { 245 riaIndex = cp.addUtf8("RuntimeInvisibleAnnotations"); 246 } 247 248 final List<Attribute> newAttributes = new ArrayList<>(); 249 if (rvaData.length > 2) { 250 newAttributes.add( 251 new RuntimeVisibleAnnotations(rvaIndex, rvaData.length, 252 new DataInputStream(new ByteArrayInputStream(rvaData)), cp.getConstantPool())); 253 } 254 if (riaData.length > 2) { 255 newAttributes.add( 256 new RuntimeInvisibleAnnotations(riaIndex, riaData.length, 257 new DataInputStream(new ByteArrayInputStream(riaData)), cp.getConstantPool())); 258 } 259 260 return newAttributes.toArray(new Attribute[newAttributes.size()]); 261 } catch (final IOException e) { 262 System.err.println("IOException whilst processing annotations"); 263 e.printStackTrace(); 264 } 265 return null; 266 } 267 268 269 /** 270 * Annotations against a class are stored in one of four attribute kinds: 271 * - RuntimeVisibleParameterAnnotations 272 * - RuntimeInvisibleParameterAnnotations 273 */ getParameterAnnotationAttributes( final ConstantPoolGen cp, final List<AnnotationEntryGen>[] vec)274 static Attribute[] getParameterAnnotationAttributes( 275 final ConstantPoolGen cp, 276 final List<AnnotationEntryGen>[] /*Array of lists, array size depends on #params */vec) { 277 final int[] visCount = new int[vec.length]; 278 int totalVisCount = 0; 279 final int[] invisCount = new int[vec.length]; 280 int totalInvisCount = 0; 281 try { 282 for (int i = 0; i < vec.length; i++) { 283 if (vec[i] != null) { 284 for (final AnnotationEntryGen element : vec[i]) { 285 if (element.isRuntimeVisible()) { 286 visCount[i]++; 287 totalVisCount++; 288 } else { 289 invisCount[i]++; 290 totalInvisCount++; 291 } 292 } 293 } 294 } 295 // Lets do the visible ones 296 final ByteArrayOutputStream rvaBytes = new ByteArrayOutputStream(); 297 try (DataOutputStream rvaDos = new DataOutputStream(rvaBytes)) { 298 rvaDos.writeByte(vec.length); // First goes number of parameters 299 for (int i = 0; i < vec.length; i++) { 300 rvaDos.writeShort(visCount[i]); 301 if (visCount[i] > 0) { 302 for (final AnnotationEntryGen element : vec[i]) { 303 if (element.isRuntimeVisible()) { 304 element.dump(rvaDos); 305 } 306 } 307 } 308 } 309 } 310 // Lets do the invisible ones 311 final ByteArrayOutputStream riaBytes = new ByteArrayOutputStream(); 312 try (DataOutputStream riaDos = new DataOutputStream(riaBytes)) { 313 riaDos.writeByte(vec.length); // First goes number of parameters 314 for (int i = 0; i < vec.length; i++) { 315 riaDos.writeShort(invisCount[i]); 316 if (invisCount[i] > 0) { 317 for (final AnnotationEntryGen element : vec[i]) { 318 if (!element.isRuntimeVisible()) { 319 element.dump(riaDos); 320 } 321 } 322 } 323 } 324 } 325 final byte[] rvaData = rvaBytes.toByteArray(); 326 final byte[] riaData = riaBytes.toByteArray(); 327 int rvaIndex = -1; 328 int riaIndex = -1; 329 if (totalVisCount > 0) { 330 rvaIndex = cp.addUtf8("RuntimeVisibleParameterAnnotations"); 331 } 332 if (totalInvisCount > 0) { 333 riaIndex = cp.addUtf8("RuntimeInvisibleParameterAnnotations"); 334 } 335 final List<Attribute> newAttributes = new ArrayList<>(); 336 if (totalVisCount > 0) { 337 newAttributes 338 .add(new RuntimeVisibleParameterAnnotations(rvaIndex, 339 rvaData.length, new DataInputStream(new ByteArrayInputStream(rvaData)), cp.getConstantPool())); 340 } 341 if (totalInvisCount > 0) { 342 newAttributes 343 .add(new RuntimeInvisibleParameterAnnotations(riaIndex, 344 riaData.length, new DataInputStream(new ByteArrayInputStream(riaData)), cp.getConstantPool())); 345 } 346 return newAttributes.toArray(new Attribute[newAttributes.size()]); 347 } catch (final IOException e) { 348 System.err 349 .println("IOException whilst processing parameter annotations"); 350 e.printStackTrace(); 351 } 352 return null; 353 } 354 355 } 356