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