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 num_pairs;
035        private final CPUTF8[] element_names;
036        private final ElementValue[] element_values;
037        private final CPUTF8 type;
038
039        // Resolved values
040        private int type_index;
041        private int[] name_indexes;
042
043        public Annotation(final int num_pairs, final CPUTF8 type, final CPUTF8[] element_names,
044            final ElementValue[] element_values) {
045            this.num_pairs = num_pairs;
046            this.type = type;
047            this.element_names = element_names;
048            this.element_values = element_values;
049        }
050
051        public int getLength() {
052            int length = 4;
053            for (int i = 0; i < num_pairs; i++) {
054                length += 2;
055                length += element_values[i].getLength();
056            }
057            return length;
058        }
059
060        public void resolve(final ClassConstantPool pool) {
061            type.resolve(pool);
062            type_index = pool.indexOf(type);
063            name_indexes = new int[num_pairs];
064            for (int i = 0; i < element_names.length; i++) {
065                element_names[i].resolve(pool);
066                name_indexes[i] = pool.indexOf(element_names[i]);
067                element_values[i].resolve(pool);
068            }
069        }
070
071        public void writeBody(final DataOutputStream dos) throws IOException {
072            dos.writeShort(type_index);
073            dos.writeShort(num_pairs);
074            for (int i = 0; i < num_pairs; i++) {
075                dos.writeShort(name_indexes[i]);
076                element_values[i].writeBody(dos);
077            }
078        }
079
080        public List<Object> getClassFileEntries() {
081            final List<Object> entries = new ArrayList<>();
082            for (int i = 0; i < element_names.length; i++) {
083                entries.add(element_names[i]);
084                entries.addAll(element_values[i].getClassFileEntries());
085            }
086            entries.add(type);
087            return entries;
088        }
089    }
090
091    public static class ElementValue {
092
093        private final Object value;
094        private final int tag;
095
096        // resolved value index if it's a constant
097        private int constant_value_index = -1;
098
099        public ElementValue(final int tag, final Object value) {
100            this.tag = tag;
101            this.value = value;
102        }
103
104        public List<Object> getClassFileEntries() {
105            final List<Object> entries = new ArrayList<>(1);
106            if (value instanceof CPNameAndType) {
107                // used to represent enum, so don't include the actual CPNameAndType
108                entries.add(((CPNameAndType) value).name);
109                entries.add(((CPNameAndType) value).descriptor);
110            } else if (value instanceof ClassFileEntry) {
111                // TODO? ClassFileEntry is an Object
112                entries.add(value);
113            } else if (value instanceof ElementValue[]) {
114                final ElementValue[] values = (ElementValue[]) value;
115                for (ElementValue value2 : values) {
116                    entries.addAll(value2.getClassFileEntries());
117                }
118            } else if (value instanceof Annotation) {
119                entries.addAll(((Annotation) value).getClassFileEntries());
120            }
121            return entries;
122        }
123
124        public void resolve(final ClassConstantPool pool) {
125            if (value instanceof CPConstant) {
126                ((CPConstant) value).resolve(pool);
127                constant_value_index = pool.indexOf((CPConstant) value);
128            } else if (value instanceof CPClass) {
129                ((CPClass) value).resolve(pool);
130                constant_value_index = pool.indexOf((CPClass) value);
131            } else if (value instanceof CPUTF8) {
132                ((CPUTF8) value).resolve(pool);
133                constant_value_index = pool.indexOf((CPUTF8) value);
134            } else if (value instanceof CPNameAndType) {
135                ((CPNameAndType) value).resolve(pool);
136            } else if (value instanceof Annotation) {
137                ((Annotation) value).resolve(pool);
138            } else if (value instanceof ElementValue[]) {
139                final ElementValue[] nestedValues = (ElementValue[]) value;
140                for (ElementValue nestedValue : nestedValues) {
141                    nestedValue.resolve(pool);
142                }
143            }
144        }
145
146        public void writeBody(final DataOutputStream dos) throws IOException {
147            dos.writeByte(tag);
148            if (constant_value_index != -1) {
149                dos.writeShort(constant_value_index);
150            } else if (value instanceof CPNameAndType) {
151                ((CPNameAndType) value).writeBody(dos);
152            } else if (value instanceof Annotation) {
153                ((Annotation) value).writeBody(dos);
154            } else if (value instanceof ElementValue[]) {
155                final ElementValue[] nestedValues = (ElementValue[]) value;
156                dos.writeShort(nestedValues.length);
157                for (ElementValue nestedValue : nestedValues) {
158                    nestedValue.writeBody(dos);
159                }
160            } else {
161                throw new Error("");
162            }
163        }
164
165        public int getLength() {
166            switch (tag) {
167            case 'B':
168            case 'C':
169            case 'D':
170            case 'F':
171            case 'I':
172            case 'J':
173            case 'S':
174            case 'Z':
175            case 'c':
176            case 's':
177                return 3;
178            case 'e':
179                return 5;
180            case '[':
181                int length = 3;
182                final ElementValue[] nestedValues = (ElementValue[]) value;
183                for (ElementValue nestedValue : nestedValues) {
184                    length += nestedValue.getLength();
185                }
186                return length;
187            case '@':
188                return (1 + ((Annotation) value).getLength());
189            }
190            return 0;
191        }
192    }
193
194    public AnnotationsAttribute(final CPUTF8 attributeName) {
195        super(attributeName);
196    }
197
198}