001 /* 002 * $Id: ClassNode.java,v 1.50 2005/06/13 09:31:00 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.50 $ 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 } 403 else { 404 method = (MethodNode) declaredMethods.get(0); 405 } 406 BlockStatement block = null; 407 Statement statement = method.getCode(); 408 if (statement == null) { 409 block = new BlockStatement(); 410 } 411 else if (statement instanceof BlockStatement) { 412 block = (BlockStatement) statement; 413 } 414 else { 415 block = new BlockStatement(); 416 block.addStatement(statement); 417 } 418 block.addStatements(staticStatements); 419 } 420 421 /** 422 * @return a list of methods which match the given name 423 */ 424 public List getDeclaredMethods(String name) { 425 List answer = new ArrayList(); 426 for (Iterator iter = methods.iterator(); iter.hasNext();) { 427 MethodNode method = (MethodNode) iter.next(); 428 if (name.equals(method.getName())) { 429 answer.add(method); 430 } 431 } 432 return answer; 433 } 434 435 /** 436 * @return a list of methods which match the given name 437 */ 438 public List getMethods(String name) { 439 List answer = new ArrayList(); 440 ClassNode node = this; 441 do { 442 for (Iterator iter = node.methods.iterator(); iter.hasNext();) { 443 MethodNode method = (MethodNode) iter.next(); 444 if (name.equals(method.getName())) { 445 answer.add(method); 446 } 447 } 448 node = node.getSuperClassNode(); 449 } 450 while (node != null); 451 return answer; 452 } 453 454 /** 455 * @return the method matching the given name and parameters or null 456 */ 457 public MethodNode getDeclaredMethod(String name, Parameter[] parameters) { 458 for (Iterator iter = methods.iterator(); iter.hasNext();) { 459 MethodNode method = (MethodNode) iter.next(); 460 if (name.equals(method.getName()) && parametersEqual(method.getParameters(), parameters)) { 461 return method; 462 } 463 } 464 return null; 465 } 466 467 /** 468 * @return true if this node is derived from the given class node 469 */ 470 public boolean isDerivedFrom(String name) { 471 ClassNode node = getSuperClassNode(); 472 while (node != null) { 473 if (name.equals(node.getName())) { 474 return true; 475 } 476 node = node.getSuperClassNode(); 477 } 478 return false; 479 } 480 481 /** 482 * @return true if this class is derived from a groovy object 483 * i.e. it implements GroovyObject 484 */ 485 public boolean isDerivedFromGroovyObject() { 486 return implementsInteface(GroovyObject.class.getName()); 487 } 488 489 /** 490 * @param name the fully qualified name of the interface 491 * @return true if this class or any base class implements the given interface 492 */ 493 public boolean implementsInteface(String name) { 494 ClassNode node = this; 495 do { 496 if (node.declaresInterface(name)) { 497 return true; 498 } 499 node = node.getSuperClassNode(); 500 } 501 while (node != null); 502 return false; 503 } 504 505 /** 506 * @param name the fully qualified name of the interface 507 * @return true if this class declares that it implements the given interface 508 */ 509 public boolean declaresInterface(String name) { 510 int size = interfaces.length; 511 for (int i = 0; i < size; i++) { 512 if (name.equals(interfaces[i])) { 513 return true; 514 } 515 } 516 return false; 517 } 518 519 /** 520 * @return the ClassNode of the super class of this type 521 */ 522 public ClassNode getSuperClassNode() { 523 if (superClass != null && superClass.length() > 0 && superClassNode == null && !name.equals("java.lang.Object")) { 524 // lets try find the class in the compile unit 525 String temp = resolveClassName(superClass); 526 if (temp == null) { 527 throw new MissingClassException(superClass, this, "No such superclass"); 528 } 529 else { 530 superClass = temp; 531 } 532 superClassNode = findClassNode(superClass); 533 } 534 return superClassNode; 535 } 536 537 /** 538 * Attempts to lookup the fully qualified class name in the compile unit or classpath 539 * 540 * @param type fully qulified type name 541 * @return the ClassNode for this type or null if it could not be found 542 */ 543 public ClassNode findClassNode(String type) { 544 ClassNode answer = null; 545 CompileUnit theCompileUnit = getCompileUnit(); 546 if (theCompileUnit != null) { 547 answer = theCompileUnit.getClass(type); 548 if (answer == null) { 549 Class theClass; 550 try { 551 theClass = theCompileUnit.loadClass(type); 552 answer = createClassNode(theClass); 553 } 554 catch (ClassNotFoundException e) { 555 // lets ignore class not found exceptions 556 log.log(Level.WARNING, "Cannot find class: " + type, e); 557 } 558 } 559 } 560 return answer; 561 } 562 563 protected ClassNode createClassNode(Class theClass) { 564 Class[] classInterfaces = theClass.getInterfaces(); 565 int size = classInterfaces.length; 566 String[] interfaceNames = new String[size]; 567 for (int i = 0; i < size; i++) { 568 interfaceNames[i] = classInterfaces[i].getName(); 569 } 570 571 String className = null; 572 if (theClass.getSuperclass() != null) { 573 className = theClass.getSuperclass().getName(); 574 } 575 ClassNode answer = 576 new ClassNode(theClass.getName(), 577 theClass.getModifiers(), 578 className, 579 interfaceNames, 580 MixinNode.EMPTY_ARRAY); 581 answer.compileUnit = getCompileUnit(); 582 Method[] declaredMethods = theClass.getDeclaredMethods(); 583 for (int i = 0; i < declaredMethods.length; i++) { 584 answer.addMethod(createMethodNode(declaredMethods[i])); 585 } 586 Constructor[] declaredConstructors = theClass.getDeclaredConstructors(); 587 for (int i = 0; i < declaredConstructors.length; i++) { 588 answer.addConstructor(createConstructorNode(declaredConstructors[i])); 589 } 590 return answer; 591 } 592 593 594 /** 595 * Factory method to create a new ConstructorNode via reflection 596 */ 597 private ConstructorNode createConstructorNode(Constructor constructor) { 598 Parameter[] parameters = createParameters(constructor.getParameterTypes()); 599 return new ConstructorNode(constructor.getModifiers(), parameters, EmptyStatement.INSTANCE); 600 } 601 602 /** 603 * Factory method to create a new MethodNode via reflection 604 */ 605 protected MethodNode createMethodNode(Method method) { 606 Parameter[] parameters = createParameters(method.getParameterTypes()); 607 return new MethodNode(method.getName(), method.getModifiers(), method.getReturnType().getName(), parameters, EmptyStatement.INSTANCE); 608 } 609 610 /** 611 * @param types 612 * @return 613 */ 614 protected Parameter[] createParameters(Class[] types) { 615 Parameter[] parameters = Parameter.EMPTY_ARRAY; 616 int size = types.length; 617 if (size > 0) { 618 parameters = new Parameter[size]; 619 for (int i = 0; i < size; i++) { 620 parameters[i] = createParameter(types[i], i); 621 } 622 } 623 return parameters; 624 } 625 626 protected Parameter createParameter(Class parameterType, int idx) { 627 return new Parameter(parameterType.getName(), "param" + idx); 628 } 629 630 631 public String resolveClassName(String type) { 632 String answer = null; 633 if (type != null) { 634 if (getName().equals(type) || getNameWithoutPackage().equals(type)) { 635 return getName(); 636 } 637 // try to resolve Class names 638 answer = tryResolveClassAndInnerClass(type); 639 640 // try to resolve a public static inner class' name 641 String replacedPointType = type; 642 while (answer == null && replacedPointType.indexOf('.') > -1) { 643 int lastPoint = replacedPointType.lastIndexOf('.'); 644 replacedPointType = new StringBuffer() 645 .append(replacedPointType.substring(0, lastPoint)).append("$") 646 .append(replacedPointType.substring(lastPoint + 1)).toString(); 647 answer = tryResolveClassAndInnerClass(replacedPointType); 648 } 649 } 650 return answer; 651 } 652 653 private String tryResolveClassAndInnerClass(String type) { 654 String answer = tryResolveClassFromCompileUnit(type); 655 if (answer == null) { 656 // lets try class in same package 657 String packageName = getPackageName(); 658 if (packageName != null && packageName.length() > 0) { 659 answer = tryResolveClassFromCompileUnit(packageName + "." + type); 660 } 661 } 662 if (answer == null) { 663 // lets try use the packages imported in the module 664 if (module != null) { 665 //System.out.println("Looking up inside the imported packages: " + module.getImportPackages()); 666 667 for (Iterator iter = module.getImportPackages().iterator(); iter.hasNext();) { 668 String packageName = (String) iter.next(); 669 answer = tryResolveClassFromCompileUnit(packageName + type); 670 if (answer != null) { 671 return answer; 672 } 673 } 674 } 675 } 676 if (answer == null) { 677 for (int i = 0, size = defaultImports.length; i < size; i++) { 678 String packagePrefix = defaultImports[i]; 679 answer = tryResolveClassFromCompileUnit(packagePrefix + "." + type); 680 if (answer != null) { 681 return answer; 682 } 683 } 684 } 685 return answer; 686 } 687 688 /** 689 * @param type 690 * @return 691 */ 692 protected String tryResolveClassFromCompileUnit(String type) { 693 CompileUnit theCompileUnit = getCompileUnit(); 694 if (theCompileUnit != null) { 695 if (theCompileUnit.getClass(type) != null) { 696 return type; 697 } 698 699 try { 700 theCompileUnit.loadClass(type); 701 return type; 702 } catch (AccessControlException ace) { 703 //Percolate this for better diagnostic info 704 throw ace; 705 } catch (ClassGeneratorException cge) { 706 throw cge; 707 }catch (Throwable e) { 708 // fall through 709 } 710 } 711 return null; 712 } 713 714 public CompileUnit getCompileUnit() { 715 if (compileUnit == null && module != null) { 716 compileUnit = module.getUnit(); 717 } 718 return compileUnit; 719 } 720 721 /** 722 * @return true if the two arrays are of the same size and have the same contents 723 */ 724 protected boolean parametersEqual(Parameter[] a, Parameter[] b) { 725 if (a.length == b.length) { 726 boolean answer = true; 727 for (int i = 0; i < a.length; i++) { 728 if (!a[i].getType().equals(b[i].getType())) { 729 answer = false; 730 break; 731 } 732 } 733 return answer; 734 } 735 return false; 736 } 737 738 /** 739 * @return the name of the class for the given identifier if it is a class 740 * otherwise return null 741 */ 742 public String getClassNameForExpression(String identifier) { 743 // lets see if it really is a class name 744 String className = null; 745 if (module != null) { 746 className = module.getImport(identifier); 747 if (className == null) { 748 if (module.getUnit().getClass(identifier) != null) { 749 className = identifier; 750 } 751 else { 752 // lets prepend the package name to see if its in our 753 // package 754 String packageName = getPackageName(); 755 if (packageName != null) { 756 String guessName = packageName + "." + identifier; 757 if (module.getUnit().getClass(guessName) != null) { 758 className = guessName; 759 } 760 else if (guessName.equals(name)) { 761 className = name; 762 } 763 } 764 } 765 } 766 } 767 else { 768 System.out.println("No module for class: " + getName()); 769 } 770 return className; 771 } 772 773 /** 774 * @return the package name of this class 775 */ 776 public String getPackageName() { 777 int idx = name.lastIndexOf('.'); 778 if (idx > 0) { 779 return name.substring(0, idx); 780 } 781 return null; 782 } 783 784 public String getNameWithoutPackage() { 785 int idx = name.lastIndexOf('.'); 786 if (idx > 0) { 787 return name.substring(idx + 1); 788 } 789 return name; 790 } 791 792 public void visitContents(GroovyClassVisitor visitor) { 793 // now lets visit the contents of the class 794 for (Iterator iter = getProperties().iterator(); iter.hasNext();) { 795 visitor.visitProperty((PropertyNode) iter.next()); 796 } 797 798 for (Iterator iter = getFields().iterator(); iter.hasNext();) { 799 visitor.visitField((FieldNode) iter.next()); 800 } 801 802 for (Iterator iter = getDeclaredConstructors().iterator(); iter.hasNext();) { 803 visitor.visitConstructor((ConstructorNode) iter.next()); 804 } 805 806 for (Iterator iter = getMethods().iterator(); iter.hasNext();) { 807 visitor.visitMethod((MethodNode) iter.next()); 808 } 809 } 810 811 public MethodNode getGetterMethod(String getterName) { 812 for (Iterator iter = methods.iterator(); iter.hasNext();) { 813 MethodNode method = (MethodNode) iter.next(); 814 if (getterName.equals(method.getName()) 815 && !"void".equals(method.getReturnType()) 816 && method.getParameters().length == 0) { 817 return method; 818 } 819 } 820 return null; 821 } 822 823 public MethodNode getSetterMethod(String getterName) { 824 for (Iterator iter = methods.iterator(); iter.hasNext();) { 825 MethodNode method = (MethodNode) iter.next(); 826 if (getterName.equals(method.getName()) 827 && "void".equals(method.getReturnType()) 828 && method.getParameters().length == 1) { 829 return method; 830 } 831 } 832 return null; 833 } 834 835 /** 836 * Is this class delcared in a static method (such as a closure / inner class declared in a static method) 837 * 838 * @return 839 */ 840 public boolean isStaticClass() { 841 return staticClass; 842 } 843 844 public void setStaticClass(boolean staticClass) { 845 this.staticClass = staticClass; 846 } 847 848 /** 849 * @return Returns true if this inner class or closure was declared inside a script body 850 */ 851 public boolean isScriptBody() { 852 return scriptBody; 853 } 854 855 public void setScriptBody(boolean scriptBody) { 856 this.scriptBody = scriptBody; 857 } 858 859 public boolean isScript() { 860 return script | isDerivedFrom(Script.class.getName()); 861 } 862 863 public void setScript(boolean script) { 864 this.script = script; 865 } 866 867 public String toString() { 868 return super.toString() + "[name: " + name + "]"; 869 } 870 871 /** 872 * Returns true if the given method has a possibly matching method with the given name and arguments 873 */ 874 public boolean hasPossibleMethod(String name, Expression arguments) { 875 int count = 0; 876 877 if (arguments instanceof TupleExpression) { 878 TupleExpression tuple = (TupleExpression) arguments; 879 // TODO this won't strictly be true when using list expension in argument calls 880 count = tuple.getExpressions().size(); 881 } 882 ClassNode node = this; 883 do { 884 for (Iterator iter = node.methods.iterator(); iter.hasNext();) { 885 MethodNode method = (MethodNode) iter.next(); 886 if (name.equals(method.getName()) && method.getParameters().length == count) { 887 return true; 888 } 889 } 890 node = node.getSuperClassNode(); 891 } 892 while (node != null); 893 return false; 894 } 895 }