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