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     */
018    package org.apache.bcel.classfile;
019    
020    import java.io.ByteArrayInputStream;
021    import java.io.DataInput;
022    import java.io.DataOutputStream;
023    import java.io.IOException;
024    
025    import org.apache.bcel.Constants;
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 1152077 2011-07-29 02:29:42Z dbrosius $
032     * @author  <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
033     * @see     Attribute
034     */
035    public final class Signature extends Attribute {
036    
037        private static final long serialVersionUID = 5808807822688827177L;
038        private int signature_index;
039    
040    
041        /**
042         * Initialize from another object. Note that both objects use the same
043         * references (shallow copy). Use clone() for a physical copy.
044         */
045        public Signature(Signature c) {
046            this(c.getNameIndex(), c.getLength(), c.getSignatureIndex(), c.getConstantPool());
047        }
048    
049    
050        /**
051         * Construct object from file stream.
052         * @param name_index Index in constant pool to CONSTANT_Utf8
053         * @param length Content length in bytes
054         * @param file Input stream
055         * @param constant_pool Array of constants
056         * @throws IOException
057         */
058        Signature(int name_index, int length, DataInput file, ConstantPool constant_pool)
059                throws IOException {
060            this(name_index, length, file.readUnsignedShort(), constant_pool);
061        }
062    
063    
064        /**
065         * @param name_index Index in constant pool to CONSTANT_Utf8
066         * @param length Content length in bytes
067         * @param signature_index Index in constant pool to CONSTANT_Utf8
068         * @param constant_pool Array of constants
069         */
070        public Signature(int name_index, int length, int signature_index, ConstantPool constant_pool) {
071            super(Constants.ATTR_SIGNATURE, name_index, length, constant_pool);
072            this.signature_index = signature_index;
073        }
074    
075    
076        /**
077         * Called by objects that are traversing the nodes of the tree implicitely
078         * defined by the contents of a Java class. I.e., the hierarchy of methods,
079         * fields, attributes, etc. spawns a tree of objects.
080         *
081         * @param v Visitor object
082         */
083        @Override
084        public void accept( Visitor v ) {
085            //System.err.println("Visiting non-standard Signature object");
086            v.visitSignature(this);
087        }
088    
089    
090        /**
091         * Dump source file attribute to file stream in binary format.
092         *
093         * @param file Output file stream
094         * @throws IOException
095         */
096        @Override
097        public final void dump( DataOutputStream file ) throws IOException {
098            super.dump(file);
099            file.writeShort(signature_index);
100        }
101    
102    
103        /**
104         * @return Index in constant pool of source file name.
105         */
106        public final int getSignatureIndex() {
107            return signature_index;
108        }
109    
110    
111        /**
112         * @param signature_index the index info the constant pool of this signature
113         */
114        public final void setSignatureIndex( int signature_index ) {
115            this.signature_index = signature_index;
116        }
117    
118    
119        /**
120         * @return GJ signature.
121         */
122        public final String getSignature() {
123            ConstantUtf8 c = (ConstantUtf8) constant_pool.getConstant(signature_index,
124                    Constants.CONSTANT_Utf8);
125            return c.getBytes();
126        }
127    
128        /**
129         * Extends ByteArrayInputStream to make 'unreading' chars possible.
130         */
131        private static final class MyByteArrayInputStream extends ByteArrayInputStream {
132    
133            MyByteArrayInputStream(String data) {
134                super(data.getBytes());
135            }
136    
137    
138            final String getData() {
139                return new String(buf);
140            }
141    
142    
143            final void unread() {
144                if (pos > 0) {
145                    pos--;
146                }
147            }
148        }
149    
150    
151        private static boolean identStart( int ch ) {
152            return ch == 'T' || ch == 'L';
153        }
154    
155    
156        private static final void matchIdent( MyByteArrayInputStream in, StringBuffer buf ) {
157            int ch;
158            if ((ch = in.read()) == -1) {
159                throw new RuntimeException("Illegal signature: " + in.getData()
160                        + " no ident, reaching EOF");
161            }
162            //System.out.println("return from ident:" + (char)ch);
163            if (!identStart(ch)) {
164                StringBuffer buf2 = new StringBuffer();
165                int count = 1;
166                while (Character.isJavaIdentifierPart((char) ch)) {
167                    buf2.append((char) ch);
168                    count++;
169                    ch = in.read();
170                }
171                if (ch == ':') { // Ok, formal parameter
172                    in.skip("Ljava/lang/Object".length());
173                    buf.append(buf2);
174                    ch = in.read();
175                    in.unread();
176                    //System.out.println("so far:" + buf2 + ":next:" +(char)ch);
177                } else {
178                    for (int i = 0; i < count; i++) {
179                        in.unread();
180                    }
181                }
182                return;
183            }
184            StringBuilder buf2 = new StringBuilder();
185            ch = in.read();
186            do {
187                buf2.append((char) ch);
188                ch = in.read();
189                //System.out.println("within ident:"+ (char)ch);
190            } while ((ch != -1) && (Character.isJavaIdentifierPart((char) ch) || (ch == '/')));
191            buf.append(buf2.toString().replace('/', '.'));
192            //System.out.println("regular return ident:"+ (char)ch + ":" + buf2);
193            if (ch != -1) {
194                in.unread();
195            }
196        }
197    
198    
199        private static final void matchGJIdent( MyByteArrayInputStream in, StringBuffer buf ) {
200            int ch;
201            matchIdent(in, buf);
202            ch = in.read();
203            if ((ch == '<') || ch == '(') { // Parameterized or method
204                //System.out.println("Enter <");
205                buf.append((char) ch);
206                matchGJIdent(in, buf);
207                while (((ch = in.read()) != '>') && (ch != ')')) { // List of parameters
208                    if (ch == -1) {
209                        throw new RuntimeException("Illegal signature: " + in.getData()
210                                + " reaching EOF");
211                    }
212                    //System.out.println("Still no >");
213                    buf.append(", ");
214                    in.unread();
215                    matchGJIdent(in, buf); // Recursive call
216                }
217                //System.out.println("Exit >");
218                buf.append((char) ch);
219            } else {
220                in.unread();
221            }
222            ch = in.read();
223            if (identStart(ch)) {
224                in.unread();
225                matchGJIdent(in, buf);
226            } else if (ch == ')') {
227                in.unread();
228                return;
229            } else if (ch != ';') {
230                throw new RuntimeException("Illegal signature: " + in.getData() + " read " + (char) ch);
231            }
232        }
233    
234    
235        public static String translate( String s ) {
236            //System.out.println("Sig:" + s);
237            StringBuffer buf = new StringBuffer();
238            matchGJIdent(new MyByteArrayInputStream(s), buf);
239            return buf.toString();
240        }
241    
242    
243        public static final boolean isFormalParameterList( String s ) {
244            return s.startsWith("<") && (s.indexOf(':') > 0);
245        }
246    
247    
248        public static final boolean isActualParameterList( 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            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( ConstantPool _constant_pool ) {
268            return (Signature) clone();
269        }
270    }