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