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