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