001 /* 002 * $Id: ClassNode.java,v 1.52 2005/07/06 13:38:05 blackdrag Exp $ 003 * 004 * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved. 005 * 006 * Redistribution and use of this software and associated documentation 007 * ("Software"), with or without modification, are permitted provided that the 008 * following conditions are met: 009 * 1. Redistributions of source code must retain copyright statements and 010 * notices. Redistributions must also contain a copy of this document. 011 * 2. Redistributions in binary form must reproduce the above copyright 012 * notice, this list of conditions and the following disclaimer in the 013 * documentation and/or other materials provided with the distribution. 014 * 3. The name "groovy" must not be used to endorse or promote products 015 * derived from this Software without prior written permission of The Codehaus. 016 * For written permission, please contact info@codehaus.org. 017 * 4. Products derived from this Software may not be called "groovy" nor may 018 * "groovy" appear in their names without prior written permission of The 019 * Codehaus. "groovy" is a registered trademark of The Codehaus. 020 * 5. Due credit should be given to The Codehaus - http://groovy.codehaus.org/ 021 * 022 * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY 023 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 024 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 025 * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR 026 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 027 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 028 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 029 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 030 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 031 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 032 * DAMAGE. 033 * 034 */ 035 package org.codehaus.groovy.ast; 036 037 import groovy.lang.GroovyObject; 038 import groovy.lang.MissingClassException; 039 import groovy.lang.Script; 040 import org.codehaus.groovy.ast.expr.Expression; 041 import org.codehaus.groovy.ast.expr.TupleExpression; 042 import org.codehaus.groovy.ast.stmt.BlockStatement; 043 import org.codehaus.groovy.ast.stmt.EmptyStatement; 044 import org.codehaus.groovy.ast.stmt.Statement; 045 import org.codehaus.groovy.classgen.ClassGeneratorException; 046 import org.objectweb.asm.Opcodes; 047 048 import java.lang.reflect.Constructor; 049 import java.lang.reflect.Method; 050 import java.security.AccessControlException; 051 import java.util.ArrayList; 052 import java.util.HashMap; 053 import java.util.Iterator; 054 import java.util.List; 055 import java.util.Map; 056 import java.util.logging.Level; 057 import java.util.logging.Logger; 058 059 /** 060 * Represents a class declaration 061 * 062 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a> 063 * @version $Revision: 1.52 $ 064 */ 065 public class ClassNode extends AnnotatedNode implements Opcodes { 066 067 private static final String[] defaultImports = {"java.lang", "java.util", "groovy.lang", "groovy.util"}; 068 069 private transient Logger log = Logger.getLogger(getClass().getName()); 070 071 private String name; 072 private int modifiers; 073 private String superClass; 074 private String[] interfaces; 075 private MixinNode[] mixins; 076 private List constructors = new ArrayList(); 077 private List methods = new ArrayList(); 078 private List fields = new ArrayList(); 079 private List properties = new ArrayList(); 080 private Map fieldIndex = new HashMap(); 081 private ModuleNode module; 082 private CompileUnit compileUnit; 083 private boolean staticClass = false; 084 private boolean scriptBody = false; 085 private boolean script; 086 private ClassNode superClassNode; 087 088 089 //br added to track the enclosing method for local inner classes 090 private MethodNode enclosingMethod = null; 091 092 public MethodNode getEnclosingMethod() { 093 return enclosingMethod; 094 } 095 096 public void setEnclosingMethod(MethodNode enclosingMethod) { 097 this.enclosingMethod = enclosingMethod; 098 } 099 100 101 /** 102 * @param name is the full name of the class 103 * @param modifiers the modifiers, 104 * @param superClass the base class name - use "java.lang.Object" if no direct 105 * base class 106 * @see org.objectweb.asm.Opcodes 107 */ 108 public ClassNode(String name, int modifiers, String superClass) { 109 this(name, modifiers, superClass, EMPTY_STRING_ARRAY, MixinNode.EMPTY_ARRAY); 110 } 111 112 /** 113 * @param name is the full name of the class 114 * @param modifiers the modifiers, 115 * @param superClass the base class name - use "java.lang.Object" if no direct 116 * base class 117 * @see org.objectweb.asm.Opcodes 118 */ 119 public ClassNode(String name, int modifiers, String superClass, String[] interfaces, MixinNode[] mixins) { 120 this.name = name; 121 this.modifiers = modifiers; 122 this.superClass = superClass; 123 this.interfaces = interfaces; 124 this.mixins = mixins; 125 126 //br for better JVM comformance 127 /*if ((modifiers & ACC_SUPER) == 0) { 128 this.modifiers += ACC_SUPER; 129 }*/ 130 } 131 132 public String getSuperClass() { 133 return superClass; 134 } 135 136 public void setSuperClass(String superClass) { 137 this.superClass = superClass; 138 } 139 140 public List getFields() { 141 return fields; 142 } 143 144 public String[] getInterfaces() { 145 return interfaces; 146 } 147 148 public MixinNode[] getMixins() { 149 return mixins; 150 } 151 152 public List getMethods() { 153 return methods; 154 } 155 156 public List getAbstractMethods() { 157 158 List result = new ArrayList(); 159 for (Iterator methIt = getAllDeclaredMethods().iterator(); methIt.hasNext();) { 160 MethodNode method = (MethodNode) methIt.next(); 161 if (method.isAbstract()) { 162 result.add(method); 163 } 164 } 165 if (result.size() == 0) { 166 return null; 167 } 168 else { 169 return result; 170 } 171 } 172 173 public List getAllDeclaredMethods() { 174 return new ArrayList(getDeclaredMethodsMap().values()); 175 } 176 177 178 protected Map getDeclaredMethodsMap() { 179 // Start off with the methods from the superclass. 180 ClassNode parent = getSuperClassNode(); 181 Map result = null; 182 if (parent != null) { 183 result = parent.getDeclaredMethodsMap(); 184 } 185 else { 186 result = new HashMap(); 187 } 188 189 // add in unimplemented abstract methods from the interfaces 190 for (int i = 0; i < interfaces.length; i++) { 191 String interfaceName = interfaces[i]; 192 ClassNode iface = findClassNode(interfaceName); 193 Map ifaceMethodsMap = iface.getDeclaredMethodsMap(); 194 for (Iterator iter = ifaceMethodsMap.keySet().iterator(); iter.hasNext();) { 195 String methSig = (String) iter.next(); 196 if (!result.containsKey(methSig)) { 197 MethodNode methNode = (MethodNode) ifaceMethodsMap.get(methSig); 198 result.put(methSig, methNode); 199 } 200 } 201 } 202 203 // And add in the methods implemented in this class. 204 for (Iterator iter = getMethods().iterator(); iter.hasNext();) { 205 MethodNode method = (MethodNode) iter.next(); 206 String sig = method.getTypeDescriptor(); 207 if (result.containsKey(sig)) { 208 MethodNode inheritedMethod = (MethodNode) result.get(sig); 209 if (inheritedMethod.isAbstract()) { 210 result.put(sig, method); 211 } 212 } 213 else { 214 result.put(sig, method); 215 } 216 } 217 return result; 218 } 219 220 protected int findMatchingMethodInList(MethodNode method, List methods) { 221 for (int i = 0; i < methods.size(); i++) { 222 MethodNode someMeth = (MethodNode) methods.get(i); 223 if (someMeth.getName().equals(method.getName()) 224 && parametersEqual(someMeth.getParameters(), method.getParameters())) { 225 return i; 226 } 227 } 228 return -1; 229 } 230 231 public String getName() { 232 return name; 233 } 234 235 public int getModifiers() { 236 return modifiers; 237 } 238 239 public List getProperties() { 240 return properties; 241 } 242 243 public List getDeclaredConstructors() { 244 return constructors; 245 } 246 247 public ModuleNode getModule() { 248 return module; 249 } 250 251 public void setModule(ModuleNode module) { 252 this.module = module; 253 if (module != null) { 254 this.compileUnit = module.getUnit(); 255 } 256 } 257 258 public void addField(FieldNode node) { 259 node.setDeclaringClass(this); 260 node.setOwner(getName()); 261 fields.add(node); 262 fieldIndex.put(node.getName(), node); 263 } 264 265 public void addProperty(PropertyNode node) { 266 node.setDeclaringClass(this); 267 FieldNode field = node.getField(); 268 addField(field); 269 270 properties.add(node); 271 } 272 273 public PropertyNode addProperty(String name, 274 int modifiers, 275 String type, 276 Expression initialValueExpression, 277 Statement getterBlock, 278 Statement setterBlock) { 279 PropertyNode node = 280 new PropertyNode(name, modifiers, type, getName(), initialValueExpression, getterBlock, setterBlock); 281 addProperty(node); 282 return node; 283 } 284 285 public void addConstructor(ConstructorNode node) { 286 node.setDeclaringClass(this); 287 constructors.add(node); 288 } 289 290 public ConstructorNode addConstructor(int modifiers, Parameter[] parameters, Statement code) { 291 ConstructorNode node = new ConstructorNode(modifiers, parameters, code); 292 addConstructor(node); 293 return node; 294 } 295 296 public void addMethod(MethodNode node) { 297 node.setDeclaringClass(this); 298 methods.add(node); 299 } 300 301 /** 302 * IF a method with the given name and parameters is already defined then it is returned 303 * otherwise the given method is added to this node. This method is useful for 304 * default method adding like getProperty() or invokeMethod() where there may already 305 * be a method defined in a class and so the default implementations should not be added 306 * if already present. 307 */ 308 public MethodNode addMethod(String name, 309 int modifiers, 310 String returnType, 311 Parameter[] parameters, 312 Statement code) { 313 MethodNode other = getDeclaredMethod(name, parameters); 314 // lets not add duplicate methods 315 if (other != null) { 316 return other; 317 } 318 MethodNode node = new MethodNode(name, modifiers, returnType, parameters, code); 319 addMethod(node); 320 return node; 321 } 322 323 /** 324 * Adds a synthetic method as part of the compilation process 325 */ 326 public MethodNode addSyntheticMethod(String name, 327 int modifiers, 328 String returnType, 329 Parameter[] parameters, 330 Statement code) { 331 MethodNode answer = addMethod(name, modifiers, returnType, parameters, code); 332 answer.setSynthetic(true); 333 return answer; 334 } 335 336 public FieldNode addField(String name, int modifiers, String type, Expression initialValue) { 337 FieldNode node = new FieldNode(name, modifiers, type, getName(), initialValue); 338 addField(node); 339 return node; 340 } 341 342 public void addInterface(String name) { 343 // lets check if it already implements an interface 344 boolean skip = false; 345 for (int i = 0; i < interfaces.length; i++) { 346 if (name.equals(interfaces[i])) { 347 skip = true; 348 } 349 } 350 if (!skip) { 351 String[] newInterfaces = new String[interfaces.length + 1]; 352 System.arraycopy(interfaces, 0, newInterfaces, 0, interfaces.length); 353 newInterfaces[interfaces.length] = name; 354 interfaces = newInterfaces; 355 } 356 } 357 358 public void addMixin(MixinNode mixin) { 359 // lets check if it already uses a mixin 360 boolean skip = false; 361 String mixinName = mixin.getName(); 362 for (int i = 0; i < mixins.length; i++) { 363 if (mixinName.equals(mixins[i].getName())) { 364 skip = true; 365 } 366 } 367 if (!skip) { 368 MixinNode[] newMixins = new MixinNode[mixins.length + 1]; 369 System.arraycopy(mixins, 0, newMixins, 0, mixins.length); 370 newMixins[mixins.length] = mixin; 371 mixins = newMixins; 372 } 373 } 374 375 public FieldNode getField(String name) { 376 return (FieldNode) fieldIndex.get(name); 377 } 378 379 /** 380 * @return the field node on the outer class or null if this is not an 381 * inner class 382 */ 383 public FieldNode getOuterField(String name) { 384 return null; 385 } 386 387 /** 388 * Helper method to avoid casting to inner class 389 * 390 * @return 391 */ 392 public ClassNode getOuterClass() { 393 return null; 394 } 395 396 public void addStaticInitializerStatements(List staticStatements) { 397 MethodNode method = null; 398 List declaredMethods = getDeclaredMethods("<clinit>"); 399 if (declaredMethods.isEmpty()) { 400 method = 401 addMethod("<clinit>", ACC_PUBLIC | ACC_STATIC, "void", Parameter.EMPTY_ARRAY, new BlockStatement()); 402 method.setSynthetic(true); 403 } 404 else { 405 method = (MethodNode) declaredMethods.get(0); 406 } 407 BlockStatement block = null; 408 Statement statement = method.getCode(); 409 if (statement == null) { 410 block = new BlockStatement(); 411 } 412 else if (statement instanceof BlockStatement) { 413 block = (BlockStatement) statement; 414 } 415 else { 416 block = new BlockStatement(); 417 block.addStatement(statement); 418 } 419 block.addStatements(staticStatements); 420 } 421 422 /** 423 * @return a list of methods which match the given name 424 */ 425 public List getDeclaredMethods(String name) { 426 List answer = new ArrayList(); 427 for (Iterator iter = methods.iterator(); iter.hasNext();) { 428 MethodNode method = (MethodNode) iter.next(); 429 if (name.equals(method.getName())) { 430 answer.add(method); 431 } 432 } 433 return answer; 434 } 435 436 /** 437 * @return a list of methods which match the given name 438 */ 439 public List getMethods(String name) { 440 List answer = new ArrayList(); 441 ClassNode node = this; 442 do { 443 for (Iterator iter = node.methods.iterator(); iter.hasNext();) { 444 MethodNode method = (MethodNode) iter.next(); 445 if (name.equals(method.getName())) { 446 answer.add(method); 447 } 448 } 449 node = node.getSuperClassNode(); 450 } 451 while (node != null); 452 return answer; 453 } 454 455 /** 456 * @return the method matching the given name and parameters or null 457 */ 458 public MethodNode getDeclaredMethod(String name, Parameter[] parameters) { 459 for (Iterator iter = methods.iterator(); iter.hasNext();) { 460 MethodNode method = (MethodNode) iter.next(); 461 if (name.equals(method.getName()) && parametersEqual(method.getParameters(), parameters)) { 462 return method; 463 } 464 } 465 return null; 466 } 467 468 /** 469 * @return true if this node is derived from the given class node 470 */ 471 public boolean isDerivedFrom(String name) { 472 ClassNode node = getSuperClassNode(); 473 while (node != null) { 474 if (name.equals(node.getName())) { 475 return true; 476 } 477 node = node.getSuperClassNode(); 478 } 479 return false; 480 } 481 482 /** 483 * @return true if this class is derived from a groovy object 484 * i.e. it implements GroovyObject 485 */ 486 public boolean isDerivedFromGroovyObject() { 487 return implementsInteface(GroovyObject.class.getName()); 488 } 489 490 /** 491 * @param name the fully qualified name of the interface 492 * @return true if this class or any base class implements the given interface 493 */ 494 public boolean implementsInteface(String name) { 495 ClassNode node = this; 496 do { 497 if (node.declaresInterface(name)) { 498 return true; 499 } 500 node = node.getSuperClassNode(); 501 } 502 while (node != null); 503 return false; 504 } 505 506 /** 507 * @param name the fully qualified name of the interface 508 * @return true if this class declares that it implements the given interface 509 */ 510 public boolean declaresInterface(String name) { 511 int size = interfaces.length; 512 for (int i = 0; i < size; i++) { 513 if (name.equals(interfaces[i])) { 514 return true; 515 } 516 } 517 return false; 518 } 519 520 /** 521 * @return the ClassNode of the super class of this type 522 */ 523 public ClassNode getSuperClassNode() { 524 if (superClass != null && superClass.length() > 0 && superClassNode == null && !name.equals("java.lang.Object")) { 525 // lets try find the class in the compile unit 526 String temp = resolveClassName(superClass); 527 if (temp == null) { 528 throw new MissingClassException(superClass, this, "No such superclass"); 529 } 530 else { 531 superClass = temp; 532 } 533 superClassNode = findClassNode(superClass); 534 } 535 return superClassNode; 536 } 537 538 /** 539 * Attempts to lookup the fully qualified class name in the compile unit or classpath 540 * 541 * @param type fully qulified type name 542 * @return the ClassNode for this type or null if it could not be found 543 */ 544 public ClassNode findClassNode(String type) { 545 ClassNode answer = null; 546 CompileUnit theCompileUnit = getCompileUnit(); 547 if (theCompileUnit != null) { 548 answer = theCompileUnit.getClass(type); 549 if (answer == null) { 550 Class theClass; 551 try { 552 theClass = theCompileUnit.loadClass(type); 553 answer = createClassNode(theClass); 554 } 555 catch (ClassNotFoundException e) { 556 // lets ignore class not found exceptions 557 log.log(Level.WARNING, "Cannot find class: " + type, e); 558 } 559 } 560 } 561 return answer; 562 } 563 564 protected ClassNode createClassNode(Class theClass) { 565 Class[] classInterfaces = theClass.getInterfaces(); 566 int size = classInterfaces.length; 567 String[] interfaceNames = new String[size]; 568 for (int i = 0; i < size; i++) { 569 interfaceNames[i] = classInterfaces[i].getName(); 570 } 571 572 String className = null; 573 if (theClass.getSuperclass() != null) { 574 className = theClass.getSuperclass().getName(); 575 } 576 ClassNode answer = 577 new ClassNode(theClass.getName(), 578 theClass.getModifiers(), 579 className, 580 interfaceNames, 581 MixinNode.EMPTY_ARRAY); 582 answer.compileUnit = getCompileUnit(); 583 Method[] declaredMethods = theClass.getDeclaredMethods(); 584 for (int i = 0; i < declaredMethods.length; i++) { 585 answer.addMethod(createMethodNode(declaredMethods[i])); 586 } 587 Constructor[] declaredConstructors = theClass.getDeclaredConstructors(); 588 for (int i = 0; i < declaredConstructors.length; i++) { 589 answer.addConstructor(createConstructorNode(declaredConstructors[i])); 590 } 591 return answer; 592 } 593 594 595 /** 596 * Factory method to create a new ConstructorNode via reflection 597 */ 598 private ConstructorNode createConstructorNode(Constructor constructor) { 599 Parameter[] parameters = createParameters(constructor.getParameterTypes()); 600 return new ConstructorNode(constructor.getModifiers(), parameters, EmptyStatement.INSTANCE); 601 } 602 603 /** 604 * Factory method to create a new MethodNode via reflection 605 */ 606 protected MethodNode createMethodNode(Method method) { 607 Parameter[] parameters = createParameters(method.getParameterTypes()); 608 return new MethodNode(method.getName(), method.getModifiers(), method.getReturnType().getName(), parameters, EmptyStatement.INSTANCE); 609 } 610 611 /** 612 * @param types 613 * @return 614 */ 615 protected Parameter[] createParameters(Class[] types) { 616 Parameter[] parameters = Parameter.EMPTY_ARRAY; 617 int size = types.length; 618 if (size > 0) { 619 parameters = new Parameter[size]; 620 for (int i = 0; i < size; i++) { 621 parameters[i] = createParameter(types[i], i); 622 } 623 } 624 return parameters; 625 } 626 627 protected Parameter createParameter(Class parameterType, int idx) { 628 return new Parameter(parameterType.getName(), "param" + idx); 629 } 630 631 632 public String resolveClassName(String type) { 633 String answer = null; 634 if (type != null) { 635 if (getName().equals(type) || getNameWithoutPackage().equals(type)) { 636 return getName(); 637 } 638 // try to resolve Class names 639 answer = tryResolveClassAndInnerClass(type); 640 641 // try to resolve a public static inner class' name 642 String replacedPointType = type; 643 while (answer == null && replacedPointType.indexOf('.') > -1) { 644 int lastPoint = replacedPointType.lastIndexOf('.'); 645 replacedPointType = new StringBuffer() 646 .append(replacedPointType.substring(0, lastPoint)).append("$") 647 .append(replacedPointType.substring(lastPoint + 1)).toString(); 648 answer = tryResolveClassAndInnerClass(replacedPointType); 649 } 650 } 651 return answer; 652 } 653 654 private String tryResolveClassAndInnerClass(String type) { 655 String answer = tryResolveClassFromCompileUnit(type); 656 if (answer == null) { 657 // lets try class in same package 658 String packageName = getPackageName(); 659 if (packageName != null && packageName.length() > 0) { 660 answer = tryResolveClassFromCompileUnit(packageName + "." + type); 661 } 662 } 663 if (answer == null) { 664 // lets try use the packages imported in the module 665 if (module != null) { 666 //System.out.println("Looking up inside the imported packages: " + module.getImportPackages()); 667 668 for (Iterator iter = module.getImportPackages().iterator(); iter.hasNext();) { 669 String packageName = (String) iter.next(); 670 answer = tryResolveClassFromCompileUnit(packageName + type); 671 if (answer != null) { 672 return answer; 673 } 674 } 675 } 676 } 677 if (answer == null) { 678 for (int i = 0, size = defaultImports.length; i < size; i++) { 679 String packagePrefix = defaultImports[i]; 680 answer = tryResolveClassFromCompileUnit(packagePrefix + "." + type); 681 if (answer != null) { 682 return answer; 683 } 684 } 685 } 686 return answer; 687 } 688 689 /** 690 * @param type 691 * @return 692 */ 693 protected String tryResolveClassFromCompileUnit(String type) { 694 CompileUnit theCompileUnit = getCompileUnit(); 695 if (theCompileUnit != null) { 696 if (theCompileUnit.getClass(type) != null) { 697 return type; 698 } 699 700 try { 701 theCompileUnit.loadClass(type); 702 return type; 703 } catch (AccessControlException ace) { 704 //Percolate this for better diagnostic info 705 throw ace; 706 } catch (ClassGeneratorException cge) { 707 throw cge; 708 }catch (Throwable e) { 709 // fall through 710 } 711 } 712 return null; 713 } 714 715 public CompileUnit getCompileUnit() { 716 if (compileUnit == null && module != null) { 717 compileUnit = module.getUnit(); 718 } 719 return compileUnit; 720 } 721 722 /** 723 * @return true if the two arrays are of the same size and have the same contents 724 */ 725 protected boolean parametersEqual(Parameter[] a, Parameter[] b) { 726 if (a.length == b.length) { 727 boolean answer = true; 728 for (int i = 0; i < a.length; i++) { 729 if (!a[i].getType().equals(b[i].getType())) { 730 answer = false; 731 break; 732 } 733 } 734 return answer; 735 } 736 return false; 737 } 738 739 /** 740 * @return the name of the class for the given identifier if it is a class 741 * otherwise return null 742 */ 743 public String getClassNameForExpression(String identifier) { 744 // lets see if it really is a class name 745 String className = null; 746 if (module != null) { 747 className = module.getImport(identifier); 748 if (className == null) { 749 if (module.getUnit().getClass(identifier) != null) { 750 className = identifier; 751 } 752 else { 753 // lets prepend the package name to see if its in our 754 // package 755 String packageName = getPackageName(); 756 if (packageName != null) { 757 String guessName = packageName + "." + identifier; 758 if (module.getUnit().getClass(guessName) != null) { 759 className = guessName; 760 } 761 else if (guessName.equals(name)) { 762 className = name; 763 } 764 } 765 } 766 } 767 } 768 else { 769 System.out.println("No module for class: " + getName()); 770 } 771 return className; 772 } 773 774 /** 775 * @return the package name of this class 776 */ 777 public String getPackageName() { 778 int idx = name.lastIndexOf('.'); 779 if (idx > 0) { 780 return name.substring(0, idx); 781 } 782 return null; 783 } 784 785 public String getNameWithoutPackage() { 786 int idx = name.lastIndexOf('.'); 787 if (idx > 0) { 788 return name.substring(idx + 1); 789 } 790 return name; 791 } 792 793 public void visitContents(GroovyClassVisitor visitor) { 794 // now lets visit the contents of the class 795 for (Iterator iter = getProperties().iterator(); iter.hasNext();) { 796 visitor.visitProperty((PropertyNode) iter.next()); 797 } 798 799 for (Iterator iter = getFields().iterator(); iter.hasNext();) { 800 visitor.visitField((FieldNode) iter.next()); 801 } 802 803 for (Iterator iter = getDeclaredConstructors().iterator(); iter.hasNext();) { 804 visitor.visitConstructor((ConstructorNode) iter.next()); 805 } 806 807 for (Iterator iter = getMethods().iterator(); iter.hasNext();) { 808 visitor.visitMethod((MethodNode) iter.next()); 809 } 810 } 811 812 public MethodNode getGetterMethod(String getterName) { 813 for (Iterator iter = methods.iterator(); iter.hasNext();) { 814 MethodNode method = (MethodNode) iter.next(); 815 if (getterName.equals(method.getName()) 816 && !"void".equals(method.getReturnType()) 817 && method.getParameters().length == 0) { 818 return method; 819 } 820 } 821 return null; 822 } 823 824 public MethodNode getSetterMethod(String getterName) { 825 for (Iterator iter = methods.iterator(); iter.hasNext();) { 826 MethodNode method = (MethodNode) iter.next(); 827 if (getterName.equals(method.getName()) 828 && "void".equals(method.getReturnType()) 829 && method.getParameters().length == 1) { 830 return method; 831 } 832 } 833 return null; 834 } 835 836 /** 837 * Is this class delcared in a static method (such as a closure / inner class declared in a static method) 838 * 839 * @return 840 */ 841 public boolean isStaticClass() { 842 return staticClass; 843 } 844 845 public void setStaticClass(boolean staticClass) { 846 this.staticClass = staticClass; 847 } 848 849 /** 850 * @return Returns true if this inner class or closure was declared inside a script body 851 */ 852 public boolean isScriptBody() { 853 return scriptBody; 854 } 855 856 public void setScriptBody(boolean scriptBody) { 857 this.scriptBody = scriptBody; 858 } 859 860 public boolean isScript() { 861 return script | isDerivedFrom(Script.class.getName()); 862 } 863 864 public void setScript(boolean script) { 865 this.script = script; 866 } 867 868 public String toString() { 869 return super.toString() + "[name: " + name + "]"; 870 } 871 872 /** 873 * Returns true if the given method has a possibly matching method with the given name and arguments 874 */ 875 public boolean hasPossibleMethod(String name, Expression arguments) { 876 int count = 0; 877 878 if (arguments instanceof TupleExpression) { 879 TupleExpression tuple = (TupleExpression) arguments; 880 // TODO this won't strictly be true when using list expension in argument calls 881 count = tuple.getExpressions().size(); 882 } 883 ClassNode node = this; 884 do { 885 for (Iterator iter = node.methods.iterator(); iter.hasNext();) { 886 MethodNode method = (MethodNode) iter.next(); 887 if (name.equals(method.getName()) && method.getParameters().length == count) { 888 return true; 889 } 890 } 891 node = node.getSuperClassNode(); 892 } 893 while (node != null); 894 return false; 895 } 896 897 public boolean isInterface(){ 898 return (getModifiers() & Opcodes.ACC_INTERFACE) > 0; 899 } 900 }