1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 package org.apache.bcel.generic; 19 20 import java.util.ArrayList; 21 import java.util.List; 22 23 import org.apache.bcel.Const; 24 import org.apache.bcel.classfile.AccessFlags; 25 import org.apache.bcel.classfile.AnnotationEntry; 26 import org.apache.bcel.classfile.Annotations; 27 import org.apache.bcel.classfile.Attribute; 28 import org.apache.bcel.classfile.ConstantPool; 29 import org.apache.bcel.classfile.Field; 30 import org.apache.bcel.classfile.JavaClass; 31 import org.apache.bcel.classfile.Method; 32 import org.apache.bcel.classfile.RuntimeInvisibleAnnotations; 33 import org.apache.bcel.classfile.RuntimeVisibleAnnotations; 34 import org.apache.bcel.classfile.SourceFile; 35 import org.apache.bcel.util.BCELComparator; 36 37 /** 38 * Template class for building up a java class. May be initialized with an 39 * existing java class (file). 40 * 41 * @see JavaClass 42 * @version $Id$ 43 */ 44 public class ClassGen extends AccessFlags implements Cloneable { 45 46 /* Corresponds to the fields found in a JavaClass object. 47 */ 48 private String class_name; 49 private String super_class_name; 50 private final String file_name; 51 private int class_name_index = -1; 52 private int superclass_name_index = -1; 53 private int major = Const.MAJOR_1_1; 54 private int minor = Const.MINOR_1_1; 55 private ConstantPoolGen cp; // Template for building up constant pool 56 // ArrayLists instead of arrays to gather fields, methods, etc. 57 private final List<Field> field_vec = new ArrayList<>(); 58 private final List<Method> method_vec = new ArrayList<>(); 59 private final List<Attribute> attribute_vec = new ArrayList<>(); 60 private final List<String> interface_vec = new ArrayList<>(); 61 private final List<AnnotationEntryGen> annotation_vec = new ArrayList<>(); 62 63 private static BCELComparator _cmp = new BCELComparator() { 64 65 @Override 66 public boolean equals( final Object o1, final Object o2 ) { 67 final ClassGen THIS = (ClassGen) o1; 68 final ClassGen THAT = (ClassGen) o2; 69 return THIS.getClassName().equals(THAT.getClassName()); 70 } 71 72 73 @Override 74 public int hashCode( final Object o ) { 75 final ClassGen THIS = (ClassGen) o; 76 return THIS.getClassName().hashCode(); 77 } 78 }; 79 80 81 /** Convenience constructor to set up some important values initially. 82 * 83 * @param class_name fully qualified class name 84 * @param super_class_name fully qualified superclass name 85 * @param file_name source file name 86 * @param access_flags access qualifiers 87 * @param interfaces implemented interfaces 88 * @param cp constant pool to use 89 */ ClassGen(final String class_name, final String super_class_name, final String file_name, final int access_flags, final String[] interfaces, final ConstantPoolGen cp)90 public ClassGen(final String class_name, final String super_class_name, final String file_name, final int access_flags, 91 final String[] interfaces, final ConstantPoolGen cp) { 92 super(access_flags); 93 this.class_name = class_name; 94 this.super_class_name = super_class_name; 95 this.file_name = file_name; 96 this.cp = cp; 97 // Put everything needed by default into the constant pool and the vectors 98 if (file_name != null) { 99 addAttribute(new SourceFile(cp.addUtf8("SourceFile"), 2, cp.addUtf8(file_name), cp 100 .getConstantPool())); 101 } 102 class_name_index = cp.addClass(class_name); 103 superclass_name_index = cp.addClass(super_class_name); 104 if (interfaces != null) { 105 for (final String interface1 : interfaces) { 106 addInterface(interface1); 107 } 108 } 109 } 110 111 112 /** Convenience constructor to set up some important values initially. 113 * 114 * @param class_name fully qualified class name 115 * @param super_class_name fully qualified superclass name 116 * @param file_name source file name 117 * @param access_flags access qualifiers 118 * @param interfaces implemented interfaces 119 */ ClassGen(final String class_name, final String super_class_name, final String file_name, final int access_flags, final String[] interfaces)120 public ClassGen(final String class_name, final String super_class_name, final String file_name, final int access_flags, 121 final String[] interfaces) { 122 this(class_name, super_class_name, file_name, access_flags, interfaces, 123 new ConstantPoolGen()); 124 } 125 126 127 /** 128 * Initialize with existing class. 129 * @param clazz JavaClass object (e.g. read from file) 130 */ ClassGen(final JavaClass clazz)131 public ClassGen(final JavaClass clazz) { 132 super(clazz.getAccessFlags()); 133 class_name_index = clazz.getClassNameIndex(); 134 superclass_name_index = clazz.getSuperclassNameIndex(); 135 class_name = clazz.getClassName(); 136 super_class_name = clazz.getSuperclassName(); 137 file_name = clazz.getSourceFileName(); 138 cp = new ConstantPoolGen(clazz.getConstantPool()); 139 major = clazz.getMajor(); 140 minor = clazz.getMinor(); 141 final Attribute[] attributes = clazz.getAttributes(); 142 // J5TODO: Could make unpacking lazy, done on first reference 143 final AnnotationEntryGen[] annotations = unpackAnnotations(attributes); 144 final Method[] methods = clazz.getMethods(); 145 final Field[] fields = clazz.getFields(); 146 final String[] interfaces = clazz.getInterfaceNames(); 147 for (final String interface1 : interfaces) { 148 addInterface(interface1); 149 } 150 for (final Attribute attribute : attributes) { 151 if (!(attribute instanceof Annotations)) { 152 addAttribute(attribute); 153 } 154 } 155 for (final AnnotationEntryGen annotation : annotations) { 156 addAnnotationEntry(annotation); 157 } 158 for (final Method method : methods) { 159 addMethod(method); 160 } 161 for (final Field field : fields) { 162 addField(field); 163 } 164 } 165 166 /** 167 * Look for attributes representing annotations and unpack them. 168 */ unpackAnnotations(final Attribute[] attrs)169 private AnnotationEntryGen[] unpackAnnotations(final Attribute[] attrs) 170 { 171 final List<AnnotationEntryGen> annotationGenObjs = new ArrayList<>(); 172 for (final Attribute attr : attrs) { 173 if (attr instanceof RuntimeVisibleAnnotations) 174 { 175 final RuntimeVisibleAnnotations rva = (RuntimeVisibleAnnotations) attr; 176 final AnnotationEntry[] annos = rva.getAnnotationEntries(); 177 for (final AnnotationEntry a : annos) { 178 annotationGenObjs.add(new AnnotationEntryGen(a, 179 getConstantPool(), false)); 180 } 181 } 182 else 183 if (attr instanceof RuntimeInvisibleAnnotations) 184 { 185 final RuntimeInvisibleAnnotations ria = (RuntimeInvisibleAnnotations) attr; 186 final AnnotationEntry[] annos = ria.getAnnotationEntries(); 187 for (final AnnotationEntry a : annos) { 188 annotationGenObjs.add(new AnnotationEntryGen(a, 189 getConstantPool(), false)); 190 } 191 } 192 } 193 return annotationGenObjs.toArray(new AnnotationEntryGen[annotationGenObjs.size()]); 194 } 195 196 197 /** 198 * @return the (finally) built up Java class object. 199 */ getJavaClass()200 public JavaClass getJavaClass() { 201 final int[] interfaces = getInterfaces(); 202 final Field[] fields = getFields(); 203 final Method[] methods = getMethods(); 204 Attribute[] attributes = null; 205 if (annotation_vec.isEmpty()) { 206 attributes = getAttributes(); 207 } else { 208 // TODO: Sometime later, trash any attributes called 'RuntimeVisibleAnnotations' or 'RuntimeInvisibleAnnotations' 209 final Attribute[] annAttributes = AnnotationEntryGen.getAnnotationAttributes(cp, getAnnotationEntries()); 210 attributes = new Attribute[attribute_vec.size()+annAttributes.length]; 211 attribute_vec.toArray(attributes); 212 System.arraycopy(annAttributes,0,attributes,attribute_vec.size(),annAttributes.length); 213 } 214 // Must be last since the above calls may still add something to it 215 final ConstantPool _cp = this.cp.getFinalConstantPool(); 216 return new JavaClass(class_name_index, superclass_name_index, file_name, major, minor, 217 super.getAccessFlags(), _cp, interfaces, fields, methods, attributes); 218 } 219 220 221 /** 222 * Add an interface to this class, i.e., this class has to implement it. 223 * @param name interface to implement (fully qualified class name) 224 */ addInterface( final String name )225 public void addInterface( final String name ) { 226 interface_vec.add(name); 227 } 228 229 230 /** 231 * Remove an interface from this class. 232 * @param name interface to remove (fully qualified name) 233 */ removeInterface( final String name )234 public void removeInterface( final String name ) { 235 interface_vec.remove(name); 236 } 237 238 239 /** 240 * @return major version number of class file 241 */ getMajor()242 public int getMajor() { 243 return major; 244 } 245 246 247 /** Set major version number of class file, default value is 45 (JDK 1.1) 248 * @param major major version number 249 */ setMajor( final int major )250 public void setMajor( final int major ) { // TODO could be package-protected - only called by test code 251 this.major = major; 252 } 253 254 255 /** Set minor version number of class file, default value is 3 (JDK 1.1) 256 * @param minor minor version number 257 */ setMinor( final int minor )258 public void setMinor( final int minor ) { // TODO could be package-protected - only called by test code 259 this.minor = minor; 260 } 261 262 /** 263 * @return minor version number of class file 264 */ getMinor()265 public int getMinor() { 266 return minor; 267 } 268 269 270 /** 271 * Add an attribute to this class. 272 * @param a attribute to add 273 */ addAttribute( final Attribute a )274 public void addAttribute( final Attribute a ) { 275 attribute_vec.add(a); 276 } 277 addAnnotationEntry(final AnnotationEntryGen a)278 public void addAnnotationEntry(final AnnotationEntryGen a) { 279 annotation_vec.add(a); 280 } 281 282 283 /** 284 * Add a method to this class. 285 * @param m method to add 286 */ addMethod( final Method m )287 public void addMethod( final Method m ) { 288 method_vec.add(m); 289 } 290 291 292 /** 293 * Convenience method. 294 * 295 * Add an empty constructor to this class that does nothing but calling super(). 296 * @param access_flags rights for constructor 297 */ addEmptyConstructor( final int access_flags )298 public void addEmptyConstructor( final int access_flags ) { 299 final InstructionList il = new InstructionList(); 300 il.append(InstructionConst.THIS); // Push `this' 301 il.append(new INVOKESPECIAL(cp.addMethodref(super_class_name, "<init>", "()V"))); 302 il.append(InstructionConst.RETURN); 303 final MethodGen mg = new MethodGen(access_flags, Type.VOID, Type.NO_ARGS, null, "<init>", 304 class_name, il, cp); 305 mg.setMaxStack(1); 306 addMethod(mg.getMethod()); 307 } 308 309 310 /** 311 * Add a field to this class. 312 * @param f field to add 313 */ addField( final Field f )314 public void addField( final Field f ) { 315 field_vec.add(f); 316 } 317 318 containsField( final Field f )319 public boolean containsField( final Field f ) { 320 return field_vec.contains(f); 321 } 322 323 324 /** @return field object with given name, or null 325 */ containsField( final String name )326 public Field containsField( final String name ) { 327 for (final Field f : field_vec) { 328 if (f.getName().equals(name)) { 329 return f; 330 } 331 } 332 return null; 333 } 334 335 336 /** @return method object with given name and signature, or null 337 */ containsMethod( final String name, final String signature )338 public Method containsMethod( final String name, final String signature ) { 339 for (final Method m : method_vec) { 340 if (m.getName().equals(name) && m.getSignature().equals(signature)) { 341 return m; 342 } 343 } 344 return null; 345 } 346 347 348 /** 349 * Remove an attribute from this class. 350 * @param a attribute to remove 351 */ removeAttribute( final Attribute a )352 public void removeAttribute( final Attribute a ) { 353 attribute_vec.remove(a); 354 } 355 356 357 /** 358 * Remove a method from this class. 359 * @param m method to remove 360 */ removeMethod( final Method m )361 public void removeMethod( final Method m ) { 362 method_vec.remove(m); 363 } 364 365 366 /** Replace given method with new one. If the old one does not exist 367 * add the new_ method to the class anyway. 368 */ replaceMethod( final Method old, final Method new_ )369 public void replaceMethod( final Method old, final Method new_ ) { 370 if (new_ == null) { 371 throw new ClassGenException("Replacement method must not be null"); 372 } 373 final int i = method_vec.indexOf(old); 374 if (i < 0) { 375 method_vec.add(new_); 376 } else { 377 method_vec.set(i, new_); 378 } 379 } 380 381 382 /** Replace given field with new one. If the old one does not exist 383 * add the new_ field to the class anyway. 384 */ replaceField( final Field old, final Field new_ )385 public void replaceField( final Field old, final Field new_ ) { 386 if (new_ == null) { 387 throw new ClassGenException("Replacement method must not be null"); 388 } 389 final int i = field_vec.indexOf(old); 390 if (i < 0) { 391 field_vec.add(new_); 392 } else { 393 field_vec.set(i, new_); 394 } 395 } 396 397 398 /** 399 * Remove a field to this class. 400 * @param f field to remove 401 */ removeField( final Field f )402 public void removeField( final Field f ) { 403 field_vec.remove(f); 404 } 405 406 getClassName()407 public String getClassName() { 408 return class_name; 409 } 410 411 getSuperclassName()412 public String getSuperclassName() { 413 return super_class_name; 414 } 415 416 getFileName()417 public String getFileName() { 418 return file_name; 419 } 420 421 setClassName( final String name )422 public void setClassName( final String name ) { 423 class_name = name.replace('/', '.'); 424 class_name_index = cp.addClass(name); 425 } 426 427 setSuperclassName( final String name )428 public void setSuperclassName( final String name ) { 429 super_class_name = name.replace('/', '.'); 430 superclass_name_index = cp.addClass(name); 431 } 432 433 getMethods()434 public Method[] getMethods() { 435 return method_vec.toArray(new Method[method_vec.size()]); 436 } 437 438 setMethods( final Method[] methods )439 public void setMethods( final Method[] methods ) { 440 method_vec.clear(); 441 for (final Method method : methods) { 442 addMethod(method); 443 } 444 } 445 446 setMethodAt( final Method method, final int pos )447 public void setMethodAt( final Method method, final int pos ) { 448 method_vec.set(pos, method); 449 } 450 451 getMethodAt( final int pos )452 public Method getMethodAt( final int pos ) { 453 return method_vec.get(pos); 454 } 455 456 getInterfaceNames()457 public String[] getInterfaceNames() { 458 final int size = interface_vec.size(); 459 final String[] interfaces = new String[size]; 460 interface_vec.toArray(interfaces); 461 return interfaces; 462 } 463 464 getInterfaces()465 public int[] getInterfaces() { 466 final int size = interface_vec.size(); 467 final int[] interfaces = new int[size]; 468 for (int i = 0; i < size; i++) { 469 interfaces[i] = cp.addClass(interface_vec.get(i)); 470 } 471 return interfaces; 472 } 473 474 getFields()475 public Field[] getFields() { 476 return field_vec.toArray(new Field[field_vec.size()]); 477 } 478 479 getAttributes()480 public Attribute[] getAttributes() { 481 return attribute_vec.toArray(new Attribute[attribute_vec.size()]); 482 } 483 484 // J5TODO: Should we make calling unpackAnnotations() lazy and put it in here? getAnnotationEntries()485 public AnnotationEntryGen[] getAnnotationEntries() { 486 return annotation_vec.toArray(new AnnotationEntryGen[annotation_vec.size()]); 487 } 488 489 getConstantPool()490 public ConstantPoolGen getConstantPool() { 491 return cp; 492 } 493 494 setConstantPool( final ConstantPoolGen constant_pool )495 public void setConstantPool( final ConstantPoolGen constant_pool ) { 496 cp = constant_pool; 497 } 498 499 setClassNameIndex( final int class_name_index )500 public void setClassNameIndex( final int class_name_index ) { 501 this.class_name_index = class_name_index; 502 class_name = cp.getConstantPool().getConstantString(class_name_index, 503 Const.CONSTANT_Class).replace('/', '.'); 504 } 505 506 setSuperclassNameIndex( final int superclass_name_index )507 public void setSuperclassNameIndex( final int superclass_name_index ) { 508 this.superclass_name_index = superclass_name_index; 509 super_class_name = cp.getConstantPool().getConstantString(superclass_name_index, 510 Const.CONSTANT_Class).replace('/', '.'); 511 } 512 513 getSuperclassNameIndex()514 public int getSuperclassNameIndex() { 515 return superclass_name_index; 516 } 517 518 getClassNameIndex()519 public int getClassNameIndex() { 520 return class_name_index; 521 } 522 523 private List<ClassObserver> observers; 524 525 526 /** Add observer for this object. 527 */ addObserver( final ClassObserver o )528 public void addObserver( final ClassObserver o ) { 529 if (observers == null) { 530 observers = new ArrayList<>(); 531 } 532 observers.add(o); 533 } 534 535 536 /** Remove observer for this object. 537 */ removeObserver( final ClassObserver o )538 public void removeObserver( final ClassObserver o ) { 539 if (observers != null) { 540 observers.remove(o); 541 } 542 } 543 544 545 /** Call notify() method on all observers. This method is not called 546 * automatically whenever the state has changed, but has to be 547 * called by the user after he has finished editing the object. 548 */ update()549 public void update() { 550 if (observers != null) { 551 for (final ClassObserver observer : observers) { 552 observer.notify(this); 553 } 554 } 555 } 556 557 558 @Override clone()559 public Object clone() { 560 try { 561 return super.clone(); 562 } catch (final CloneNotSupportedException e) { 563 throw new Error("Clone Not Supported"); // never happens 564 } 565 } 566 567 568 /** 569 * @return Comparison strategy object 570 */ getComparator()571 public static BCELComparator getComparator() { 572 return _cmp; 573 } 574 575 576 /** 577 * @param comparator Comparison strategy object 578 */ setComparator( final BCELComparator comparator )579 public static void setComparator( final BCELComparator comparator ) { 580 _cmp = comparator; 581 } 582 583 584 /** 585 * Return value as defined by given BCELComparator strategy. 586 * By default two ClassGen objects are said to be equal when 587 * their class names are equal. 588 * 589 * @see java.lang.Object#equals(java.lang.Object) 590 */ 591 @Override equals( final Object obj )592 public boolean equals( final Object obj ) { 593 return _cmp.equals(this, obj); 594 } 595 596 597 /** 598 * Return value as defined by given BCELComparator strategy. 599 * By default return the hashcode of the class name. 600 * 601 * @see java.lang.Object#hashCode() 602 */ 603 @Override hashCode()604 public int hashCode() { 605 return _cmp.hashCode(this); 606 } 607 } 608