001 package serp.bytecode; 002 003 import java.io.*; 004 import java.net.*; 005 import java.util.*; 006 007 import serp.bytecode.lowlevel.*; 008 import serp.bytecode.visitor.*; 009 import serp.util.*; 010 011 /** 012 * The BCClass represents a class object in the bytecode framework, in many 013 * ways mirroring the {@link Class} class of Java reflection. The represented 014 * class might be a primitive, array, existing object type, or some new user- 015 * defined type. As with most entities in the bytecode framework, the BCClass 016 * contains methods to manipulate the low-level state of the class (constant 017 * pool indexes, etc), but these can and should be ignored in 018 * favor of the available high-level methods. 019 * 020 * <p>A BCClass instance is loaded from a {@link Project} and remains 021 * attached to that project for its lifetime. If a BCClass is removed from 022 * its project, the result of any further operations on the class are 023 * undefined.</p> 024 * 025 * <p>Note that if a BCClass represents a primitive or array type, all of the 026 * available mutator methods and any methods that access the constant pool 027 * will throw {@link UnsupportedOperationException}s.</p> 028 * 029 * @author Abe White 030 */ 031 public class BCClass extends Annotated implements VisitAcceptor { 032 private Project _project = null; 033 private State _state = null; 034 private ClassLoader _loader = null; 035 036 /** 037 * Hide constructor. For use by the owning project only. 038 */ 039 BCClass(Project project) { 040 _project = project; 041 } 042 043 /** 044 * Set the class state. For use by the owning project only. 045 */ 046 void setState(State state) { 047 _state = state; 048 } 049 050 /** 051 * Invalidate this class. 052 */ 053 void invalidate() { 054 _project = null; 055 _state = State.INVALID; 056 } 057 058 ////////////////// 059 // I/O operations 060 ////////////////// 061 062 /** 063 * Initialize from the class definition in the given file. For use by 064 * the owning project only. 065 */ 066 void read(File classFile, ClassLoader loader) throws IOException { 067 InputStream in = new FileInputStream(classFile); 068 try { 069 read(in, loader); 070 } finally { 071 in.close(); 072 } 073 } 074 075 /** 076 * Initialize from the class definition in the given stream. For use by 077 * the owning project only. 078 */ 079 void read(InputStream instream, ClassLoader loader) 080 throws IOException { 081 DataInput in = new DataInputStream(instream); 082 083 // header information 084 _state.setMagic(in.readInt()); 085 _state.setMinorVersion(in.readUnsignedShort()); 086 _state.setMajorVersion(in.readUnsignedShort()); 087 088 // constant pool 089 _state.getPool().read(in); 090 091 // access flags 092 _state.setAccessFlags(in.readUnsignedShort()); 093 094 // class, super class, interfaces 095 _state.setIndex(in.readUnsignedShort()); 096 _state.setSuperclassIndex(in.readUnsignedShort()); 097 098 List interfaces = _state.getInterfacesHolder(); 099 interfaces.clear(); 100 int interfaceCount = in.readUnsignedShort(); 101 for (int i = 0; i < interfaceCount; i++) 102 interfaces.add(Numbers.valueOf(in.readUnsignedShort())); 103 104 // fields 105 List fields = _state.getFieldsHolder(); 106 fields.clear(); 107 int fieldCount = in.readUnsignedShort(); 108 BCField field; 109 for (int i = 0; i < fieldCount; i++) { 110 field = new BCField(this); 111 fields.add(field); 112 field.read(in); 113 } 114 115 // methods 116 List methods = _state.getMethodsHolder(); 117 methods.clear(); 118 int methodCount = in.readUnsignedShort(); 119 BCMethod method; 120 for (int i = 0; i < methodCount; i++) { 121 method = new BCMethod(this); 122 methods.add(method); 123 method.read(in); 124 } 125 126 readAttributes(in); 127 _loader = loader; 128 } 129 130 /** 131 * Initialize from the bytecode of the definition of the given class. 132 * For use by the owning project only. 133 */ 134 void read(Class type) throws IOException { 135 // find out the length of the package name 136 int dotIndex = type.getName().lastIndexOf('.') + 1; 137 138 // strip the package off of the class name 139 String className = type.getName().substring(dotIndex); 140 141 // attempt to get the class file for the class as a stream 142 InputStream in = type.getResourceAsStream(className + ".class"); 143 try { 144 read(in, type.getClassLoader()); 145 } finally { 146 in.close(); 147 } 148 } 149 150 /** 151 * Initialize from the given parsed bytecode. 152 * For use by the owning project only. 153 */ 154 void read(BCClass orig) { 155 try { 156 ByteArrayInputStream in = new ByteArrayInputStream 157 (orig.toByteArray()); 158 read(in, orig.getClassLoader()); 159 in.close(); 160 } catch (IOException ioe) { 161 throw new RuntimeException(ioe.toString()); 162 } 163 } 164 165 /** 166 * Write the class bytecode to the .class file in the proper directory of 167 * the CLASSPATH. The file must exist already, so this method only works 168 * on existing classes. 169 */ 170 public void write() throws IOException { 171 String name = getName(); 172 int dotIndex = name.lastIndexOf('.') + 1; 173 name = name.substring(dotIndex); 174 Class type = getType(); 175 176 // attempt to get the class file for the class as a stream; 177 // we need to use the url decoder in case the target directory 178 // has spaces in it 179 OutputStream out = new FileOutputStream(URLDecoder.decode 180 (type.getResource(name + ".class").getFile())); 181 try { 182 write(out); 183 } finally { 184 out.close(); 185 } 186 } 187 188 /** 189 * Write the class bytecode to the specified file. 190 */ 191 public void write(File classFile) throws IOException { 192 OutputStream out = new FileOutputStream(classFile); 193 try { 194 write(out); 195 } finally { 196 out.close(); 197 } 198 } 199 200 /** 201 * Write the class bytecode to the specified stream. 202 */ 203 public void write(OutputStream outstream) throws IOException { 204 DataOutput out = new DataOutputStream(outstream); 205 206 // header information 207 out.writeInt(_state.getMagic()); 208 out.writeShort(_state.getMinorVersion()); 209 out.writeShort(_state.getMajorVersion()); 210 211 // constant pool 212 _state.getPool().write(out); 213 214 // access flags 215 out.writeShort(_state.getAccessFlags()); 216 217 // class, super class 218 out.writeShort(_state.getIndex()); 219 out.writeShort(_state.getSuperclassIndex()); 220 221 // interfaces 222 List interfaces = _state.getInterfacesHolder(); 223 out.writeShort(interfaces.size()); 224 for (Iterator itr = interfaces.iterator(); itr.hasNext();) 225 out.writeShort(((Number) itr.next()).intValue()); 226 227 // fields 228 List fields = _state.getFieldsHolder(); 229 out.writeShort(fields.size()); 230 for (Iterator itr = fields.iterator(); itr.hasNext();) 231 ((BCField) itr.next()).write(out); 232 233 // methods 234 List methods = _state.getMethodsHolder(); 235 out.writeShort(methods.size()); 236 for (Iterator itr = methods.iterator(); itr.hasNext();) 237 ((BCMethod) itr.next()).write(out); 238 239 // attributes 240 writeAttributes(out); 241 } 242 243 /** 244 * Return the bytecode of this class as a byte array, possibly for use 245 * in a custom {@link ClassLoader}. 246 */ 247 public byte[] toByteArray() { 248 ByteArrayOutputStream out = new ByteArrayOutputStream(); 249 try { 250 write(out); 251 out.flush(); 252 return out.toByteArray(); 253 } catch (IOException ioe) { 254 throw new RuntimeException(ioe.toString()); 255 } finally { 256 try { out.close(); } catch (IOException ioe) {} 257 } 258 } 259 260 ///////////////////// 261 // Access operations 262 ///////////////////// 263 264 /** 265 * Return the magic number for this class; if this is a valid type, this 266 * should be equal to {@link Constants#VALID_MAGIC} (the default value). 267 */ 268 public int getMagic() { 269 return _state.getMagic(); 270 } 271 272 /** 273 * Set the magic number for this class; if this is a valid type, this 274 * should be equal to {@link Constants#VALID_MAGIC} (the default value). 275 */ 276 public void setMagic(int magic) { 277 _state.setMagic(magic); 278 } 279 280 /** 281 * Return the major version of the bytecode spec used for this class. 282 * JVMs are only required to operate with versions that they understand; 283 * leaving the default value of {@link Constants#MAJOR_VERSION} is safe. 284 */ 285 public int getMajorVersion() { 286 return _state.getMajorVersion(); 287 } 288 289 /** 290 * Set the major version of the bytecode spec used for this class. 291 * JVMs are only required to operate with versions that they understand; 292 * leaving the default value of {@link Constants#MAJOR_VERSION} is safe. 293 */ 294 public void setMajorVersion(int majorVersion) { 295 _state.setMajorVersion(majorVersion); 296 } 297 298 /** 299 * Get the minor version of the bytecode spec used for this class. 300 * JVMs are only required to operate with versions that they understand; 301 * leaving the default value of {@link Constants#MINOR_VERSION} is safe. 302 */ 303 public int getMinorVersion() { 304 return _state.getMinorVersion(); 305 } 306 307 /** 308 * Set the minor version of the bytecode spec used for this class. 309 * JVMs are only required to operate with versions that they understand; 310 * leaving the default value of {@link Constants#MINOR_VERSION} is safe. 311 */ 312 public void setMinorVersion(int minorVersion) { 313 _state.setMinorVersion(minorVersion); 314 } 315 316 /** 317 * Return the access flags for this class as a bit array of 318 * ACCESS_XXX constants from {@link Constants}. This can be used to 319 * transfer access flags between classes without getting/setting each 320 * possible flag. 321 */ 322 public int getAccessFlags() { 323 return _state.getAccessFlags(); 324 } 325 326 /** 327 * Set the access flags for this class as a bit array of 328 * ACCESS_XXX constants from {@link Constants}. This can be used to 329 * transfer access flags between classes without getting/setting each 330 * possible flag. 331 */ 332 public void setAccessFlags(int access) { 333 _state.setAccessFlags(access); 334 } 335 336 /** 337 * Manipulate the class access flags. 338 */ 339 public boolean isPublic() { 340 return (getAccessFlags() & Constants.ACCESS_PUBLIC) > 0; 341 } 342 343 /** 344 * Manipulate the class access flags. 345 */ 346 public void makePublic() { 347 setAccessFlags(getAccessFlags() | Constants.ACCESS_PUBLIC); 348 } 349 350 /** 351 * Manipulate the class access flags. 352 */ 353 public boolean isPackage() { 354 return !isPublic(); 355 } 356 357 /** 358 * Manipulate the class access flags. 359 */ 360 public void makePackage() { 361 setAccessFlags(getAccessFlags() & ~Constants.ACCESS_PUBLIC); 362 } 363 364 /** 365 * Manipulate the class access flags. 366 */ 367 public boolean isFinal() { 368 return (getAccessFlags() & Constants.ACCESS_FINAL) > 0; 369 } 370 371 /** 372 * Manipulate the class access flags. 373 */ 374 public void setFinal(boolean on) { 375 if (on) 376 setAccessFlags(getAccessFlags() | Constants.ACCESS_FINAL); 377 else 378 setAccessFlags(getAccessFlags() & ~Constants.ACCESS_FINAL); 379 } 380 381 /** 382 * Manipulate the class access flags. 383 */ 384 public boolean isInterface() { 385 return (getAccessFlags() & Constants.ACCESS_INTERFACE) > 0; 386 } 387 388 /** 389 * Manipulate the class access flags. 390 */ 391 public void setInterface(boolean on) { 392 if (on) { 393 setAccessFlags(getAccessFlags() | Constants.ACCESS_INTERFACE); 394 setAbstract(true); 395 } else 396 setAccessFlags(getAccessFlags() & ~Constants.ACCESS_INTERFACE); 397 } 398 399 /** 400 * Manipulate the class access flags. 401 */ 402 public boolean isAbstract() { 403 return (getAccessFlags() & Constants.ACCESS_ABSTRACT) > 0; 404 } 405 406 /** 407 * Manipulate the class access flags. 408 */ 409 public void setAbstract(boolean on) { 410 if (on) 411 setAccessFlags(getAccessFlags() | Constants.ACCESS_ABSTRACT); 412 else 413 setAccessFlags(getAccessFlags() & ~Constants.ACCESS_ABSTRACT); 414 } 415 416 /** 417 * Manipulate the class access flags. 418 */ 419 public boolean isSynthetic() { 420 return (getAccessFlags() & Constants.ACCESS_SYNTHETIC) > 0; 421 } 422 423 /** 424 * Manipulate the class access flags. 425 */ 426 public void setSynthetic(boolean on) { 427 if (on) 428 setAccessFlags(getAccessFlags() | Constants.ACCESS_SYNTHETIC); 429 else 430 setAccessFlags(getAccessFlags() & ~Constants.ACCESS_SYNTHETIC); 431 } 432 433 /** 434 * Manipulate the class access flags. 435 */ 436 public boolean isAnnotation() { 437 return (getAccessFlags() & Constants.ACCESS_ANNOTATION) > 0; 438 } 439 440 /** 441 * Manipulate the class access flags. Setting to true also makes this 442 * an interface. 443 */ 444 public void setAnnotation(boolean on) { 445 if (on) { 446 setAccessFlags(getAccessFlags() | Constants.ACCESS_ANNOTATION); 447 setAccessFlags(getAccessFlags() | Constants.ACCESS_INTERFACE); 448 } else 449 setAccessFlags(getAccessFlags() & ~Constants.ACCESS_ANNOTATION); 450 } 451 452 /** 453 * Manipulate the class access flags. 454 */ 455 public boolean isEnum() { 456 return (getAccessFlags() & Constants.ACCESS_ENUM) > 0; 457 } 458 459 /** 460 * Manipulate the class access flags. 461 */ 462 public void setEnum(boolean on) { 463 if (on) 464 setAccessFlags(getAccessFlags() | Constants.ACCESS_ENUM); 465 else 466 setAccessFlags(getAccessFlags() & ~Constants.ACCESS_ENUM); 467 } 468 469 /** 470 * Return true if this class is a primitive type. 471 */ 472 public boolean isPrimitive() { 473 return _state.isPrimitive(); 474 } 475 476 /** 477 * Return true if this class is an array type. 478 */ 479 public boolean isArray() { 480 return _state.isArray(); 481 } 482 483 ///////////////////////// 484 // Class name operations 485 ///////////////////////// 486 487 /** 488 * Return the {@link ConstantPool} index of the 489 * {@link ClassEntry} for this class. Returns 0 if the class does not 490 * have a constant pool (such as a primitive or array). 491 */ 492 public int getIndex() { 493 return _state.getIndex(); 494 } 495 496 /** 497 * Set the {@link ConstantPool} index of the {@link ClassEntry} for this 498 * class. Unlike most other low-level methods, the index 499 * will be checked against the pool immediately; 500 * classes must have a valid name at all times. 501 */ 502 public void setIndex(int index) { 503 String oldName = getName(); 504 String newName = ((ClassEntry) getPool().getEntry(index)). 505 getNameEntry().getValue(); 506 beforeRename(oldName, newName); 507 _state.setIndex(index); 508 } 509 510 /** 511 * Return the name of this class, including package name. The name will 512 * be in a form suitable for a {@link Class#forName} call. 513 */ 514 public String getName() { 515 return _state.getName(); 516 } 517 518 /** 519 * Return the name of the class only, without package. 520 */ 521 public String getClassName() { 522 String name = _project.getNameCache().getExternalForm(getName(), true); 523 return name.substring(name.lastIndexOf('.') + 1); 524 } 525 526 /** 527 * Return the package name only, without class, or null if none. 528 */ 529 public String getPackageName() { 530 String name = _project.getNameCache().getExternalForm(getName(), true); 531 int index = name.lastIndexOf('.'); 532 if (index == -1) 533 return null; 534 return name.substring(0, index); 535 } 536 537 /** 538 * Set the name of this class, including package name. 539 */ 540 public void setName(String name) { 541 name = _project.getNameCache().getExternalForm(name, false); 542 String oldName = getName(); 543 544 // get a reference to the class entry for this class 545 int index = getIndex(); 546 if (index == 0) 547 index = getPool().findClassEntry(name, true); 548 ClassEntry entry = (ClassEntry) getPool().getEntry(index); 549 550 // make sure the rename is ok with the project 551 beforeRename(oldName, name); 552 553 // reset the name index of the class entry to the new name 554 int nameIndex = getPool().findUTF8Entry(_project.getNameCache(). 555 getInternalForm(name, false), true); 556 entry.setNameIndex(nameIndex); 557 558 // we might have just added a new entry; set the index 559 _state.setIndex(index); 560 } 561 562 /** 563 * Return the {@link Class} object for this class, if it is loadable. 564 */ 565 public Class getType() { 566 return Strings.toClass(getName(), getClassLoader()); 567 } 568 569 /** 570 * Return the component type name of this class, or null if not an array. 571 * The name will be in a form suitable for a {@link Class#forName} call. 572 */ 573 public String getComponentName() { 574 return _state.getComponentName(); 575 } 576 577 /** 578 * Return the component type of this class, or null if not an array. 579 */ 580 public Class getComponentType() { 581 String componentName = getComponentName(); 582 if (componentName == null) 583 return null; 584 return Strings.toClass(componentName, getClassLoader()); 585 } 586 587 /** 588 * Return the component type of this class, or null if not an array. 589 */ 590 public BCClass getComponentBC() { 591 String componentName = getComponentName(); 592 if (componentName == null) 593 return null; 594 return getProject().loadClass(componentName, getClassLoader()); 595 } 596 597 ///////////////////////// 598 // Superclass operations 599 ///////////////////////// 600 601 /** 602 * Return the {@link ConstantPool} index of the 603 * {@link ClassEntry} for the superclass of this class. Returns -1 if 604 * the class does not have a constant pool (such as a primitive or array). 605 */ 606 public int getSuperclassIndex() { 607 return _state.getSuperclassIndex(); 608 } 609 610 /** 611 * Set the {@link ConstantPool} index of the 612 * {@link ClassEntry} for the superclass of this class. 613 */ 614 public void setSuperclassIndex(int index) { 615 _state.setSuperclassIndex(index); 616 } 617 618 /** 619 * Return the name of the superclass for this class, including package 620 * name. The name will be in a form suitable for a 621 * {@link Class#forName} call, or null for types without superclasses. 622 */ 623 public String getSuperclassName() { 624 return _state.getSuperclassName(); 625 } 626 627 /** 628 * Return the {@link Class} object for the superclass of this class, if it 629 * is loadable. Returns null for types without superclasses. 630 */ 631 public Class getSuperclassType() { 632 String name = getSuperclassName(); 633 if (name == null) 634 return null; 635 return Strings.toClass(name, getClassLoader()); 636 } 637 638 /** 639 * Return the bytecode of the superclass of this class, or 640 * null for types without superclasses. 641 */ 642 public BCClass getSuperclassBC() { 643 String name = getSuperclassName(); 644 if (name == null) 645 return null; 646 return getProject().loadClass(name, getClassLoader()); 647 } 648 649 /** 650 * Set the superclass of this class. 651 */ 652 public void setSuperclass(String name) { 653 if (name == null) 654 setSuperclassIndex(0); 655 else 656 setSuperclassIndex(getPool().findClassEntry(_project.getNameCache(). 657 getInternalForm(name, false), true)); 658 } 659 660 /** 661 * Set the superclass of this class. 662 */ 663 public void setSuperclass(Class type) { 664 if (type == null) 665 setSuperclass((String) null); 666 else 667 setSuperclass(type.getName()); 668 } 669 670 /** 671 * Set the superclass of this class. 672 */ 673 public void setSuperclass(BCClass type) { 674 if (type == null) 675 setSuperclass((String) null); 676 else 677 setSuperclass(type.getName()); 678 } 679 680 //////////////////////// 681 // Interface operations 682 //////////////////////// 683 684 /** 685 * Return the list of {@link ConstantPool} indexes of the 686 * {@link ClassEntry}s describing all the interfaces this class declares 687 * that it implements/extends. 688 * 689 * @return the implmented interfaces, or an empty array if none 690 */ 691 public int[] getDeclaredInterfaceIndexes() { 692 List interfaces = _state.getInterfacesHolder(); 693 int[] indexes = new int[interfaces.size()]; 694 for (int i = 0; i < interfaces.size(); i++) 695 indexes[i] = ((Number) interfaces.get(i)).intValue(); 696 return indexes; 697 } 698 699 /** 700 * Set the list of {@link ConstantPool} indexes of the 701 * {@link ClassEntry}s describing all the interfaces this class declares 702 * it implements/extends; set to null or an empty array if none. 703 */ 704 public void setDeclaredInterfaceIndexes(int[] interfaceIndexes) { 705 List stateIndexes = _state.getInterfacesHolder(); 706 stateIndexes.clear(); 707 Integer idx; 708 for (int i = 0; i < interfaceIndexes.length; i++) { 709 idx = Numbers.valueOf(interfaceIndexes[i]); 710 if (!stateIndexes.contains(idx)) 711 stateIndexes.add(idx); 712 } 713 } 714 715 /** 716 * Return the names of the interfaces declared for this class, including 717 * package names, or an empty array if none. The names will be in a form 718 * suitable for a {@link Class#forName} call. 719 */ 720 public String[] getDeclaredInterfaceNames() { 721 int[] indexes = getDeclaredInterfaceIndexes(); 722 String[] names = new String[indexes.length]; 723 ClassEntry entry; 724 for (int i = 0; i < indexes.length; i++) { 725 entry = (ClassEntry) getPool().getEntry(indexes[i]); 726 names[i] = _project.getNameCache().getExternalForm 727 (entry.getNameEntry().getValue(), false); 728 } 729 return names; 730 } 731 732 /** 733 * Return the {@link Class} objects for the declared interfaces of this 734 * class, or an empty array if none. 735 */ 736 public Class[] getDeclaredInterfaceTypes() { 737 String[] names = getDeclaredInterfaceNames(); 738 Class[] types = new Class[names.length]; 739 for (int i = 0; i < names.length; i++) 740 types[i] = Strings.toClass(names[i], getClassLoader()); 741 return types; 742 } 743 744 /** 745 * Return the bytecode for the declared interfaces of this class, or an 746 * empty array if none. 747 */ 748 public BCClass[] getDeclaredInterfaceBCs() { 749 String[] names = getDeclaredInterfaceNames(); 750 BCClass[] types = new BCClass[names.length]; 751 for (int i = 0; i < names.length; i++) 752 types[i] = getProject().loadClass(names[i], getClassLoader()); 753 return types; 754 } 755 756 /** 757 * Set the interfaces declared implemented/extended by this class; set to 758 * null or an empty array if none. 759 */ 760 public void setDeclaredInterfaces(String[] interfaces) { 761 clearDeclaredInterfaces(); 762 if (interfaces != null) 763 for (int i = 0; i < interfaces.length; i++) 764 declareInterface(interfaces[i]); 765 } 766 767 /** 768 * Set the interfaces declared implemented/extended by this class; set to 769 * null or an empty array if none. 770 */ 771 public void setDeclaredInterfaces(Class[] interfaces) { 772 String[] names = null; 773 if (interfaces != null) { 774 names = new String[interfaces.length]; 775 for (int i = 0; i < interfaces.length; i++) 776 names[i] = interfaces[i].getName(); 777 } 778 setDeclaredInterfaces(names); 779 } 780 781 /** 782 * Set the interfaces declared implemented/extended by this class; set to 783 * null or an empty array if none. 784 */ 785 public void setDeclaredInterfaces(BCClass[] interfaces) { 786 String[] names = null; 787 if (interfaces != null) { 788 names = new String[interfaces.length]; 789 for (int i = 0; i < interfaces.length; i++) 790 names[i] = interfaces[i].getName(); 791 } 792 setDeclaredInterfaces(names); 793 } 794 795 /** 796 * Return the names of all unique interfaces implemented by this class, 797 * including those of all superclasses. The names will be returned in a 798 * form suitable for a {@link Class#forName} call. 799 * This method does not recurse into interfaces-of-interfaces. 800 */ 801 public String[] getInterfaceNames() { 802 Collection allNames = new LinkedList(); 803 String[] names; 804 for (BCClass type = this; type != null; type = type.getSuperclassBC()) { 805 names = type.getDeclaredInterfaceNames(); 806 for (int i = 0; i < names.length; i++) 807 allNames.add(names[i]); 808 } 809 return (String[]) allNames.toArray(new String[allNames.size()]); 810 } 811 812 /** 813 * Return the {@link Class} objects of all unique interfaces implemented 814 * by this class, including those of all superclasses. 815 * This method does not recurse into interfaces-of-interfaces. 816 */ 817 public Class[] getInterfaceTypes() { 818 Collection allTypes = new LinkedList(); 819 Class[] types; 820 for (BCClass type = this; type != null; type = type.getSuperclassBC()) { 821 types = type.getDeclaredInterfaceTypes(); 822 for (int i = 0; i < types.length; i++) 823 allTypes.add(types[i]); 824 } 825 return (Class[]) allTypes.toArray(new Class[allTypes.size()]); 826 } 827 828 /** 829 * Return the bytecode of all unique interfaces implemented by this class, 830 * including those of all superclasses. 831 * This method does not recurse into interfaces-of-interfaces. 832 */ 833 public BCClass[] getInterfaceBCs() { 834 Collection allTypes = new LinkedList(); 835 BCClass[] types; 836 for (BCClass type = this; type != null; type = type.getSuperclassBC()) { 837 types = type.getDeclaredInterfaceBCs(); 838 for (int i = 0; i < types.length; i++) 839 allTypes.add(types[i]); 840 } 841 return (BCClass[]) allTypes.toArray(new BCClass[allTypes.size()]); 842 } 843 844 /** 845 * Clear this class of all interface declarations. 846 */ 847 public void clearDeclaredInterfaces() { 848 _state.getInterfacesHolder().clear(); 849 } 850 851 /** 852 * Remove an interface declared by this class. 853 * 854 * @return true if the class had the interface, false otherwise 855 */ 856 public boolean removeDeclaredInterface(String name) { 857 String[] names = getDeclaredInterfaceNames(); 858 Iterator itr = _state.getInterfacesHolder().iterator(); 859 for (int i = 0; i < names.length; i++) { 860 itr.next(); 861 if (names[i].equals(name)) { 862 itr.remove(); 863 return true; 864 } 865 } 866 return false; 867 } 868 869 /** 870 * Remove an interface declared by this class. 871 * 872 * @return true if the class had the interface, false otherwise 873 */ 874 public boolean removeDeclaredInterface(Class type) { 875 if (type == null) 876 return false; 877 return removeDeclaredInterface(type.getName()); 878 } 879 880 /** 881 * Remove an interface declared by this class. 882 * 883 * @return true if the class had the interface, false otherwise 884 */ 885 public boolean removeDeclaredInterface(BCClass type) { 886 if (type == null) 887 return false; 888 return removeDeclaredInterface(type.getName()); 889 } 890 891 /** 892 * Rearrange declared interface order. 893 */ 894 public void moveDeclaredInterface(int fromIdx, int toIdx) { 895 if (fromIdx == toIdx) 896 return; 897 List interfaces = _state.getInterfacesHolder(); 898 Object o = interfaces.remove(fromIdx); 899 interfaces.add(toIdx, o); 900 } 901 902 /** 903 * Add an interface to those declared by this class. 904 */ 905 public void declareInterface(String name) { 906 Integer index = Numbers.valueOf(getPool().findClassEntry(_project. 907 getNameCache().getInternalForm(name, false), true)); 908 List interfaces = _state.getInterfacesHolder(); 909 if (!interfaces.contains(index)) 910 interfaces.add(index); 911 } 912 913 /** 914 * Add an interface to those declared by this class. 915 */ 916 public void declareInterface(Class type) { 917 declareInterface(type.getName()); 918 } 919 920 /** 921 * Add an interface to those declared by this class. 922 */ 923 public void declareInterface(BCClass type) { 924 declareInterface(type.getName()); 925 } 926 927 /** 928 * Return true if this class or any of its superclasses implement/extend 929 * the given interface/class. 930 * This method does not recurse into interfaces-of-interfaces. 931 */ 932 public boolean isInstanceOf(String name) { 933 name = _project.getNameCache().getExternalForm(name, false); 934 String[] interfaces = getInterfaceNames(); 935 for (int i = 0; i < interfaces.length; i++) 936 if (interfaces[i].equals(name)) 937 return true; 938 for (BCClass type = this; type != null; type = type.getSuperclassBC()) 939 if (type.getName().equals(name)) 940 return true; 941 return false; 942 } 943 944 /** 945 * Return true if this class or any of its superclasses implement/extend 946 * the given interface/class. 947 * This method does not recurse into interfaces-of-interfaces. 948 */ 949 public boolean isInstanceOf(Class type) { 950 if (type == null) 951 return false; 952 return isInstanceOf(type.getName()); 953 } 954 955 /** 956 * Return true if this class or any of its superclasses implement/extend 957 * the given interface/class. 958 * This method does not recurse into interfaces-of-interfaces. 959 */ 960 public boolean isInstanceOf(BCClass type) { 961 if (type == null) 962 return false; 963 return isInstanceOf(type.getName()); 964 } 965 966 ////////////////////// 967 // Field operations 968 ////////////////////// 969 970 /** 971 * Return all the declared fields of this class, or an empty array if none. 972 */ 973 public BCField[] getDeclaredFields() { 974 List fields = _state.getFieldsHolder(); 975 return (BCField[]) fields.toArray(new BCField[fields.size()]); 976 } 977 978 /** 979 * Return the declared field with the given name, or null if none. 980 */ 981 public BCField getDeclaredField(String name) { 982 BCField[] fields = getDeclaredFields(); 983 for (int i = 0; i < fields.length; i++) 984 if (fields[i].getName().equals(name)) 985 return fields[i]; 986 return null; 987 } 988 989 /** 990 * Return all the fields of this class, including those of all 991 * superclasses, or an empty array if none. 992 */ 993 public BCField[] getFields() { 994 Collection allFields = new LinkedList(); 995 BCField[] fields; 996 for (BCClass type = this; type != null; type = type.getSuperclassBC()) { 997 fields = type.getDeclaredFields(); 998 for (int i = 0; i < fields.length; i++) 999 allFields.add(fields[i]); 1000 } 1001 return (BCField[]) allFields.toArray(new BCField[allFields.size()]); 1002 } 1003 1004 /** 1005 * Return all fields with the given name, including those of all 1006 * superclasses, or an empty array if none. 1007 */ 1008 public BCField[] getFields(String name) { 1009 List matches = new LinkedList(); 1010 BCField[] fields = getFields(); 1011 for (int i = 0; i < fields.length; i++) 1012 if (fields[i].getName().equals(name)) 1013 matches.add(fields[i]); 1014 return (BCField[]) matches.toArray(new BCField[matches.size()]); 1015 } 1016 1017 /** 1018 * Set the fields for this class; this method is useful for importing all 1019 * fields from another class. Set to null or empty array if none. 1020 */ 1021 public void setDeclaredFields(BCField[] fields) { 1022 clearDeclaredFields(); 1023 if (fields != null) 1024 for (int i = 0; i < fields.length; i++) 1025 declareField(fields[i]); 1026 } 1027 1028 /** 1029 * Import the information from given field as a new field in this class. 1030 * 1031 * @return the added field 1032 */ 1033 public BCField declareField(BCField field) { 1034 BCField newField = declareField(field.getName(), field.getTypeName()); 1035 newField.setAccessFlags(field.getAccessFlags()); 1036 newField.setAttributes(field.getAttributes()); 1037 return newField; 1038 } 1039 1040 /** 1041 * Add a field to this class. 1042 * 1043 * @return the added field 1044 */ 1045 public BCField declareField(String name, String type) { 1046 BCField field = new BCField(this); 1047 _state.getFieldsHolder().add(field); 1048 field.initialize(name, _project.getNameCache().getInternalForm(type, 1049 true)); 1050 return field; 1051 } 1052 1053 /** 1054 * Add a field to this class. 1055 * 1056 * @return the added field 1057 */ 1058 public BCField declareField(String name, Class type) { 1059 String typeName = (type == null) ? null : type.getName(); 1060 return declareField(name, typeName); 1061 } 1062 1063 /** 1064 * Add a field to this class. 1065 * 1066 * @return the added field 1067 */ 1068 public BCField declareField(String name, BCClass type) { 1069 String typeName = (type == null) ? null : type.getName(); 1070 return declareField(name, typeName); 1071 } 1072 1073 /** 1074 * Clear all fields from this class. 1075 */ 1076 public void clearDeclaredFields() { 1077 List fields = _state.getFieldsHolder(); 1078 BCField field; 1079 for (Iterator itr = fields.iterator(); itr.hasNext();) { 1080 field = (BCField) itr.next(); 1081 itr.remove(); 1082 field.invalidate(); 1083 } 1084 } 1085 1086 /** 1087 * Remove a field from this class. After this method, the removed field 1088 * will be invalid, and the result of any operations on it is undefined. 1089 * 1090 * @return true if this class contained the field, false otherwise 1091 */ 1092 public boolean removeDeclaredField(String name) { 1093 List fields = _state.getFieldsHolder(); 1094 BCField field; 1095 for (Iterator itr = fields.iterator(); itr.hasNext();) { 1096 field = (BCField) itr.next(); 1097 if (field.getName().equals(name)) { 1098 itr.remove(); 1099 field.invalidate(); 1100 return true; 1101 } 1102 } 1103 return false; 1104 } 1105 1106 /** 1107 * Remove a field from this class. After this method, the removed field 1108 * will be invalid, and the result of any operations on it is undefined. 1109 * 1110 * @return true if this class contained the field, false otherwise 1111 */ 1112 public boolean removeDeclaredField(BCField field) { 1113 if (field == null) 1114 return false; 1115 return removeDeclaredField(field.getName()); 1116 } 1117 1118 /** 1119 * Rearrange declared field order. 1120 */ 1121 public void moveDeclaredField(int fromIdx, int toIdx) { 1122 if (fromIdx == toIdx) 1123 return; 1124 List fields = _state.getFieldsHolder(); 1125 Object o = fields.remove(fromIdx); 1126 fields.add(toIdx, o); 1127 } 1128 1129 ////////////////////// 1130 // Method operations 1131 ////////////////////// 1132 1133 /** 1134 * Return all methods declared by this class. Constructors and static 1135 * initializers are included. 1136 */ 1137 public BCMethod[] getDeclaredMethods() { 1138 List methods = _state.getMethodsHolder(); 1139 return (BCMethod[]) methods.toArray(new BCMethod[methods.size()]); 1140 } 1141 1142 /** 1143 * Return the declared method with the given name, or null if none. 1144 * If multiple methods are declared with the given name, which is returned 1145 * is undefined. 1146 * Note that in bytecode, constructors are named <code><init></code> 1147 * and static initializers are named <code><clinit></code>. 1148 */ 1149 public BCMethod getDeclaredMethod(String name) { 1150 BCMethod[] methods = getDeclaredMethods(); 1151 for (int i = 0; i < methods.length; i++) 1152 if (methods[i].getName().equals(name)) 1153 return methods[i]; 1154 return null; 1155 } 1156 1157 /** 1158 * Return all the declared methods with the given name, or an empty array 1159 * if none. 1160 * Note that in bytecode, constructors are named <code><init></code> 1161 * and static initializers are named <code><clinit></code>. 1162 */ 1163 public BCMethod[] getDeclaredMethods(String name) { 1164 Collection matches = new LinkedList(); 1165 BCMethod[] methods = getDeclaredMethods(); 1166 for (int i = 0; i < methods.length; i++) 1167 if (methods[i].getName().equals(name)) 1168 matches.add(methods[i]); 1169 return (BCMethod[]) matches.toArray(new BCMethod[matches.size()]); 1170 } 1171 1172 /** 1173 * Return the declared method with the given name and parameter types, 1174 * or null if none. If multiple methods are declared with the given name 1175 * and parameters, which is returned is undefined. 1176 * Note that in bytecode, constructors are named <code><init></code> 1177 * and static initializers are named <code><clinit></code>. 1178 */ 1179 public BCMethod getDeclaredMethod(String name, String[] paramTypes) { 1180 if (paramTypes == null) 1181 paramTypes = new String[0]; 1182 1183 BCMethod[] methods = getDeclaredMethods(); 1184 for (int i = 0; i < methods.length; i++) { 1185 if (methods[i].getName().equals(name) 1186 && paramsMatch(methods[i], paramTypes)) 1187 return methods[i]; 1188 } 1189 return null; 1190 } 1191 1192 /** 1193 * Return true iff the given method's parameters match <code>params</code>. 1194 */ 1195 private boolean paramsMatch(BCMethod meth, String[] params) { 1196 String[] mparams = meth.getParamNames(); 1197 if (mparams.length != params.length) 1198 return false; 1199 1200 for (int i = 0; i < params.length; i++) { 1201 if (!mparams[i].equals(_project.getNameCache(). 1202 getExternalForm(params[i], false))) 1203 return false; 1204 } 1205 return true; 1206 } 1207 1208 /** 1209 * Return the declared method with the given name and parameter types, 1210 * or null if none. If multiple methods are declared with the given name 1211 * and parameters, which is returned is undefined. 1212 * Note that in bytecode, constructors are named <code><init></code> 1213 * and static initializers are named <code><clinit></code>. 1214 */ 1215 public BCMethod getDeclaredMethod(String name, Class[] paramTypes) { 1216 if (paramTypes == null) 1217 return getDeclaredMethod(name, (String[]) null); 1218 1219 String[] paramNames = new String[paramTypes.length]; 1220 for (int i = 0; i < paramTypes.length; i++) 1221 paramNames[i] = paramTypes[i].getName(); 1222 return getDeclaredMethod(name, paramNames); 1223 } 1224 1225 /** 1226 * Return the declared method with the given name and parameter types, 1227 * or null if none. If multiple methods are declared with the given name 1228 * and parameters, which is returned is undefined. 1229 * Note that in bytecode, constructors are named <code><init></code> 1230 * and static initializers are named <code><clinit></code>. 1231 */ 1232 public BCMethod getDeclaredMethod(String name, BCClass[] paramTypes) { 1233 if (paramTypes == null) 1234 return getDeclaredMethod(name, (String[]) null); 1235 1236 String[] paramNames = new String[paramTypes.length]; 1237 for (int i = 0; i < paramTypes.length; i++) 1238 paramNames[i] = paramTypes[i].getName(); 1239 return getDeclaredMethod(name, paramNames); 1240 } 1241 1242 /** 1243 * Return all declared methods with the given name and parameter types. 1244 * Note that in bytecode, constructors are named <code><init></code> 1245 * and static initializers are named <code><clinit></code>. 1246 */ 1247 public BCMethod[] getDeclaredMethods(String name, String[] paramTypes) { 1248 if (paramTypes == null) 1249 paramTypes = new String[0]; 1250 1251 BCMethod[] methods = getDeclaredMethods(); 1252 List matches = null; 1253 for (int i = 0; i < methods.length; i++) { 1254 if (methods[i].getName().equals(name) 1255 && paramsMatch(methods[i], paramTypes)) { 1256 if (matches == null) 1257 matches = new ArrayList(3); 1258 matches.add(methods[i]); 1259 } 1260 } 1261 if (matches == null) 1262 return new BCMethod[0]; 1263 return (BCMethod[]) matches.toArray(new BCMethod[matches.size()]); 1264 } 1265 1266 /** 1267 * Return all declared methods with the given name and parameter types. 1268 * Note that in bytecode, constructors are named <code><init></code> 1269 * and static initializers are named <code><clinit></code>. 1270 */ 1271 public BCMethod[] getDeclaredMethods(String name, Class[] paramTypes) { 1272 if (paramTypes == null) 1273 return getDeclaredMethods(name, (String[]) null); 1274 1275 String[] paramNames = new String[paramTypes.length]; 1276 for (int i = 0; i < paramTypes.length; i++) 1277 paramNames[i] = paramTypes[i].getName(); 1278 return getDeclaredMethods(name, paramNames); 1279 } 1280 1281 /** 1282 * Return all declared methods with the given name and parameter types. 1283 * Note that in bytecode, constructors are named <code><init></code> 1284 * and static initializers are named <code><clinit></code>. 1285 */ 1286 public BCMethod[] getDeclaredMethods(String name, BCClass[] paramTypes) { 1287 if (paramTypes == null) 1288 return getDeclaredMethods(name, (String[]) null); 1289 1290 String[] paramNames = new String[paramTypes.length]; 1291 for (int i = 0; i < paramTypes.length; i++) 1292 paramNames[i] = paramTypes[i].getName(); 1293 return getDeclaredMethods(name, paramNames); 1294 } 1295 1296 /** 1297 * Return the declared method with the given name and signature, 1298 * or null if none. 1299 * Note that in bytecode, constructors are named <code><init></code> 1300 * and static initializers are named <code><clinit></code>. 1301 */ 1302 public BCMethod getDeclaredMethod(String name, String returnType, 1303 String[] paramTypes) { 1304 if (paramTypes == null) 1305 paramTypes = new String[0]; 1306 1307 BCMethod[] methods = getDeclaredMethods(); 1308 for (int i = 0; i < methods.length; i++) { 1309 if (methods[i].getName().equals(name) 1310 && methods[i].getReturnName().equals(_project.getNameCache(). 1311 getExternalForm(returnType, false)) 1312 && paramsMatch(methods[i], paramTypes)) 1313 return methods[i]; 1314 } 1315 return null; 1316 } 1317 1318 /** 1319 * Return the declared method with the given name and signature, 1320 * or null if none. 1321 * Note that in bytecode, constructors are named <code><init></code> 1322 * and static initializers are named <code><clinit></code>. 1323 */ 1324 public BCMethod getDeclaredMethod(String name, Class returnType, 1325 Class[] paramTypes) { 1326 if (paramTypes == null) 1327 return getDeclaredMethod(name, returnType.getName(), 1328 (String[]) null); 1329 1330 String[] paramNames = new String[paramTypes.length]; 1331 for (int i = 0; i < paramTypes.length; i++) 1332 paramNames[i] = paramTypes[i].getName(); 1333 return getDeclaredMethod(name, returnType.getName(), paramNames); 1334 } 1335 1336 /** 1337 * Return the declared method with the given name and signature, 1338 * or null if none. 1339 * Note that in bytecode, constructors are named <code><init></code> 1340 * and static initializers are named <code><clinit></code>. 1341 */ 1342 public BCMethod getDeclaredMethod(String name, BCClass returnType, 1343 BCClass[] paramTypes) { 1344 if (paramTypes == null) 1345 return getDeclaredMethod(name, returnType.getName(), 1346 (String[]) null); 1347 1348 String[] paramNames = new String[paramTypes.length]; 1349 for (int i = 0; i < paramTypes.length; i++) 1350 paramNames[i] = paramTypes[i].getName(); 1351 return getDeclaredMethod(name, returnType.getName(), paramNames); 1352 } 1353 1354 /** 1355 * Return the methods of this class, including those of all superclasses, 1356 * or an empty array if none. 1357 * The base version of methods that are overridden will be included, as 1358 * will all constructors and static initializers. 1359 * The methods will be ordered from those in the most-specific type up to 1360 * those in {@link Object}. 1361 */ 1362 public BCMethod[] getMethods() { 1363 Collection allMethods = new LinkedList(); 1364 BCMethod[] methods; 1365 for (BCClass type = this; type != null; type = type.getSuperclassBC()) { 1366 methods = type.getDeclaredMethods(); 1367 for (int i = 0; i < methods.length; i++) 1368 allMethods.add(methods[i]); 1369 } 1370 return (BCMethod[]) allMethods.toArray(new BCMethod[allMethods.size()]); 1371 } 1372 1373 /** 1374 * Return the methods with the given name, including those of all 1375 * superclasses, or an empty array if none. 1376 * Note that in bytecode, constructors are named <code><init></code> 1377 * and static initializers are named <code><clinit></code>. 1378 */ 1379 public BCMethod[] getMethods(String name) { 1380 Collection matches = new LinkedList(); 1381 BCMethod[] methods = getMethods(); 1382 for (int i = 0; i < methods.length; i++) 1383 if (methods[i].getName().equals(name)) 1384 matches.add(methods[i]); 1385 return (BCMethod[]) matches.toArray(new BCMethod[matches.size()]); 1386 } 1387 1388 /** 1389 * Return the methods with the given name and parameter types, including 1390 * those of all superclasses, or an empty array if none. 1391 * Note that in bytecode, constructors are named <code><init></code> 1392 * and static initializers are named <code><clinit></code>. 1393 */ 1394 public BCMethod[] getMethods(String name, String[] paramTypes) { 1395 if (paramTypes == null) 1396 paramTypes = new String[0]; 1397 1398 String[] curParams; 1399 boolean match; 1400 BCMethod[] methods = getMethods(); 1401 Collection matches = new LinkedList(); 1402 for (int i = 0; i < methods.length; i++) { 1403 if (!methods[i].getName().equals(name)) 1404 continue; 1405 curParams = methods[i].getParamNames(); 1406 if (curParams.length != paramTypes.length) 1407 continue; 1408 1409 match = true; 1410 for (int j = 0; j < paramTypes.length; j++) { 1411 if (!curParams[j].equals(_project.getNameCache(). 1412 getExternalForm(paramTypes[j], false))) { 1413 match = false; 1414 break; 1415 } 1416 } 1417 if (match) 1418 matches.add(methods[i]); 1419 } 1420 return (BCMethod[]) matches.toArray(new BCMethod[matches.size()]); 1421 } 1422 1423 /** 1424 * Return the methods with the given name and parameter types, including 1425 * those of all superclasses, or an empty array if none. 1426 * Note that in bytecode, constructors are named <code><init></code> 1427 * and static initializers are named <code><clinit></code>. 1428 */ 1429 public BCMethod[] getMethods(String name, Class[] paramTypes) { 1430 if (paramTypes == null) 1431 return getMethods(name, (String[]) null); 1432 1433 String[] paramNames = new String[paramTypes.length]; 1434 for (int i = 0; i < paramTypes.length; i++) 1435 paramNames[i] = paramTypes[i].getName(); 1436 return getMethods(name, paramNames); 1437 } 1438 1439 /** 1440 * Return the methods with the given name and parameter types, including 1441 * those of all superclasses, or an empty array if none. 1442 * Note that in bytecode, constructors are named <code><init></code> 1443 * and static initializers are named <code><clinit></code>. 1444 */ 1445 public BCMethod[] getMethods(String name, BCClass[] paramTypes) { 1446 if (paramTypes == null) 1447 return getMethods(name, (String[]) null); 1448 1449 String[] paramNames = new String[paramTypes.length]; 1450 for (int i = 0; i < paramTypes.length; i++) 1451 paramNames[i] = paramTypes[i].getName(); 1452 return getMethods(name, paramNames); 1453 } 1454 1455 /** 1456 * Set the methods for this class; this method is useful for importing all 1457 * methods from another class. Set to null or empty array if none. 1458 */ 1459 public void setDeclaredMethods(BCMethod[] methods) { 1460 clearDeclaredMethods(); 1461 if (methods != null) 1462 for (int i = 0; i < methods.length; i++) 1463 declareMethod(methods[i]); 1464 } 1465 1466 /** 1467 * Import the information in the given method as a new method of this class. 1468 * 1469 * @return the added method 1470 */ 1471 public BCMethod declareMethod(BCMethod method) { 1472 BCMethod newMethod = declareMethod(method.getName(), 1473 method.getReturnName(), method.getParamNames()); 1474 newMethod.setAccessFlags(method.getAccessFlags()); 1475 newMethod.setAttributes(method.getAttributes()); 1476 return newMethod; 1477 } 1478 1479 /** 1480 * Add a method to this class. 1481 * Note that in bytecode, constructors are named <code><init></code> 1482 * and static initializers are named <code><clinit></code>. 1483 * 1484 * @return the added method 1485 */ 1486 public BCMethod declareMethod(String name, String returnType, 1487 String[] paramTypes) { 1488 BCMethod method = new BCMethod(this); 1489 _state.getMethodsHolder().add(method); 1490 method.initialize(name, _project.getNameCache(). 1491 getDescriptor(returnType, paramTypes)); 1492 return method; 1493 } 1494 1495 /** 1496 * Add a method to this class. 1497 * Note that in bytecode, constructors are named <code><init></code> 1498 * and static initializers are named <code><clinit></code>. 1499 * 1500 * @return the added method 1501 */ 1502 public BCMethod declareMethod(String name, Class returnType, 1503 Class[] paramTypes) { 1504 String[] paramNames = null; 1505 if (paramTypes != null) { 1506 paramNames = new String[paramTypes.length]; 1507 for (int i = 0; i < paramTypes.length; i++) 1508 paramNames[i] = paramTypes[i].getName(); 1509 } 1510 String returnName = (returnType == null) ? null : returnType.getName(); 1511 return declareMethod(name, returnName, paramNames); 1512 } 1513 1514 /** 1515 * Add a method to this class. 1516 * Note that in bytecode, constructors are named <code><init></code> 1517 * and static initializers are named <code><clinit></code>. 1518 * 1519 * @return the added method 1520 */ 1521 public BCMethod declareMethod(String name, BCClass returnType, 1522 BCClass[] paramTypes) { 1523 String[] paramNames = null; 1524 if (paramTypes != null) { 1525 paramNames = new String[paramTypes.length]; 1526 for (int i = 0; i < paramTypes.length; i++) 1527 paramNames[i] = paramTypes[i].getName(); 1528 } 1529 String returnName = (returnType == null) ? null : returnType.getName(); 1530 return declareMethod(name, returnName, paramNames); 1531 } 1532 1533 /** 1534 * Clear all declared methods from this class. 1535 */ 1536 public void clearDeclaredMethods() { 1537 List methods = _state.getMethodsHolder(); 1538 BCMethod method; 1539 for (Iterator itr = methods.iterator(); itr.hasNext();) { 1540 method = (BCMethod) itr.next(); 1541 itr.remove(); 1542 method.invalidate(); 1543 } 1544 } 1545 1546 /** 1547 * Remove a method from this class. After this method, the removed method 1548 * will be invalid, and the result of any operations on it is undefined. 1549 * If multiple methods match the given name, which is removed is undefined. 1550 * Note that in bytecode, constructors are named <code><init></code> 1551 * and static initializers are named <code><clinit></code>. 1552 * 1553 * @return true if this class contained the method, false otherwise 1554 */ 1555 public boolean removeDeclaredMethod(String name) { 1556 List methods = _state.getMethodsHolder(); 1557 BCMethod method; 1558 for (Iterator itr = methods.iterator(); itr.hasNext();) { 1559 method = (BCMethod) itr.next(); 1560 if (method.getName().equals(name)) { 1561 itr.remove(); 1562 method.invalidate(); 1563 return true; 1564 } 1565 } 1566 return false; 1567 } 1568 1569 /** 1570 * Removes a method from this class. After this method, the removed method 1571 * will be invalid, and the result of any operations on it is undefined. 1572 * 1573 * @return true if this class contained the method, false otherwise 1574 */ 1575 public boolean removeDeclaredMethod(BCMethod method) { 1576 if (method == null) 1577 return false; 1578 return removeDeclaredMethod(method.getName(), method.getParamNames()); 1579 } 1580 1581 /** 1582 * Removes a method from this class. After this method, the removed method 1583 * will be invalid, and the result of any operations on it is undefined. 1584 * Note that in bytecode, constructors are named <code><init></code> 1585 * and static initializers are named <code><clinit></code>. 1586 * 1587 * @return true if this class contained the method, false otherwise 1588 */ 1589 public boolean removeDeclaredMethod(String name, String[] paramTypes) { 1590 if (paramTypes == null) 1591 paramTypes = new String[0]; 1592 1593 String[] curParams; 1594 boolean match; 1595 List methods = _state.getMethodsHolder(); 1596 BCMethod method; 1597 for (Iterator itr = methods.iterator(); itr.hasNext();) { 1598 method = (BCMethod) itr.next(); 1599 if (!method.getName().equals(name)) 1600 continue; 1601 curParams = method.getParamNames(); 1602 if (curParams.length != paramTypes.length) 1603 continue; 1604 1605 match = true; 1606 for (int j = 0; j < paramTypes.length; j++) { 1607 if (!curParams[j].equals(_project.getNameCache(). 1608 getExternalForm(paramTypes[j], false))) { 1609 match = false; 1610 break; 1611 } 1612 } 1613 if (match) { 1614 itr.remove(); 1615 method.invalidate(); 1616 return true; 1617 } 1618 } 1619 return false; 1620 } 1621 1622 /** 1623 * Removes a method from this class. After this method, the removed method 1624 * will be invalid, and the result of any operations on it is undefined. 1625 * Note that in bytecode, constructors are named <code><init></code> 1626 * and static initializers are named <code><clinit></code>. 1627 * 1628 * @return true if this class contained the method, false otherwise 1629 */ 1630 public boolean removeDeclaredMethod(String name, Class[] paramTypes) { 1631 if (paramTypes == null) 1632 return removeDeclaredMethod(name, (String[]) null); 1633 1634 String[] paramNames = new String[paramTypes.length]; 1635 for (int i = 0; i < paramTypes.length; i++) 1636 paramNames[i] = paramTypes[i].getName(); 1637 return removeDeclaredMethod(name, paramNames); 1638 } 1639 1640 /** 1641 * Removes a method from this class. After this method, the removed method 1642 * will be invalid, and the result of any operations on it is undefined. 1643 * Note that in bytecode, constructors are named <code><init></code> 1644 * and static initializers are named <code><clinit></code>. 1645 * 1646 * @return true if this class contained the method, false otherwise 1647 */ 1648 public boolean removeDeclaredMethod(String name, BCClass[] paramTypes) { 1649 if (paramTypes == null) 1650 return removeDeclaredMethod(name, (String[]) null); 1651 1652 String[] paramNames = new String[paramTypes.length]; 1653 for (int i = 0; i < paramTypes.length; i++) 1654 paramNames[i] = paramTypes[i].getName(); 1655 return removeDeclaredMethod(name, paramNames); 1656 } 1657 1658 /** 1659 * Rearrange method order. 1660 */ 1661 public void moveDeclaredMethod(int fromIdx, int toIdx) { 1662 if (fromIdx == toIdx) 1663 return; 1664 List methods = _state.getMethodsHolder(); 1665 Object o = methods.remove(fromIdx); 1666 methods.add(toIdx, o); 1667 } 1668 1669 /////////////////////// 1670 // Convenience methods 1671 /////////////////////// 1672 1673 /** 1674 * Convenience method to add a default constructor to this class. 1675 * If a default constructor already exists, this method will return it 1676 * without modification. 1677 * This method can only be called if the superclass has been set. 1678 * 1679 * @return the default constructor 1680 */ 1681 public BCMethod addDefaultConstructor() { 1682 BCMethod method = getDeclaredMethod("<init>", (String[]) null); 1683 if (method != null) 1684 return method; 1685 1686 method = declareMethod("<init>", void.class, null); 1687 Code code = method.getCode(true); 1688 code.setMaxStack(1); 1689 code.setMaxLocals(1); 1690 1691 code.xload().setThis(); 1692 code.invokespecial() 1693 .setMethod(getSuperclassName(), "<init>", "void", null); 1694 code.vreturn(); 1695 return method; 1696 } 1697 1698 /** 1699 * Return source file information for the class. 1700 * Acts internally through the {@link Attributes} interface. 1701 * 1702 * @param add if true, a new source file attribute will be added 1703 * if not already present 1704 * @return the source file information, or null if none and the 1705 * <code>add</code> param is set to false 1706 */ 1707 public SourceFile getSourceFile(boolean add) { 1708 SourceFile source = (SourceFile) getAttribute(Constants.ATTR_SOURCE); 1709 if (!add || (source != null)) 1710 return source; 1711 return (SourceFile) addAttribute(Constants.ATTR_SOURCE); 1712 } 1713 1714 /** 1715 * Remove the source file attribute for the class. 1716 * Acts internally through the {@link Attributes} interface. 1717 * 1718 * @return true if there was a file to remove 1719 */ 1720 public boolean removeSourceFile() { 1721 return removeAttribute(Constants.ATTR_SOURCE); 1722 } 1723 1724 /** 1725 * Return inner classes information for the class. 1726 * Acts internally through the {@link Attributes} interface. 1727 * 1728 * @param add if true, a new inner classes attribute will be added 1729 * if not already present 1730 * @return the inner classes information, or null if none and the 1731 * <code>add</code> param is set to false 1732 */ 1733 public InnerClasses getInnerClasses(boolean add) { 1734 InnerClasses inner = (InnerClasses) getAttribute 1735 (Constants.ATTR_INNERCLASS); 1736 if (!add || (inner != null)) 1737 return inner; 1738 return (InnerClasses) addAttribute(Constants.ATTR_INNERCLASS); 1739 } 1740 1741 /** 1742 * Remove the inner classes attribute for the class. 1743 * Acts internally through the {@link Attributes} interface. 1744 * 1745 * @return true if there was an attribute to remove 1746 */ 1747 public boolean removeInnerClasses() { 1748 return removeAttribute(Constants.ATTR_INNERCLASS); 1749 } 1750 1751 /** 1752 * Convenience method to return deprecation information for the class. 1753 * Acts internally through the {@link Attributes} interface. 1754 */ 1755 public boolean isDeprecated() { 1756 return getAttribute(Constants.ATTR_DEPRECATED) != null; 1757 } 1758 1759 /** 1760 * Convenience method to set whether this class should be considered 1761 * deprecated. Acts internally through the {@link Attributes} interface. 1762 */ 1763 public void setDeprecated(boolean on) { 1764 if (!on) 1765 removeAttribute(Constants.ATTR_DEPRECATED); 1766 else if (!isDeprecated()) 1767 addAttribute(Constants.ATTR_DEPRECATED); 1768 } 1769 1770 /////////////////////////////////// 1771 // Implementation of VisitAcceptor 1772 /////////////////////////////////// 1773 1774 public void acceptVisit(BCVisitor visit) { 1775 visit.enterBCClass(this); 1776 1777 ConstantPool pool = null; 1778 try { 1779 pool = _state.getPool(); 1780 } catch (UnsupportedOperationException uoe) { 1781 } 1782 if (pool != null) 1783 pool.acceptVisit(visit); 1784 1785 BCField[] fields = getDeclaredFields(); 1786 for (int i = 0; i < fields.length; i++) { 1787 visit.enterBCMember(fields[i]); 1788 fields[i].acceptVisit(visit); 1789 visit.exitBCMember(fields[i]); 1790 } 1791 1792 BCMethod[] methods = getDeclaredMethods(); 1793 for (int i = 0; i < methods.length; i++) { 1794 visit.enterBCMember(methods[i]); 1795 methods[i].acceptVisit(visit); 1796 visit.exitBCMember(methods[i]); 1797 } 1798 1799 visitAttributes(visit); 1800 visit.exitBCClass(this); 1801 } 1802 1803 //////////////////////////////// 1804 // Implementation of Attributes 1805 //////////////////////////////// 1806 1807 public Project getProject() { 1808 return _project; 1809 } 1810 1811 public ConstantPool getPool() { 1812 return _state.getPool(); 1813 } 1814 1815 public ClassLoader getClassLoader() { 1816 if (_loader != null) 1817 return _loader; 1818 return Thread.currentThread().getContextClassLoader(); 1819 } 1820 1821 public boolean isValid() { 1822 return _project != null; 1823 } 1824 1825 Collection getAttributesHolder() { 1826 return _state.getAttributesHolder(); 1827 } 1828 1829 /////////////////////////////// 1830 // Implementation of Annotated 1831 /////////////////////////////// 1832 1833 BCClass getBCClass() { 1834 return this; 1835 } 1836 1837 /** 1838 * Attempts to change the class name with the owning project. The project 1839 * can reject the change if a class with the given new name already 1840 * exists; therefore this method should be called before the change is 1841 * recorded in the class. 1842 */ 1843 private void beforeRename(String oldName, String newName) { 1844 if ((_project != null) && (oldName != null)) 1845 _project.renameClass(oldName, newName, this); 1846 } 1847 }