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