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.Iterator;
21 import java.util.List;
22 import org.apache.bcel.Constants;
23 import org.apache.bcel.classfile.AccessFlags;
24 import org.apache.bcel.classfile.Attribute;
25 import org.apache.bcel.classfile.ConstantPool;
26 import org.apache.bcel.classfile.Field;
27 import org.apache.bcel.classfile.JavaClass;
28 import org.apache.bcel.classfile.Method;
29 import org.apache.bcel.classfile.SourceFile;
30 import org.apache.bcel.util.BCELComparator;
31
32 /***
33 * Template class for building up a java class. May be initialized with an
34 * existing java class (file).
35 *
36 * @see JavaClass
37 * @version $Id: ClassGen.java 386056 2006-03-15 11:31:56Z tcurdt $
38 * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
39 */
40 public class ClassGen extends AccessFlags implements Cloneable {
41
42
43
44 private String class_name, super_class_name, file_name;
45 private int class_name_index = -1, superclass_name_index = -1;
46 private int major = Constants.MAJOR_1_1, minor = Constants.MINOR_1_1;
47 private ConstantPoolGen cp;
48
49 private List field_vec = new ArrayList();
50 private List method_vec = new ArrayList();
51 private List attribute_vec = new ArrayList();
52 private List interface_vec = new ArrayList();
53 private static BCELComparator _cmp = new BCELComparator() {
54
55 public boolean equals( Object o1, Object o2 ) {
56 ClassGen THIS = (ClassGen) o1;
57 ClassGen THAT = (ClassGen) o2;
58 return THIS.getClassName().equals(THAT.getClassName());
59 }
60
61
62 public int hashCode( Object o ) {
63 ClassGen THIS = (ClassGen) o;
64 return THIS.getClassName().hashCode();
65 }
66 };
67
68
69 /*** Convenience constructor to set up some important values initially.
70 *
71 * @param class_name fully qualified class name
72 * @param super_class_name fully qualified superclass name
73 * @param file_name source file name
74 * @param access_flags access qualifiers
75 * @param interfaces implemented interfaces
76 * @param cp constant pool to use
77 */
78 public ClassGen(String class_name, String super_class_name, String file_name, int access_flags,
79 String[] interfaces, ConstantPoolGen cp) {
80 this.class_name = class_name;
81 this.super_class_name = super_class_name;
82 this.file_name = file_name;
83 this.access_flags = access_flags;
84 this.cp = cp;
85
86 if (file_name != null) {
87 addAttribute(new SourceFile(cp.addUtf8("SourceFile"), 2, cp.addUtf8(file_name), cp
88 .getConstantPool()));
89 }
90 class_name_index = cp.addClass(class_name);
91 superclass_name_index = cp.addClass(super_class_name);
92 if (interfaces != null) {
93 for (int i = 0; i < interfaces.length; i++) {
94 addInterface(interfaces[i]);
95 }
96 }
97 }
98
99
100 /*** Convenience constructor to set up some important values initially.
101 *
102 * @param class_name fully qualified class name
103 * @param super_class_name fully qualified superclass name
104 * @param file_name source file name
105 * @param access_flags access qualifiers
106 * @param interfaces implemented interfaces
107 */
108 public ClassGen(String class_name, String super_class_name, String file_name, int access_flags,
109 String[] interfaces) {
110 this(class_name, super_class_name, file_name, access_flags, interfaces,
111 new ConstantPoolGen());
112 }
113
114
115 /***
116 * Initialize with existing class.
117 * @param clazz JavaClass object (e.g. read from file)
118 */
119 public ClassGen(JavaClass clazz) {
120 class_name_index = clazz.getClassNameIndex();
121 superclass_name_index = clazz.getSuperclassNameIndex();
122 class_name = clazz.getClassName();
123 super_class_name = clazz.getSuperclassName();
124 file_name = clazz.getSourceFileName();
125 access_flags = clazz.getAccessFlags();
126 cp = new ConstantPoolGen(clazz.getConstantPool());
127 major = clazz.getMajor();
128 minor = clazz.getMinor();
129 Attribute[] attributes = clazz.getAttributes();
130 Method[] methods = clazz.getMethods();
131 Field[] fields = clazz.getFields();
132 String[] interfaces = clazz.getInterfaceNames();
133 for (int i = 0; i < interfaces.length; i++) {
134 addInterface(interfaces[i]);
135 }
136 for (int i = 0; i < attributes.length; i++) {
137 addAttribute(attributes[i]);
138 }
139 for (int i = 0; i < methods.length; i++) {
140 addMethod(methods[i]);
141 }
142 for (int i = 0; i < fields.length; i++) {
143 addField(fields[i]);
144 }
145 }
146
147
148 /***
149 * @return the (finally) built up Java class object.
150 */
151 public JavaClass getJavaClass() {
152 int[] interfaces = getInterfaces();
153 Field[] fields = getFields();
154 Method[] methods = getMethods();
155 Attribute[] attributes = getAttributes();
156
157 ConstantPool _cp = this.cp.getFinalConstantPool();
158 return new JavaClass(class_name_index, superclass_name_index, file_name, major, minor,
159 access_flags, _cp, interfaces, fields, methods, attributes);
160 }
161
162
163 /***
164 * Add an interface to this class, i.e., this class has to implement it.
165 * @param name interface to implement (fully qualified class name)
166 */
167 public void addInterface( String name ) {
168 interface_vec.add(name);
169 }
170
171
172 /***
173 * Remove an interface from this class.
174 * @param name interface to remove (fully qualified name)
175 */
176 public void removeInterface( String name ) {
177 interface_vec.remove(name);
178 }
179
180
181 /***
182 * @return major version number of class file
183 */
184 public int getMajor() {
185 return major;
186 }
187
188
189 /*** Set major version number of class file, default value is 45 (JDK 1.1)
190 * @param major major version number
191 */
192 public void setMajor( int major ) {
193 this.major = major;
194 }
195
196
197 /*** Set minor version number of class file, default value is 3 (JDK 1.1)
198 * @param minor minor version number
199 */
200 public void setMinor( int minor ) {
201 this.minor = minor;
202 }
203
204
205 /***
206 * @return minor version number of class file
207 */
208 public int getMinor() {
209 return minor;
210 }
211
212
213 /***
214 * Add an attribute to this class.
215 * @param a attribute to add
216 */
217 public void addAttribute( Attribute a ) {
218 attribute_vec.add(a);
219 }
220
221
222 /***
223 * Add a method to this class.
224 * @param m method to add
225 */
226 public void addMethod( Method m ) {
227 method_vec.add(m);
228 }
229
230
231 /***
232 * Convenience method.
233 *
234 * Add an empty constructor to this class that does nothing but calling super().
235 * @param access_flags rights for constructor
236 */
237 public void addEmptyConstructor( int access_flags ) {
238 InstructionList il = new InstructionList();
239 il.append(InstructionConstants.THIS);
240 il.append(new INVOKESPECIAL(cp.addMethodref(super_class_name, "<init>", "()V")));
241 il.append(InstructionConstants.RETURN);
242 MethodGen mg = new MethodGen(access_flags, Type.VOID, Type.NO_ARGS, null, "<init>",
243 class_name, il, cp);
244 mg.setMaxStack(1);
245 addMethod(mg.getMethod());
246 }
247
248
249 /***
250 * Add a field to this class.
251 * @param f field to add
252 */
253 public void addField( Field f ) {
254 field_vec.add(f);
255 }
256
257
258 public boolean containsField( Field f ) {
259 return field_vec.contains(f);
260 }
261
262
263 /*** @return field object with given name, or null
264 */
265 public Field containsField( String name ) {
266 for (Iterator e = field_vec.iterator(); e.hasNext();) {
267 Field f = (Field) e.next();
268 if (f.getName().equals(name)) {
269 return f;
270 }
271 }
272 return null;
273 }
274
275
276 /*** @return method object with given name and signature, or null
277 */
278 public Method containsMethod( String name, String signature ) {
279 for (Iterator e = method_vec.iterator(); e.hasNext();) {
280 Method m = (Method) e.next();
281 if (m.getName().equals(name) && m.getSignature().equals(signature)) {
282 return m;
283 }
284 }
285 return null;
286 }
287
288
289 /***
290 * Remove an attribute from this class.
291 * @param a attribute to remove
292 */
293 public void removeAttribute( Attribute a ) {
294 attribute_vec.remove(a);
295 }
296
297
298 /***
299 * Remove a method from this class.
300 * @param m method to remove
301 */
302 public void removeMethod( Method m ) {
303 method_vec.remove(m);
304 }
305
306
307 /*** Replace given method with new one. If the old one does not exist
308 * add the new_ method to the class anyway.
309 */
310 public void replaceMethod( Method old, Method new_ ) {
311 if (new_ == null) {
312 throw new ClassGenException("Replacement method must not be null");
313 }
314 int i = method_vec.indexOf(old);
315 if (i < 0) {
316 method_vec.add(new_);
317 } else {
318 method_vec.set(i, new_);
319 }
320 }
321
322
323 /*** Replace given field with new one. If the old one does not exist
324 * add the new_ field to the class anyway.
325 */
326 public void replaceField( Field old, Field new_ ) {
327 if (new_ == null) {
328 throw new ClassGenException("Replacement method must not be null");
329 }
330 int i = field_vec.indexOf(old);
331 if (i < 0) {
332 field_vec.add(new_);
333 } else {
334 field_vec.set(i, new_);
335 }
336 }
337
338
339 /***
340 * Remove a field to this class.
341 * @param f field to remove
342 */
343 public void removeField( Field f ) {
344 field_vec.remove(f);
345 }
346
347
348 public String getClassName() {
349 return class_name;
350 }
351
352
353 public String getSuperclassName() {
354 return super_class_name;
355 }
356
357
358 public String getFileName() {
359 return file_name;
360 }
361
362
363 public void setClassName( String name ) {
364 class_name = name.replace('/', '.');
365 class_name_index = cp.addClass(name);
366 }
367
368
369 public void setSuperclassName( String name ) {
370 super_class_name = name.replace('/', '.');
371 superclass_name_index = cp.addClass(name);
372 }
373
374
375 public Method[] getMethods() {
376 return (Method[]) method_vec.toArray(new Method[method_vec.size()]);
377 }
378
379
380 public void setMethods( Method[] methods ) {
381 method_vec.clear();
382 for (int m = 0; m < methods.length; m++) {
383 addMethod(methods[m]);
384 }
385 }
386
387
388 public void setMethodAt( Method method, int pos ) {
389 method_vec.set(pos, method);
390 }
391
392
393 public Method getMethodAt( int pos ) {
394 return (Method) method_vec.get(pos);
395 }
396
397
398 public String[] getInterfaceNames() {
399 int size = interface_vec.size();
400 String[] interfaces = new String[size];
401 interface_vec.toArray(interfaces);
402 return interfaces;
403 }
404
405
406 public int[] getInterfaces() {
407 int size = interface_vec.size();
408 int[] interfaces = new int[size];
409 for (int i = 0; i < size; i++) {
410 interfaces[i] = cp.addClass((String) interface_vec.get(i));
411 }
412 return interfaces;
413 }
414
415
416 public Field[] getFields() {
417 return (Field[]) field_vec.toArray(new Field[field_vec.size()]);
418 }
419
420
421 public Attribute[] getAttributes() {
422 return (Attribute[]) attribute_vec.toArray(new Attribute[attribute_vec.size()]);
423 }
424
425
426 public ConstantPoolGen getConstantPool() {
427 return cp;
428 }
429
430
431 public void setConstantPool( ConstantPoolGen constant_pool ) {
432 cp = constant_pool;
433 }
434
435
436 public void setClassNameIndex( int class_name_index ) {
437 this.class_name_index = class_name_index;
438 class_name = cp.getConstantPool().getConstantString(class_name_index,
439 Constants.CONSTANT_Class).replace('/', '.');
440 }
441
442
443 public void setSuperclassNameIndex( int superclass_name_index ) {
444 this.superclass_name_index = superclass_name_index;
445 super_class_name = cp.getConstantPool().getConstantString(superclass_name_index,
446 Constants.CONSTANT_Class).replace('/', '.');
447 }
448
449
450 public int getSuperclassNameIndex() {
451 return superclass_name_index;
452 }
453
454
455 public int getClassNameIndex() {
456 return class_name_index;
457 }
458
459 private ArrayList observers;
460
461
462 /*** Add observer for this object.
463 */
464 public void addObserver( ClassObserver o ) {
465 if (observers == null) {
466 observers = new ArrayList();
467 }
468 observers.add(o);
469 }
470
471
472 /*** Remove observer for this object.
473 */
474 public void removeObserver( ClassObserver o ) {
475 if (observers != null) {
476 observers.remove(o);
477 }
478 }
479
480
481 /*** Call notify() method on all observers. This method is not called
482 * automatically whenever the state has changed, but has to be
483 * called by the user after he has finished editing the object.
484 */
485 public void update() {
486 if (observers != null) {
487 for (Iterator e = observers.iterator(); e.hasNext();) {
488 ((ClassObserver) e.next()).notify(this);
489 }
490 }
491 }
492
493
494 public Object clone() {
495 try {
496 return super.clone();
497 } catch (CloneNotSupportedException e) {
498 System.err.println(e);
499 return null;
500 }
501 }
502
503
504 /***
505 * @return Comparison strategy object
506 */
507 public static BCELComparator getComparator() {
508 return _cmp;
509 }
510
511
512 /***
513 * @param comparator Comparison strategy object
514 */
515 public static void setComparator( BCELComparator comparator ) {
516 _cmp = comparator;
517 }
518
519
520 /***
521 * Return value as defined by given BCELComparator strategy.
522 * By default two ClassGen objects are said to be equal when
523 * their class names are equal.
524 *
525 * @see java.lang.Object#equals(java.lang.Object)
526 */
527 public boolean equals( Object obj ) {
528 return _cmp.equals(this, obj);
529 }
530
531
532 /***
533 * Return value as defined by given BCELComparator strategy.
534 * By default return the hashcode of the class name.
535 *
536 * @see java.lang.Object#hashCode()
537 */
538 public int hashCode() {
539 return _cmp.hashCode(this);
540 }
541 }