AnnotationEntryGen.java

  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. package org.apache.bcel.generic;

  18. import java.io.ByteArrayInputStream;
  19. import java.io.ByteArrayOutputStream;
  20. import java.io.DataInput;
  21. import java.io.DataInputStream;
  22. import java.io.DataOutputStream;
  23. import java.io.IOException;
  24. import java.util.ArrayList;
  25. import java.util.List;
  26. import java.util.stream.Collectors;

  27. import org.apache.bcel.classfile.AnnotationEntry;
  28. import org.apache.bcel.classfile.Attribute;
  29. import org.apache.bcel.classfile.ConstantUtf8;
  30. import org.apache.bcel.classfile.ElementValuePair;
  31. import org.apache.bcel.classfile.RuntimeInvisibleAnnotations;
  32. import org.apache.bcel.classfile.RuntimeInvisibleParameterAnnotations;
  33. import org.apache.bcel.classfile.RuntimeVisibleAnnotations;
  34. import org.apache.bcel.classfile.RuntimeVisibleParameterAnnotations;
  35. import org.apache.commons.lang3.ArrayUtils;
  36. import org.apache.commons.lang3.stream.Streams;

  37. /**
  38.  * @since 6.0
  39.  */
  40. public class AnnotationEntryGen {

  41.     static final AnnotationEntryGen[] EMPTY_ARRAY = {};

  42.     /**
  43.      * Converts a list of AnnotationGen objects into a set of attributes that can be attached to the class file.
  44.      *
  45.      * @param cp The constant pool gen where we can create the necessary name refs
  46.      * @param annotationEntryGens An array of AnnotationGen objects
  47.      */
  48.     static Attribute[] getAnnotationAttributes(final ConstantPoolGen cp, final AnnotationEntryGen[] annotationEntryGens) {
  49.         if (ArrayUtils.isEmpty(annotationEntryGens)) {
  50.             return Attribute.EMPTY_ARRAY;
  51.         }

  52.         try {
  53.             int countVisible = 0;
  54.             int countInvisible = 0;

  55.             // put the annotations in the right output stream
  56.             for (final AnnotationEntryGen a : annotationEntryGens) {
  57.                 if (a.isRuntimeVisible()) {
  58.                     countVisible++;
  59.                 } else {
  60.                     countInvisible++;
  61.                 }
  62.             }

  63.             final ByteArrayOutputStream rvaBytes = new ByteArrayOutputStream();
  64.             final ByteArrayOutputStream riaBytes = new ByteArrayOutputStream();
  65.             try (DataOutputStream rvaDos = new DataOutputStream(rvaBytes); DataOutputStream riaDos = new DataOutputStream(riaBytes)) {

  66.                 rvaDos.writeShort(countVisible);
  67.                 riaDos.writeShort(countInvisible);

  68.                 // put the annotations in the right output stream
  69.                 for (final AnnotationEntryGen a : annotationEntryGens) {
  70.                     if (a.isRuntimeVisible()) {
  71.                         a.dump(rvaDos);
  72.                     } else {
  73.                         a.dump(riaDos);
  74.                     }
  75.                 }
  76.             }

  77.             final byte[] rvaData = rvaBytes.toByteArray();
  78.             final byte[] riaData = riaBytes.toByteArray();

  79.             int rvaIndex = -1;
  80.             int riaIndex = -1;

  81.             if (rvaData.length > 2) {
  82.                 rvaIndex = cp.addUtf8("RuntimeVisibleAnnotations");
  83.             }
  84.             if (riaData.length > 2) {
  85.                 riaIndex = cp.addUtf8("RuntimeInvisibleAnnotations");
  86.             }

  87.             final List<Attribute> newAttributes = new ArrayList<>();
  88.             if (rvaData.length > 2) {
  89.                 newAttributes
  90.                     .add(new RuntimeVisibleAnnotations(rvaIndex, rvaData.length, new DataInputStream(new ByteArrayInputStream(rvaData)), cp.getConstantPool()));
  91.             }
  92.             if (riaData.length > 2) {
  93.                 newAttributes.add(
  94.                     new RuntimeInvisibleAnnotations(riaIndex, riaData.length, new DataInputStream(new ByteArrayInputStream(riaData)), cp.getConstantPool()));
  95.             }

  96.             return newAttributes.toArray(Attribute.EMPTY_ARRAY);
  97.         } catch (final IOException e) {
  98.             System.err.println("IOException whilst processing annotations");
  99.             e.printStackTrace();
  100.         }
  101.         return null;
  102.     }

  103.     /**
  104.      * Annotations against a class are stored in one of four attribute kinds: - RuntimeVisibleParameterAnnotations -
  105.      * RuntimeInvisibleParameterAnnotations
  106.      */
  107.     static Attribute[] getParameterAnnotationAttributes(final ConstantPoolGen cp,
  108.         final List<AnnotationEntryGen>[] /* Array of lists, array size depends on #params */ vec) {
  109.         final int[] visCount = new int[vec.length];
  110.         int totalVisCount = 0;
  111.         final int[] invisCount = new int[vec.length];
  112.         int totalInvisCount = 0;
  113.         try {
  114.             for (int i = 0; i < vec.length; i++) {
  115.                 if (vec[i] != null) {
  116.                     for (final AnnotationEntryGen element : vec[i]) {
  117.                         if (element.isRuntimeVisible()) {
  118.                             visCount[i]++;
  119.                             totalVisCount++;
  120.                         } else {
  121.                             invisCount[i]++;
  122.                             totalInvisCount++;
  123.                         }
  124.                     }
  125.                 }
  126.             }
  127.             // Lets do the visible ones
  128.             final ByteArrayOutputStream rvaBytes = new ByteArrayOutputStream();
  129.             try (DataOutputStream rvaDos = new DataOutputStream(rvaBytes)) {
  130.                 rvaDos.writeByte(vec.length); // First goes number of parameters
  131.                 for (int i = 0; i < vec.length; i++) {
  132.                     rvaDos.writeShort(visCount[i]);
  133.                     if (visCount[i] > 0) {
  134.                         for (final AnnotationEntryGen element : vec[i]) {
  135.                             if (element.isRuntimeVisible()) {
  136.                                 element.dump(rvaDos);
  137.                             }
  138.                         }
  139.                     }
  140.                 }
  141.             }
  142.             // Lets do the invisible ones
  143.             final ByteArrayOutputStream riaBytes = new ByteArrayOutputStream();
  144.             try (DataOutputStream riaDos = new DataOutputStream(riaBytes)) {
  145.                 riaDos.writeByte(vec.length); // First goes number of parameters
  146.                 for (int i = 0; i < vec.length; i++) {
  147.                     riaDos.writeShort(invisCount[i]);
  148.                     if (invisCount[i] > 0) {
  149.                         for (final AnnotationEntryGen element : vec[i]) {
  150.                             if (!element.isRuntimeVisible()) {
  151.                                 element.dump(riaDos);
  152.                             }
  153.                         }
  154.                     }
  155.                 }
  156.             }
  157.             final byte[] rvaData = rvaBytes.toByteArray();
  158.             final byte[] riaData = riaBytes.toByteArray();
  159.             int rvaIndex = -1;
  160.             int riaIndex = -1;
  161.             if (totalVisCount > 0) {
  162.                 rvaIndex = cp.addUtf8("RuntimeVisibleParameterAnnotations");
  163.             }
  164.             if (totalInvisCount > 0) {
  165.                 riaIndex = cp.addUtf8("RuntimeInvisibleParameterAnnotations");
  166.             }
  167.             final List<Attribute> newAttributes = new ArrayList<>();
  168.             if (totalVisCount > 0) {
  169.                 newAttributes.add(new RuntimeVisibleParameterAnnotations(rvaIndex, rvaData.length, new DataInputStream(new ByteArrayInputStream(rvaData)),
  170.                     cp.getConstantPool()));
  171.             }
  172.             if (totalInvisCount > 0) {
  173.                 newAttributes.add(new RuntimeInvisibleParameterAnnotations(riaIndex, riaData.length, new DataInputStream(new ByteArrayInputStream(riaData)),
  174.                     cp.getConstantPool()));
  175.             }
  176.             return newAttributes.toArray(Attribute.EMPTY_ARRAY);
  177.         } catch (final IOException e) {
  178.             System.err.println("IOException whilst processing parameter annotations");
  179.             e.printStackTrace();
  180.         }
  181.         return null;
  182.     }

  183.     public static AnnotationEntryGen read(final DataInput dis, final ConstantPoolGen cpool, final boolean b) throws IOException {
  184.         final AnnotationEntryGen a = new AnnotationEntryGen(cpool);
  185.         a.typeIndex = dis.readUnsignedShort();
  186.         final int elemValuePairCount = dis.readUnsignedShort();
  187.         for (int i = 0; i < elemValuePairCount; i++) {
  188.             final int nidx = dis.readUnsignedShort();
  189.             a.addElementNameValuePair(new ElementValuePairGen(nidx, ElementValueGen.readElementValue(dis, cpool), cpool));
  190.         }
  191.         a.isRuntimeVisible(b);
  192.         return a;
  193.     }

  194.     private int typeIndex;

  195.     private List<ElementValuePairGen> evs;

  196.     private final ConstantPoolGen cpool;

  197.     private boolean isRuntimeVisible;

  198.     /**
  199.      * Here we are taking a fixed annotation of type Annotation and building a modifiable AnnotationGen object. If the pool
  200.      * passed in is for a different class file, then copyPoolEntries should have been passed as true as that will force us
  201.      * to do a deep copy of the annotation and move the cpool entries across. We need to copy the type and the element name
  202.      * value pairs and the visibility.
  203.      */
  204.     public AnnotationEntryGen(final AnnotationEntry a, final ConstantPoolGen cpool, final boolean copyPoolEntries) {
  205.         this.cpool = cpool;
  206.         if (copyPoolEntries) {
  207.             typeIndex = cpool.addUtf8(a.getAnnotationType());
  208.         } else {
  209.             typeIndex = a.getAnnotationTypeIndex();
  210.         }
  211.         isRuntimeVisible = a.isRuntimeVisible();
  212.         evs = copyValues(a.getElementValuePairs(), cpool, copyPoolEntries);
  213.     }

  214.     private AnnotationEntryGen(final ConstantPoolGen cpool) {
  215.         this.cpool = cpool;
  216.     }

  217.     public AnnotationEntryGen(final ObjectType type, final List<ElementValuePairGen> elements, final boolean vis, final ConstantPoolGen cpool) {
  218.         this.cpool = cpool;
  219.         this.typeIndex = cpool.addUtf8(type.getSignature());
  220.         evs = elements;
  221.         isRuntimeVisible = vis;
  222.     }

  223.     public void addElementNameValuePair(final ElementValuePairGen evp) {
  224.         if (evs == null) {
  225.             evs = new ArrayList<>();
  226.         }
  227.         evs.add(evp);
  228.     }

  229.     private List<ElementValuePairGen> copyValues(final ElementValuePair[] in, final ConstantPoolGen cpool, final boolean copyPoolEntries) {
  230.         return Streams.of(in).map(nvp -> new ElementValuePairGen(nvp, cpool, copyPoolEntries)).collect(Collectors.toList());
  231.     }

  232.     public void dump(final DataOutputStream dos) throws IOException {
  233.         dos.writeShort(typeIndex); // u2 index of type name in cpool
  234.         dos.writeShort(evs.size()); // u2 element_value pair count
  235.         for (final ElementValuePairGen envp : evs) {
  236.             envp.dump(dos);
  237.         }
  238.     }

  239.     /**
  240.      * Retrieve an immutable version of this AnnotationGen
  241.      */
  242.     public AnnotationEntry getAnnotation() {
  243.         final AnnotationEntry a = new AnnotationEntry(typeIndex, cpool.getConstantPool(), isRuntimeVisible);
  244.         for (final ElementValuePairGen element : evs) {
  245.             a.addElementNameValuePair(element.getElementNameValuePair());
  246.         }
  247.         return a;
  248.     }

  249.     public int getTypeIndex() {
  250.         return typeIndex;
  251.     }

  252.     public final String getTypeName() {
  253.         return getTypeSignature(); // BCELBUG: Should I use this instead?
  254.         // Utility.signatureToString(getTypeSignature());
  255.     }

  256.     public final String getTypeSignature() {
  257.         // ConstantClass c = (ConstantClass) cpool.getConstant(typeIndex);
  258.         final ConstantUtf8 utf8 = (ConstantUtf8) cpool.getConstant(typeIndex/* c.getNameIndex() */);
  259.         return utf8.getBytes();
  260.     }

  261.     /**
  262.      * Returns list of ElementNameValuePair objects.
  263.      *
  264.      * @return list of ElementNameValuePair objects.
  265.      */
  266.     public List<ElementValuePairGen> getValues() {
  267.         return evs;
  268.     }

  269.     public boolean isRuntimeVisible() {
  270.         return isRuntimeVisible;
  271.     }

  272.     private void isRuntimeVisible(final boolean b) {
  273.         isRuntimeVisible = b;
  274.     }

  275.     public String toShortString() {
  276.         final StringBuilder s = new StringBuilder();
  277.         s.append("@").append(getTypeName()).append("(");
  278.         for (int i = 0; i < evs.size(); i++) {
  279.             s.append(evs.get(i));
  280.             if (i + 1 < evs.size()) {
  281.                 s.append(",");
  282.             }
  283.         }
  284.         s.append(")");
  285.         return s.toString();
  286.     }

  287.     @Override
  288.     public String toString() {
  289.         final StringBuilder s = new StringBuilder(32); // CHECKSTYLE IGNORE MagicNumber
  290.         s.append("AnnotationGen:[").append(getTypeName()).append(" #").append(evs.size()).append(" {");
  291.         for (int i = 0; i < evs.size(); i++) {
  292.             s.append(evs.get(i));
  293.             if (i + 1 < evs.size()) {
  294.                 s.append(",");
  295.             }
  296.         }
  297.         s.append("}]");
  298.         return s.toString();
  299.     }

  300. }