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.bcel.classfile;
018
019import java.io.DataInput;
020import java.io.DataInputStream;
021import java.io.DataOutputStream;
022import java.io.IOException;
023import java.util.HashMap;
024import java.util.Map;
025
026import org.apache.bcel.Const;
027
028/**
029 * Abstract super class for <em>Attribute</em> objects. Currently the <em>ConstantValue</em>, <em>SourceFile</em>,
030 * <em>Code</em>, <em>Exceptiontable</em>, <em>LineNumberTable</em>, <em>LocalVariableTable</em>, <em>InnerClasses</em>
031 * and <em>Synthetic</em> attributes are supported. The <em>Unknown</em> attribute stands for non-standard-attributes.
032 *
033 * @see ConstantValue
034 * @see SourceFile
035 * @see Code
036 * @see Unknown
037 * @see ExceptionTable
038 * @see LineNumberTable
039 * @see LocalVariableTable
040 * @see InnerClasses
041 * @see Synthetic
042 * @see Deprecated
043 * @see Signature
044 */
045public abstract class Attribute implements Cloneable, Node {
046
047    private static final boolean debug = Boolean.getBoolean(Attribute.class.getCanonicalName() + ".debug"); // Debugging on/off
048
049    private static final Map<String, Object> READERS = new HashMap<>();
050
051    /**
052     * Empty array.
053     *
054     * @since 6.6.0
055     */
056    public static final Attribute[] EMPTY_ARRAY = {};
057
058    /**
059     * Add an Attribute reader capable of parsing (user-defined) attributes named "name". You should not add readers for the
060     * standard attributes such as "LineNumberTable", because those are handled internally.
061     *
062     * @param name the name of the attribute as stored in the class file
063     * @param attributeReader the reader object
064     * @deprecated (6.0) Use {@link #addAttributeReader(String, UnknownAttributeReader)} instead
065     */
066    @java.lang.Deprecated
067    public static void addAttributeReader(final String name, final AttributeReader attributeReader) {
068        READERS.put(name, attributeReader);
069    }
070
071    /**
072     * Add an Attribute reader capable of parsing (user-defined) attributes named "name". You should not add readers for the
073     * standard attributes such as "LineNumberTable", because those are handled internally.
074     *
075     * @param name the name of the attribute as stored in the class file
076     * @param unknownAttributeReader the reader object
077     */
078    public static void addAttributeReader(final String name, final UnknownAttributeReader unknownAttributeReader) {
079        READERS.put(name, unknownAttributeReader);
080    }
081
082    protected static void println(final String msg) {
083        if (debug) {
084            System.err.println(msg);
085        }
086    }
087
088    /**
089     * Class method reads one attribute from the input data stream. This method must not be accessible from the outside. It
090     * is called by the Field and Method constructor methods.
091     *
092     * @see Field
093     * @see Method
094     *
095     * @param file Input stream
096     * @param constantPool Array of constants
097     * @return Attribute
098     * @throws IOException if an I/O error occurs.
099     * @since 6.0
100     */
101    public static Attribute readAttribute(final DataInput file, final ConstantPool constantPool) throws IOException {
102        byte tag = Const.ATTR_UNKNOWN; // Unknown attribute
103        // Get class name from constant pool via `name_index' indirection
104        final int nameIndex = file.readUnsignedShort();
105        final String name = constantPool.getConstantUtf8(nameIndex).getBytes();
106
107        // Length of data in bytes
108        final int length = file.readInt();
109
110        // Compare strings to find known attribute
111        for (byte i = 0; i < Const.KNOWN_ATTRIBUTES; i++) {
112            if (name.equals(Const.getAttributeName(i))) {
113                tag = i; // found!
114                break;
115            }
116        }
117
118        // Call proper constructor, depending on `tag'
119        switch (tag) {
120        case Const.ATTR_UNKNOWN:
121            final Object r = READERS.get(name);
122            if (r instanceof UnknownAttributeReader) {
123                return ((UnknownAttributeReader) r).createAttribute(nameIndex, length, file, constantPool);
124            }
125            return new Unknown(nameIndex, length, file, constantPool);
126        case Const.ATTR_CONSTANT_VALUE:
127            return new ConstantValue(nameIndex, length, file, constantPool);
128        case Const.ATTR_SOURCE_FILE:
129            return new SourceFile(nameIndex, length, file, constantPool);
130        case Const.ATTR_CODE:
131            return new Code(nameIndex, length, file, constantPool);
132        case Const.ATTR_EXCEPTIONS:
133            return new ExceptionTable(nameIndex, length, file, constantPool);
134        case Const.ATTR_LINE_NUMBER_TABLE:
135            return new LineNumberTable(nameIndex, length, file, constantPool);
136        case Const.ATTR_LOCAL_VARIABLE_TABLE:
137            return new LocalVariableTable(nameIndex, length, file, constantPool);
138        case Const.ATTR_INNER_CLASSES:
139            return new InnerClasses(nameIndex, length, file, constantPool);
140        case Const.ATTR_SYNTHETIC:
141            return new Synthetic(nameIndex, length, file, constantPool);
142        case Const.ATTR_DEPRECATED:
143            return new Deprecated(nameIndex, length, file, constantPool);
144        case Const.ATTR_PMG:
145            return new PMGClass(nameIndex, length, file, constantPool);
146        case Const.ATTR_SIGNATURE:
147            return new Signature(nameIndex, length, file, constantPool);
148        case Const.ATTR_STACK_MAP:
149            // old style stack map: unneeded for JDK5 and below;
150            // illegal(?) for JDK6 and above. So just delete with a warning.
151            println("Warning: Obsolete StackMap attribute ignored.");
152            return new Unknown(nameIndex, length, file, constantPool);
153        case Const.ATTR_RUNTIME_VISIBLE_ANNOTATIONS:
154            return new RuntimeVisibleAnnotations(nameIndex, length, file, constantPool);
155        case Const.ATTR_RUNTIME_INVISIBLE_ANNOTATIONS:
156            return new RuntimeInvisibleAnnotations(nameIndex, length, file, constantPool);
157        case Const.ATTR_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS:
158            return new RuntimeVisibleParameterAnnotations(nameIndex, length, file, constantPool);
159        case Const.ATTR_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS:
160            return new RuntimeInvisibleParameterAnnotations(nameIndex, length, file, constantPool);
161        case Const.ATTR_ANNOTATION_DEFAULT:
162            return new AnnotationDefault(nameIndex, length, file, constantPool);
163        case Const.ATTR_LOCAL_VARIABLE_TYPE_TABLE:
164            return new LocalVariableTypeTable(nameIndex, length, file, constantPool);
165        case Const.ATTR_ENCLOSING_METHOD:
166            return new EnclosingMethod(nameIndex, length, file, constantPool);
167        case Const.ATTR_STACK_MAP_TABLE:
168            // read new style stack map: StackMapTable. The rest of the code
169            // calls this a StackMap for historical reasons.
170            return new StackMap(nameIndex, length, file, constantPool);
171        case Const.ATTR_BOOTSTRAP_METHODS:
172            return new BootstrapMethods(nameIndex, length, file, constantPool);
173        case Const.ATTR_METHOD_PARAMETERS:
174            return new MethodParameters(nameIndex, length, file, constantPool);
175        case Const.ATTR_MODULE:
176            return new Module(nameIndex, length, file, constantPool);
177        case Const.ATTR_MODULE_PACKAGES:
178            return new ModulePackages(nameIndex, length, file, constantPool);
179        case Const.ATTR_MODULE_MAIN_CLASS:
180            return new ModuleMainClass(nameIndex, length, file, constantPool);
181        case Const.ATTR_NEST_HOST:
182            return new NestHost(nameIndex, length, file, constantPool);
183        case Const.ATTR_NEST_MEMBERS:
184            return new NestMembers(nameIndex, length, file, constantPool);
185        default:
186            // Never reached
187            throw new IllegalStateException("Unrecognized attribute type tag parsed: " + tag);
188        }
189    }
190
191    /**
192     * Class method reads one attribute from the input data stream. This method must not be accessible from the outside. It
193     * is called by the Field and Method constructor methods.
194     *
195     * @see Field
196     * @see Method
197     *
198     * @param file Input stream
199     * @param constantPool Array of constants
200     * @return Attribute
201     * @throws IOException if an I/O error occurs.
202     */
203    public static Attribute readAttribute(final DataInputStream file, final ConstantPool constantPool) throws IOException {
204        return readAttribute((DataInput) file, constantPool);
205    }
206
207    /**
208     * Remove attribute reader
209     *
210     * @param name the name of the attribute as stored in the class file
211     */
212    public static void removeAttributeReader(final String name) {
213        READERS.remove(name);
214    }
215
216    /**
217     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
218     */
219    @java.lang.Deprecated
220    protected int name_index; // Points to attribute name in constant pool TODO make private (has getter & setter)
221
222    /**
223     * @deprecated (since 6.0) (since 6.0) will be made private; do not access directly, use getter/setter
224     */
225    @java.lang.Deprecated
226    protected int length; // Content length of attribute field TODO make private (has getter & setter)
227
228    /**
229     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
230     */
231    @java.lang.Deprecated
232    protected byte tag; // Tag to distinguish subclasses TODO make private & final; supposed to be immutable
233
234    /**
235     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
236     */
237    @java.lang.Deprecated
238    protected ConstantPool constant_pool; // TODO make private (has getter & setter)
239
240    protected Attribute(final byte tag, final int nameIndex, final int length, final ConstantPool constantPool) {
241        this.tag = tag;
242        this.name_index = nameIndex;
243        this.length = length;
244        this.constant_pool = constantPool;
245    }
246
247    /**
248     * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
249     * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
250     *
251     * @param v Visitor object
252     */
253    @Override
254    public abstract void accept(Visitor v);
255
256    /**
257     * Use copy() if you want to have a deep copy(), i.e., with all references copied correctly.
258     *
259     * @return shallow copy of this attribute
260     */
261    @Override
262    public Object clone() {
263        Attribute attr = null;
264        try {
265            attr = (Attribute) super.clone();
266        } catch (final CloneNotSupportedException e) {
267            throw new Error("Clone Not Supported"); // never happens
268        }
269        return attr;
270    }
271
272    /**
273     * @return deep copy of this attribute
274     */
275    public abstract Attribute copy(ConstantPool constantPool);
276
277    /**
278     * Dump attribute to file stream in binary format.
279     *
280     * @param file Output file stream
281     * @throws IOException if an I/O error occurs.
282     */
283    public void dump(final DataOutputStream file) throws IOException {
284        file.writeShort(name_index);
285        file.writeInt(length);
286    }
287
288    /**
289     * @return Constant pool used by this object.
290     * @see ConstantPool
291     */
292    public final ConstantPool getConstantPool() {
293        return constant_pool;
294    }
295
296    /**
297     * @return Length of attribute field in bytes.
298     */
299    public final int getLength() {
300        return length;
301    }
302
303    /**
304     * @return Name of attribute
305     * @since 6.0
306     */
307    public String getName() {
308        return constant_pool.getConstantUtf8(name_index).getBytes();
309    }
310
311    /**
312     * @return Name index in constant pool of attribute name.
313     */
314    public final int getNameIndex() {
315        return name_index;
316    }
317
318    /**
319     * @return Tag of attribute, i.e., its type. Value may not be altered, thus there is no setTag() method.
320     */
321    public final byte getTag() {
322        return tag;
323    }
324
325    /**
326     * @param constantPool Constant pool to be used for this object.
327     * @see ConstantPool
328     */
329    public final void setConstantPool(final ConstantPool constantPool) {
330        this.constant_pool = constantPool;
331    }
332
333    /**
334     * @param length length in bytes.
335     */
336    public final void setLength(final int length) {
337        this.length = length;
338    }
339
340    /**
341     * @param nameIndex of attribute.
342     */
343    public final void setNameIndex(final int nameIndex) {
344        this.name_index = nameIndex;
345    }
346
347    /**
348     * @return attribute name.
349     */
350    @Override
351    public String toString() {
352        return Const.getAttributeName(tag);
353    }
354}