AnnotationsAttribute.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one
  3.  * or more contributor license agreements.  See the NOTICE file
  4.  * distributed with this work for additional information
  5.  * regarding copyright ownership.  The ASF licenses this file
  6.  * to you under the Apache License, Version 2.0 (the
  7.  * "License"); you may not use this file except in compliance
  8.  * with the License.  You may obtain a copy of the License at
  9.  *
  10.  *   https://www.apache.org/licenses/LICENSE-2.0
  11.  *
  12.  * Unless required by applicable law or agreed to in writing,
  13.  * software distributed under the License is distributed on an
  14.  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15.  * KIND, either express or implied.  See the License for the
  16.  * specific language governing permissions and limitations
  17.  * under the License.
  18.  */
  19. package org.apache.commons.compress.harmony.unpack200.bytecode;

  20. import java.io.DataOutputStream;
  21. import java.io.IOException;
  22. import java.util.ArrayList;
  23. import java.util.List;

  24. /**
  25.  * Abstracts Annotations attributes.
  26.  */
  27. public abstract class AnnotationsAttribute extends Attribute {

  28.     /**
  29.      * Represents the annotation structure for class file attributes.
  30.      */
  31.     public static class Annotation {

  32.         private final int numPairs;
  33.         private final CPUTF8[] elementNames;
  34.         private final ElementValue[] elementValues;
  35.         private final CPUTF8 type;

  36.         // Resolved values
  37.         private int typeIndex;
  38.         private int[] nameIndexes;

  39.         /**
  40.          * Constructs a new instance.
  41.          *
  42.          * @param numPairs      Number of pairs, matches the lengths of {@code elementNames} and {@code elementValues}.
  43.          * @param type          Type.
  44.          * @param elementNames  Element names.
  45.          * @param elementValues Element values.
  46.          */
  47.         public Annotation(final int numPairs, final CPUTF8 type, final CPUTF8[] elementNames, final ElementValue[] elementValues) {
  48.             this.numPairs = numPairs;
  49.             this.type = type;
  50.             this.elementNames = elementNames;
  51.             this.elementValues = elementValues;
  52.         }

  53.         /**
  54.          * Gets a list of class file entries.
  55.          *
  56.          * @return a list of class file entries.
  57.          */
  58.         public List<Object> getClassFileEntries() {
  59.             final List<Object> entries = new ArrayList<>();
  60.             for (int i = 0; i < elementNames.length; i++) {
  61.                 entries.add(elementNames[i]);
  62.                 entries.addAll(elementValues[i].getClassFileEntries());
  63.             }
  64.             entries.add(type);
  65.             return entries;
  66.         }

  67.         /**
  68.          * Gets the cumulative length of all element values.
  69.          *
  70.          * @return the cumulative length of all element values.
  71.          */
  72.         public int getLength() {
  73.             int length = 4;
  74.             for (int i = 0; i < numPairs; i++) {
  75.                 length += 2;
  76.                 length += elementValues[i].getLength();
  77.             }
  78.             return length;
  79.         }

  80.         /**
  81.          * Resolves this instance against a given pool.
  82.          *
  83.          * @param pool a class constant pool.
  84.          */
  85.         public void resolve(final ClassConstantPool pool) {
  86.             type.resolve(pool);
  87.             typeIndex = pool.indexOf(type);
  88.             nameIndexes = new int[numPairs];
  89.             for (int i = 0; i < elementNames.length; i++) {
  90.                 elementNames[i].resolve(pool);
  91.                 nameIndexes[i] = pool.indexOf(elementNames[i]);
  92.                 elementValues[i].resolve(pool);
  93.             }
  94.         }

  95.         /**
  96.          * Writes this instance to the given output stream.
  97.          *
  98.          * @param dos the output stream.
  99.          * @throws IOException if an I/O error occurs.
  100.          */
  101.         public void writeBody(final DataOutputStream dos) throws IOException {
  102.             dos.writeShort(typeIndex);
  103.             dos.writeShort(numPairs);
  104.             for (int i = 0; i < numPairs; i++) {
  105.                 dos.writeShort(nameIndexes[i]);
  106.                 elementValues[i].writeBody(dos);
  107.             }
  108.         }
  109.     }

  110.     /**
  111.      * Pairs a tag and value.
  112.      */
  113.     public static class ElementValue {

  114.         private final Object value;
  115.         private final int tag;

  116.         // resolved value index if it's a constant
  117.         private int constantValueIndex = -1;

  118.         /**
  119.          * Constructs a new instance.
  120.          *
  121.          * @param tag a tag.
  122.          * @param value a value.
  123.          */
  124.         public ElementValue(final int tag, final Object value) {
  125.             this.tag = tag;
  126.             this.value = value;
  127.         }

  128.         /**
  129.          * Gets a list of class file entries.
  130.          *
  131.          * @return a list of class file entries.
  132.          */
  133.         public List<Object> getClassFileEntries() {
  134.             final List<Object> entries = new ArrayList<>(1);
  135.             if (value instanceof CPNameAndType) {
  136.                 // used to represent enum, so don't include the actual CPNameAndType
  137.                 entries.add(((CPNameAndType) value).name);
  138.                 entries.add(((CPNameAndType) value).descriptor);
  139.             } else if (value instanceof ClassFileEntry) {
  140.                 // TODO? ClassFileEntry is an Object
  141.                 entries.add(value);
  142.             } else if (value instanceof ElementValue[]) {
  143.                 final ElementValue[] values = (ElementValue[]) value;
  144.                 for (final ElementValue value2 : values) {
  145.                     entries.addAll(value2.getClassFileEntries());
  146.                 }
  147.             } else if (value instanceof Annotation) {
  148.                 entries.addAll(((Annotation) value).getClassFileEntries());
  149.             }
  150.             return entries;
  151.         }

  152.         /**
  153.          * Gets the length.
  154.          *
  155.          * @return the length.
  156.          */
  157.         public int getLength() {
  158.             switch (tag) {
  159.             case 'B':
  160.             case 'C':
  161.             case 'D':
  162.             case 'F':
  163.             case 'I':
  164.             case 'J':
  165.             case 'S':
  166.             case 'Z':
  167.             case 'c':
  168.             case 's':
  169.                 return 3;
  170.             case 'e':
  171.                 return 5;
  172.             case '[':
  173.                 int length = 3;
  174.                 final ElementValue[] nestedValues = (ElementValue[]) value;
  175.                 for (final ElementValue nestedValue : nestedValues) {
  176.                     length += nestedValue.getLength();
  177.                 }
  178.                 return length;
  179.             case '@':
  180.                 return 1 + ((Annotation) value).getLength();
  181.             }
  182.             return 0;
  183.         }

  184.         /**
  185.          * Resolves this instance against a given pool.
  186.          *
  187.          * @param pool a class constant pool.
  188.          */
  189.         public void resolve(final ClassConstantPool pool) {
  190.             if (value instanceof CPConstant) {
  191.                 ((CPConstant) value).resolve(pool);
  192.                 constantValueIndex = pool.indexOf((CPConstant) value);
  193.             } else if (value instanceof CPClass) {
  194.                 ((CPClass) value).resolve(pool);
  195.                 constantValueIndex = pool.indexOf((CPClass) value);
  196.             } else if (value instanceof CPUTF8) {
  197.                 ((CPUTF8) value).resolve(pool);
  198.                 constantValueIndex = pool.indexOf((CPUTF8) value);
  199.             } else if (value instanceof CPNameAndType) {
  200.                 ((CPNameAndType) value).resolve(pool);
  201.             } else if (value instanceof Annotation) {
  202.                 ((Annotation) value).resolve(pool);
  203.             } else if (value instanceof ElementValue[]) {
  204.                 final ElementValue[] nestedValues = (ElementValue[]) value;
  205.                 for (final ElementValue nestedValue : nestedValues) {
  206.                     nestedValue.resolve(pool);
  207.                 }
  208.             }
  209.         }

  210.         /**
  211.          * Writes this instance to the given output stream.
  212.          *
  213.          * @param dos the output stream.
  214.          * @throws IOException if an I/O error occurs.
  215.          */
  216.         public void writeBody(final DataOutputStream dos) throws IOException {
  217.             dos.writeByte(tag);
  218.             if (constantValueIndex != -1) {
  219.                 dos.writeShort(constantValueIndex);
  220.             } else if (value instanceof CPNameAndType) {
  221.                 ((CPNameAndType) value).writeBody(dos);
  222.             } else if (value instanceof Annotation) {
  223.                 ((Annotation) value).writeBody(dos);
  224.             } else if (value instanceof ElementValue[]) {
  225.                 final ElementValue[] nestedValues = (ElementValue[]) value;
  226.                 dos.writeShort(nestedValues.length);
  227.                 for (final ElementValue nestedValue : nestedValues) {
  228.                     nestedValue.writeBody(dos);
  229.                 }
  230.             } else {
  231.                 throw new Error("");
  232.             }
  233.         }
  234.     }

  235.     /**
  236.      * Constructs a new instance for an attribute name.
  237.      *
  238.      * @param attributeName an attribute name.
  239.      */
  240.     public AnnotationsAttribute(final CPUTF8 attributeName) {
  241.         super(attributeName);
  242.     }

  243. }