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}