1/*
2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
3 */
4/*
5 * Licensed to the Apache Software Foundation (ASF) under one or more
6 * contributor license agreements.  See the NOTICE file distributed with
7 * this work for additional information regarding copyright ownership.
8 * The ASF licenses this file to You under the Apache License, Version 2.0
9 * (the "License"); you may not use this file except in compliance with
10 * the License.  You may obtain a copy of the License at
11 *
12 *      http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20package com.sun.org.apache.bcel.internal.classfile;
21
22import java.io.DataInput;
23import java.io.DataInputStream;
24import java.io.DataOutputStream;
25import java.io.IOException;
26import java.util.HashMap;
27import java.util.Map;
28
29import com.sun.org.apache.bcel.internal.Const;
30
31/**
32 * Abstract super class for <em>Attribute</em> objects. Currently the
33 * <em>ConstantValue</em>, <em>SourceFile</em>, <em>Code</em>,
34 * <em>Exceptiontable</em>, <em>LineNumberTable</em>,
35 * <em>LocalVariableTable</em>, <em>InnerClasses</em> and
36 * <em>Synthetic</em> attributes are supported. The <em>Unknown</em>
37 * attribute stands for non-standard-attributes.
38 *
39 * @version $Id: Attribute.java 1750029 2016-06-23 22:14:38Z sebb $
40 * @see ConstantValue
41 * @see SourceFile
42 * @see Code
43 * @see Unknown
44 * @see ExceptionTable
45 * @see LineNumberTable
46 * @see LocalVariableTable
47 * @see InnerClasses
48 * @see Synthetic
49 * @see Deprecated
50 * @see Signature
51 */
52public abstract class Attribute implements Cloneable, Node {
53
54    private int name_index; // Points to attribute name in constant pool
55    private int length; // Content length of attribute field
56    private final byte tag; // Tag to distinguish subclasses
57    private ConstantPool constant_pool;
58
59    protected Attribute(final byte tag, final int name_index, final int length, final ConstantPool constant_pool) {
60        this.tag = tag;
61        this.name_index = name_index;
62        this.length = length;
63        this.constant_pool = constant_pool;
64    }
65
66    /**
67     * Called by objects that are traversing the nodes of the tree implicitely
68     * defined by the contents of a Java class. I.e., the hierarchy of methods,
69     * fields, attributes, etc. spawns a tree of objects.
70     *
71     * @param v Visitor object
72     */
73    @Override
74    public abstract void accept(Visitor v);
75
76    /**
77     * Dump attribute to file stream in binary format.
78     *
79     * @param file Output file stream
80     * @throws IOException
81     */
82    public void dump(final DataOutputStream file) throws IOException {
83        file.writeShort(name_index);
84        file.writeInt(length);
85    }
86
87    private static final Map<String, Object> readers = new HashMap<>();
88
89    /**
90     * Add an Attribute reader capable of parsing (user-defined) attributes
91     * named "name". You should not add readers for the standard attributes such
92     * as "LineNumberTable", because those are handled internally.
93     *
94     * @param name the name of the attribute as stored in the class file
95     * @param r the reader object
96     */
97    public static void addAttributeReader(final String name, final UnknownAttributeReader r) {
98        readers.put(name, r);
99    }
100
101    /**
102     * Remove attribute reader
103     *
104     * @param name the name of the attribute as stored in the class file
105     */
106    public static void removeAttributeReader(final String name) {
107        readers.remove(name);
108    }
109
110    /**
111     * Class method reads one attribute from the input data stream. This method
112     * must not be accessible from the outside. It is called by the Field and
113     * Method constructor methods.
114     *
115     * @see Field
116     * @see Method
117     *
118     * @param file Input stream
119     * @param constant_pool Array of constants
120     * @return Attribute
121     * @throws IOException
122     * @throws ClassFormatException
123     */
124    public static Attribute readAttribute(final DataInputStream file, final ConstantPool constant_pool)
125            throws IOException, ClassFormatException {
126        return readAttribute((DataInput) file, constant_pool);
127    }
128
129    /**
130     * Class method reads one attribute from the input data stream. This method
131     * must not be accessible from the outside. It is called by the Field and
132     * Method constructor methods.
133     *
134     * @see Field
135     * @see Method
136     *
137     * @param file Input stream
138     * @param constant_pool Array of constants
139     * @return Attribute
140     * @throws IOException
141     * @throws ClassFormatException
142     * @since 6.0
143     */
144    public static Attribute readAttribute(final DataInput file, final ConstantPool constant_pool)
145            throws IOException, ClassFormatException {
146        byte tag = Const.ATTR_UNKNOWN; // Unknown attribute
147        // Get class name from constant pool via `name_index' indirection
148        final int name_index = file.readUnsignedShort();
149        final ConstantUtf8 c = (ConstantUtf8) constant_pool.getConstant(name_index, Const.CONSTANT_Utf8);
150        final String name = c.getBytes();
151
152        // Length of data in bytes
153        final int length = file.readInt();
154
155        // Compare strings to find known attribute
156        for (byte i = 0; i < Const.KNOWN_ATTRIBUTES; i++) {
157            if (name.equals(Const.getAttributeName(i))) {
158                tag = i; // found!
159                break;
160            }
161        }
162
163        // Call proper constructor, depending on `tag'
164        switch (tag) {
165            case Const.ATTR_UNKNOWN:
166                final Object r = readers.get(name);
167                if (r instanceof UnknownAttributeReader) {
168                    return ((UnknownAttributeReader) r).createAttribute(name_index, length, file, constant_pool);
169                }
170                return new Unknown(name_index, length, file, constant_pool);
171            case Const.ATTR_CONSTANT_VALUE:
172                return new ConstantValue(name_index, length, file, constant_pool);
173            case Const.ATTR_SOURCE_FILE:
174                return new SourceFile(name_index, length, file, constant_pool);
175            case Const.ATTR_CODE:
176                return new Code(name_index, length, file, constant_pool);
177            case Const.ATTR_EXCEPTIONS:
178                return new ExceptionTable(name_index, length, file, constant_pool);
179            case Const.ATTR_LINE_NUMBER_TABLE:
180                return new LineNumberTable(name_index, length, file, constant_pool);
181            case Const.ATTR_LOCAL_VARIABLE_TABLE:
182                return new LocalVariableTable(name_index, length, file, constant_pool);
183            case Const.ATTR_INNER_CLASSES:
184                return new InnerClasses(name_index, length, file, constant_pool);
185            case Const.ATTR_SYNTHETIC:
186                return new Synthetic(name_index, length, file, constant_pool);
187            case Const.ATTR_DEPRECATED:
188                return new Deprecated(name_index, length, file, constant_pool);
189            case Const.ATTR_PMG:
190                return new PMGClass(name_index, length, file, constant_pool);
191            case Const.ATTR_SIGNATURE:
192                return new Signature(name_index, length, file, constant_pool);
193            case Const.ATTR_STACK_MAP:
194                return new StackMap(name_index, length, file, constant_pool);
195            case Const.ATTR_RUNTIME_VISIBLE_ANNOTATIONS:
196                return new RuntimeVisibleAnnotations(name_index, length, file, constant_pool);
197            case Const.ATTR_RUNTIME_INVISIBLE_ANNOTATIONS:
198                return new RuntimeInvisibleAnnotations(name_index, length, file, constant_pool);
199            case Const.ATTR_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS:
200                return new RuntimeVisibleParameterAnnotations(name_index, length, file, constant_pool);
201            case Const.ATTR_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS:
202                return new RuntimeInvisibleParameterAnnotations(name_index, length, file, constant_pool);
203            case Const.ATTR_ANNOTATION_DEFAULT:
204                return new AnnotationDefault(name_index, length, file, constant_pool);
205            case Const.ATTR_LOCAL_VARIABLE_TYPE_TABLE:
206                return new LocalVariableTypeTable(name_index, length, file, constant_pool);
207            case Const.ATTR_ENCLOSING_METHOD:
208                return new EnclosingMethod(name_index, length, file, constant_pool);
209            case Const.ATTR_STACK_MAP_TABLE:
210                return new StackMap(name_index, length, file, constant_pool);
211            case Const.ATTR_BOOTSTRAP_METHODS:
212                return new BootstrapMethods(name_index, length, file, constant_pool);
213            case Const.ATTR_METHOD_PARAMETERS:
214                return new MethodParameters(name_index, length, file, constant_pool);
215            default:
216                // Never reached
217                throw new IllegalStateException("Unrecognized attribute type tag parsed: " + tag);
218        }
219    }
220
221    /**
222     * @return Name of attribute
223     * @since 6.0
224     */
225    public String getName() {
226        final ConstantUtf8 c = (ConstantUtf8) constant_pool.getConstant(name_index, Const.CONSTANT_Utf8);
227        return c.getBytes();
228    }
229
230    /**
231     * @return Length of attribute field in bytes.
232     */
233    public final int getLength() {
234        return length;
235    }
236
237    /**
238     * @param length length in bytes.
239     */
240    public final void setLength(final int length) {
241        this.length = length;
242    }
243
244    /**
245     * @param name_index of attribute.
246     */
247    public final void setNameIndex(final int name_index) {
248        this.name_index = name_index;
249    }
250
251    /**
252     * @return Name index in constant pool of attribute name.
253     */
254    public final int getNameIndex() {
255        return name_index;
256    }
257
258    /**
259     * @return Tag of attribute, i.e., its type. Value may not be altered, thus
260     * there is no setTag() method.
261     */
262    public final byte getTag() {
263        return tag;
264    }
265
266    /**
267     * @return Constant pool used by this object.
268     * @see ConstantPool
269     */
270    public final ConstantPool getConstantPool() {
271        return constant_pool;
272    }
273
274    /**
275     * @param constant_pool Constant pool to be used for this object.
276     * @see ConstantPool
277     */
278    public final void setConstantPool(final ConstantPool constant_pool) {
279        this.constant_pool = constant_pool;
280    }
281
282    /**
283     * Use copy() if you want to have a deep copy(), i.e., with all references
284     * copied correctly.
285     *
286     * @return shallow copy of this attribute
287     */
288    @Override
289    public Object clone() {
290        Attribute attr = null;
291        try {
292            attr = (Attribute) super.clone();
293        } catch (final CloneNotSupportedException e) {
294            throw new Error("Clone Not Supported"); // never happens
295        }
296        return attr;
297    }
298
299    /**
300     * @return deep copy of this attribute
301     */
302    public abstract Attribute copy(ConstantPool _constant_pool);
303
304    /**
305     * @return attribute name.
306     */
307    @Override
308    public String toString() {
309        return Const.getAttributeName(tag);
310    }
311}
312