View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   *
17   */
18  package org.apache.bcel.classfile;
19  
20  import java.io.DataInput;
21  import java.io.DataInputStream;
22  import java.io.DataOutputStream;
23  import java.io.IOException;
24  import java.util.HashMap;
25  import java.util.Map;
26  
27  import org.apache.bcel.Const;
28  
29  /**
30   * Abstract super class for <em>Attribute</em> objects. Currently the
31   * <em>ConstantValue</em>, <em>SourceFile</em>, <em>Code</em>,
32   * <em>Exceptiontable</em>, <em>LineNumberTable</em>,
33   * <em>LocalVariableTable</em>, <em>InnerClasses</em> and
34   * <em>Synthetic</em> attributes are supported. The <em>Unknown</em>
35   * attribute stands for non-standard-attributes.
36   *
37   * @version $Id: Attribute.html 1018313 2017-09-18 09:03:04Z britter $
38   * @see ConstantValue
39   * @see SourceFile
40   * @see Code
41   * @see Unknown
42   * @see ExceptionTable
43   * @see LineNumberTable
44   * @see LocalVariableTable
45   * @see InnerClasses
46   * @see Synthetic
47   * @see Deprecated
48   * @see Signature
49   */
50  public abstract class Attribute implements Cloneable, Node {
51  
52      /**
53       * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
54       */
55      @java.lang.Deprecated
56      protected int name_index; // Points to attribute name in constant pool TODO make private (has getter & setter)
57  
58      /**
59       * @deprecated (since 6.0) (since 6.0) will be made private; do not access directly, use getter/setter
60       */
61      @java.lang.Deprecated
62      protected int length; // Content length of attribute field TODO make private (has getter & setter)
63  
64      /**
65       * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
66       */
67      @java.lang.Deprecated
68      protected byte tag; // Tag to distinguish subclasses TODO make private & final; supposed to be immutable
69  
70      /**
71       * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
72       */
73      @java.lang.Deprecated
74      protected ConstantPool constant_pool; // TODO make private (has getter & setter)
75  
76      protected Attribute(final byte tag, final int name_index, final int length, final ConstantPool constant_pool)
77      {
78          this.tag = tag;
79          this.name_index = name_index;
80          this.length = length;
81          this.constant_pool = constant_pool;
82      }
83  
84      /**
85       * Called by objects that are traversing the nodes of the tree implicitely
86       * defined by the contents of a Java class. I.e., the hierarchy of methods,
87       * fields, attributes, etc. spawns a tree of objects.
88       *
89       * @param v
90       *            Visitor object
91       */
92      @Override
93      public abstract void accept(Visitor v);
94  
95      /**
96       * Dump attribute to file stream in binary format.
97       *
98       * @param file
99       *            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 }