    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}