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