View Javadoc
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  
21  import java.io.DataOutputStream;
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  /**
27   * Abstracts Annotations attributes.
28   */
29  public abstract class AnnotationsAttribute extends Attribute {
30  
31      /**
32       * Represents the annotation structure for class file attributes.
33       */
34      public static class Annotation {
35  
36          private final int numPairs;
37          private final CPUTF8[] elementNames;
38          private final ElementValue[] elementValues;
39          private final CPUTF8 type;
40  
41          // Resolved values
42          private int typeIndex;
43          private int[] nameIndexes;
44  
45          /**
46           * Constructs a new instance.
47           *
48           * @param numPairs      Number of pairs, matches the lengths of {@code elementNames} and {@code elementValues}.
49           * @param type          Type.
50           * @param elementNames  Element names.
51           * @param elementValues Element values.
52           */
53          public Annotation(final int numPairs, final CPUTF8 type, final CPUTF8[] elementNames, final ElementValue[] elementValues) {
54              this.numPairs = numPairs;
55              this.type = type;
56              this.elementNames = elementNames;
57              this.elementValues = elementValues;
58          }
59  
60          /**
61           * Gets a list of class file entries.
62           *
63           * @return a list of class file entries.
64           */
65          public List<Object> getClassFileEntries() {
66              final List<Object> entries = new ArrayList<>();
67              for (int i = 0; i < elementNames.length; i++) {
68                  entries.add(elementNames[i]);
69                  entries.addAll(elementValues[i].getClassFileEntries());
70              }
71              entries.add(type);
72              return entries;
73          }
74  
75          /**
76           * Gets the cumulative length of all element values.
77           *
78           * @return the cumulative length of all element values.
79           */
80          public int getLength() {
81              int length = 4;
82              for (int i = 0; i < numPairs; i++) {
83                  length += 2;
84                  length += elementValues[i].getLength();
85              }
86              return length;
87          }
88  
89          /**
90           * Resolves this instance against a given pool.
91           *
92           * @param pool a class constant pool.
93           */
94          public void resolve(final ClassConstantPool pool) {
95              type.resolve(pool);
96              typeIndex = pool.indexOf(type);
97              nameIndexes = new int[numPairs];
98              for (int i = 0; i < elementNames.length; i++) {
99                  elementNames[i].resolve(pool);
100                 nameIndexes[i] = pool.indexOf(elementNames[i]);
101                 elementValues[i].resolve(pool);
102             }
103         }
104 
105         /**
106          * Writes this instance to the given output stream.
107          *
108          * @param dos the output stream.
109          * @throws IOException if an I/O error occurs.
110          */
111         public void writeBody(final DataOutputStream dos) throws IOException {
112             dos.writeShort(typeIndex);
113             dos.writeShort(numPairs);
114             for (int i = 0; i < numPairs; i++) {
115                 dos.writeShort(nameIndexes[i]);
116                 elementValues[i].writeBody(dos);
117             }
118         }
119     }
120 
121     /**
122      * Pairs a tag and value.
123      */
124     public static class ElementValue {
125 
126         private final Object value;
127         private final int tag;
128 
129         // resolved value index if it's a constant
130         private int constantValueIndex = -1;
131 
132         /**
133          * Constructs a new instance.
134          *
135          * @param tag a tag.
136          * @param value a value.
137          */
138         public ElementValue(final int tag, final Object value) {
139             this.tag = tag;
140             this.value = value;
141         }
142 
143         /**
144          * Gets a list of class file entries.
145          *
146          * @return a list of class file entries.
147          */
148         public List<Object> getClassFileEntries() {
149             final List<Object> entries = new ArrayList<>(1);
150             if (value instanceof CPNameAndType) {
151                 // used to represent enum, so don't include the actual CPNameAndType
152                 entries.add(((CPNameAndType) value).name);
153                 entries.add(((CPNameAndType) value).descriptor);
154             } else if (value instanceof ClassFileEntry) {
155                 // TODO? ClassFileEntry is an Object
156                 entries.add(value);
157             } else if (value instanceof ElementValue[]) {
158                 final ElementValue[] values = (ElementValue[]) value;
159                 for (final ElementValue value2 : values) {
160                     entries.addAll(value2.getClassFileEntries());
161                 }
162             } else if (value instanceof Annotation) {
163                 entries.addAll(((Annotation) value).getClassFileEntries());
164             }
165             return entries;
166         }
167 
168         /**
169          * Gets the length.
170          *
171          * @return the length.
172          */
173         public int getLength() {
174             switch (tag) {
175             case 'B':
176             case 'C':
177             case 'D':
178             case 'F':
179             case 'I':
180             case 'J':
181             case 'S':
182             case 'Z':
183             case 'c':
184             case 's':
185                 return 3;
186             case 'e':
187                 return 5;
188             case '[':
189                 int length = 3;
190                 final ElementValue[] nestedValues = (ElementValue[]) value;
191                 for (final ElementValue nestedValue : nestedValues) {
192                     length += nestedValue.getLength();
193                 }
194                 return length;
195             case '@':
196                 return 1 + ((Annotation) value).getLength();
197             }
198             return 0;
199         }
200 
201         /**
202          * Resolves this instance against a given pool.
203          *
204          * @param pool a class constant pool.
205          */
206         public void resolve(final ClassConstantPool pool) {
207             if (value instanceof CPConstant) {
208                 ((CPConstant) value).resolve(pool);
209                 constantValueIndex = pool.indexOf((CPConstant) value);
210             } else if (value instanceof CPClass) {
211                 ((CPClass) value).resolve(pool);
212                 constantValueIndex = pool.indexOf((CPClass) value);
213             } else if (value instanceof CPUTF8) {
214                 ((CPUTF8) value).resolve(pool);
215                 constantValueIndex = pool.indexOf((CPUTF8) value);
216             } else if (value instanceof CPNameAndType) {
217                 ((CPNameAndType) value).resolve(pool);
218             } else if (value instanceof Annotation) {
219                 ((Annotation) value).resolve(pool);
220             } else if (value instanceof ElementValue[]) {
221                 final ElementValue[] nestedValues = (ElementValue[]) value;
222                 for (final ElementValue nestedValue : nestedValues) {
223                     nestedValue.resolve(pool);
224                 }
225             }
226         }
227 
228         /**
229          * Writes this instance to the given output stream.
230          *
231          * @param dos the output stream.
232          * @throws IOException if an I/O error occurs.
233          */
234         public void writeBody(final DataOutputStream dos) throws IOException {
235             dos.writeByte(tag);
236             if (constantValueIndex != -1) {
237                 dos.writeShort(constantValueIndex);
238             } else if (value instanceof CPNameAndType) {
239                 ((CPNameAndType) value).writeBody(dos);
240             } else if (value instanceof Annotation) {
241                 ((Annotation) value).writeBody(dos);
242             } else if (value instanceof ElementValue[]) {
243                 final ElementValue[] nestedValues = (ElementValue[]) value;
244                 dos.writeShort(nestedValues.length);
245                 for (final ElementValue nestedValue : nestedValues) {
246                     nestedValue.writeBody(dos);
247                 }
248             } else {
249                 throw new Error("");
250             }
251         }
252     }
253 
254     /**
255      * Constructs a new instance for an attribute name.
256      *
257      * @param attributeName an attribute name.
258      */
259     public AnnotationsAttribute(final CPUTF8 attributeName) {
260         super(attributeName);
261     }
262 
263 }