1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.bcel.classfile;
18
19 import java.io.ByteArrayInputStream;
20 import java.io.DataInput;
21 import java.io.DataOutputStream;
22 import java.io.IOException;
23 import java.nio.charset.StandardCharsets;
24 import java.util.Objects;
25
26 import org.apache.bcel.Const;
27 import org.apache.bcel.util.Args;
28
29
30
31
32
33
34 public final class Signature extends Attribute {
35
36
37
38
39 private static final class MyByteArrayInputStream extends ByteArrayInputStream {
40
41 MyByteArrayInputStream(final String data) {
42 super(data.getBytes(StandardCharsets.UTF_8));
43 }
44
45 String getData() {
46 return new String(buf, StandardCharsets.UTF_8);
47 }
48
49 void unread() {
50 if (pos > 0) {
51 pos--;
52 }
53 }
54 }
55
56 private static boolean identStart(final int ch) {
57 return ch == 'T' || ch == 'L';
58 }
59
60
61 public static boolean isActualParameterList(final String s) {
62 return s.startsWith("L") && s.endsWith(">;");
63 }
64
65
66 public static boolean isFormalParameterList(final String s) {
67 return s.startsWith("<") && s.indexOf(':') > 0;
68 }
69
70 private static void matchGJIdent(final MyByteArrayInputStream in, final StringBuilder buf) {
71 int ch;
72 matchIdent(in, buf);
73 ch = in.read();
74 if (ch == '<' || ch == '(') {
75
76 buf.append((char) ch);
77 matchGJIdent(in, buf);
78 while ((ch = in.read()) != '>' && ch != ')') {
79 if (ch == -1) {
80 throw new IllegalArgumentException("Illegal signature: " + in.getData() + " reaching EOF");
81 }
82
83 buf.append(", ");
84 in.unread();
85 matchGJIdent(in, buf);
86 }
87
88 buf.append((char) ch);
89 } else {
90 in.unread();
91 }
92 ch = in.read();
93 if (identStart(ch)) {
94 in.unread();
95 matchGJIdent(in, buf);
96 } else if (ch == ')') {
97 in.unread();
98 } else if (ch != ';') {
99 throw new IllegalArgumentException("Illegal signature: " + in.getData() + " read " + (char) ch);
100 }
101 }
102
103 private static void matchIdent(final MyByteArrayInputStream in, final StringBuilder buf) {
104 int ch;
105 if ((ch = in.read()) == -1) {
106 throw new IllegalArgumentException("Illegal signature: " + in.getData() + " no ident, reaching EOF");
107 }
108
109 if (!identStart(ch)) {
110 final StringBuilder buf2 = new StringBuilder();
111 int count = 1;
112 while (Character.isJavaIdentifierPart((char) ch)) {
113 buf2.append((char) ch);
114 count++;
115 ch = in.read();
116 }
117 if (ch == ':') {
118 final int skipExpected = "Ljava/lang/Object".length();
119 final long skipActual = in.skip(skipExpected);
120 if (skipActual != skipExpected) {
121 throw new IllegalStateException(String.format("Unexpected skip: expected=%,d, actual=%,d", skipExpected, skipActual));
122 }
123 buf.append(buf2);
124 ch = in.read();
125 in.unread();
126
127 } else {
128 for (int i = 0; i < count; i++) {
129 in.unread();
130 }
131 }
132 return;
133 }
134 final StringBuilder buf2 = new StringBuilder();
135 ch = in.read();
136 do {
137 buf2.append((char) ch);
138 ch = in.read();
139
140 } while (ch != -1 && (Character.isJavaIdentifierPart((char) ch) || ch == '/'));
141 buf.append(Utility.pathToPackage(buf2.toString()));
142
143 if (ch != -1) {
144 in.unread();
145 }
146 }
147
148 public static String translate(final String s) {
149
150 final StringBuilder buf = new StringBuilder();
151 matchGJIdent(new MyByteArrayInputStream(s), buf);
152 return buf.toString();
153 }
154
155 private int signatureIndex;
156
157
158
159
160
161
162
163
164
165
166 Signature(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
167 this(nameIndex, length, input.readUnsignedShort(), constantPool);
168 }
169
170
171
172
173
174
175
176 public Signature(final int nameIndex, final int length, final int signatureIndex, final ConstantPool constantPool) {
177 super(Const.ATTR_SIGNATURE, nameIndex, Args.require(length, 2, "Signature length attribute"), constantPool);
178 this.signatureIndex = signatureIndex;
179
180 Objects.requireNonNull(constantPool.getConstantUtf8(signatureIndex), "constantPool.getConstantUtf8(signatureIndex)");
181 }
182
183
184
185
186
187
188
189 public Signature(final Signature c) {
190 this(c.getNameIndex(), c.getLength(), c.getSignatureIndex(), c.getConstantPool());
191 }
192
193
194
195
196
197
198
199 @Override
200 public void accept(final Visitor v) {
201
202 v.visitSignature(this);
203 }
204
205
206
207
208 @Override
209 public Attribute copy(final ConstantPool constantPool) {
210 return (Attribute) clone();
211 }
212
213
214
215
216
217
218
219 @Override
220 public void dump(final DataOutputStream file) throws IOException {
221 super.dump(file);
222 file.writeShort(signatureIndex);
223 }
224
225
226
227
228 public String getSignature() {
229 return super.getConstantPool().getConstantUtf8(signatureIndex).getBytes();
230 }
231
232
233
234
235 public int getSignatureIndex() {
236 return signatureIndex;
237 }
238
239
240
241
242 public void setSignatureIndex(final int signatureIndex) {
243 this.signatureIndex = signatureIndex;
244 }
245
246
247
248
249 @Override
250 public String toString() {
251 return "Signature: " + getSignature();
252 }
253 }