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