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 Collection 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 Collection 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 Collection 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 Collection 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 Collection 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 Collection 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 Collection interfaces = _state.getInterfacesHolder(); 693 int[] indexes = new int[interfaces.size()]; 694 Iterator itr = interfaces.iterator(); 695 for (int i = 0, max = interfaces.size(); i < max; i++) 696 indexes[i] = ((Number) itr.next()).intValue(); 697 return indexes; 698 } 699 700 /** 701 * Set the list of {@link ConstantPool} indexes of the 702 * {@link ClassEntry}s describing all the interfaces this class declares 703 * it implements/extends; set to null or an empty array if none. 704 */ 705 public void setDeclaredInterfaceIndexes(int[] interfaceIndexes) { 706 Collection stateIndexes = _state.getInterfacesHolder(); 707 stateIndexes.clear(); 708 Integer idx; 709 for (int i = 0; i < interfaceIndexes.length; i++) { 710 idx = Numbers.valueOf(interfaceIndexes[i]); 711 if (!stateIndexes.contains(idx)) 712 stateIndexes.add(idx); 713 } 714 } 715 716 /** 717 * Return the names of the interfaces declared for this class, including 718 * package names, or an empty array if none. The names will be in a form 719 * suitable for a {@link Class#forName} call. 720 */ 721 public String[] getDeclaredInterfaceNames() { 722 int[] indexes = getDeclaredInterfaceIndexes(); 723 String[] names = new String[indexes.length]; 724 ClassEntry entry; 725 for (int i = 0; i < indexes.length; i++) { 726 entry = (ClassEntry) getPool().getEntry(indexes[i]); 727 names[i] = _project.getNameCache().getExternalForm 728 (entry.getNameEntry().getValue(), false); 729 } 730 return names; 731 } 732 733 /** 734 * Return the {@link Class} objects for the declared interfaces of this 735 * class, or an empty array if none. 736 */ 737 public Class[] getDeclaredInterfaceTypes() { 738 String[] names = getDeclaredInterfaceNames(); 739 Class[] types = new Class[names.length]; 740 for (int i = 0; i < names.length; i++) 741 types[i] = Strings.toClass(names[i], getClassLoader()); 742 return types; 743 } 744 745 /** 746 * Return the bytecode for the declared interfaces of this class, or an 747 * empty array if none. 748 */ 749 public BCClass[] getDeclaredInterfaceBCs() { 750 String[] names = getDeclaredInterfaceNames(); 751 BCClass[] types = new BCClass[names.length]; 752 for (int i = 0; i < names.length; i++) 753 types[i] = getProject().loadClass(names[i], getClassLoader()); 754 return types; 755 } 756 757 /** 758 * Set the interfaces declared implemented/extended by this class; set to 759 * null or an empty array if none. 760 */ 761 public void setDeclaredInterfaces(String[] interfaces) { 762 clearDeclaredInterfaces(); 763 if (interfaces != null) 764 for (int i = 0; i < interfaces.length; i++) 765 declareInterface(interfaces[i]); 766 } 767 768 /** 769 * Set the interfaces declared implemented/extended by this class; set to 770 * null or an empty array if none. 771 */ 772 public void setDeclaredInterfaces(Class[] interfaces) { 773 String[] names = null; 774 if (interfaces != null) { 775 names = new String[interfaces.length]; 776 for (int i = 0; i < interfaces.length; i++) 777 names[i] = interfaces[i].getName(); 778 } 779 setDeclaredInterfaces(names); 780 } 781 782 /** 783 * Set the interfaces declared implemented/extended by this class; set to 784 * null or an empty array if none. 785 */ 786 public void setDeclaredInterfaces(BCClass[] interfaces) { 787 String[] names = null; 788 if (interfaces != null) { 789 names = new String[interfaces.length]; 790 for (int i = 0; i < interfaces.length; i++) 791 names[i] = interfaces[i].getName(); 792 } 793 setDeclaredInterfaces(names); 794 } 795 796 /** 797 * Return the names of all unique interfaces implemented by this class, 798 * including those of all superclasses. The names will be returned in a 799 * form suitable for a {@link Class#forName} call. 800 * This method does not recurse into interfaces-of-interfaces. 801 */ 802 public String[] getInterfaceNames() { 803 Collection allNames = new LinkedList(); 804 String[] names; 805 for (BCClass type = this; type != null; type = type.getSuperclassBC()) { 806 names = type.getDeclaredInterfaceNames(); 807 for (int i = 0; i < names.length; i++) 808 allNames.add(names[i]); 809 } 810 return (String[]) allNames.toArray(new String[allNames.size()]); 811 } 812 813 /** 814 * Return the {@link Class} objects of all unique interfaces implemented 815 * by this class, including those of all superclasses. 816 * This method does not recurse into interfaces-of-interfaces. 817 */ 818 public Class[] getInterfaceTypes() { 819 Collection allTypes = new LinkedList(); 820 Class[] types; 821 for (BCClass type = this; type != null; type = type.getSuperclassBC()) { 822 types = type.getDeclaredInterfaceTypes(); 823 for (int i = 0; i < types.length; i++) 824 allTypes.add(types[i]); 825 } 826 return (Class[]) allTypes.toArray(new Class[allTypes.size()]); 827 } 828 829 /** 830 * Return the bytecode of all unique interfaces implemented by this class, 831 * including those of all superclasses. 832 * This method does not recurse into interfaces-of-interfaces. 833 */ 834 public BCClass[] getInterfaceBCs() { 835 Collection allTypes = new LinkedList(); 836 BCClass[] types; 837 for (BCClass type = this; type != null; type = type.getSuperclassBC()) { 838 types = type.getDeclaredInterfaceBCs(); 839 for (int i = 0; i < types.length; i++) 840 allTypes.add(types[i]); 841 } 842 return (BCClass[]) allTypes.toArray(new BCClass[allTypes.size()]); 843 } 844 845 /** 846 * Clear this class of all interface declarations. 847 */ 848 public void clearDeclaredInterfaces() { 849 _state.getInterfacesHolder().clear(); 850 } 851 852 /** 853 * Remove an interface declared by this class. 854 * 855 * @return true if the class had the interface, false otherwise 856 */ 857 public boolean removeDeclaredInterface(String name) { 858 String[] names = getDeclaredInterfaceNames(); 859 Iterator itr = _state.getInterfacesHolder().iterator(); 860 for (int i = 0; i < names.length; i++) { 861 itr.next(); 862 if (names[i].equals(name)) { 863 itr.remove(); 864 return true; 865 } 866 } 867 return false; 868 } 869 870 /** 871 * Remove an interface declared by this class. 872 * 873 * @return true if the class had the interface, false otherwise 874 */ 875 public boolean removeDeclaredInterface(Class type) { 876 if (type == null) 877 return false; 878 return removeDeclaredInterface(type.getName()); 879 } 880 881 /** 882 * Remove an interface declared by this class. 883 * 884 * @return true if the class had the interface, false otherwise 885 */ 886 public boolean removeDeclaredInterface(BCClass type) { 887 if (type == null) 888 return false; 889 return removeDeclaredInterface(type.getName()); 890 } 891 892 /** 893 * Add an interface to those declared by this class. 894 */ 895 public void declareInterface(String name) { 896 Integer index = Numbers.valueOf(getPool().findClassEntry(_project. 897 getNameCache().getInternalForm(name, false), true)); 898 Collection interfaces = _state.getInterfacesHolder(); 899 if (!interfaces.contains(index)) 900 interfaces.add(index); 901 } 902 903 /** 904 * Add an interface to those declared by this class. 905 */ 906 public void declareInterface(Class type) { 907 declareInterface(type.getName()); 908 } 909 910 /** 911 * Add an interface to those declared by this class. 912 */ 913 public void declareInterface(BCClass type) { 914 declareInterface(type.getName()); 915 } 916 917 /** 918 * Return true if this class or any of its superclasses implement/extend 919 * the given interface/class. 920 * This method does not recurse into interfaces-of-interfaces. 921 */ 922 public boolean isInstanceOf(String name) { 923 name = _project.getNameCache().getExternalForm(name, false); 924 String[] interfaces = getInterfaceNames(); 925 for (int i = 0; i < interfaces.length; i++) 926 if (interfaces[i].equals(name)) 927 return true; 928 for (BCClass type = this; type != null; type = type.getSuperclassBC()) 929 if (type.getName().equals(name)) 930 return true; 931 return false; 932 } 933 934 /** 935 * Return true if this class or any of its superclasses implement/extend 936 * the given interface/class. 937 * This method does not recurse into interfaces-of-interfaces. 938 */ 939 public boolean isInstanceOf(Class type) { 940 if (type == null) 941 return false; 942 return isInstanceOf(type.getName()); 943 } 944 945 /** 946 * Return true if this class or any of its superclasses implement/extend 947 * the given interface/class. 948 * This method does not recurse into interfaces-of-interfaces. 949 */ 950 public boolean isInstanceOf(BCClass type) { 951 if (type == null) 952 return false; 953 return isInstanceOf(type.getName()); 954 } 955 956 ////////////////////// 957 // Field operations 958 ////////////////////// 959 960 /** 961 * Return all the declared fields of this class, or an empty array if none. 962 */ 963 public BCField[] getDeclaredFields() { 964 Collection fields = _state.getFieldsHolder(); 965 return (BCField[]) fields.toArray(new BCField[fields.size()]); 966 } 967 968 /** 969 * Return the declared field with the given name, or null if none. 970 */ 971 public BCField getDeclaredField(String name) { 972 BCField[] fields = getDeclaredFields(); 973 for (int i = 0; i < fields.length; i++) 974 if (fields[i].getName().equals(name)) 975 return fields[i]; 976 return null; 977 } 978 979 /** 980 * Return all the fields of this class, including those of all 981 * superclasses, or an empty array if none. 982 */ 983 public BCField[] getFields() { 984 Collection allFields = new LinkedList(); 985 BCField[] fields; 986 for (BCClass type = this; type != null; type = type.getSuperclassBC()) { 987 fields = type.getDeclaredFields(); 988 for (int i = 0; i < fields.length; i++) 989 allFields.add(fields[i]); 990 } 991 return (BCField[]) allFields.toArray(new BCField[allFields.size()]); 992 } 993 994 /** 995 * Return all fields with the given name, including those of all 996 * superclasses, or an empty array if none. 997 */ 998 public BCField[] getFields(String name) { 999 List matches = new LinkedList(); 1000 BCField[] fields = getFields(); 1001 for (int i = 0; i < fields.length; i++) 1002 if (fields[i].getName().equals(name)) 1003 matches.add(fields[i]); 1004 return (BCField[]) matches.toArray(new BCField[matches.size()]); 1005 } 1006 1007 /** 1008 * Set the fields for this class; this method is useful for importing all 1009 * fields from another class. Set to null or empty array if none. 1010 */ 1011 public void setDeclaredFields(BCField[] fields) { 1012 clearDeclaredFields(); 1013 if (fields != null) 1014 for (int i = 0; i < fields.length; i++) 1015 declareField(fields[i]); 1016 } 1017 1018 /** 1019 * Import the information from given field as a new field in this class. 1020 * 1021 * @return the added field 1022 */ 1023 public BCField declareField(BCField field) { 1024 BCField newField = declareField(field.getName(), field.getTypeName()); 1025 newField.setAccessFlags(field.getAccessFlags()); 1026 newField.setAttributes(field.getAttributes()); 1027 return newField; 1028 } 1029 1030 /** 1031 * Add a field to this class. 1032 * 1033 * @return the added field 1034 */ 1035 public BCField declareField(String name, String type) { 1036 BCField field = new BCField(this); 1037 _state.getFieldsHolder().add(field); 1038 field.initialize(name, _project.getNameCache().getInternalForm(type, 1039 true)); 1040 return field; 1041 } 1042 1043 /** 1044 * Add a field to this class. 1045 * 1046 * @return the added field 1047 */ 1048 public BCField declareField(String name, Class type) { 1049 String typeName = (type == null) ? null : type.getName(); 1050 return declareField(name, typeName); 1051 } 1052 1053 /** 1054 * Add a field to this class. 1055 * 1056 * @return the added field 1057 */ 1058 public BCField declareField(String name, BCClass type) { 1059 String typeName = (type == null) ? null : type.getName(); 1060 return declareField(name, typeName); 1061 } 1062 1063 /** 1064 * Clear all fields from this class. 1065 */ 1066 public void clearDeclaredFields() { 1067 Collection fields = _state.getFieldsHolder(); 1068 BCField field; 1069 for (Iterator itr = fields.iterator(); itr.hasNext();) { 1070 field = (BCField) itr.next(); 1071 itr.remove(); 1072 field.invalidate(); 1073 } 1074 } 1075 1076 /** 1077 * Remove a field from this class. After this method, the removed field 1078 * will be invalid, and the result of any operations on it is undefined. 1079 * 1080 * @return true if this class contained the field, false otherwise 1081 */ 1082 public boolean removeDeclaredField(String name) { 1083 Collection fields = _state.getFieldsHolder(); 1084 BCField field; 1085 for (Iterator itr = fields.iterator(); itr.hasNext();) { 1086 field = (BCField) itr.next(); 1087 if (field.getName().equals(name)) { 1088 itr.remove(); 1089 field.invalidate(); 1090 return true; 1091 } 1092 } 1093 return false; 1094 } 1095 1096 /** 1097 * Remove a field from this class. After this method, the removed field 1098 * will be invalid, and the result of any operations on it is undefined. 1099 * 1100 * @return true if this class contained the field, false otherwise 1101 */ 1102 public boolean removeDeclaredField(BCField field) { 1103 if (field == null) 1104 return false; 1105 return removeDeclaredField(field.getName()); 1106 } 1107 1108 ////////////////////// 1109 // Method operations 1110 ////////////////////// 1111 1112 /** 1113 * Return all methods declared by this class. Constructors and static 1114 * initializers are included. 1115 */ 1116 public BCMethod[] getDeclaredMethods() { 1117 Collection methods = _state.getMethodsHolder(); 1118 return (BCMethod[]) methods.toArray(new BCMethod[methods.size()]); 1119 } 1120 1121 /** 1122 * Return the declared method with the given name, or null if none. 1123 * If multiple methods are declared with the given name, which is returned 1124 * is undefined. 1125 * Note that in bytecode, constructors are named <code><init></code> 1126 * and static initializers are named <code><clinit></code>. 1127 */ 1128 public BCMethod getDeclaredMethod(String name) { 1129 BCMethod[] methods = getDeclaredMethods(); 1130 for (int i = 0; i < methods.length; i++) 1131 if (methods[i].getName().equals(name)) 1132 return methods[i]; 1133 return null; 1134 } 1135 1136 /** 1137 * Return all the declared methods with the given name, or an empty array 1138 * if none. 1139 * Note that in bytecode, constructors are named <code><init></code> 1140 * and static initializers are named <code><clinit></code>. 1141 */ 1142 public BCMethod[] getDeclaredMethods(String name) { 1143 Collection matches = new LinkedList(); 1144 BCMethod[] methods = getDeclaredMethods(); 1145 for (int i = 0; i < methods.length; i++) 1146 if (methods[i].getName().equals(name)) 1147 matches.add(methods[i]); 1148 return (BCMethod[]) matches.toArray(new BCMethod[matches.size()]); 1149 } 1150 1151 /** 1152 * Return the declared method with the given name and parameter types, 1153 * or null if none. 1154 * Note that in bytecode, constructors are named <code><init></code> 1155 * and static initializers are named <code><clinit></code>. 1156 */ 1157 public BCMethod getDeclaredMethod(String name, String[] paramTypes) { 1158 if (paramTypes == null) 1159 paramTypes = new String[0]; 1160 1161 String[] curParams; 1162 boolean match; 1163 BCMethod[] methods = getDeclaredMethods(); 1164 for (int i = 0; i < methods.length; i++) { 1165 if (!methods[i].getName().equals(name)) 1166 continue; 1167 curParams = methods[i].getParamNames(); 1168 if (curParams.length != paramTypes.length) 1169 continue; 1170 1171 match = true; 1172 for (int j = 0; j < paramTypes.length; j++) { 1173 if (!curParams[j].equals(_project.getNameCache(). 1174 getExternalForm(paramTypes[j], false))) { 1175 match = false; 1176 break; 1177 } 1178 } 1179 if (match) 1180 return methods[i]; 1181 } 1182 return null; 1183 } 1184 1185 /** 1186 * Return the declared method with the given name and parameter types, 1187 * or null if none. 1188 * Note that in bytecode, constructors are named <code><init></code> 1189 * and static initializers are named <code><clinit></code>. 1190 */ 1191 public BCMethod getDeclaredMethod(String name, Class[] paramTypes) { 1192 if (paramTypes == null) 1193 return getDeclaredMethod(name, (String[]) null); 1194 1195 String[] paramNames = new String[paramTypes.length]; 1196 for (int i = 0; i < paramTypes.length; i++) 1197 paramNames[i] = paramTypes[i].getName(); 1198 return getDeclaredMethod(name, paramNames); 1199 } 1200 1201 /** 1202 * Return the declared method with the given name and parameter types, 1203 * or null if none. 1204 * Note that in bytecode, constructors are named <code><init></code> 1205 * and static initializers are named <code><clinit></code>. 1206 */ 1207 public BCMethod getDeclaredMethod(String name, BCClass[] paramTypes) { 1208 if (paramTypes == null) 1209 return getDeclaredMethod(name, (String[]) null); 1210 1211 String[] paramNames = new String[paramTypes.length]; 1212 for (int i = 0; i < paramTypes.length; i++) 1213 paramNames[i] = paramTypes[i].getName(); 1214 return getDeclaredMethod(name, paramNames); 1215 } 1216 1217 /** 1218 * Return the methods of this class, including those of all superclasses, 1219 * or an empty array if none. 1220 * The base version of methods that are overridden will be included, as 1221 * will all constructors and static initializers. 1222 * The methods will be ordered from those in the most-specific type up to 1223 * those in {@link Object}. 1224 */ 1225 public BCMethod[] getMethods() { 1226 Collection allMethods = new LinkedList(); 1227 BCMethod[] methods; 1228 for (BCClass type = this; type != null; type = type.getSuperclassBC()) { 1229 methods = type.getDeclaredMethods(); 1230 for (int i = 0; i < methods.length; i++) 1231 allMethods.add(methods[i]); 1232 } 1233 return (BCMethod[]) allMethods.toArray(new BCMethod[allMethods.size()]); 1234 } 1235 1236 /** 1237 * Return the methods with the given name, including those of all 1238 * superclasses, or an empty array if none. 1239 * Note that in bytecode, constructors are named <code><init></code> 1240 * and static initializers are named <code><clinit></code>. 1241 */ 1242 public BCMethod[] getMethods(String name) { 1243 Collection matches = new LinkedList(); 1244 BCMethod[] methods = getMethods(); 1245 for (int i = 0; i < methods.length; i++) 1246 if (methods[i].getName().equals(name)) 1247 matches.add(methods[i]); 1248 return (BCMethod[]) matches.toArray(new BCMethod[matches.size()]); 1249 } 1250 1251 /** 1252 * Return the methods with the given name and parameter types, including 1253 * those of all superclasses, or an empty array if none. 1254 * Note that in bytecode, constructors are named <code><init></code> 1255 * and static initializers are named <code><clinit></code>. 1256 */ 1257 public BCMethod[] getMethods(String name, String[] paramTypes) { 1258 if (paramTypes == null) 1259 paramTypes = new String[0]; 1260 1261 String[] curParams; 1262 boolean match; 1263 BCMethod[] methods = getMethods(); 1264 Collection matches = new LinkedList(); 1265 for (int i = 0; i < methods.length; i++) { 1266 if (!methods[i].getName().equals(name)) 1267 continue; 1268 curParams = methods[i].getParamNames(); 1269 if (curParams.length != paramTypes.length) 1270 continue; 1271 1272 match = true; 1273 for (int j = 0; j < paramTypes.length; j++) { 1274 if (!curParams[j].equals(_project.getNameCache(). 1275 getExternalForm(paramTypes[j], false))) { 1276 match = false; 1277 break; 1278 } 1279 } 1280 if (match) 1281 matches.add(methods[i]); 1282 } 1283 return (BCMethod[]) matches.toArray(new BCMethod[matches.size()]); 1284 } 1285 1286 /** 1287 * Return the methods with the given name and parameter types, including 1288 * those of all superclasses, or an empty array if none. 1289 * Note that in bytecode, constructors are named <code><init></code> 1290 * and static initializers are named <code><clinit></code>. 1291 */ 1292 public BCMethod[] getMethods(String name, Class[] paramTypes) { 1293 if (paramTypes == null) 1294 return getMethods(name, (String[]) null); 1295 1296 String[] paramNames = new String[paramTypes.length]; 1297 for (int i = 0; i < paramTypes.length; i++) 1298 paramNames[i] = paramTypes[i].getName(); 1299 return getMethods(name, paramNames); 1300 } 1301 1302 /** 1303 * Return the methods with the given name and parameter types, including 1304 * those of all superclasses, or an empty array if none. 1305 * Note that in bytecode, constructors are named <code><init></code> 1306 * and static initializers are named <code><clinit></code>. 1307 */ 1308 public BCMethod[] getMethods(String name, BCClass[] paramTypes) { 1309 if (paramTypes == null) 1310 return getMethods(name, (String[]) null); 1311 1312 String[] paramNames = new String[paramTypes.length]; 1313 for (int i = 0; i < paramTypes.length; i++) 1314 paramNames[i] = paramTypes[i].getName(); 1315 return getMethods(name, paramNames); 1316 } 1317 1318 /** 1319 * Set the methods for this class; this method is useful for importing all 1320 * methods from another class. Set to null or empty array if none. 1321 */ 1322 public void setDeclaredMethods(BCMethod[] methods) { 1323 clearDeclaredMethods(); 1324 if (methods != null) 1325 for (int i = 0; i < methods.length; i++) 1326 declareMethod(methods[i]); 1327 } 1328 1329 /** 1330 * Import the information in the given method as a new method of this class. 1331 * 1332 * @return the added method 1333 */ 1334 public BCMethod declareMethod(BCMethod method) { 1335 BCMethod newMethod = declareMethod(method.getName(), 1336 method.getReturnName(), method.getParamNames()); 1337 newMethod.setAccessFlags(method.getAccessFlags()); 1338 newMethod.setAttributes(method.getAttributes()); 1339 return newMethod; 1340 } 1341 1342 /** 1343 * Add a method to this class. 1344 * Note that in bytecode, constructors are named <code><init></code> 1345 * and static initializers are named <code><clinit></code>. 1346 * 1347 * @return the added method 1348 */ 1349 public BCMethod declareMethod(String name, String returnType, 1350 String[] paramTypes) { 1351 BCMethod method = new BCMethod(this); 1352 _state.getMethodsHolder().add(method); 1353 method.initialize(name, _project.getNameCache(). 1354 getDescriptor(returnType, paramTypes)); 1355 return method; 1356 } 1357 1358 /** 1359 * Add a method to this class. 1360 * Note that in bytecode, constructors are named <code><init></code> 1361 * and static initializers are named <code><clinit></code>. 1362 * 1363 * @return the added method 1364 */ 1365 public BCMethod declareMethod(String name, Class returnType, 1366 Class[] paramTypes) { 1367 String[] paramNames = null; 1368 if (paramTypes != null) { 1369 paramNames = new String[paramTypes.length]; 1370 for (int i = 0; i < paramTypes.length; i++) 1371 paramNames[i] = paramTypes[i].getName(); 1372 } 1373 String returnName = (returnType == null) ? null : returnType.getName(); 1374 return declareMethod(name, returnName, paramNames); 1375 } 1376 1377 /** 1378 * Add a method to this class. 1379 * Note that in bytecode, constructors are named <code><init></code> 1380 * and static initializers are named <code><clinit></code>. 1381 * 1382 * @return the added method 1383 */ 1384 public BCMethod declareMethod(String name, BCClass returnType, 1385 BCClass[] paramTypes) { 1386 String[] paramNames = null; 1387 if (paramTypes != null) { 1388 paramNames = new String[paramTypes.length]; 1389 for (int i = 0; i < paramTypes.length; i++) 1390 paramNames[i] = paramTypes[i].getName(); 1391 } 1392 String returnName = (returnType == null) ? null : returnType.getName(); 1393 return declareMethod(name, returnName, paramNames); 1394 } 1395 1396 /** 1397 * Clear all declared methods from this class. 1398 */ 1399 public void clearDeclaredMethods() { 1400 Collection methods = _state.getMethodsHolder(); 1401 BCMethod method; 1402 for (Iterator itr = methods.iterator(); itr.hasNext();) { 1403 method = (BCMethod) itr.next(); 1404 itr.remove(); 1405 method.invalidate(); 1406 } 1407 } 1408 1409 /** 1410 * Remove a method from this class. After this method, the removed method 1411 * will be invalid, and the result of any operations on it is undefined. 1412 * If multiple methods match the given name, which is removed is undefined. 1413 * Note that in bytecode, constructors are named <code><init></code> 1414 * and static initializers are named <code><clinit></code>. 1415 * 1416 * @return true if this class contained the method, false otherwise 1417 */ 1418 public boolean removeDeclaredMethod(String name) { 1419 Collection methods = _state.getMethodsHolder(); 1420 BCMethod method; 1421 for (Iterator itr = methods.iterator(); itr.hasNext();) { 1422 method = (BCMethod) itr.next(); 1423 if (method.getName().equals(name)) { 1424 itr.remove(); 1425 method.invalidate(); 1426 return true; 1427 } 1428 } 1429 return false; 1430 } 1431 1432 /** 1433 * Removes a method from this class. After this method, the removed method 1434 * will be invalid, and the result of any operations on it is undefined. 1435 * 1436 * @return true if this class contained the method, false otherwise 1437 */ 1438 public boolean removeDeclaredMethod(BCMethod method) { 1439 if (method == null) 1440 return false; 1441 return removeDeclaredMethod(method.getName(), method.getParamNames()); 1442 } 1443 1444 /** 1445 * Removes a method from this class. After this method, the removed method 1446 * will be invalid, and the result of any operations on it is undefined. 1447 * Note that in bytecode, constructors are named <code><init></code> 1448 * and static initializers are named <code><clinit></code>. 1449 * 1450 * @return true if this class contained the method, false otherwise 1451 */ 1452 public boolean removeDeclaredMethod(String name, String[] paramTypes) { 1453 if (paramTypes == null) 1454 paramTypes = new String[0]; 1455 1456 String[] curParams; 1457 boolean match; 1458 Collection methods = _state.getMethodsHolder(); 1459 BCMethod method; 1460 for (Iterator itr = methods.iterator(); itr.hasNext();) { 1461 method = (BCMethod) itr.next(); 1462 if (!method.getName().equals(name)) 1463 continue; 1464 curParams = method.getParamNames(); 1465 if (curParams.length != paramTypes.length) 1466 continue; 1467 1468 match = true; 1469 for (int j = 0; j < paramTypes.length; j++) { 1470 if (!curParams[j].equals(_project.getNameCache(). 1471 getExternalForm(paramTypes[j], false))) { 1472 match = false; 1473 break; 1474 } 1475 } 1476 if (match) { 1477 itr.remove(); 1478 method.invalidate(); 1479 return true; 1480 } 1481 } 1482 return false; 1483 } 1484 1485 /** 1486 * Removes a method from this class. After this method, the removed method 1487 * will be invalid, and the result of any operations on it is undefined. 1488 * Note that in bytecode, constructors are named <code><init></code> 1489 * and static initializers are named <code><clinit></code>. 1490 * 1491 * @return true if this class contained the method, false otherwise 1492 */ 1493 public boolean removeDeclaredMethod(String name, Class[] paramTypes) { 1494 if (paramTypes == null) 1495 return removeDeclaredMethod(name, (String[]) null); 1496 1497 String[] paramNames = new String[paramTypes.length]; 1498 for (int i = 0; i < paramTypes.length; i++) 1499 paramNames[i] = paramTypes[i].getName(); 1500 return removeDeclaredMethod(name, paramNames); 1501 } 1502 1503 /** 1504 * Removes a method from this class. After this method, the removed method 1505 * will be invalid, and the result of any operations on it is undefined. 1506 * Note that in bytecode, constructors are named <code><init></code> 1507 * and static initializers are named <code><clinit></code>. 1508 * 1509 * @return true if this class contained the method, false otherwise 1510 */ 1511 public boolean removeDeclaredMethod(String name, BCClass[] paramTypes) { 1512 if (paramTypes == null) 1513 return removeDeclaredMethod(name, (String[]) null); 1514 1515 String[] paramNames = new String[paramTypes.length]; 1516 for (int i = 0; i < paramTypes.length; i++) 1517 paramNames[i] = paramTypes[i].getName(); 1518 return removeDeclaredMethod(name, paramNames); 1519 } 1520 1521 /////////////////////// 1522 // Convenience methods 1523 /////////////////////// 1524 1525 /** 1526 * Convenience method to add a default constructor to this class. 1527 * If a default constructor already exists, this method will return it 1528 * without modification. 1529 * This method can only be called if the superclass has been set. 1530 * 1531 * @return the default constructor 1532 */ 1533 public BCMethod addDefaultConstructor() { 1534 BCMethod method = getDeclaredMethod("<init>", (String[]) null); 1535 if (method != null) 1536 return method; 1537 1538 method = declareMethod("<init>", void.class, null); 1539 Code code = method.getCode(true); 1540 code.setMaxStack(1); 1541 code.setMaxLocals(1); 1542 1543 code.xload().setThis(); 1544 code.invokespecial() 1545 .setMethod(getSuperclassName(), "<init>", "void", null); 1546 code.vreturn(); 1547 return method; 1548 } 1549 1550 /** 1551 * Return source file information for the class. 1552 * Acts internally through the {@link Attributes} interface. 1553 * 1554 * @param add if true, a new source file attribute will be added 1555 * if not already present 1556 * @return the source file information, or null if none and the 1557 * <code>add</code> param is set to false 1558 */ 1559 public SourceFile getSourceFile(boolean add) { 1560 SourceFile source = (SourceFile) getAttribute(Constants.ATTR_SOURCE); 1561 if (!add || (source != null)) 1562 return source; 1563 return (SourceFile) addAttribute(Constants.ATTR_SOURCE); 1564 } 1565 1566 /** 1567 * Remove the source file attribute for the class. 1568 * Acts internally through the {@link Attributes} interface. 1569 * 1570 * @return true if there was a file to remove 1571 */ 1572 public boolean removeSourceFile() { 1573 return removeAttribute(Constants.ATTR_SOURCE); 1574 } 1575 1576 /** 1577 * Return inner classes information for the class. 1578 * Acts internally through the {@link Attributes} interface. 1579 * 1580 * @param add if true, a new inner classes attribute will be added 1581 * if not already present 1582 * @return the inner classes information, or null if none and the 1583 * <code>add</code> param is set to false 1584 */ 1585 public InnerClasses getInnerClasses(boolean add) { 1586 InnerClasses inner = (InnerClasses) getAttribute 1587 (Constants.ATTR_INNERCLASS); 1588 if (!add || (inner != null)) 1589 return inner; 1590 return (InnerClasses) addAttribute(Constants.ATTR_INNERCLASS); 1591 } 1592 1593 /** 1594 * Remove the inner classes attribute for the class. 1595 * Acts internally through the {@link Attributes} interface. 1596 * 1597 * @return true if there was an attribute to remove 1598 */ 1599 public boolean removeInnerClasses() { 1600 return removeAttribute(Constants.ATTR_INNERCLASS); 1601 } 1602 1603 /** 1604 * Convenience method to return deprecation information for the class. 1605 * Acts internally through the {@link Attributes} interface. 1606 */ 1607 public boolean isDeprecated() { 1608 return getAttribute(Constants.ATTR_DEPRECATED) != null; 1609 } 1610 1611 /** 1612 * Convenience method to set whether this class should be considered 1613 * deprecated. Acts internally through the {@link Attributes} interface. 1614 */ 1615 public void setDeprecated(boolean on) { 1616 if (!on) 1617 removeAttribute(Constants.ATTR_DEPRECATED); 1618 else if (!isDeprecated()) 1619 addAttribute(Constants.ATTR_DEPRECATED); 1620 } 1621 1622 /////////////////////////////////// 1623 // Implementation of VisitAcceptor 1624 /////////////////////////////////// 1625 1626 public void acceptVisit(BCVisitor visit) { 1627 visit.enterBCClass(this); 1628 1629 ConstantPool pool = null; 1630 try { 1631 pool = _state.getPool(); 1632 } catch (UnsupportedOperationException uoe) { 1633 } 1634 if (pool != null) 1635 pool.acceptVisit(visit); 1636 1637 BCField[] fields = getDeclaredFields(); 1638 for (int i = 0; i < fields.length; i++) { 1639 visit.enterBCMember(fields[i]); 1640 fields[i].acceptVisit(visit); 1641 visit.exitBCMember(fields[i]); 1642 } 1643 1644 BCMethod[] methods = getDeclaredMethods(); 1645 for (int i = 0; i < methods.length; i++) { 1646 visit.enterBCMember(methods[i]); 1647 methods[i].acceptVisit(visit); 1648 visit.exitBCMember(methods[i]); 1649 } 1650 1651 visitAttributes(visit); 1652 visit.exitBCClass(this); 1653 } 1654 1655 //////////////////////////////// 1656 // Implementation of Attributes 1657 //////////////////////////////// 1658 1659 public Project getProject() { 1660 return _project; 1661 } 1662 1663 public ConstantPool getPool() { 1664 return _state.getPool(); 1665 } 1666 1667 public ClassLoader getClassLoader() { 1668 if (_loader != null) 1669 return _loader; 1670 return Thread.currentThread().getContextClassLoader(); 1671 } 1672 1673 public boolean isValid() { 1674 return _project != null; 1675 } 1676 1677 Collection getAttributesHolder() { 1678 return _state.getAttributesHolder(); 1679 } 1680 1681 /////////////////////////////// 1682 // Implementation of Annotated 1683 /////////////////////////////// 1684 1685 BCClass getBCClass() { 1686 return this; 1687 } 1688 1689 /** 1690 * Attempts to change the class name with the owning project. The project 1691 * can reject the change if a class with the given new name already 1692 * exists; therefore this method should be called before the change is 1693 * recorded in the class. 1694 */ 1695 private void beforeRename(String oldName, String newName) { 1696 if ((_project != null) && (oldName != null)) 1697 _project.renameClass(oldName, newName, this); 1698 } 1699 }