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