001 /* 002 * $Id: ClassNode.java,v 1.59 2005/11/15 23:44:55 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 039 import org.codehaus.groovy.GroovyBugError; 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.objectweb.asm.Opcodes; 046 047 import java.lang.reflect.Array; 048 import java.lang.reflect.Constructor; 049 import java.lang.reflect.Field; 050 import java.lang.reflect.Method; 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 057 /** 058 * Represents a class declaration 059 * 060 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a> 061 * @version $Revision: 1.59 $ 062 */ 063 public class ClassNode extends AnnotatedNode implements Opcodes { 064 065 //private static final String[] defaultImports = {"java.lang", "java.util", "groovy.lang", "groovy.util"}; 066 //private transient Logger log = Logger.getLogger(getClass().getName()); 067 068 private String name; 069 private int modifiers; 070 private ClassNode[] interfaces; 071 private MixinNode[] mixins; 072 private List constructors = new ArrayList(); 073 private List methods = new ArrayList(); 074 private List fields = new ArrayList(); 075 private List properties = new ArrayList(); 076 private Map fieldIndex = new HashMap(); 077 private ModuleNode module; 078 private CompileUnit compileUnit; 079 private boolean staticClass = false; 080 private boolean scriptBody = false; 081 private boolean script; 082 private ClassNode superClass; 083 boolean isPrimaryNode; 084 085 // clazz!=null when resolved 086 private Class clazz; 087 // only false when this classNode is constructed from a class 088 private boolean lazyInitDone=true; 089 protected boolean resolved=true; 090 private ClassNode componentType = null; 091 092 private ClassNode redirect=null; 093 094 protected ClassNode redirect(){ 095 if (redirect==null) return this; 096 return redirect.redirect(); 097 } 098 099 public void setRedirect(ClassNode cn) { 100 if (isPrimaryNode) throw new GroovyBugError("tried to set a redirect for a primary ClassNode ("+getName()+"->"+cn.getName()+")."); 101 redirect = cn.redirect(); 102 } 103 104 public ClassNode makeArray() { 105 if (redirect!=null) return redirect().makeArray(); 106 ClassNode cn; 107 if (clazz!=null) { 108 Class ret = Array.newInstance(clazz,0).getClass(); 109 // don't use the ClassHelper here! 110 cn = new ClassNode(ret,this); 111 } else { 112 cn = new ClassNode(this); 113 } 114 return cn; 115 } 116 117 public boolean isPrimaryClassNode(){ 118 return redirect().isPrimaryNode; 119 } 120 121 private ClassNode(ClassNode componentType) { 122 this(componentType.getName()+"[]", ACC_PUBLIC, ClassHelper.OBJECT_TYPE); 123 this.componentType = componentType.redirect(); 124 isPrimaryNode=false; 125 resolved = false; 126 } 127 128 private ClassNode(Class c, ClassNode componentType) { 129 this(c); 130 this.componentType = componentType; 131 isPrimaryNode=false; 132 } 133 134 public ClassNode(Class c) { 135 this(c.getName(), c.getModifiers(), null, null ,MixinNode.EMPTY_ARRAY); 136 clazz=c; 137 lazyInitDone=false; 138 resolved = true; 139 CompileUnit cu = getCompileUnit(); 140 if (cu!=null) cu.addClass(this); 141 isPrimaryNode=false; 142 } 143 144 /** 145 * the complete class structure will be initialized only when really 146 * needed to avoid having too much objects during compilation 147 */ 148 private void lazyClassInit() { 149 150 Field[] fields = clazz.getDeclaredFields(); 151 for (int i=0;i<fields.length;i++){ 152 addField(fields[i].getName(),fields[i].getModifiers(),this,null); 153 } 154 Method[] methods = clazz.getDeclaredMethods(); 155 for (int i=0;i<methods.length;i++){ 156 Method m = methods[i]; 157 MethodNode mn = new MethodNode(m.getName(), m.getModifiers(), ClassHelper.make(m.getReturnType()), createParameters(m.getParameterTypes()), null); 158 addMethod(mn); 159 } 160 Constructor[] constructors = clazz.getConstructors(); 161 for (int i=0;i<constructors.length;i++){ 162 Constructor ctor = constructors[i]; 163 addConstructor(ctor.getModifiers(),createParameters(ctor.getParameterTypes()),null); 164 } 165 Class sc = clazz.getSuperclass(); 166 if (sc!=null) superClass = ClassHelper.make(sc); 167 buildInterfaceTypes(clazz); 168 lazyInitDone=true; 169 } 170 171 private void buildInterfaceTypes(Class c) { 172 Class[] interfaces = c.getInterfaces(); 173 ClassNode[] ret = new ClassNode[interfaces.length]; 174 for (int i=0;i<interfaces.length;i++){ 175 ret[i] = ClassHelper.make(interfaces[i]); 176 } 177 this.interfaces = ret; 178 } 179 180 181 //br added to track the enclosing method for local inner classes 182 private MethodNode enclosingMethod = null; 183 184 public MethodNode getEnclosingMethod() { 185 return redirect().enclosingMethod; 186 } 187 188 public void setEnclosingMethod(MethodNode enclosingMethod) { 189 redirect().enclosingMethod = enclosingMethod; 190 } 191 192 193 /** 194 * @param name is the full name of the class 195 * @param modifiers the modifiers, 196 * @param superClass the base class name - use "java.lang.Object" if no direct 197 * base class 198 * @see org.objectweb.asm.Opcodes 199 */ 200 public ClassNode(String name, int modifiers, ClassNode superClass) { 201 this(name, modifiers, superClass, ClassHelper.EMPTY_TYPE_ARRAY, MixinNode.EMPTY_ARRAY); 202 } 203 204 /** 205 * @param name is the full name of the class 206 * @param modifiers the modifiers, 207 * @param superClass the base class name - use "java.lang.Object" if no direct 208 * base class 209 * @see org.objectweb.asm.Opcodes 210 */ 211 public ClassNode(String name, int modifiers, ClassNode superClass, ClassNode[] interfaces, MixinNode[] mixins) { 212 this.name = name; 213 this.modifiers = modifiers; 214 this.superClass = superClass; 215 this.interfaces = interfaces; 216 this.mixins = mixins; 217 this.resolved = true; 218 isPrimaryNode = true; 219 } 220 221 public void setSuperClass(ClassNode superClass) { 222 redirect().superClass = superClass; 223 } 224 225 public List getFields() { 226 if (!lazyInitDone) { 227 lazyClassInit(); 228 } 229 if (redirect!=null) return redirect().getFields(); 230 return fields; 231 } 232 233 public ClassNode[] getInterfaces() { 234 if (!lazyInitDone) { 235 lazyClassInit(); 236 } 237 if (redirect!=null) return redirect().getInterfaces(); 238 return interfaces; 239 } 240 241 public MixinNode[] getMixins() { 242 return redirect().mixins; 243 } 244 245 public List getMethods() { 246 if (!lazyInitDone) { 247 lazyClassInit(); 248 } 249 if (redirect!=null) return redirect().getMethods(); 250 return methods; 251 } 252 253 public List getAbstractMethods() { 254 255 List result = new ArrayList(); 256 for (Iterator methIt = getAllDeclaredMethods().iterator(); methIt.hasNext();) { 257 MethodNode method = (MethodNode) methIt.next(); 258 if (method.isAbstract()) { 259 result.add(method); 260 } 261 } 262 if (result.size() == 0) { 263 return null; 264 } 265 else { 266 return result; 267 } 268 } 269 270 public List getAllDeclaredMethods() { 271 return new ArrayList(getDeclaredMethodsMap().values()); 272 } 273 274 275 protected Map getDeclaredMethodsMap() { 276 // Start off with the methods from the superclass. 277 ClassNode parent = getSuperClass(); 278 Map result = null; 279 if (parent != null) { 280 result = parent.getDeclaredMethodsMap(); 281 } 282 else { 283 result = new HashMap(); 284 } 285 286 // add in unimplemented abstract methods from the interfaces 287 ClassNode[] interfaces = getInterfaces(); 288 for (int i = 0; i < interfaces.length; i++) { 289 ClassNode iface = interfaces[i]; 290 Map ifaceMethodsMap = iface.getDeclaredMethodsMap(); 291 for (Iterator iter = ifaceMethodsMap.keySet().iterator(); iter.hasNext();) { 292 String methSig = (String) iter.next(); 293 if (!result.containsKey(methSig)) { 294 MethodNode methNode = (MethodNode) ifaceMethodsMap.get(methSig); 295 result.put(methSig, methNode); 296 } 297 } 298 } 299 300 // And add in the methods implemented in this class. 301 for (Iterator iter = getMethods().iterator(); iter.hasNext();) { 302 MethodNode method = (MethodNode) iter.next(); 303 String sig = method.getTypeDescriptor(); 304 if (result.containsKey(sig)) { 305 MethodNode inheritedMethod = (MethodNode) result.get(sig); 306 if (inheritedMethod.isAbstract()) { 307 result.put(sig, method); 308 } 309 } 310 else { 311 result.put(sig, method); 312 } 313 } 314 return result; 315 } 316 317 public String getName() { 318 return redirect().name; 319 } 320 321 public String setName(String name) { 322 return redirect().name=name; 323 } 324 325 public int getModifiers() { 326 return redirect().modifiers; 327 } 328 329 public List getProperties() { 330 return redirect().properties; 331 } 332 333 public List getDeclaredConstructors() { 334 if (!lazyInitDone) { 335 lazyClassInit(); 336 } 337 return redirect().constructors; 338 } 339 340 public ModuleNode getModule() { 341 return redirect().module; 342 } 343 344 public void setModule(ModuleNode module) { 345 redirect().module = module; 346 if (module != null) { 347 redirect().compileUnit = module.getUnit(); 348 } 349 } 350 351 public void addField(FieldNode node) { 352 node.setDeclaringClass(redirect()); 353 node.setOwner(redirect()); 354 redirect().fields.add(node); 355 redirect().fieldIndex.put(node.getName(), node); 356 } 357 358 public void addProperty(PropertyNode node) { 359 node.setDeclaringClass(redirect()); 360 FieldNode field = node.getField(); 361 addField(field); 362 363 redirect().properties.add(node); 364 } 365 366 public PropertyNode addProperty(String name, 367 int modifiers, 368 ClassNode type, 369 Expression initialValueExpression, 370 Statement getterBlock, 371 Statement setterBlock) { 372 for (Iterator iter = getProperties().iterator(); iter.hasNext();) { 373 PropertyNode pn = (PropertyNode) iter.next(); 374 if (pn.getName().equals(name)) return pn; 375 } 376 PropertyNode node = 377 new PropertyNode(name, modifiers, type, redirect(), initialValueExpression, getterBlock, setterBlock); 378 addProperty(node); 379 return node; 380 } 381 382 public void addConstructor(ConstructorNode node) { 383 node.setDeclaringClass(this); 384 redirect().constructors.add(node); 385 } 386 387 public ConstructorNode addConstructor(int modifiers, Parameter[] parameters, Statement code) { 388 ConstructorNode node = new ConstructorNode(modifiers, parameters, code); 389 addConstructor(node); 390 return node; 391 } 392 393 public void addMethod(MethodNode node) { 394 node.setDeclaringClass(this); 395 redirect().methods.add(node); 396 } 397 398 /** 399 * IF a method with the given name and parameters is already defined then it is returned 400 * otherwise the given method is added to this node. This method is useful for 401 * default method adding like getProperty() or invokeMethod() where there may already 402 * be a method defined in a class and so the default implementations should not be added 403 * if already present. 404 */ 405 public MethodNode addMethod(String name, 406 int modifiers, 407 ClassNode returnType, 408 Parameter[] parameters, 409 Statement code) { 410 MethodNode other = getDeclaredMethod(name, parameters); 411 // lets not add duplicate methods 412 if (other != null) { 413 return other; 414 } 415 MethodNode node = new MethodNode(name, modifiers, returnType, parameters, code); 416 addMethod(node); 417 return node; 418 } 419 420 /** 421 * Adds a synthetic method as part of the compilation process 422 */ 423 public MethodNode addSyntheticMethod(String name, 424 int modifiers, 425 ClassNode returnType, 426 Parameter[] parameters, 427 Statement code) { 428 MethodNode answer = addMethod(name, modifiers, returnType, parameters, code); 429 answer.setSynthetic(true); 430 return answer; 431 } 432 433 public FieldNode addField(String name, int modifiers, ClassNode type, Expression initialValue) { 434 FieldNode node = new FieldNode(name, modifiers, type, redirect(), initialValue); 435 addField(node); 436 return node; 437 } 438 439 public void addInterface(ClassNode type) { 440 // lets check if it already implements an interface 441 boolean skip = false; 442 ClassNode[] interfaces = redirect().interfaces; 443 for (int i = 0; i < interfaces.length; i++) { 444 if (type.equals(interfaces[i])) { 445 skip = true; 446 } 447 } 448 if (!skip) { 449 ClassNode[] newInterfaces = new ClassNode[interfaces.length + 1]; 450 System.arraycopy(interfaces, 0, newInterfaces, 0, interfaces.length); 451 newInterfaces[interfaces.length] = type; 452 redirect().interfaces = newInterfaces; 453 } 454 } 455 456 public boolean equals(Object o) { 457 if (redirect!=null) return redirect().equals(o); 458 ClassNode cn = (ClassNode) o; 459 return (cn.getName().equals(getName())); 460 } 461 462 public void addMixin(MixinNode mixin) { 463 // lets check if it already uses a mixin 464 MixinNode[] mixins = redirect().mixins; 465 boolean skip = false; 466 for (int i = 0; i < mixins.length; i++) { 467 if (mixin.equals(mixins[i])) { 468 skip = true; 469 } 470 } 471 if (!skip) { 472 MixinNode[] newMixins = new MixinNode[mixins.length + 1]; 473 System.arraycopy(mixins, 0, newMixins, 0, mixins.length); 474 newMixins[mixins.length] = mixin; 475 redirect().mixins = newMixins; 476 } 477 } 478 479 public FieldNode getField(String name) { 480 return (FieldNode) redirect().fieldIndex.get(name); 481 } 482 483 /** 484 * @return the field node on the outer class or null if this is not an 485 * inner class 486 */ 487 public FieldNode getOuterField(String name) { 488 return null; 489 } 490 491 /** 492 * Helper method to avoid casting to inner class 493 * 494 * @return 495 */ 496 public ClassNode getOuterClass() { 497 return null; 498 } 499 500 public void addStaticInitializerStatements(List staticStatements) { 501 MethodNode method = null; 502 List declaredMethods = getDeclaredMethods("<clinit>"); 503 if (declaredMethods.isEmpty()) { 504 method = 505 addMethod("<clinit>", ACC_PUBLIC | ACC_STATIC, ClassHelper.VOID_TYPE, Parameter.EMPTY_ARRAY, new BlockStatement()); 506 method.setSynthetic(true); 507 } 508 else { 509 method = (MethodNode) declaredMethods.get(0); 510 } 511 BlockStatement block = null; 512 Statement statement = method.getCode(); 513 if (statement == null) { 514 block = new BlockStatement(); 515 } 516 else if (statement instanceof BlockStatement) { 517 block = (BlockStatement) statement; 518 } 519 else { 520 block = new BlockStatement(); 521 block.addStatement(statement); 522 } 523 block.addStatements(staticStatements); 524 } 525 526 /** 527 * @return a list of methods which match the given name 528 */ 529 public List getDeclaredMethods(String name) { 530 List answer = new ArrayList(); 531 for (Iterator iter = getMethods().iterator(); iter.hasNext();) { 532 MethodNode method = (MethodNode) iter.next(); 533 if (name.equals(method.getName())) { 534 answer.add(method); 535 } 536 } 537 return answer; 538 } 539 540 /** 541 * @return a list of methods which match the given name 542 */ 543 public List getMethods(String name) { 544 List answer = new ArrayList(); 545 ClassNode node = this; 546 do { 547 for (Iterator iter = node.getMethods().iterator(); iter.hasNext();) { 548 MethodNode method = (MethodNode) iter.next(); 549 if (name.equals(method.getName())) { 550 answer.add(method); 551 } 552 } 553 node = node.getSuperClass(); 554 } 555 while (node != null); 556 return answer; 557 } 558 559 /** 560 * @return the method matching the given name and parameters or null 561 */ 562 public MethodNode getDeclaredMethod(String name, Parameter[] parameters) { 563 for (Iterator iter = getMethods().iterator(); iter.hasNext();) { 564 MethodNode method = (MethodNode) iter.next(); 565 if (name.equals(method.getName()) && parametersEqual(method.getParameters(), parameters)) { 566 return method; 567 } 568 } 569 return null; 570 } 571 572 /** 573 * @return true if this node is derived from the given class node 574 */ 575 public boolean isDerivedFrom(ClassNode type) { 576 ClassNode node = getSuperClass(); 577 while (node != null) { 578 if (type.equals(node)) { 579 return true; 580 } 581 node = node.getSuperClass(); 582 } 583 return false; 584 } 585 586 /** 587 * @return true if this class is derived from a groovy object 588 * i.e. it implements GroovyObject 589 */ 590 public boolean isDerivedFromGroovyObject() { 591 return implementsInteface(GroovyObject.class.getName()); 592 } 593 594 /** 595 * @param name the fully qualified name of the interface 596 * @return true if this class or any base class implements the given interface 597 */ 598 public boolean implementsInteface(String name) { 599 ClassNode node = redirect(); 600 do { 601 if (node.declaresInterface(name)) { 602 return true; 603 } 604 node = node.getSuperClass(); 605 } 606 while (node != null); 607 return false; 608 } 609 610 /** 611 * @param name the fully qualified name of the interface 612 * @return true if this class declares that it implements the given interface 613 */ 614 public boolean declaresInterface(String name) { 615 ClassNode[] interfaces = redirect().getInterfaces(); 616 int size = interfaces.length; 617 for (int i = 0; i < size; i++) { 618 if (interfaces[i].getName().equals(name)) { 619 return true; 620 } 621 } 622 return false; 623 } 624 625 /** 626 * @return the ClassNode of the super class of this type 627 */ 628 public ClassNode getSuperClass() { 629 if (!lazyInitDone) { 630 lazyClassInit(); 631 } 632 if (!isResolved()) { 633 throw new GroovyBugError("Classnode#getSuperClass for "+getName()+" called before class resolving"); 634 } 635 return redirect().superClass; 636 } 637 638 /** 639 * Factory method to create a new MethodNode via reflection 640 */ 641 protected MethodNode createMethodNode(Method method) { 642 Parameter[] parameters = createParameters(method.getParameterTypes()); 643 return new MethodNode(method.getName(), method.getModifiers(), ClassHelper.make(method.getReturnType()), parameters, EmptyStatement.INSTANCE); 644 } 645 646 /** 647 * @param types 648 * @return 649 */ 650 protected Parameter[] createParameters(Class[] types) { 651 Parameter[] parameters = Parameter.EMPTY_ARRAY; 652 int size = types.length; 653 if (size > 0) { 654 parameters = new Parameter[size]; 655 for (int i = 0; i < size; i++) { 656 parameters[i] = createParameter(types[i], i); 657 } 658 } 659 return parameters; 660 } 661 662 protected Parameter createParameter(Class parameterType, int idx) { 663 return new Parameter(ClassHelper.make(parameterType), "param" + idx); 664 } 665 666 public CompileUnit getCompileUnit() { 667 if (redirect!=null) return redirect().getCompileUnit(); 668 if (compileUnit == null && module != null) { 669 compileUnit = module.getUnit(); 670 } 671 return compileUnit; 672 } 673 674 protected void setCompileUnit(CompileUnit cu) { 675 if (redirect!=null) redirect().setCompileUnit(cu); 676 if (compileUnit!= null) compileUnit = cu; 677 } 678 679 /** 680 * @return true if the two arrays are of the same size and have the same contents 681 */ 682 protected boolean parametersEqual(Parameter[] a, Parameter[] b) { 683 if (a.length == b.length) { 684 boolean answer = true; 685 for (int i = 0; i < a.length; i++) { 686 if (!a[i].getType().equals(b[i].getType())) { 687 answer = false; 688 break; 689 } 690 } 691 return answer; 692 } 693 return false; 694 } 695 696 /** 697 * @return the package name of this class 698 */ 699 public String getPackageName() { 700 int idx = getName().lastIndexOf('.'); 701 if (idx > 0) { 702 return getName().substring(0, idx); 703 } 704 return null; 705 } 706 707 public String getNameWithoutPackage() { 708 int idx = getName().lastIndexOf('.'); 709 if (idx > 0) { 710 return getName().substring(idx + 1); 711 } 712 return getName(); 713 } 714 715 public void visitContents(GroovyClassVisitor visitor) { 716 717 // now lets visit the contents of the class 718 for (Iterator iter = getProperties().iterator(); iter.hasNext();) { 719 PropertyNode pn = (PropertyNode) iter.next(); 720 visitor.visitProperty(pn); 721 } 722 723 for (Iterator iter = getFields().iterator(); iter.hasNext();) { 724 FieldNode fn = (FieldNode) iter.next(); 725 visitor.visitField(fn); 726 } 727 728 for (Iterator iter = getDeclaredConstructors().iterator(); iter.hasNext();) { 729 ConstructorNode cn = (ConstructorNode) iter.next(); 730 visitor.visitConstructor(cn); 731 } 732 733 for (Iterator iter = getMethods().iterator(); iter.hasNext();) { 734 MethodNode mn = (MethodNode) iter.next(); 735 visitor.visitMethod(mn); 736 } 737 } 738 739 public MethodNode getGetterMethod(String getterName) { 740 for (Iterator iter = getMethods().iterator(); iter.hasNext();) { 741 MethodNode method = (MethodNode) iter.next(); 742 if (getterName.equals(method.getName()) 743 && ClassHelper.VOID_TYPE!=method.getReturnType() 744 && method.getParameters().length == 0) { 745 return method; 746 } 747 } 748 return null; 749 } 750 751 public MethodNode getSetterMethod(String getterName) { 752 for (Iterator iter = getMethods().iterator(); iter.hasNext();) { 753 MethodNode method = (MethodNode) iter.next(); 754 if (getterName.equals(method.getName()) 755 && ClassHelper.VOID_TYPE==method.getReturnType() 756 && method.getParameters().length == 1) { 757 return method; 758 } 759 } 760 return null; 761 } 762 763 /** 764 * Is this class delcared in a static method (such as a closure / inner class declared in a static method) 765 * 766 * @return 767 */ 768 public boolean isStaticClass() { 769 return redirect().staticClass; 770 } 771 772 public void setStaticClass(boolean staticClass) { 773 redirect().staticClass = staticClass; 774 } 775 776 /** 777 * @return Returns true if this inner class or closure was declared inside a script body 778 */ 779 public boolean isScriptBody() { 780 return redirect().scriptBody; 781 } 782 783 public void setScriptBody(boolean scriptBody) { 784 redirect().scriptBody = scriptBody; 785 } 786 787 public boolean isScript() { 788 return redirect().script || isDerivedFrom(ClassHelper.SCRIPT_TYPE); 789 } 790 791 public void setScript(boolean script) { 792 redirect().script = script; 793 } 794 795 public String toString() { 796 return super.toString() + "[name: " + getName() + "]"; 797 } 798 799 /** 800 * Returns true if the given method has a possibly matching method with the given name and arguments 801 */ 802 public boolean hasPossibleMethod(String name, Expression arguments) { 803 int count = 0; 804 805 if (arguments instanceof TupleExpression) { 806 TupleExpression tuple = (TupleExpression) arguments; 807 // TODO this won't strictly be true when using list expension in argument calls 808 count = tuple.getExpressions().size(); 809 } 810 ClassNode node = this; 811 do { 812 for (Iterator iter = getMethods().iterator(); iter.hasNext();) { 813 MethodNode method = (MethodNode) iter.next(); 814 if (name.equals(method.getName()) && method.getParameters().length == count) { 815 return true; 816 } 817 } 818 node = node.getSuperClass(); 819 } 820 while (node != null); 821 return false; 822 } 823 824 public boolean isInterface(){ 825 return (getModifiers() & Opcodes.ACC_INTERFACE) > 0; 826 } 827 828 public boolean isResolved(){ 829 return redirect().resolved || (componentType != null && componentType.isResolved()); 830 } 831 832 public boolean isArray(){ 833 return componentType!=null; 834 } 835 836 public ClassNode getComponentType() { 837 return componentType; 838 } 839 840 public Class getTypeClass(){ 841 Class c = redirect().clazz; 842 if (c!=null) return c; 843 ClassNode component = redirect().componentType; 844 if (component!=null && component.isResolved()){ 845 ClassNode cn = component.makeArray(); 846 setRedirect(cn); 847 return redirect().clazz; 848 } 849 throw new GroovyBugError("ClassNode#getTypeClass for "+getName()+" is called before the type class is set "); 850 } 851 852 public boolean hasPackageName(){ 853 return redirect().name.indexOf('.')>0; 854 } 855 }