    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}