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.ByteArrayInputStream;
21 import java.io.DataInput;
22 import java.io.DataOutputStream;
23 import java.io.IOException;
24 
25 import org.apache.bcel.Const;
26 
27 /**
28  * This class is derived from <em>Attribute</em> and represents a reference
29  * to a GJ attribute.
30  *
31  * @version $Id$
32  * @see     Attribute
33  */
34 public final class Signature extends Attribute {
35 
36     private int signature_index;
37 
38 
39     /**
40      * Initialize from another object. Note that both objects use the same
41      * references (shallow copy). Use clone() for a physical copy.
42      */
Signature(final Signature c)43     public Signature(final Signature c) {
44         this(c.getNameIndex(), c.getLength(), c.getSignatureIndex(), c.getConstantPool());
45     }
46 
47 
48     /**
49      * Construct object from file stream.
50      * @param name_index Index in constant pool to CONSTANT_Utf8
51      * @param length Content length in bytes
52      * @param input Input stream
53      * @param constant_pool Array of constants
54      * @throws IOException
55      */
Signature(final int name_index, final int length, final DataInput input, final ConstantPool constant_pool)56     Signature(final int name_index, final int length, final DataInput input, final ConstantPool constant_pool)
57             throws IOException {
58         this(name_index, length, input.readUnsignedShort(), constant_pool);
59     }
60 
61 
62     /**
63      * @param name_index Index in constant pool to CONSTANT_Utf8
64      * @param length Content length in bytes
65      * @param signature_index Index in constant pool to CONSTANT_Utf8
66      * @param constant_pool Array of constants
67      */
Signature(final int name_index, final int length, final int signature_index, final ConstantPool constant_pool)68     public Signature(final int name_index, final int length, final int signature_index, final ConstantPool constant_pool) {
69         super(Const.ATTR_SIGNATURE, name_index, length, constant_pool);
70         this.signature_index = signature_index;
71     }
72 
73 
74     /**
75      * Called by objects that are traversing the nodes of the tree implicitely
76      * defined by the contents of a Java class. I.e., the hierarchy of methods,
77      * fields, attributes, etc. spawns a tree of objects.
78      *
79      * @param v Visitor object
80      */
81     @Override
accept( final Visitor v )82     public void accept( final Visitor v ) {
83         //System.err.println("Visiting non-standard Signature object");
84         v.visitSignature(this);
85     }
86 
87 
88     /**
89      * Dump source file attribute to file stream in binary format.
90      *
91      * @param file Output file stream
92      * @throws IOException
93      */
94     @Override
dump( final DataOutputStream file )95     public final void dump( final DataOutputStream file ) throws IOException {
96         super.dump(file);
97         file.writeShort(signature_index);
98     }
99 
100 
101     /**
102      * @return Index in constant pool of source file name.
103      */
getSignatureIndex()104     public final int getSignatureIndex() {
105         return signature_index;
106     }
107 
108 
109     /**
110      * @param signature_index the index info the constant pool of this signature
111      */
setSignatureIndex( final int signature_index )112     public final void setSignatureIndex( final int signature_index ) {
113         this.signature_index = signature_index;
114     }
115 
116 
117     /**
118      * @return GJ signature.
119      */
getSignature()120     public final String getSignature() {
121         final ConstantUtf8 c = (ConstantUtf8) super.getConstantPool().getConstant(signature_index,
122                 Const.CONSTANT_Utf8);
123         return c.getBytes();
124     }
125 
126     /**
127      * Extends ByteArrayInputStream to make 'unreading' chars possible.
128      */
129     private static final class MyByteArrayInputStream extends ByteArrayInputStream {
130 
MyByteArrayInputStream(final String data)131         MyByteArrayInputStream(final String data) {
132             super(data.getBytes());
133         }
134 
135 
getData()136         final String getData() {
137             return new String(buf);
138         }
139 
140 
unread()141         final void unread() {
142             if (pos > 0) {
143                 pos--;
144             }
145         }
146     }
147 
148 
identStart( final int ch )149     private static boolean identStart( final int ch ) {
150         return ch == 'T' || ch == 'L';
151     }
152 
153 
matchIdent( final MyByteArrayInputStream in, final StringBuilder buf )154     private static void matchIdent( final MyByteArrayInputStream in, final StringBuilder buf ) {
155         int ch;
156         if ((ch = in.read()) == -1) {
157             throw new RuntimeException("Illegal signature: " + in.getData()
158                     + " no ident, reaching EOF");
159         }
160         //System.out.println("return from ident:" + (char)ch);
161         if (!identStart(ch)) {
162             final StringBuilder buf2 = new StringBuilder();
163             int count = 1;
164             while (Character.isJavaIdentifierPart((char) ch)) {
165                 buf2.append((char) ch);
166                 count++;
167                 ch = in.read();
168             }
169             if (ch == ':') { // Ok, formal parameter
170                 in.skip("Ljava/lang/Object".length());
171                 buf.append(buf2);
172                 ch = in.read();
173                 in.unread();
174                 //System.out.println("so far:" + buf2 + ":next:" +(char)ch);
175             } else {
176                 for (int i = 0; i < count; i++) {
177                     in.unread();
178                 }
179             }
180             return;
181         }
182         final StringBuilder buf2 = new StringBuilder();
183         ch = in.read();
184         do {
185             buf2.append((char) ch);
186             ch = in.read();
187             //System.out.println("within ident:"+ (char)ch);
188         } while ((ch != -1) && (Character.isJavaIdentifierPart((char) ch) || (ch == '/')));
189         buf.append(buf2.toString().replace('/', '.'));
190         //System.out.println("regular return ident:"+ (char)ch + ":" + buf2);
191         if (ch != -1) {
192             in.unread();
193         }
194     }
195 
196 
matchGJIdent( final MyByteArrayInputStream in, final StringBuilder buf )197     private static void matchGJIdent( final MyByteArrayInputStream in, final StringBuilder buf ) {
198         int ch;
199         matchIdent(in, buf);
200         ch = in.read();
201         if ((ch == '<') || ch == '(') { // Parameterized or method
202             //System.out.println("Enter <");
203             buf.append((char) ch);
204             matchGJIdent(in, buf);
205             while (((ch = in.read()) != '>') && (ch != ')')) { // List of parameters
206                 if (ch == -1) {
207                     throw new RuntimeException("Illegal signature: " + in.getData()
208                             + " reaching EOF");
209                 }
210                 //System.out.println("Still no >");
211                 buf.append(", ");
212                 in.unread();
213                 matchGJIdent(in, buf); // Recursive call
214             }
215             //System.out.println("Exit >");
216             buf.append((char) ch);
217         } else {
218             in.unread();
219         }
220         ch = in.read();
221         if (identStart(ch)) {
222             in.unread();
223             matchGJIdent(in, buf);
224         } else if (ch == ')') {
225             in.unread();
226             return;
227         } else if (ch != ';') {
228             throw new RuntimeException("Illegal signature: " + in.getData() + " read " + (char) ch);
229         }
230     }
231 
232 
translate( final String s )233     public static String translate( final String s ) {
234         //System.out.println("Sig:" + s);
235         final StringBuilder buf = new StringBuilder();
236         matchGJIdent(new MyByteArrayInputStream(s), buf);
237         return buf.toString();
238     }
239 
240 
241     // @since 6.0 is no longer final
isFormalParameterList( final String s )242     public static boolean isFormalParameterList( final String s ) {
243         return s.startsWith("<") && (s.indexOf(':') > 0);
244     }
245 
246 
247     // @since 6.0 is no longer final
isActualParameterList( final String s )248     public static boolean isActualParameterList( final String s ) {
249         return s.startsWith("L") && s.endsWith(">;");
250     }
251 
252 
253     /**
254      * @return String representation
255      */
256     @Override
toString()257     public final String toString() {
258         final String s = getSignature();
259         return "Signature: " + s;
260     }
261 
262 
263     /**
264      * @return deep copy of this attribute
265      */
266     @Override
copy( final ConstantPool _constant_pool )267     public Attribute copy( final ConstantPool _constant_pool ) {
268         return (Attribute) clone();
269     }
270 }
271