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