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