001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.commons.compress.harmony.unpack200.bytecode; 020 021import java.io.DataOutputStream; 022import java.io.IOException; 023import java.util.ArrayList; 024import java.util.List; 025 026/** 027 * Abstracts Annotations attributes. 028 */ 029public abstract class AnnotationsAttribute extends Attribute { 030 031 /** 032 * Represents the annotation structure for class file attributes. 033 */ 034 public static class Annotation { 035 036 private final int numPairs; 037 private final CPUTF8[] elementNames; 038 private final ElementValue[] elementValues; 039 private final CPUTF8 type; 040 041 // Resolved values 042 private int typeIndex; 043 private int[] nameIndexes; 044 045 /** 046 * Constructs a new instance. 047 * 048 * @param numPairs Number of pairs, matches the lengths of {@code elementNames} and {@code elementValues}. 049 * @param type Type. 050 * @param elementNames Element names. 051 * @param elementValues Element values. 052 */ 053 public Annotation(final int numPairs, final CPUTF8 type, final CPUTF8[] elementNames, final ElementValue[] elementValues) { 054 this.numPairs = numPairs; 055 this.type = type; 056 this.elementNames = elementNames; 057 this.elementValues = elementValues; 058 } 059 060 /** 061 * Gets a list of class file entries. 062 * 063 * @return a list of class file entries. 064 */ 065 public List<Object> getClassFileEntries() { 066 final List<Object> entries = new ArrayList<>(); 067 for (int i = 0; i < elementNames.length; i++) { 068 entries.add(elementNames[i]); 069 entries.addAll(elementValues[i].getClassFileEntries()); 070 } 071 entries.add(type); 072 return entries; 073 } 074 075 /** 076 * Gets the cumulative length of all element values. 077 * 078 * @return the cumulative length of all element values. 079 */ 080 public int getLength() { 081 int length = 4; 082 for (int i = 0; i < numPairs; i++) { 083 length += 2; 084 length += elementValues[i].getLength(); 085 } 086 return length; 087 } 088 089 /** 090 * Resolves this instance against a given pool. 091 * 092 * @param pool a class constant pool. 093 */ 094 public void resolve(final ClassConstantPool pool) { 095 type.resolve(pool); 096 typeIndex = pool.indexOf(type); 097 nameIndexes = new int[numPairs]; 098 for (int i = 0; i < elementNames.length; i++) { 099 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}