author | joehw |
Wed, 26 Jun 2019 05:49:59 +0000 | |
changeset 55496 | 8e0ae3830fca |
parent 47216 | 71c04702a3d5 |
permissions | -rw-r--r-- |
6 | 1 |
/* |
55496 | 2 |
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. |
6 | 3 |
*/ |
44797
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
4 |
/* |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
5 |
* Licensed to the Apache Software Foundation (ASF) under one or more |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
6 |
* contributor license agreements. See the NOTICE file distributed with |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
7 |
* this work for additional information regarding copyright ownership. |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
8 |
* The ASF licenses this file to You under the Apache License, Version 2.0 |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
9 |
* (the "License"); you may not use this file except in compliance with |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
10 |
* the License. You may obtain a copy of the License at |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
11 |
* |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
12 |
* http://www.apache.org/licenses/LICENSE-2.0 |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
13 |
* |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
14 |
* Unless required by applicable law or agreed to in writing, software |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
15 |
* distributed under the License is distributed on an "AS IS" BASIS, |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
16 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
17 |
* See the License for the specific language governing permissions and |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
18 |
* limitations under the License. |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
19 |
*/ |
6 | 20 |
package com.sun.org.apache.bcel.internal.classfile; |
21 |
||
46174 | 22 |
import java.io.ByteArrayOutputStream; |
23 |
import java.io.DataOutputStream; |
|
24 |
import java.io.File; |
|
25 |
import java.io.FileOutputStream; |
|
26 |
import java.io.IOException; |
|
27 |
import java.io.OutputStream; |
|
28 |
import java.util.ArrayList; |
|
55496 | 29 |
import java.util.Objects; |
30 |
import java.util.StringTokenizer; |
|
46174 | 31 |
import java.util.List; |
32 |
import java.util.Set; |
|
33 |
import java.util.TreeSet; |
|
6 | 34 |
|
46174 | 35 |
import com.sun.org.apache.bcel.internal.Const; |
36 |
import com.sun.org.apache.bcel.internal.generic.Type; |
|
37 |
import com.sun.org.apache.bcel.internal.util.BCELComparator; |
|
38 |
import com.sun.org.apache.bcel.internal.util.ClassQueue; |
|
39 |
import com.sun.org.apache.bcel.internal.util.SyntheticRepository; |
|
6 | 40 |
|
41 |
/** |
|
46174 | 42 |
* Represents a Java class, i.e., the data structures, constant pool, fields, |
43 |
* methods and commands contained in a Java .class file. See <a |
|
44 |
* href="http://docs.oracle.com/javase/specs/">JVM specification</a> for |
|
45 |
* details. The intent of this class is to represent a parsed or otherwise |
|
46 |
* existing class file. Those interested in programatically generating classes |
|
6 | 47 |
* should see the <a href="../generic/ClassGen.html">ClassGen</a> class. |
55496 | 48 |
|
49 |
* @version $Id$ |
|
6 | 50 |
* @see com.sun.org.apache.bcel.internal.generic.ClassGen |
55496 | 51 |
* @LastModified: Jun 2019 |
6 | 52 |
*/ |
46174 | 53 |
public class JavaClass extends AccessFlags implements Cloneable, Node, Comparable<JavaClass> { |
6 | 54 |
|
46174 | 55 |
private String file_name; |
56 |
private String package_name; |
|
57 |
private String source_file_name = "<Unknown>"; |
|
58 |
private int class_name_index; |
|
59 |
private int superclass_name_index; |
|
60 |
private String class_name; |
|
61 |
private String superclass_name; |
|
62 |
private int major; |
|
63 |
private int minor; // Compiler version |
|
64 |
private ConstantPool constant_pool; // Constant pool |
|
65 |
private int[] interfaces; // implemented interfaces |
|
66 |
private String[] interface_names; |
|
67 |
private Field[] fields; // Fields, i.e., variables of class |
|
68 |
private Method[] methods; // methods defined in the class |
|
69 |
private Attribute[] attributes; // attributes defined in the class |
|
70 |
private AnnotationEntry[] annotations; // annotations defined on the class |
|
71 |
private byte source = HEAP; // Generated in memory |
|
72 |
private boolean isAnonymous = false; |
|
73 |
private boolean isNested = false; |
|
74 |
private boolean computedNestedTypeStatus = false; |
|
75 |
public static final byte HEAP = 1; |
|
76 |
public static final byte FILE = 2; |
|
77 |
public static final byte ZIP = 3; |
|
6 | 78 |
|
46174 | 79 |
private static BCELComparator bcelComparator = new BCELComparator() { |
80 |
||
81 |
@Override |
|
55496 | 82 |
public boolean equals( final Object o1, final Object o2 ) { |
46174 | 83 |
final JavaClass THIS = (JavaClass) o1; |
84 |
final JavaClass THAT = (JavaClass) o2; |
|
55496 | 85 |
return Objects.equals(THIS.getClassName(), THAT.getClassName()); |
46174 | 86 |
} |
87 |
||
55496 | 88 |
|
46174 | 89 |
@Override |
55496 | 90 |
public int hashCode( final Object o ) { |
46174 | 91 |
final JavaClass THIS = (JavaClass) o; |
92 |
return THIS.getClassName().hashCode(); |
|
93 |
} |
|
94 |
}; |
|
95 |
/** |
|
55496 | 96 |
* In cases where we go ahead and create something, |
97 |
* use the default SyntheticRepository, because we |
|
98 |
* don't know any better. |
|
46174 | 99 |
*/ |
100 |
private transient com.sun.org.apache.bcel.internal.util.Repository repository |
|
101 |
= SyntheticRepository.getInstance(); |
|
6 | 102 |
|
55496 | 103 |
|
46174 | 104 |
/** |
105 |
* Constructor gets all contents as arguments. |
|
106 |
* |
|
107 |
* @param class_name_index Index into constant pool referencing a |
|
108 |
* ConstantClass that represents this class. |
|
109 |
* @param superclass_name_index Index into constant pool referencing a |
|
110 |
* ConstantClass that represents this class's superclass. |
|
111 |
* @param file_name File name |
|
112 |
* @param major Major compiler version |
|
113 |
* @param minor Minor compiler version |
|
114 |
* @param access_flags Access rights defined by bit flags |
|
115 |
* @param constant_pool Array of constants |
|
116 |
* @param interfaces Implemented interfaces |
|
117 |
* @param fields Class fields |
|
118 |
* @param methods Class methods |
|
119 |
* @param attributes Class attributes |
|
120 |
* @param source Read from file or generated in memory? |
|
121 |
*/ |
|
122 |
public JavaClass(final int class_name_index, final int superclass_name_index, |
|
123 |
final String file_name, final int major, final int minor, final int access_flags, |
|
124 |
final ConstantPool constant_pool, int[] interfaces, Field[] fields, |
|
125 |
Method[] methods, Attribute[] attributes, final byte source) { |
|
126 |
super(access_flags); |
|
127 |
if (interfaces == null) { |
|
128 |
interfaces = new int[0]; |
|
129 |
} |
|
130 |
if (attributes == null) { |
|
131 |
attributes = new Attribute[0]; |
|
132 |
} |
|
133 |
if (fields == null) { |
|
134 |
fields = new Field[0]; |
|
135 |
} |
|
136 |
if (methods == null) { |
|
137 |
methods = new Method[0]; |
|
138 |
} |
|
139 |
this.class_name_index = class_name_index; |
|
140 |
this.superclass_name_index = superclass_name_index; |
|
141 |
this.file_name = file_name; |
|
142 |
this.major = major; |
|
143 |
this.minor = minor; |
|
144 |
this.constant_pool = constant_pool; |
|
145 |
this.interfaces = interfaces; |
|
146 |
this.fields = fields; |
|
147 |
this.methods = methods; |
|
148 |
this.attributes = attributes; |
|
149 |
this.source = source; |
|
150 |
// Get source file name if available |
|
151 |
for (final Attribute attribute : attributes) { |
|
152 |
if (attribute instanceof SourceFile) { |
|
153 |
source_file_name = ((SourceFile) attribute).getSourceFileName(); |
|
154 |
break; |
|
155 |
} |
|
156 |
} |
|
157 |
/* According to the specification the following entries must be of type |
|
158 |
* `ConstantClass' but we check that anyway via the |
|
159 |
* `ConstPool.getConstant' method. |
|
160 |
*/ |
|
161 |
class_name = constant_pool.getConstantString(class_name_index, Const.CONSTANT_Class); |
|
162 |
class_name = Utility.compactClassName(class_name, false); |
|
163 |
final int index = class_name.lastIndexOf('.'); |
|
164 |
if (index < 0) { |
|
165 |
package_name = ""; |
|
166 |
} else { |
|
167 |
package_name = class_name.substring(0, index); |
|
168 |
} |
|
169 |
if (superclass_name_index > 0) { |
|
170 |
// May be zero -> class is java.lang.Object |
|
171 |
superclass_name = constant_pool.getConstantString(superclass_name_index, |
|
172 |
Const.CONSTANT_Class); |
|
173 |
superclass_name = Utility.compactClassName(superclass_name, false); |
|
174 |
} else { |
|
175 |
superclass_name = "java.lang.Object"; |
|
176 |
} |
|
177 |
interface_names = new String[interfaces.length]; |
|
178 |
for (int i = 0; i < interfaces.length; i++) { |
|
179 |
final String str = constant_pool.getConstantString(interfaces[i], Const.CONSTANT_Class); |
|
180 |
interface_names[i] = Utility.compactClassName(str, false); |
|
181 |
} |
|
182 |
} |
|
6 | 183 |
|
55496 | 184 |
|
46174 | 185 |
/** |
186 |
* Constructor gets all contents as arguments. |
|
187 |
* |
|
188 |
* @param class_name_index Class name |
|
189 |
* @param superclass_name_index Superclass name |
|
190 |
* @param file_name File name |
|
191 |
* @param major Major compiler version |
|
192 |
* @param minor Minor compiler version |
|
193 |
* @param access_flags Access rights defined by bit flags |
|
194 |
* @param constant_pool Array of constants |
|
195 |
* @param interfaces Implemented interfaces |
|
196 |
* @param fields Class fields |
|
197 |
* @param methods Class methods |
|
198 |
* @param attributes Class attributes |
|
199 |
*/ |
|
200 |
public JavaClass(final int class_name_index, final int superclass_name_index, |
|
201 |
final String file_name, final int major, final int minor, final int access_flags, |
|
202 |
final ConstantPool constant_pool, final int[] interfaces, final Field[] fields, |
|
203 |
final Method[] methods, final Attribute[] attributes) { |
|
204 |
this(class_name_index, superclass_name_index, file_name, major, minor, access_flags, |
|
205 |
constant_pool, interfaces, fields, methods, attributes, HEAP); |
|
206 |
} |
|
6 | 207 |
|
55496 | 208 |
|
46174 | 209 |
/** |
55496 | 210 |
* Called by objects that are traversing the nodes of the tree implicitely |
46174 | 211 |
* defined by the contents of a Java class. I.e., the hierarchy of methods, |
212 |
* fields, attributes, etc. spawns a tree of objects. |
|
213 |
* |
|
214 |
* @param v Visitor object |
|
215 |
*/ |
|
216 |
@Override |
|
55496 | 217 |
public void accept( final Visitor v ) { |
46174 | 218 |
v.visitJavaClass(this); |
6 | 219 |
} |
220 |
||
46174 | 221 |
/** |
222 |
* Dump class to a file. |
|
223 |
* |
|
224 |
* @param file Output file |
|
225 |
* @throws IOException |
|
6 | 226 |
*/ |
46174 | 227 |
public void dump(final File file) throws IOException { |
228 |
final String parent = file.getParent(); |
|
229 |
if (parent != null) { |
|
230 |
final File dir = new File(parent); |
|
231 |
if (!dir.mkdirs()) { // either was not created or already existed |
|
55496 | 232 |
if (!dir.isDirectory()) { |
46174 | 233 |
throw new IOException("Could not create the directory " + dir); |
234 |
} |
|
235 |
} |
|
236 |
} |
|
237 |
try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(file))) { |
|
238 |
dump(dos); |
|
239 |
} |
|
6 | 240 |
} |
241 |
||
55496 | 242 |
|
46174 | 243 |
/** |
244 |
* Dump class to a file named file_name. |
|
245 |
* |
|
246 |
* @param _file_name Output file name |
|
247 |
* @throws IOException |
|
248 |
*/ |
|
55496 | 249 |
public void dump( final String _file_name ) throws IOException { |
46174 | 250 |
dump(new File(_file_name)); |
251 |
} |
|
6 | 252 |
|
55496 | 253 |
|
46174 | 254 |
/** |
255 |
* @return class in binary format |
|
256 |
*/ |
|
257 |
public byte[] getBytes() { |
|
258 |
final ByteArrayOutputStream s = new ByteArrayOutputStream(); |
|
259 |
final DataOutputStream ds = new DataOutputStream(s); |
|
260 |
try { |
|
261 |
dump(ds); |
|
262 |
} catch (final IOException e) { |
|
263 |
System.err.println("Error dumping class: " + e.getMessage()); |
|
264 |
} finally { |
|
265 |
try { |
|
266 |
ds.close(); |
|
267 |
} catch (final IOException e2) { |
|
268 |
System.err.println("Error dumping class: " + e2.getMessage()); |
|
269 |
} |
|
270 |
} |
|
271 |
return s.toByteArray(); |
|
6 | 272 |
} |
273 |
||
274 |
||
46174 | 275 |
/** |
276 |
* Dump Java class to output stream in binary format. |
|
277 |
* |
|
278 |
* @param file Output stream |
|
279 |
* @throws IOException |
|
280 |
*/ |
|
55496 | 281 |
public void dump( final OutputStream file ) throws IOException { |
282 |
dump(new DataOutputStream(file)); |
|
283 |
} |
|
284 |
||
285 |
||
286 |
/** |
|
287 |
* Dump Java class to output stream in binary format. |
|
288 |
* |
|
289 |
* @param file Output stream |
|
290 |
* @throws IOException |
|
291 |
*/ |
|
292 |
public void dump( final DataOutputStream file ) throws IOException { |
|
46174 | 293 |
file.writeInt(Const.JVM_CLASSFILE_MAGIC); |
294 |
file.writeShort(minor); |
|
295 |
file.writeShort(major); |
|
296 |
constant_pool.dump(file); |
|
297 |
file.writeShort(super.getAccessFlags()); |
|
298 |
file.writeShort(class_name_index); |
|
299 |
file.writeShort(superclass_name_index); |
|
300 |
file.writeShort(interfaces.length); |
|
301 |
for (final int interface1 : interfaces) { |
|
302 |
file.writeShort(interface1); |
|
303 |
} |
|
304 |
file.writeShort(fields.length); |
|
305 |
for (final Field field : fields) { |
|
306 |
field.dump(file); |
|
307 |
} |
|
308 |
file.writeShort(methods.length); |
|
309 |
for (final Method method : methods) { |
|
310 |
method.dump(file); |
|
311 |
} |
|
312 |
if (attributes != null) { |
|
313 |
file.writeShort(attributes.length); |
|
314 |
for (final Attribute attribute : attributes) { |
|
315 |
attribute.dump(file); |
|
316 |
} |
|
317 |
} else { |
|
318 |
file.writeShort(0); |
|
319 |
} |
|
320 |
file.flush(); |
|
321 |
} |
|
6 | 322 |
|
55496 | 323 |
|
46174 | 324 |
/** |
325 |
* @return Attributes of the class. |
|
326 |
*/ |
|
327 |
public Attribute[] getAttributes() { |
|
328 |
return attributes; |
|
6 | 329 |
} |
330 |
||
46174 | 331 |
/** |
332 |
* @return Annotations on the class |
|
333 |
* @since 6.0 |
|
334 |
*/ |
|
335 |
public AnnotationEntry[] getAnnotationEntries() { |
|
336 |
if (annotations == null) { |
|
337 |
annotations = AnnotationEntry.createAnnotationEntries(getAttributes()); |
|
338 |
} |
|
6 | 339 |
|
46174 | 340 |
return annotations; |
341 |
} |
|
6 | 342 |
|
46174 | 343 |
/** |
344 |
* @return Class name. |
|
345 |
*/ |
|
346 |
public String getClassName() { |
|
347 |
return class_name; |
|
348 |
} |
|
6 | 349 |
|
55496 | 350 |
|
46174 | 351 |
/** |
352 |
* @return Package name. |
|
353 |
*/ |
|
354 |
public String getPackageName() { |
|
355 |
return package_name; |
|
356 |
} |
|
6 | 357 |
|
55496 | 358 |
|
46174 | 359 |
/** |
360 |
* @return Class name index. |
|
361 |
*/ |
|
362 |
public int getClassNameIndex() { |
|
363 |
return class_name_index; |
|
364 |
} |
|
6 | 365 |
|
55496 | 366 |
|
46174 | 367 |
/** |
368 |
* @return Constant pool. |
|
369 |
*/ |
|
370 |
public ConstantPool getConstantPool() { |
|
371 |
return constant_pool; |
|
372 |
} |
|
6 | 373 |
|
55496 | 374 |
|
46174 | 375 |
/** |
55496 | 376 |
* @return Fields, i.e., variables of the class. Like the JVM spec |
377 |
* mandates for the classfile format, these fields are those specific to |
|
378 |
* this class, and not those of the superclass or superinterfaces. |
|
46174 | 379 |
*/ |
380 |
public Field[] getFields() { |
|
381 |
return fields; |
|
6 | 382 |
} |
383 |
||
55496 | 384 |
|
46174 | 385 |
/** |
386 |
* @return File name of class, aka SourceFile attribute value |
|
387 |
*/ |
|
388 |
public String getFileName() { |
|
389 |
return file_name; |
|
390 |
} |
|
6 | 391 |
|
55496 | 392 |
|
46174 | 393 |
/** |
394 |
* @return Names of implemented interfaces. |
|
395 |
*/ |
|
396 |
public String[] getInterfaceNames() { |
|
397 |
return interface_names; |
|
398 |
} |
|
6 | 399 |
|
55496 | 400 |
|
46174 | 401 |
/** |
402 |
* @return Indices in constant pool of implemented interfaces. |
|
403 |
*/ |
|
404 |
public int[] getInterfaceIndices() { |
|
405 |
return interfaces; |
|
406 |
} |
|
6 | 407 |
|
55496 | 408 |
|
46174 | 409 |
/** |
410 |
* @return Major number of class file version. |
|
411 |
*/ |
|
412 |
public int getMajor() { |
|
413 |
return major; |
|
414 |
} |
|
6 | 415 |
|
55496 | 416 |
|
46174 | 417 |
/** |
418 |
* @return Methods of the class. |
|
419 |
*/ |
|
420 |
public Method[] getMethods() { |
|
421 |
return methods; |
|
422 |
} |
|
6 | 423 |
|
55496 | 424 |
|
46174 | 425 |
/** |
55496 | 426 |
* @return A {@link Method} corresponding to |
427 |
* java.lang.reflect.Method if any |
|
46174 | 428 |
*/ |
55496 | 429 |
public Method getMethod( final java.lang.reflect.Method m ) { |
46174 | 430 |
for (final Method method : methods) { |
431 |
if (m.getName().equals(method.getName()) && (m.getModifiers() == method.getModifiers()) |
|
432 |
&& Type.getSignature(m).equals(method.getSignature())) { |
|
433 |
return method; |
|
434 |
} |
|
435 |
} |
|
436 |
return null; |
|
437 |
} |
|
6 | 438 |
|
55496 | 439 |
|
46174 | 440 |
/** |
441 |
* @return Minor number of class file version. |
|
442 |
*/ |
|
443 |
public int getMinor() { |
|
444 |
return minor; |
|
445 |
} |
|
6 | 446 |
|
55496 | 447 |
|
46174 | 448 |
/** |
449 |
* @return sbsolute path to file where this class was read from |
|
450 |
*/ |
|
451 |
public String getSourceFileName() { |
|
452 |
return source_file_name; |
|
453 |
} |
|
6 | 454 |
|
55496 | 455 |
|
46174 | 456 |
/** |
55496 | 457 |
* returns the super class name of this class. In the case that this class is |
458 |
* java.lang.Object, it will return itself (java.lang.Object). This is probably incorrect |
|
459 |
* but isn't fixed at this time to not break existing clients. |
|
46174 | 460 |
* |
461 |
* @return Superclass name. |
|
462 |
*/ |
|
463 |
public String getSuperclassName() { |
|
464 |
return superclass_name; |
|
6 | 465 |
} |
466 |
||
55496 | 467 |
|
46174 | 468 |
/** |
469 |
* @return Class name index. |
|
470 |
*/ |
|
471 |
public int getSuperclassNameIndex() { |
|
472 |
return superclass_name_index; |
|
473 |
} |
|
474 |
||
475 |
/** |
|
476 |
* @param attributes . |
|
477 |
*/ |
|
55496 | 478 |
public void setAttributes( final Attribute[] attributes ) { |
46174 | 479 |
this.attributes = attributes; |
480 |
} |
|
481 |
||
55496 | 482 |
|
46174 | 483 |
/** |
484 |
* @param class_name . |
|
485 |
*/ |
|
55496 | 486 |
public void setClassName( final String class_name ) { |
46174 | 487 |
this.class_name = class_name; |
488 |
} |
|
6 | 489 |
|
55496 | 490 |
|
46174 | 491 |
/** |
492 |
* @param class_name_index . |
|
493 |
*/ |
|
55496 | 494 |
public void setClassNameIndex( final int class_name_index ) { |
46174 | 495 |
this.class_name_index = class_name_index; |
496 |
} |
|
497 |
||
55496 | 498 |
|
46174 | 499 |
/** |
500 |
* @param constant_pool . |
|
501 |
*/ |
|
55496 | 502 |
public void setConstantPool( final ConstantPool constant_pool ) { |
46174 | 503 |
this.constant_pool = constant_pool; |
504 |
} |
|
6 | 505 |
|
55496 | 506 |
|
46174 | 507 |
/** |
508 |
* @param fields . |
|
509 |
*/ |
|
55496 | 510 |
public void setFields( final Field[] fields ) { |
46174 | 511 |
this.fields = fields; |
512 |
} |
|
513 |
||
55496 | 514 |
|
46174 | 515 |
/** |
516 |
* Set File name of class, aka SourceFile attribute value |
|
517 |
*/ |
|
55496 | 518 |
public void setFileName( final String file_name ) { |
46174 | 519 |
this.file_name = file_name; |
520 |
} |
|
6 | 521 |
|
55496 | 522 |
|
46174 | 523 |
/** |
524 |
* @param interface_names . |
|
525 |
*/ |
|
55496 | 526 |
public void setInterfaceNames( final String[] interface_names ) { |
46174 | 527 |
this.interface_names = interface_names; |
528 |
} |
|
6 | 529 |
|
55496 | 530 |
|
46174 | 531 |
/** |
532 |
* @param interfaces . |
|
533 |
*/ |
|
55496 | 534 |
public void setInterfaces( final int[] interfaces ) { |
46174 | 535 |
this.interfaces = interfaces; |
536 |
} |
|
537 |
||
55496 | 538 |
|
46174 | 539 |
/** |
540 |
* @param major . |
|
541 |
*/ |
|
55496 | 542 |
public void setMajor( final int major ) { |
46174 | 543 |
this.major = major; |
544 |
} |
|
6 | 545 |
|
55496 | 546 |
|
46174 | 547 |
/** |
548 |
* @param methods . |
|
549 |
*/ |
|
55496 | 550 |
public void setMethods( final Method[] methods ) { |
46174 | 551 |
this.methods = methods; |
552 |
} |
|
553 |
||
55496 | 554 |
|
46174 | 555 |
/** |
556 |
* @param minor . |
|
557 |
*/ |
|
55496 | 558 |
public void setMinor( final int minor ) { |
46174 | 559 |
this.minor = minor; |
560 |
} |
|
6 | 561 |
|
55496 | 562 |
|
46174 | 563 |
/** |
564 |
* Set absolute path to file this class was read from. |
|
565 |
*/ |
|
55496 | 566 |
public void setSourceFileName( final String source_file_name ) { |
46174 | 567 |
this.source_file_name = source_file_name; |
6 | 568 |
} |
46174 | 569 |
|
55496 | 570 |
|
46174 | 571 |
/** |
572 |
* @param superclass_name . |
|
573 |
*/ |
|
55496 | 574 |
public void setSuperclassName( final String superclass_name ) { |
46174 | 575 |
this.superclass_name = superclass_name; |
576 |
} |
|
577 |
||
55496 | 578 |
|
46174 | 579 |
/** |
580 |
* @param superclass_name_index . |
|
581 |
*/ |
|
55496 | 582 |
public void setSuperclassNameIndex( final int superclass_name_index ) { |
46174 | 583 |
this.superclass_name_index = superclass_name_index; |
6 | 584 |
} |
585 |
||
55496 | 586 |
|
46174 | 587 |
/** |
588 |
* @return String representing class contents. |
|
589 |
*/ |
|
590 |
@Override |
|
591 |
public String toString() { |
|
592 |
String access = Utility.accessToString(super.getAccessFlags(), true); |
|
593 |
access = access.isEmpty() ? "" : (access + " "); |
|
594 |
final StringBuilder buf = new StringBuilder(128); |
|
595 |
buf.append(access).append(Utility.classOrInterface(super.getAccessFlags())).append(" ").append( |
|
596 |
class_name).append(" extends ").append( |
|
55496 | 597 |
Utility.compactClassName(superclass_name, false)).append('\n'); |
46174 | 598 |
final int size = interfaces.length; |
599 |
if (size > 0) { |
|
600 |
buf.append("implements\t\t"); |
|
601 |
for (int i = 0; i < size; i++) { |
|
602 |
buf.append(interface_names[i]); |
|
603 |
if (i < size - 1) { |
|
604 |
buf.append(", "); |
|
605 |
} |
|
606 |
} |
|
607 |
buf.append('\n'); |
|
608 |
} |
|
609 |
buf.append("filename\t\t").append(file_name).append('\n'); |
|
610 |
buf.append("compiled from\t\t").append(source_file_name).append('\n'); |
|
611 |
buf.append("compiler version\t").append(major).append(".").append(minor).append('\n'); |
|
612 |
buf.append("access flags\t\t").append(super.getAccessFlags()).append('\n'); |
|
613 |
buf.append("constant pool\t\t").append(constant_pool.getLength()).append(" entries\n"); |
|
614 |
buf.append("ACC_SUPER flag\t\t").append(isSuper()).append("\n"); |
|
615 |
if (attributes.length > 0) { |
|
616 |
buf.append("\nAttribute(s):\n"); |
|
617 |
for (final Attribute attribute : attributes) { |
|
618 |
buf.append(indent(attribute)); |
|
619 |
} |
|
620 |
} |
|
621 |
final AnnotationEntry[] annotations = getAnnotationEntries(); |
|
55496 | 622 |
if (annotations!=null && annotations.length>0) { |
46174 | 623 |
buf.append("\nAnnotation(s):\n"); |
624 |
for (final AnnotationEntry annotation : annotations) { |
|
625 |
buf.append(indent(annotation)); |
|
626 |
} |
|
627 |
} |
|
628 |
if (fields.length > 0) { |
|
629 |
buf.append("\n").append(fields.length).append(" fields:\n"); |
|
630 |
for (final Field field : fields) { |
|
631 |
buf.append("\t").append(field).append('\n'); |
|
632 |
} |
|
633 |
} |
|
634 |
if (methods.length > 0) { |
|
635 |
buf.append("\n").append(methods.length).append(" methods:\n"); |
|
636 |
for (final Method method : methods) { |
|
637 |
buf.append("\t").append(method).append('\n'); |
|
638 |
} |
|
639 |
} |
|
640 |
return buf.toString(); |
|
6 | 641 |
} |
642 |
||
55496 | 643 |
|
644 |
private static String indent( final Object obj ) { |
|
46174 | 645 |
final StringTokenizer tok = new StringTokenizer(obj.toString(), "\n"); |
646 |
final StringBuilder buf = new StringBuilder(); |
|
647 |
while (tok.hasMoreTokens()) { |
|
648 |
buf.append("\t").append(tok.nextToken()).append("\n"); |
|
649 |
} |
|
650 |
return buf.toString(); |
|
6 | 651 |
} |
652 |
||
55496 | 653 |
|
46174 | 654 |
/** |
655 |
* @return deep copy of this class |
|
656 |
*/ |
|
657 |
public JavaClass copy() { |
|
658 |
JavaClass c = null; |
|
659 |
try { |
|
660 |
c = (JavaClass) clone(); |
|
661 |
c.constant_pool = constant_pool.copy(); |
|
662 |
c.interfaces = interfaces.clone(); |
|
663 |
c.interface_names = interface_names.clone(); |
|
664 |
c.fields = new Field[fields.length]; |
|
665 |
for (int i = 0; i < fields.length; i++) { |
|
666 |
c.fields[i] = fields[i].copy(c.constant_pool); |
|
667 |
} |
|
668 |
c.methods = new Method[methods.length]; |
|
669 |
for (int i = 0; i < methods.length; i++) { |
|
670 |
c.methods[i] = methods[i].copy(c.constant_pool); |
|
671 |
} |
|
672 |
c.attributes = new Attribute[attributes.length]; |
|
673 |
for (int i = 0; i < attributes.length; i++) { |
|
674 |
c.attributes[i] = attributes[i].copy(c.constant_pool); |
|
675 |
} |
|
676 |
} catch (final CloneNotSupportedException e) { |
|
677 |
// TODO should this throw? |
|
678 |
} |
|
679 |
return c; |
|
6 | 680 |
} |
681 |
||
55496 | 682 |
|
46174 | 683 |
public final boolean isSuper() { |
684 |
return (super.getAccessFlags() & Const.ACC_SUPER) != 0; |
|
685 |
} |
|
686 |
||
55496 | 687 |
|
46174 | 688 |
public final boolean isClass() { |
689 |
return (super.getAccessFlags() & Const.ACC_INTERFACE) == 0; |
|
690 |
} |
|
691 |
||
692 |
/** |
|
693 |
* @since 6.0 |
|
694 |
*/ |
|
695 |
public final boolean isAnonymous() { |
|
696 |
computeNestedTypeStatus(); |
|
697 |
return this.isAnonymous; |
|
6 | 698 |
} |
699 |
||
46174 | 700 |
/** |
701 |
* @since 6.0 |
|
702 |
*/ |
|
703 |
public final boolean isNested() { |
|
704 |
computeNestedTypeStatus(); |
|
705 |
return this.isNested; |
|
706 |
} |
|
6 | 707 |
|
46174 | 708 |
private void computeNestedTypeStatus() { |
709 |
if (computedNestedTypeStatus) { |
|
710 |
return; |
|
711 |
} |
|
712 |
for (final Attribute attribute : this.attributes) { |
|
55496 | 713 |
if (attribute instanceof InnerClasses) { |
714 |
final InnerClass[] innerClasses = ((InnerClasses) attribute).getInnerClasses(); |
|
715 |
for (final InnerClass innerClasse : innerClasses) { |
|
716 |
boolean innerClassAttributeRefersToMe = false; |
|
717 |
String inner_class_name = constant_pool.getConstantString(innerClasse.getInnerClassIndex(), |
|
718 |
Const.CONSTANT_Class); |
|
719 |
inner_class_name = Utility.compactClassName(inner_class_name); |
|
720 |
if (inner_class_name.equals(getClassName())) { |
|
721 |
innerClassAttributeRefersToMe = true; |
|
722 |
} |
|
723 |
if (innerClassAttributeRefersToMe) { |
|
724 |
this.isNested = true; |
|
725 |
if (innerClasse.getInnerNameIndex() == 0) { |
|
726 |
this.isAnonymous = true; |
|
727 |
} |
|
728 |
} |
|
729 |
} |
|
730 |
} |
|
46174 | 731 |
} |
732 |
this.computedNestedTypeStatus = true; |
|
733 |
} |
|
6 | 734 |
|
55496 | 735 |
|
736 |
/** @return returns either HEAP (generated), FILE, or ZIP |
|
46174 | 737 |
*/ |
738 |
public final byte getSource() { |
|
739 |
return source; |
|
6 | 740 |
} |
741 |
||
55496 | 742 |
|
743 |
/********************* New repository functionality *********************/ |
|
46174 | 744 |
/** |
55496 | 745 |
* Gets the ClassRepository which holds its definition. By default |
746 |
* this is the same as SyntheticRepository.getInstance(); |
|
46174 | 747 |
*/ |
748 |
public com.sun.org.apache.bcel.internal.util.Repository getRepository() { |
|
749 |
return repository; |
|
750 |
} |
|
751 |
||
55496 | 752 |
|
46174 | 753 |
/** |
55496 | 754 |
* Sets the ClassRepository which loaded the JavaClass. |
755 |
* Should be called immediately after parsing is done. |
|
46174 | 756 |
*/ |
757 |
public void setRepository(final com.sun.org.apache.bcel.internal.util.Repository repository) { |
|
758 |
this.repository = repository; |
|
759 |
} |
|
760 |
||
55496 | 761 |
|
762 |
/** Equivalent to runtime "instanceof" operator. |
|
46174 | 763 |
* |
764 |
* @return true if this JavaClass is derived from the super class |
|
55496 | 765 |
* @throws ClassNotFoundException if superclasses or superinterfaces |
766 |
* of this object can't be found |
|
46174 | 767 |
*/ |
55496 | 768 |
public final boolean instanceOf( final JavaClass super_class ) throws ClassNotFoundException { |
46174 | 769 |
if (this.equals(super_class)) { |
770 |
return true; |
|
771 |
} |
|
772 |
final JavaClass[] super_classes = getSuperClasses(); |
|
773 |
for (final JavaClass super_classe : super_classes) { |
|
774 |
if (super_classe.equals(super_class)) { |
|
775 |
return true; |
|
776 |
} |
|
777 |
} |
|
778 |
if (super_class.isInterface()) { |
|
779 |
return implementationOf(super_class); |
|
780 |
} |
|
781 |
return false; |
|
782 |
} |
|
783 |
||
55496 | 784 |
|
46174 | 785 |
/** |
786 |
* @return true, if this class is an implementation of interface inter |
|
55496 | 787 |
* @throws ClassNotFoundException if superclasses or superinterfaces |
788 |
* of this class can't be found |
|
46174 | 789 |
*/ |
55496 | 790 |
public boolean implementationOf( final JavaClass inter ) throws ClassNotFoundException { |
46174 | 791 |
if (!inter.isInterface()) { |
792 |
throw new IllegalArgumentException(inter.getClassName() + " is no interface"); |
|
793 |
} |
|
794 |
if (this.equals(inter)) { |
|
795 |
return true; |
|
796 |
} |
|
797 |
final JavaClass[] super_interfaces = getAllInterfaces(); |
|
798 |
for (final JavaClass super_interface : super_interfaces) { |
|
799 |
if (super_interface.equals(inter)) { |
|
800 |
return true; |
|
801 |
} |
|
802 |
} |
|
803 |
return false; |
|
6 | 804 |
} |
805 |
||
55496 | 806 |
|
46174 | 807 |
/** |
55496 | 808 |
* @return the superclass for this JavaClass object, or null if this |
809 |
* is java.lang.Object |
|
46174 | 810 |
* @throws ClassNotFoundException if the superclass can't be found |
811 |
*/ |
|
812 |
public JavaClass getSuperClass() throws ClassNotFoundException { |
|
813 |
if ("java.lang.Object".equals(getClassName())) { |
|
814 |
return null; |
|
815 |
} |
|
816 |
return repository.loadClass(getSuperclassName()); |
|
6 | 817 |
} |
818 |
||
55496 | 819 |
|
46174 | 820 |
/** |
821 |
* @return list of super classes of this class in ascending order, i.e., |
|
822 |
* java.lang.Object is always the last element |
|
823 |
* @throws ClassNotFoundException if any of the superclasses can't be found |
|
824 |
*/ |
|
825 |
public JavaClass[] getSuperClasses() throws ClassNotFoundException { |
|
826 |
JavaClass clazz = this; |
|
827 |
final List<JavaClass> allSuperClasses = new ArrayList<>(); |
|
828 |
for (clazz = clazz.getSuperClass(); clazz != null; clazz = clazz.getSuperClass()) { |
|
829 |
allSuperClasses.add(clazz); |
|
830 |
} |
|
831 |
return allSuperClasses.toArray(new JavaClass[allSuperClasses.size()]); |
|
6 | 832 |
} |
833 |
||
55496 | 834 |
|
46174 | 835 |
/** |
836 |
* Get interfaces directly implemented by this JavaClass. |
|
837 |
*/ |
|
838 |
public JavaClass[] getInterfaces() throws ClassNotFoundException { |
|
839 |
final String[] _interfaces = getInterfaceNames(); |
|
840 |
final JavaClass[] classes = new JavaClass[_interfaces.length]; |
|
841 |
for (int i = 0; i < _interfaces.length; i++) { |
|
842 |
classes[i] = repository.loadClass(_interfaces[i]); |
|
843 |
} |
|
844 |
return classes; |
|
6 | 845 |
} |
846 |
||
55496 | 847 |
|
46174 | 848 |
/** |
849 |
* Get all interfaces implemented by this JavaClass (transitively). |
|
850 |
*/ |
|
851 |
public JavaClass[] getAllInterfaces() throws ClassNotFoundException { |
|
852 |
final ClassQueue queue = new ClassQueue(); |
|
853 |
final Set<JavaClass> allInterfaces = new TreeSet<>(); |
|
854 |
queue.enqueue(this); |
|
855 |
while (!queue.empty()) { |
|
856 |
final JavaClass clazz = queue.dequeue(); |
|
857 |
final JavaClass souper = clazz.getSuperClass(); |
|
858 |
final JavaClass[] _interfaces = clazz.getInterfaces(); |
|
859 |
if (clazz.isInterface()) { |
|
860 |
allInterfaces.add(clazz); |
|
861 |
} else { |
|
862 |
if (souper != null) { |
|
863 |
queue.enqueue(souper); |
|
864 |
} |
|
865 |
} |
|
866 |
for (final JavaClass _interface : _interfaces) { |
|
867 |
queue.enqueue(_interface); |
|
868 |
} |
|
869 |
} |
|
870 |
return allInterfaces.toArray(new JavaClass[allInterfaces.size()]); |
|
6 | 871 |
} |
872 |
||
55496 | 873 |
|
46174 | 874 |
/** |
875 |
* @return Comparison strategy object |
|
876 |
*/ |
|
877 |
public static BCELComparator getComparator() { |
|
878 |
return bcelComparator; |
|
6 | 879 |
} |
880 |
||
55496 | 881 |
|
46174 | 882 |
/** |
883 |
* @param comparator Comparison strategy object |
|
884 |
*/ |
|
55496 | 885 |
public static void setComparator( final BCELComparator comparator ) { |
46174 | 886 |
bcelComparator = comparator; |
887 |
} |
|
6 | 888 |
|
55496 | 889 |
|
46174 | 890 |
/** |
55496 | 891 |
* Return value as defined by given BCELComparator strategy. |
892 |
* By default two JavaClass objects are said to be equal when |
|
893 |
* their class names are equal. |
|
46174 | 894 |
* |
895 |
* @see java.lang.Object#equals(java.lang.Object) |
|
896 |
*/ |
|
897 |
@Override |
|
55496 | 898 |
public boolean equals( final Object obj ) { |
46174 | 899 |
return bcelComparator.equals(this, obj); |
6 | 900 |
} |
901 |
||
55496 | 902 |
|
46174 | 903 |
/** |
55496 | 904 |
* Return the natural ordering of two JavaClasses. |
905 |
* This ordering is based on the class name |
|
46174 | 906 |
* @since 6.0 |
907 |
*/ |
|
908 |
@Override |
|
55496 | 909 |
public int compareTo( final JavaClass obj ) { |
46174 | 910 |
return getClassName().compareTo(obj.getClassName()); |
6 | 911 |
} |
912 |
||
55496 | 913 |
|
46174 | 914 |
/** |
55496 | 915 |
* Return value as defined by given BCELComparator strategy. |
916 |
* By default return the hashcode of the class name. |
|
46174 | 917 |
* |
918 |
* @see java.lang.Object#hashCode() |
|
919 |
*/ |
|
920 |
@Override |
|
921 |
public int hashCode() { |
|
922 |
return bcelComparator.hashCode(this); |
|
923 |
} |
|
6 | 924 |
} |