001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.compress.harmony.unpack200.bytecode; 018 019import java.io.DataOutputStream; 020import java.io.IOException; 021import java.util.ArrayList; 022import java.util.List; 023 024/** 025 * Abstract superclass for Annotations attributes 026 */ 027public abstract class AnnotationsAttribute extends Attribute { 028 029 /** 030 * Class to represent the annotation structure for class file attributes 031 */ 032 public static class Annotation { 033 034 private final int numPairs; 035 private final CPUTF8[] elementNames; 036 private final ElementValue[] elementValues; 037 private final CPUTF8 type; 038 039 // Resolved values 040 private int typeIndex; 041 private int[] nameIndexes; 042 043 public Annotation(final int numPairs, final CPUTF8 type, final CPUTF8[] elementNames, final ElementValue[] elementValues) { 044 this.numPairs = numPairs; 045 this.type = type; 046 this.elementNames = elementNames; 047 this.elementValues = elementValues; 048 } 049 050 public List<Object> getClassFileEntries() { 051 final List<Object> entries = new ArrayList<>(); 052 for (int i = 0; i < elementNames.length; i++) { 053 entries.add(elementNames[i]); 054 entries.addAll(elementValues[i].getClassFileEntries()); 055 } 056 entries.add(type); 057 return entries; 058 } 059 060 public int getLength() { 061 int length = 4; 062 for (int i = 0; i < numPairs; i++) { 063 length += 2; 064 length += elementValues[i].getLength(); 065 } 066 return length; 067 } 068 069 public void resolve(final ClassConstantPool pool) { 070 type.resolve(pool); 071 typeIndex = pool.indexOf(type); 072 nameIndexes = new int[numPairs]; 073 for (int i = 0; i < elementNames.length; i++) { 074 elementNames[i].resolve(pool); 075 nameIndexes[i] = pool.indexOf(elementNames[i]); 076 elementValues[i].resolve(pool); 077 } 078 } 079 080 public void writeBody(final DataOutputStream dos) throws IOException { 081 dos.writeShort(typeIndex); 082 dos.writeShort(numPairs); 083 for (int i = 0; i < numPairs; i++) { 084 dos.writeShort(nameIndexes[i]); 085 elementValues[i].writeBody(dos); 086 } 087 } 088 } 089 090 public static class ElementValue { 091 092 private final Object value; 093 private final int tag; 094 095 // resolved value index if it's a constant 096 private int constantValueIndex = -1; 097 098 public ElementValue(final int tag, final Object value) { 099 this.tag = tag; 100 this.value = value; 101 } 102 103 public List<Object> getClassFileEntries() { 104 final List<Object> entries = new ArrayList<>(1); 105 if (value instanceof CPNameAndType) { 106 // used to represent enum, so don't include the actual CPNameAndType 107 entries.add(((CPNameAndType) value).name); 108 entries.add(((CPNameAndType) value).descriptor); 109 } else if (value instanceof ClassFileEntry) { 110 // TODO? ClassFileEntry is an Object 111 entries.add(value); 112 } else if (value instanceof ElementValue[]) { 113 final ElementValue[] values = (ElementValue[]) value; 114 for (final ElementValue value2 : values) { 115 entries.addAll(value2.getClassFileEntries()); 116 } 117 } else if (value instanceof Annotation) { 118 entries.addAll(((Annotation) value).getClassFileEntries()); 119 } 120 return entries; 121 } 122 123 public int getLength() { 124 switch (tag) { 125 case 'B': 126 case 'C': 127 case 'D': 128 case 'F': 129 case 'I': 130 case 'J': 131 case 'S': 132 case 'Z': 133 case 'c': 134 case 's': 135 return 3; 136 case 'e': 137 return 5; 138 case '[': 139 int length = 3; 140 final ElementValue[] nestedValues = (ElementValue[]) value; 141 for (final ElementValue nestedValue : nestedValues) { 142 length += nestedValue.getLength(); 143 } 144 return length; 145 case '@': 146 return 1 + ((Annotation) value).getLength(); 147 } 148 return 0; 149 } 150 151 public void resolve(final ClassConstantPool pool) { 152 if (value instanceof CPConstant) { 153 ((CPConstant) value).resolve(pool); 154 constantValueIndex = pool.indexOf((CPConstant) value); 155 } else if (value instanceof CPClass) { 156 ((CPClass) value).resolve(pool); 157 constantValueIndex = pool.indexOf((CPClass) value); 158 } else if (value instanceof CPUTF8) { 159 ((CPUTF8) value).resolve(pool); 160 constantValueIndex = pool.indexOf((CPUTF8) value); 161 } else if (value instanceof CPNameAndType) { 162 ((CPNameAndType) value).resolve(pool); 163 } else if (value instanceof Annotation) { 164 ((Annotation) value).resolve(pool); 165 } else if (value instanceof ElementValue[]) { 166 final ElementValue[] nestedValues = (ElementValue[]) value; 167 for (final ElementValue nestedValue : nestedValues) { 168 nestedValue.resolve(pool); 169 } 170 } 171 } 172 173 public void writeBody(final DataOutputStream dos) throws IOException { 174 dos.writeByte(tag); 175 if (constantValueIndex != -1) { 176 dos.writeShort(constantValueIndex); 177 } else if (value instanceof CPNameAndType) { 178 ((CPNameAndType) value).writeBody(dos); 179 } else if (value instanceof Annotation) { 180 ((Annotation) value).writeBody(dos); 181 } else if (value instanceof ElementValue[]) { 182 final ElementValue[] nestedValues = (ElementValue[]) value; 183 dos.writeShort(nestedValues.length); 184 for (final ElementValue nestedValue : nestedValues) { 185 nestedValue.writeBody(dos); 186 } 187 } else { 188 throw new Error(""); 189 } 190 } 191 } 192 193 public AnnotationsAttribute(final CPUTF8 attributeName) { 194 super(attributeName); 195 } 196 197}