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.ByteArrayInputStream;
021import java.io.DataInput;
022import java.io.DataOutputStream;
023import java.io.IOException;
024
025import org.apache.bcel.Const;
026
027/**
028 * This class is derived from <em>Attribute</em> and represents a reference
029 * to a GJ attribute.
030 *
031 * @version $Id: Signature.java 1749603 2016-06-21 20:50:19Z ggregory $
032 * @see     Attribute
033 */
034public final class Signature extends Attribute {
035
036    private int signature_index;
037
038
039    /**
040     * Initialize from another object. Note that both objects use the same
041     * references (shallow copy). Use clone() for a physical copy.
042     */
043    public Signature(final Signature c) {
044        this(c.getNameIndex(), c.getLength(), c.getSignatureIndex(), c.getConstantPool());
045    }
046
047
048    /**
049     * Construct object from file stream.
050     * @param name_index Index in constant pool to CONSTANT_Utf8
051     * @param length Content length in bytes
052     * @param input Input stream
053     * @param constant_pool Array of constants
054     * @throws IOException
055     */
056    Signature(final int name_index, final int length, final DataInput input, final ConstantPool constant_pool)
057            throws IOException {
058        this(name_index, length, input.readUnsignedShort(), constant_pool);
059    }
060
061
062    /**
063     * @param name_index Index in constant pool to CONSTANT_Utf8
064     * @param length Content length in bytes
065     * @param signature_index Index in constant pool to CONSTANT_Utf8
066     * @param constant_pool Array of constants
067     */
068    public Signature(final int name_index, final int length, final int signature_index, final ConstantPool constant_pool) {
069        super(Const.ATTR_SIGNATURE, name_index, length, constant_pool);
070        this.signature_index = signature_index;
071    }
072
073
074    /**
075     * Called by objects that are traversing the nodes of the tree implicitely
076     * defined by the contents of a Java class. I.e., the hierarchy of methods,
077     * fields, attributes, etc. spawns a tree of objects.
078     *
079     * @param v Visitor object
080     */
081    @Override
082    public void accept( final Visitor v ) {
083        //System.err.println("Visiting non-standard Signature object");
084        v.visitSignature(this);
085    }
086
087
088    /**
089     * Dump source file attribute to file stream in binary format.
090     *
091     * @param file Output file stream
092     * @throws IOException
093     */
094    @Override
095    public final void dump( final DataOutputStream file ) throws IOException {
096        super.dump(file);
097        file.writeShort(signature_index);
098    }
099
100
101    /**
102     * @return Index in constant pool of source file name.
103     */
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     */
112    public final void setSignatureIndex( final int signature_index ) {
113        this.signature_index = signature_index;
114    }
115
116
117    /**
118     * @return GJ signature.
119     */
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
131        MyByteArrayInputStream(final String data) {
132            super(data.getBytes());
133        }
134
135
136        final String getData() {
137            return new String(buf);
138        }
139
140
141        final void unread() {
142            if (pos > 0) {
143                pos--;
144            }
145        }
146    }
147
148
149    private static boolean identStart( final int ch ) {
150        return ch == 'T' || ch == 'L';
151    }
152
153
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
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
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
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
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
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
267    public Attribute copy( final ConstantPool _constant_pool ) {
268        return (Attribute) clone();
269    }
270}