1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.bcel.generic;
18
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.List;
23 import java.util.Objects;
24
25 import org.apache.bcel.Const;
26 import org.apache.bcel.classfile.AccessFlags;
27 import org.apache.bcel.classfile.Annotations;
28 import org.apache.bcel.classfile.Attribute;
29 import org.apache.bcel.classfile.ConstantPool;
30 import org.apache.bcel.classfile.Field;
31 import org.apache.bcel.classfile.JavaClass;
32 import org.apache.bcel.classfile.Method;
33 import org.apache.bcel.classfile.RuntimeInvisibleAnnotations;
34 import org.apache.bcel.classfile.RuntimeVisibleAnnotations;
35 import org.apache.bcel.classfile.SourceFile;
36 import org.apache.bcel.classfile.Utility;
37 import org.apache.bcel.util.BCELComparator;
38 import org.apache.commons.lang3.ArrayUtils;
39
40
41
42
43
44
45 public class ClassGen extends AccessFlags implements Cloneable {
46
47 private static BCELComparator<ClassGen> bcelComparator = new BCELComparator<ClassGen>() {
48
49 @Override
50 public boolean equals(final ClassGen a, final ClassGen b) {
51 return a == b || a != null && b != null && Objects.equals(a.getClassName(), b.getClassName());
52 }
53
54 @Override
55 public int hashCode(final ClassGen o) {
56 return o != null ? Objects.hashCode(o.getClassName()) : 0;
57 }
58 };
59
60
61
62
63 public static BCELComparator<ClassGen> getComparator() {
64 return bcelComparator;
65 }
66
67
68
69
70 public static void setComparator(final BCELComparator<ClassGen> comparator) {
71 bcelComparator = comparator;
72 }
73
74
75
76
77 private String className;
78 private String superClassName;
79 private final String fileName;
80 private int classNameIndex = -1;
81 private int superclassNameIndex = -1;
82 private int major = Const.MAJOR_1_1;
83 private int minor = Const.MINOR_1_1;
84 private ConstantPoolGen cp;
85
86 private final List<Field> fieldList = new ArrayList<>();
87 private final List<Method> methodList = new ArrayList<>();
88
89 private final List<Attribute> attributeList = new ArrayList<>();
90
91 private final List<String> interfaceList = new ArrayList<>();
92
93 private final List<AnnotationEntryGen> annotationList = new ArrayList<>();
94
95 private List<ClassObserver> observers;
96
97
98
99
100
101
102 public ClassGen(final JavaClass clazz) {
103 super(clazz.getAccessFlags());
104 classNameIndex = clazz.getClassNameIndex();
105 superclassNameIndex = clazz.getSuperclassNameIndex();
106 className = clazz.getClassName();
107 superClassName = clazz.getSuperclassName();
108 fileName = clazz.getSourceFileName();
109 cp = new ConstantPoolGen(clazz.getConstantPool());
110 major = clazz.getMajor();
111 minor = clazz.getMinor();
112 final Attribute[] attributes = clazz.getAttributes();
113
114 final AnnotationEntryGen[] annotations = unpackAnnotations(attributes);
115 final String[] interfaceNames = clazz.getInterfaceNames();
116 if (interfaceNames != null) {
117 Collections.addAll(interfaceList, interfaceNames);
118 }
119 if (attributes != null) {
120 for (final Attribute attribute : attributes) {
121 if (!(attribute instanceof Annotations)) {
122 addAttribute(attribute);
123 }
124 }
125 }
126 Collections.addAll(annotationList, annotations);
127 final Method[] methods = clazz.getMethods();
128 if (methods != null) {
129 Collections.addAll(methodList, methods);
130 }
131 final Field[] fields = clazz.getFields();
132 if (fields != null) {
133 Collections.addAll(fieldList, fields);
134 }
135 }
136
137
138
139
140
141
142
143
144
145
146 public ClassGen(final String className, final String superClassName, final String fileName, final int accessFlags, final String[] interfaces) {
147 this(className, superClassName, fileName, accessFlags, interfaces, new ConstantPoolGen());
148 }
149
150
151
152
153
154
155
156
157
158
159
160 public ClassGen(final String className, final String superClassName, final String fileName, final int accessFlags, final String[] interfaces,
161 final ConstantPoolGen cp) {
162 super(accessFlags);
163 this.className = className;
164 this.superClassName = superClassName;
165 this.fileName = fileName;
166 this.cp = cp;
167
168 if (fileName != null) {
169 addAttribute(new SourceFile(cp.addUtf8("SourceFile"), 2, cp.addUtf8(fileName), cp.getConstantPool()));
170 }
171 classNameIndex = cp.addClass(className);
172 superclassNameIndex = cp.addClass(superClassName);
173 if (interfaces != null) {
174 Collections.addAll(interfaceList, interfaces);
175 }
176 }
177
178 public void addAnnotationEntry(final AnnotationEntryGen a) {
179 annotationList.add(a);
180 }
181
182
183
184
185
186
187 public void addAttribute(final Attribute a) {
188 attributeList.add(a);
189 }
190
191
192
193
194
195
196
197
198 public void addEmptyConstructor(final int accessFlags) {
199 final InstructionList il = new InstructionList();
200 il.append(InstructionConst.THIS);
201 il.append(new INVOKESPECIAL(cp.addMethodref(superClassName, Const.CONSTRUCTOR_NAME, "()V")));
202 il.append(InstructionConst.RETURN);
203 final MethodGen mg = new MethodGen(accessFlags, Type.VOID, Type.NO_ARGS, null, Const.CONSTRUCTOR_NAME, className, il, cp);
204 mg.setMaxStack(1);
205 addMethod(mg.getMethod());
206 }
207
208
209
210
211
212
213 public void addField(final Field f) {
214 fieldList.add(f);
215 }
216
217
218
219
220
221
222 public void addInterface(final String name) {
223 interfaceList.add(name);
224 }
225
226
227
228
229
230
231 public void addMethod(final Method m) {
232 methodList.add(m);
233 }
234
235
236
237
238 public void addObserver(final ClassObserver o) {
239 if (observers == null) {
240 observers = new ArrayList<>();
241 }
242 observers.add(o);
243 }
244
245 @Override
246 public Object clone() {
247 try {
248 return super.clone();
249 } catch (final CloneNotSupportedException e) {
250 throw new UnsupportedOperationException("Clone Not Supported", e);
251 }
252 }
253
254 public boolean containsField(final Field f) {
255 return fieldList.contains(f);
256 }
257
258
259
260
261 public Field containsField(final String name) {
262 for (final Field f : fieldList) {
263 if (f.getName().equals(name)) {
264 return f;
265 }
266 }
267 return null;
268 }
269
270
271
272
273 public Method containsMethod(final String name, final String signature) {
274 for (final Method m : methodList) {
275 if (m.getName().equals(name) && m.getSignature().equals(signature)) {
276 return m;
277 }
278 }
279 return null;
280 }
281
282
283
284
285
286
287
288 @Override
289 public boolean equals(final Object obj) {
290 return obj instanceof ClassGen && bcelComparator.equals(this, (ClassGen) obj);
291 }
292
293
294 public AnnotationEntryGen[] getAnnotationEntries() {
295 return annotationList.toArray(AnnotationEntryGen.EMPTY_ARRAY);
296 }
297
298 public Attribute[] getAttributes() {
299 return attributeList.toArray(Attribute.EMPTY_ARRAY);
300 }
301
302 public String getClassName() {
303 return className;
304 }
305
306 public int getClassNameIndex() {
307 return classNameIndex;
308 }
309
310 public ConstantPoolGen getConstantPool() {
311 return cp;
312 }
313
314 public Field[] getFields() {
315 return fieldList.toArray(Field.EMPTY_ARRAY);
316 }
317
318 public String getFileName() {
319 return fileName;
320 }
321
322 public String[] getInterfaceNames() {
323 return interfaceList.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
324 }
325
326 public int[] getInterfaces() {
327 final int size = interfaceList.size();
328 final int[] interfaces = new int[size];
329 Arrays.setAll(interfaces, i -> cp.addClass(interfaceList.get(i)));
330 return interfaces;
331 }
332
333
334
335
336 public JavaClass getJavaClass() {
337 final int[] interfaces = getInterfaces();
338 final Field[] fields = getFields();
339 final Method[] methods = getMethods();
340 Attribute[] attributes = null;
341 if (annotationList.isEmpty()) {
342 attributes = getAttributes();
343 } else {
344
345 final Attribute[] annAttributes = AnnotationEntryGen.getAnnotationAttributes(cp, getAnnotationEntries());
346 attributes = new Attribute[attributeList.size() + annAttributes.length];
347 attributeList.toArray(attributes);
348 System.arraycopy(annAttributes, 0, attributes, attributeList.size(), annAttributes.length);
349 }
350
351 final ConstantPool cp = this.cp.getFinalConstantPool();
352 return new JavaClass(classNameIndex, superclassNameIndex, fileName, major, minor, super.getAccessFlags(), cp, interfaces, fields, methods,
353 attributes);
354 }
355
356
357
358
359 public int getMajor() {
360 return major;
361 }
362
363 public Method getMethodAt(final int pos) {
364 return methodList.get(pos);
365 }
366
367 public Method[] getMethods() {
368 return methodList.toArray(Method.EMPTY_ARRAY);
369 }
370
371
372
373
374 public int getMinor() {
375 return minor;
376 }
377
378 public String getSuperclassName() {
379 return superClassName;
380 }
381
382 public int getSuperclassNameIndex() {
383 return superclassNameIndex;
384 }
385
386
387
388
389
390
391 @Override
392 public int hashCode() {
393 return bcelComparator.hashCode(this);
394 }
395
396
397
398
399
400
401 public void removeAttribute(final Attribute a) {
402 attributeList.remove(a);
403 }
404
405
406
407
408
409
410 public void removeField(final Field f) {
411 fieldList.remove(f);
412 }
413
414
415
416
417
418
419 public void removeInterface(final String name) {
420 interfaceList.remove(name);
421 }
422
423
424
425
426
427
428 public void removeMethod(final Method m) {
429 methodList.remove(m);
430 }
431
432
433
434
435 public void removeObserver(final ClassObserver o) {
436 if (observers != null) {
437 observers.remove(o);
438 }
439 }
440
441
442
443
444 public void replaceField(final Field old, final Field newField) {
445 if (newField == null) {
446 throw new ClassGenException("Replacement method must not be null");
447 }
448 final int i = fieldList.indexOf(old);
449 if (i < 0) {
450 fieldList.add(newField);
451 } else {
452 fieldList.set(i, newField);
453 }
454 }
455
456
457
458
459 public void replaceMethod(final Method old, final Method newMethod) {
460 if (newMethod == null) {
461 throw new ClassGenException("Replacement method must not be null");
462 }
463 final int i = methodList.indexOf(old);
464 if (i < 0) {
465 methodList.add(newMethod);
466 } else {
467 methodList.set(i, newMethod);
468 }
469 }
470
471 public void setClassName(final String name) {
472 className = Utility.pathToPackage(name);
473 classNameIndex = cp.addClass(name);
474 }
475
476 public void setClassNameIndex(final int classNameIndex) {
477 this.classNameIndex = classNameIndex;
478 this.className = Utility.pathToPackage(cp.getConstantPool().getConstantString(classNameIndex, Const.CONSTANT_Class));
479 }
480
481 public void setConstantPool(final ConstantPoolGen constantPool) {
482 cp = constantPool;
483 }
484
485
486
487
488
489
490 public void setMajor(final int major) {
491 this.major = major;
492 }
493
494 public void setMethodAt(final Method method, final int pos) {
495 methodList.set(pos, method);
496 }
497
498 public void setMethods(final Method[] methods) {
499 methodList.clear();
500 if (methods != null) {
501 Collections.addAll(methodList, methods);
502 }
503 }
504
505
506
507
508
509
510 public void setMinor(final int minor) {
511 this.minor = minor;
512 }
513
514 public void setSuperclassName(final String name) {
515 superClassName = Utility.pathToPackage(name);
516 superclassNameIndex = cp.addClass(name);
517 }
518
519 public void setSuperclassNameIndex(final int superclassNameIndex) {
520 this.superclassNameIndex = superclassNameIndex;
521 superClassName = Utility.pathToPackage(cp.getConstantPool().getConstantString(superclassNameIndex, Const.CONSTANT_Class));
522 }
523
524
525
526
527 private AnnotationEntryGen[] unpackAnnotations(final Attribute[] attributes) {
528 final List<AnnotationEntryGen> annotationGenObjs = new ArrayList<>();
529 if (attributes != null) {
530 for (final Attribute attr : attributes) {
531 if (attr instanceof RuntimeVisibleAnnotations) {
532 final RuntimeVisibleAnnotations rva = (RuntimeVisibleAnnotations) attr;
533 rva.forEach(a -> annotationGenObjs.add(new AnnotationEntryGen(a, getConstantPool(), false)));
534 } else if (attr instanceof RuntimeInvisibleAnnotations) {
535 final RuntimeInvisibleAnnotations ria = (RuntimeInvisibleAnnotations) attr;
536 ria.forEach(a -> annotationGenObjs.add(new AnnotationEntryGen(a, getConstantPool(), false)));
537 }
538 }
539 }
540 return annotationGenObjs.toArray(AnnotationEntryGen.EMPTY_ARRAY);
541 }
542
543
544
545
546
547 public void update() {
548 if (observers != null) {
549 for (final ClassObserver observer : observers) {
550 observer.notify(this);
551 }
552 }
553 }
554 }