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}