View Javadoc

    1   /*
    2    * $Id: ClassNode.java 4216 2006-11-13 16:04:23Z blackdrag $
    3    *
    4    * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
    5    *
    6    * Redistribution and use of this software and associated documentation
    7    * ("Software"), with or without modification, are permitted provided that the
    8    * following conditions are met:
    9    *  1. Redistributions of source code must retain copyright statements and
    10   * notices. Redistributions must also contain a copy of this document.
    11   *  2. Redistributions in binary form must reproduce the above copyright
    12   * notice, this list of conditions and the following disclaimer in the
    13   * documentation and/or other materials provided with the distribution.
    14   *  3. The name "groovy" must not be used to endorse or promote products
    15   * derived from this Software without prior written permission of The Codehaus.
    16   * For written permission, please contact info@codehaus.org.
    17   *  4. Products derived from this Software may not be called "groovy" nor may
    18   * "groovy" appear in their names without prior written permission of The
    19   * Codehaus. "groovy" is a registered trademark of The Codehaus.
    20   *  5. Due credit should be given to The Codehaus - http://groovy.codehaus.org/
    21   *
    22   * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
    23   * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    24   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    25   * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
    26   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    27   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    28   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    29   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    30   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    31   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
    32   * DAMAGE.
    33   *
    34   */
    35  package org.codehaus.groovy.ast;
    36
    37  import groovy.lang.GroovyObject;
    38
    39  import org.codehaus.groovy.GroovyBugError;
    40  import org.codehaus.groovy.ast.expr.Expression;
    41  import org.codehaus.groovy.ast.expr.TupleExpression;
    42  import org.codehaus.groovy.ast.stmt.BlockStatement;
    43  import org.codehaus.groovy.ast.stmt.EmptyStatement;
    44  import org.codehaus.groovy.ast.stmt.Statement;
    45  import org.objectweb.asm.Opcodes;
    46
    47  import java.lang.reflect.Array;
    48  import java.lang.reflect.Constructor;
    49  import java.lang.reflect.Field;
    50  import java.lang.reflect.Method;
    51  import java.util.ArrayList;
    52  import java.util.HashMap;
    53  import java.util.HashSet;
    54  import java.util.Iterator;
    55  import java.util.List;
    56  import java.util.Map;
    57
    58  /***
    59   * Represents a class in the AST.<br/>
    60   * A ClassNode should be created using the methods in ClassHelper.
    61   * This ClassNode may be used to represent a class declaration or
    62   * any other type. This class uses a proxy meschanism allowing to
    63   * create a class for a plain name at ast creation time. In another
    64   * phase of the compiler the real ClassNode for the plain name may be
    65   * found. To avoid the need of exchanging this ClassNode with an
    66   * instance of the correct ClassNode the correct ClassNode is set as
    67   * redirect. All method calls are then redirected to that ClassNode.
    68   * <br>
    69   * Note: the proxy mechanism is only allowed for classes being marked
    70   * as primary ClassNode which means they represent no actual class.
    71   * The redirect itself can be any type of ClassNode
    72   *
    73   * @see org.codehaus.groovy.ast.ClassHelper
    74   *
    75   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
    76   * @author Jochen Theodorou
    77   * @version $Revision: 4216 $
    78   */
    79  public class ClassNode extends AnnotatedNode implements Opcodes {
    80
    81  	public static ClassNode[] EMPTY_ARRAY = new ClassNode[0];
    82
    83      public static ClassNode THIS = new ClassNode(Object.class);
    84      public static ClassNode SUPER = new ClassNode(Object.class);
    85
    86      private String name;
    87      private int modifiers;
    88      private ClassNode[] interfaces;
    89      private MixinNode[] mixins;
    90      private List constructors = new ArrayList();
    91      private List  objectInitializers = new ArrayList();
    92      private List methods = new ArrayList();
    93      private List fields = new ArrayList();
    94      private List properties = new ArrayList();
    95      private Map fieldIndex = new HashMap();
    96      private ModuleNode module;
    97      private CompileUnit compileUnit;
    98      private boolean staticClass = false;
    99      private boolean scriptBody = false;
    100     private boolean script;
    101     private ClassNode superClass;
    102     boolean isPrimaryNode;
    103
    104     // use this to synchronize access for the lazy intit
    105     protected Object lazyInitLock = new Object();
    106
    107     // clazz!=null when resolved
    108     protected Class clazz;
    109     // only false when this classNode is constructed from a class
    110     private boolean lazyInitDone=true;
    111     // not null if if the ClassNode is an array
    112     private ClassNode componentType = null;
    113     // if not null this instance is handled as proxy
    114     // for the redirect
    115     private ClassNode redirect=null;
    116
    117     /***
    118      * Returns the ClassNode this ClassNode is redirecting to.
    119      */
    120     protected ClassNode redirect(){
    121         if (redirect==null) return this;
    122         return redirect.redirect();
    123     }
    124
    125     /***
    126      * Sets this instance as proxy for the given ClassNode.
    127      * @param cn the class to redirect to. If set to null the redirect will be removed
    128      */
    129     public void setRedirect(ClassNode cn) {
    130         if (isPrimaryNode) throw new GroovyBugError("tried to set a redirect for a primary ClassNode ("+getName()+"->"+cn.getName()+").");
    131         if (cn!=null) cn = cn.redirect();
    132         redirect = cn;
    133     }
    134
    135     /***
    136      * Returns a ClassNode representing an array of the class
    137      * represented by this ClassNode
    138      */
    139     public ClassNode makeArray() {
    140         if (redirect!=null) return redirect().makeArray();
    141         ClassNode cn;
    142         if (clazz!=null) {
    143             Class ret = Array.newInstance(clazz,0).getClass();
    144             // don't use the ClassHelper here!
    145             cn = new ClassNode(ret,this);
    146         } else {
    147             cn = new ClassNode(this);
    148         }
    149         return cn;
    150     }
    151
    152     /***
    153      * Returns if this instance is a primary ClassNode
    154      */
    155     public boolean isPrimaryClassNode(){
    156     	return redirect().isPrimaryNode || (componentType!= null && componentType.isPrimaryClassNode());
    157     }
    158
    159     /***
    160      * Constructor used by makeArray() if no real class is available
    161      */
    162     private ClassNode(ClassNode componentType) {
    163         this(componentType.getName()+"[]", ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
    164         this.componentType = componentType.redirect();
    165         isPrimaryNode=false;
    166     }
    167
    168     /***
    169      * Constructor used by makeArray() if a real class is available
    170      */
    171     private ClassNode(Class c, ClassNode componentType) {
    172         this(c);
    173         this.componentType = componentType;
    174         isPrimaryNode=false;
    175     }
    176
    177     /***
    178      * Creates a ClassNode from a real class. The resulting
    179      * ClassNode will be no primary ClassNode.
    180      */
    181     public ClassNode(Class c) {
    182         this(c.getName(), c.getModifiers(), null, null ,MixinNode.EMPTY_ARRAY);
    183         clazz=c;
    184         lazyInitDone=false;
    185         CompileUnit cu = getCompileUnit();
    186         if (cu!=null) cu.addClass(this);
    187         isPrimaryNode=false;
    188     }
    189
    190     /***
    191      * The complete class structure will be initialized only when really
    192      * needed to avoid having too much objects during compilation
    193      */
    194     private void lazyClassInit() {
    195         synchronized (lazyInitLock) {
    196             if (lazyInitDone) return;
    197
    198             Field[] fields = clazz.getDeclaredFields();
    199             for (int i=0;i<fields.length;i++){
    200                 addField(fields[i].getName(),fields[i].getModifiers(),this,null);
    201             }
    202             Method[] methods = clazz.getDeclaredMethods();
    203             for (int i=0;i<methods.length;i++){
    204                 Method m = methods[i];
    205                 MethodNode mn = new MethodNode(m.getName(), m.getModifiers(), ClassHelper.make(m.getReturnType()), createParameters(m.getParameterTypes()), ClassHelper.make(m.getExceptionTypes()), null);
    206                 addMethod(mn);
    207             }
    208             Constructor[] constructors = clazz.getDeclaredConstructors();
    209             for (int i=0;i<constructors.length;i++){
    210                 Constructor ctor = constructors[i];
    211                 addConstructor(ctor.getModifiers(),createParameters(ctor.getParameterTypes()),ClassHelper.make(ctor.getExceptionTypes()),null);
    212             }
    213             Class sc = clazz.getSuperclass();
    214             if (sc!=null) superClass = ClassHelper.make(sc);
    215             buildInterfaceTypes(clazz);
    216             lazyInitDone=true;
    217         }
    218     }
    219
    220     private void buildInterfaceTypes(Class c) {
    221         Class[] interfaces = c.getInterfaces();
    222         ClassNode[] ret = new ClassNode[interfaces.length];
    223         for (int i=0;i<interfaces.length;i++){
    224             ret[i] = ClassHelper.make(interfaces[i]);
    225         }
    226         this.interfaces = ret;
    227     }
    228
    229
    230     // added to track the enclosing method for local inner classes
    231     private MethodNode enclosingMethod = null;
    232
    233     public MethodNode getEnclosingMethod() {
    234         return redirect().enclosingMethod;
    235     }
    236
    237     public void setEnclosingMethod(MethodNode enclosingMethod) {
    238         redirect().enclosingMethod = enclosingMethod;
    239     }
    240
    241
    242     /***
    243      * @param name       is the full name of the class
    244      * @param modifiers  the modifiers,
    245      * @param superClass the base class name - use "java.lang.Object" if no direct
    246      *                   base class
    247      * @see org.objectweb.asm.Opcodes
    248      */
    249     public ClassNode(String name, int modifiers, ClassNode superClass) {
    250         this(name, modifiers, superClass, ClassHelper.EMPTY_TYPE_ARRAY, MixinNode.EMPTY_ARRAY);
    251     }
    252
    253     /***
    254      * @param name       is the full name of the class
    255      * @param modifiers  the modifiers,
    256      * @param superClass the base class name - use "java.lang.Object" if no direct
    257      *                   base class
    258      * @see org.objectweb.asm.Opcodes
    259      */
    260     public ClassNode(String name, int modifiers, ClassNode superClass, ClassNode[] interfaces, MixinNode[] mixins) {
    261         this.name = name;
    262         this.modifiers = modifiers;
    263         this.superClass = superClass;
    264         this.interfaces = interfaces;
    265         this.mixins = mixins;
    266         isPrimaryNode = true;
    267     }
    268
    269
    270     /***
    271      * Sets the superclass of this ClassNode
    272      */
    273     public void setSuperClass(ClassNode superClass) {
    274         redirect().superClass = superClass;
    275     }
    276
    277     /***
    278      * Returns a list containing FieldNode objects for
    279      * each field in the class represented by this ClassNode
    280      */
    281     public List getFields() {
    282         if (!lazyInitDone) {
    283             lazyClassInit();
    284         }
    285         if (redirect!=null) return redirect().getFields();
    286         return fields;
    287     }
    288
    289     /***
    290      * Returns an array of ClassNodes representing the
    291      * interfaces the class implements
    292      */
    293     public ClassNode[] getInterfaces() {
    294         if (!lazyInitDone) {
    295             lazyClassInit();
    296         }
    297         if (redirect!=null) return redirect().getInterfaces();
    298         return interfaces;
    299     }
    300
    301     public MixinNode[] getMixins() {
    302         return redirect().mixins;
    303     }
    304
    305     /***
    306      * Returns a list containing MethodNode objects for
    307      * each method in the class represented by this ClassNode
    308      */
    309     public List getMethods() {
    310         if (!lazyInitDone) {
    311             lazyClassInit();
    312         }
    313         if (redirect!=null) return redirect().getMethods();
    314         return methods;
    315     }
    316
    317     /***
    318      * Returns a list containing MethodNode objects for
    319      * each abstract method in the class represented by
    320      * this ClassNode
    321      */
    322     public List getAbstractMethods() {
    323
    324         HashSet abstractNodes = new HashSet();
    325         // let us collect the abstract super classes and stop at the
    326         // first non abstract super class. If such a class still
    327         // contains abstract methods, then loading that class will fail.
    328         // No need to be extra carefull here for that.
    329         ClassNode parent = this.redirect();
    330         do {
    331         	abstractNodes.add(parent);
    332             ClassNode[] interfaces = parent.getInterfaces();
    333             for (int i = 0; i < interfaces.length; i++) {
    334                 abstractNodes.add(interfaces[i].redirect());
    335             }
    336             parent = parent.getSuperClass().redirect();
    337         } while (parent!=null && ((parent.getModifiers() & Opcodes.ACC_ABSTRACT) != 0));
    338
    339         List result = new ArrayList();
    340         for (Iterator methIt = getAllDeclaredMethods().iterator(); methIt.hasNext();) {
    341             MethodNode method = (MethodNode) methIt.next();
    342             // add only abstract methods from abtract classes that
    343             // are not overwritten
    344             if ( abstractNodes.contains(method.getDeclaringClass().redirect()) &&
    345                  (method.getModifiers() & Opcodes.ACC_ABSTRACT) != 0
    346                ) {
    347                 result.add(method);
    348             }
    349         }
    350         if (result.size() == 0) {
    351             return null;
    352         }
    353         else {
    354             return result;
    355         }
    356     }
    357
    358     public List getAllDeclaredMethods() {
    359         return new ArrayList(getDeclaredMethodsMap().values());
    360     }
    361
    362
    363     protected Map getDeclaredMethodsMap() {
    364         // Start off with the methods from the superclass.
    365         ClassNode parent = getSuperClass();
    366         Map result = null;
    367         if (parent != null) {
    368             result = parent.getDeclaredMethodsMap();
    369         }
    370         else {
    371             result = new HashMap();
    372         }
    373
    374         // add in unimplemented abstract methods from the interfaces
    375         ClassNode[] interfaces = getInterfaces();
    376         for (int i = 0; i < interfaces.length; i++) {
    377             ClassNode iface = interfaces[i];
    378             Map ifaceMethodsMap = iface.getDeclaredMethodsMap();
    379             for (Iterator iter = ifaceMethodsMap.keySet().iterator(); iter.hasNext();) {
    380                 String methSig = (String) iter.next();
    381                 if (!result.containsKey(methSig)) {
    382                     MethodNode methNode = (MethodNode) ifaceMethodsMap.get(methSig);
    383                     result.put(methSig, methNode);
    384                 }
    385             }
    386         }
    387
    388         // And add in the methods implemented in this class.
    389         for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
    390             MethodNode method = (MethodNode) iter.next();
    391             String sig = method.getTypeDescriptor();
    392             result.put(sig, method);
    393         }
    394         return result;
    395     }
    396
    397     public String getName() {
    398         return redirect().name;
    399     }
    400
    401     public String setName(String name) {
    402         return redirect().name=name;
    403     }
    404
    405     public int getModifiers() {
    406         return redirect().modifiers;
    407     }
    408
    409     public List getProperties() {
    410         return redirect().properties;
    411     }
    412
    413     public List getDeclaredConstructors() {
    414         if (!lazyInitDone) {
    415             lazyClassInit();
    416         }
    417         return redirect().constructors;
    418     }
    419
    420     public ModuleNode getModule() {
    421         return redirect().module;
    422     }
    423
    424     public void setModule(ModuleNode module) {
    425         redirect().module = module;
    426         if (module != null) {
    427             redirect().compileUnit = module.getUnit();
    428         }
    429     }
    430
    431     public void addField(FieldNode node) {
    432         node.setDeclaringClass(redirect());
    433         node.setOwner(redirect());
    434         redirect().fields.add(node);
    435         redirect().fieldIndex.put(node.getName(), node);
    436     }
    437
    438     public void addProperty(PropertyNode node) {
    439         node.setDeclaringClass(redirect());
    440         FieldNode field = node.getField();
    441         addField(field);
    442
    443         redirect().properties.add(node);
    444     }
    445
    446     public PropertyNode addProperty(String name,
    447                                     int modifiers,
    448                                     ClassNode type,
    449                                     Expression initialValueExpression,
    450                                     Statement getterBlock,
    451                                     Statement setterBlock) {
    452     	for (Iterator iter = getProperties().iterator(); iter.hasNext();) {
    453 			PropertyNode pn = (PropertyNode) iter.next();
    454 			if (pn.getName().equals(name)) return pn;
    455 		}
    456         PropertyNode node =
    457                 new PropertyNode(name, modifiers, type, redirect(), initialValueExpression, getterBlock, setterBlock);
    458         addProperty(node);
    459         return node;
    460     }
    461
    462     public void addConstructor(ConstructorNode node) {
    463         node.setDeclaringClass(this);
    464         redirect().constructors.add(node);
    465     }
    466
    467     public ConstructorNode addConstructor(int modifiers, Parameter[] parameters, ClassNode[] exceptions, Statement code) {
    468         ConstructorNode node = new ConstructorNode(modifiers, parameters, exceptions, code);
    469         addConstructor(node);
    470         return node;
    471     }
    472
    473     public void addMethod(MethodNode node) {
    474         node.setDeclaringClass(this);
    475         redirect().methods.add(node);
    476     }
    477
    478     /***
    479      * IF a method with the given name and parameters is already defined then it is returned
    480      * otherwise the given method is added to this node. This method is useful for
    481      * default method adding like getProperty() or invokeMethod() where there may already
    482      * be a method defined in a class and  so the default implementations should not be added
    483      * if already present.
    484      */
    485     public MethodNode addMethod(String name,
    486                                 int modifiers,
    487                                 ClassNode returnType,
    488                                 Parameter[] parameters,
    489                                 ClassNode[] exceptions,
    490                                 Statement code) {
    491         MethodNode other = getDeclaredMethod(name, parameters);
    492         // lets not add duplicate methods
    493         if (other != null) {
    494             return other;
    495         }
    496         MethodNode node = new MethodNode(name, modifiers, returnType, parameters, exceptions, code);
    497         addMethod(node);
    498         return node;
    499     }
    500
    501     /***
    502      * Adds a synthetic method as part of the compilation process
    503      */
    504     public MethodNode addSyntheticMethod(String name,
    505                                          int modifiers,
    506                                          ClassNode returnType,
    507                                          Parameter[] parameters,
    508                                          ClassNode[] exceptions,
    509                                          Statement code) {
    510         MethodNode answer = addMethod(name, modifiers, returnType, parameters, exceptions, code);
    511         answer.setSynthetic(true);
    512         return answer;
    513     }
    514
    515     public FieldNode addField(String name, int modifiers, ClassNode type, Expression initialValue) {
    516         FieldNode node = new FieldNode(name, modifiers, type, redirect(), initialValue);
    517         addField(node);
    518         return node;
    519     }
    520
    521     public void addInterface(ClassNode type) {
    522         // lets check if it already implements an interface
    523         boolean skip = false;
    524         ClassNode[] interfaces = redirect().interfaces;
    525         for (int i = 0; i < interfaces.length; i++) {
    526             if (type.equals(interfaces[i])) {
    527                 skip = true;
    528             }
    529         }
    530         if (!skip) {
    531             ClassNode[] newInterfaces = new ClassNode[interfaces.length + 1];
    532             System.arraycopy(interfaces, 0, newInterfaces, 0, interfaces.length);
    533             newInterfaces[interfaces.length] = type;
    534             redirect().interfaces = newInterfaces;
    535         }
    536     }
    537
    538     public boolean equals(Object o) {
    539         if (redirect!=null) return redirect().equals(o);
    540         ClassNode cn = (ClassNode) o;
    541         return (cn.getName().equals(getName()));
    542     }
    543
    544     public void addMixin(MixinNode mixin) {
    545         // lets check if it already uses a mixin
    546         MixinNode[] mixins = redirect().mixins;
    547         boolean skip = false;
    548         for (int i = 0; i < mixins.length; i++) {
    549             if (mixin.equals(mixins[i])) {
    550                 skip = true;
    551             }
    552         }
    553         if (!skip) {
    554             MixinNode[] newMixins = new MixinNode[mixins.length + 1];
    555             System.arraycopy(mixins, 0, newMixins, 0, mixins.length);
    556             newMixins[mixins.length] = mixin;
    557             redirect().mixins = newMixins;
    558         }
    559     }
    560
    561     public FieldNode getField(String name) {
    562         return (FieldNode) redirect().fieldIndex.get(name);
    563     }
    564
    565     /***
    566      * @return the field node on the outer class or null if this is not an
    567      *         inner class
    568      */
    569     public FieldNode getOuterField(String name) {
    570         return null;
    571     }
    572
    573     /***
    574      * Helper method to avoid casting to inner class
    575      */
    576     public ClassNode getOuterClass() {
    577         return null;
    578     }
    579
    580     public void addObjectInitializerStatements(Statement statements) {
    581         objectInitializers.add(statements);
    582     }
    583
    584     public List getObjectInitializerStatements() {
    585         return objectInitializers;
    586     }
    587
    588     public void addStaticInitializerStatements(List staticStatements, boolean fieldInit) {
    589         MethodNode method = null;
    590         List declaredMethods = getDeclaredMethods("<clinit>");
    591         if (declaredMethods.isEmpty()) {
    592             method =
    593                     addMethod("<clinit>", ACC_PUBLIC | ACC_STATIC, ClassHelper.VOID_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, new BlockStatement());
    594             method.setSynthetic(true);
    595         }
    596         else {
    597             method = (MethodNode) declaredMethods.get(0);
    598         }
    599         BlockStatement block = null;
    600         Statement statement = method.getCode();
    601         if (statement == null) {
    602             block = new BlockStatement();
    603         }
    604         else if (statement instanceof BlockStatement) {
    605             block = (BlockStatement) statement;
    606         }
    607         else {
    608             block = new BlockStatement();
    609             block.addStatement(statement);
    610         }
    611
    612         // while anything inside a static initializer block is appended
    613         // we don't want to append in the case we have a initialization
    614         // expression of a static field. In that case we want to add
    615         // before the other statements
    616         if (!fieldInit) {
    617             block.addStatements(staticStatements);
    618         } else {
    619             List blockStatements = block.getStatements();
    620             staticStatements.addAll(blockStatements);
    621             blockStatements.clear();
    622             blockStatements.addAll(staticStatements);
    623         }
    624     }
    625
    626     /***
    627      * @return a list of methods which match the given name
    628      */
    629     public List getDeclaredMethods(String name) {
    630         List answer = new ArrayList();
    631         for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
    632             MethodNode method = (MethodNode) iter.next();
    633             if (name.equals(method.getName())) {
    634                 answer.add(method);
    635             }
    636         }
    637         return answer;
    638     }
    639
    640     /***
    641      * @return a list of methods which match the given name
    642      */
    643     public List getMethods(String name) {
    644         List answer = new ArrayList();
    645         ClassNode node = this;
    646         do {
    647             for (Iterator iter = node.getMethods().iterator(); iter.hasNext();) {
    648                 MethodNode method = (MethodNode) iter.next();
    649                 if (name.equals(method.getName())) {
    650                     answer.add(method);
    651                 }
    652             }
    653             node = node.getSuperClass();
    654         }
    655         while (node != null);
    656         return answer;
    657     }
    658
    659     /***
    660      * @return the method matching the given name and parameters or null
    661      */
    662     public MethodNode getDeclaredMethod(String name, Parameter[] parameters) {
    663         for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
    664             MethodNode method = (MethodNode) iter.next();
    665             if (name.equals(method.getName()) && parametersEqual(method.getParameters(), parameters)) {
    666                 return method;
    667             }
    668         }
    669         return null;
    670     }
    671
    672     /***
    673      * @return true if this node is derived from the given class node
    674      */
    675     public boolean isDerivedFrom(ClassNode type) {
    676         ClassNode node = this;
    677         while (node != null) {
    678             if (type.equals(node)) {
    679                 return true;
    680             }
    681             node = node.getSuperClass();
    682         }
    683         return false;
    684     }
    685
    686     /***
    687      * @return true if this class is derived from a groovy object
    688      *         i.e. it implements GroovyObject
    689      */
    690     public boolean isDerivedFromGroovyObject() {
    691         return implementsInterface(GroovyObject.class.getName());
    692     }
    693
    694     /***
    695      * @param name the fully qualified name of the interface
    696      * @return true if this class or any base class implements the given interface
    697      */
    698     public boolean implementsInterface(String name) {
    699         ClassNode node = redirect();
    700         do {
    701             if (node.declaresInterface(name)) {
    702                 return true;
    703             }
    704             node = node.getSuperClass();
    705         }
    706         while (node != null);
    707         return false;
    708     }
    709
    710     /***
    711      * @param name the fully qualified name of the interface
    712      * @return true if this class declares that it implements the given interface
    713      */
    714     public boolean declaresInterface(String name) {
    715         ClassNode[] interfaces = redirect().getInterfaces();
    716         int size = interfaces.length;
    717         for (int i = 0; i < size; i++) {
    718             if (interfaces[i].getName().equals(name)) {
    719                 return true;
    720             }
    721         }
    722         return false;
    723     }
    724
    725     /***
    726      * @return the ClassNode of the super class of this type
    727      */
    728     public ClassNode getSuperClass() {
    729         if (!lazyInitDone && !isResolved()) {
    730             throw new GroovyBugError("Classnode#getSuperClass for "+getName()+" called before class resolving");
    731         }
    732         return redirect().getUnresolvedSuperClass();
    733     }
    734
    735     public ClassNode getUnresolvedSuperClass() {
    736         if (!lazyInitDone) {
    737             lazyClassInit();
    738         }
    739         return redirect().superClass;
    740     }
    741
    742     /***
    743      * Factory method to create a new MethodNode via reflection
    744      */
    745     protected MethodNode createMethodNode(Method method) {
    746         Parameter[] parameters = createParameters(method.getParameterTypes());
    747         return new MethodNode(method.getName(), method.getModifiers(), ClassHelper.make(method.getReturnType()), parameters, ClassHelper.make(method.getExceptionTypes()), EmptyStatement.INSTANCE);
    748     }
    749
    750     /***
    751      * @param types
    752      */
    753     protected Parameter[] createParameters(Class[] types) {
    754         Parameter[] parameters = Parameter.EMPTY_ARRAY;
    755         int size = types.length;
    756         if (size > 0) {
    757             parameters = new Parameter[size];
    758             for (int i = 0; i < size; i++) {
    759                 parameters[i] = createParameter(types[i], i);
    760             }
    761         }
    762         return parameters;
    763     }
    764
    765     protected Parameter createParameter(Class parameterType, int idx) {
    766         return new Parameter(ClassHelper.make(parameterType), "param" + idx);
    767     }
    768
    769     public CompileUnit getCompileUnit() {
    770         if (redirect!=null) return redirect().getCompileUnit();
    771         if (compileUnit == null && module != null) {
    772             compileUnit = module.getUnit();
    773         }
    774         return compileUnit;
    775     }
    776
    777     protected void setCompileUnit(CompileUnit cu) {
    778         if (redirect!=null) redirect().setCompileUnit(cu);
    779         if (compileUnit!= null) compileUnit = cu;
    780     }
    781
    782     /***
    783      * @return true if the two arrays are of the same size and have the same contents
    784      */
    785     protected boolean parametersEqual(Parameter[] a, Parameter[] b) {
    786         if (a.length == b.length) {
    787             boolean answer = true;
    788             for (int i = 0; i < a.length; i++) {
    789                 if (!a[i].getType().equals(b[i].getType())) {
    790                     answer = false;
    791                     break;
    792                 }
    793             }
    794             return answer;
    795         }
    796         return false;
    797     }
    798
    799     /***
    800      * @return the package name of this class
    801      */
    802     public String getPackageName() {
    803         int idx = getName().lastIndexOf('.');
    804         if (idx > 0) {
    805             return getName().substring(0, idx);
    806         }
    807         return null;
    808     }
    809
    810     public String getNameWithoutPackage() {
    811         int idx = getName().lastIndexOf('.');
    812         if (idx > 0) {
    813             return getName().substring(idx + 1);
    814         }
    815         return getName();
    816     }
    817
    818     public void visitContents(GroovyClassVisitor visitor) {
    819
    820         // now lets visit the contents of the class
    821         for (Iterator iter = getProperties().iterator(); iter.hasNext();) {
    822             PropertyNode pn = (PropertyNode) iter.next();
    823             visitor.visitProperty(pn);
    824         }
    825
    826         for (Iterator iter = getFields().iterator(); iter.hasNext();) {
    827             FieldNode fn = (FieldNode) iter.next();
    828             visitor.visitField(fn);
    829         }
    830
    831         for (Iterator iter = getDeclaredConstructors().iterator(); iter.hasNext();) {
    832             ConstructorNode cn = (ConstructorNode) iter.next();
    833             visitor.visitConstructor(cn);
    834         }
    835
    836         for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
    837             MethodNode mn = (MethodNode) iter.next();
    838             visitor.visitMethod(mn);
    839         }
    840     }
    841
    842     public MethodNode getGetterMethod(String getterName) {
    843         for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
    844             MethodNode method = (MethodNode) iter.next();
    845             if (getterName.equals(method.getName())
    846                     && ClassHelper.VOID_TYPE!=method.getReturnType()
    847                     && method.getParameters().length == 0) {
    848                 return method;
    849             }
    850         }
    851         return null;
    852     }
    853
    854     public MethodNode getSetterMethod(String getterName) {
    855         for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
    856             MethodNode method = (MethodNode) iter.next();
    857             if (getterName.equals(method.getName())
    858                     && ClassHelper.VOID_TYPE==method.getReturnType()
    859                     && method.getParameters().length == 1) {
    860                 return method;
    861             }
    862         }
    863         return null;
    864     }
    865
    866     /***
    867      * Is this class delcared in a static method (such as a closure / inner class declared in a static method)
    868      */
    869     public boolean isStaticClass() {
    870         return redirect().staticClass;
    871     }
    872
    873     public void setStaticClass(boolean staticClass) {
    874         redirect().staticClass = staticClass;
    875     }
    876
    877     /***
    878      * @return Returns true if this inner class or closure was declared inside a script body
    879      */
    880     public boolean isScriptBody() {
    881         return redirect().scriptBody;
    882     }
    883
    884     public void setScriptBody(boolean scriptBody) {
    885         redirect().scriptBody = scriptBody;
    886     }
    887
    888     public boolean isScript() {
    889         return redirect().script || isDerivedFrom(ClassHelper.SCRIPT_TYPE);
    890     }
    891
    892     public void setScript(boolean script) {
    893         redirect().script = script;
    894     }
    895
    896     public String toString() {
    897         return super.toString() + "[name: " + getName() + "]";
    898     }
    899
    900     /***
    901      * Returns true if the given method has a possibly matching method with the given name and arguments
    902      */
    903     public boolean hasPossibleMethod(String name, Expression arguments) {
    904         int count = 0;
    905
    906         if (arguments instanceof TupleExpression) {
    907             TupleExpression tuple = (TupleExpression) arguments;
    908             // TODO this won't strictly be true when using list expension in argument calls
    909             count = tuple.getExpressions().size();
    910         }
    911         ClassNode node = this;
    912         do {
    913             for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
    914                 MethodNode method = (MethodNode) iter.next();
    915                 if (name.equals(method.getName()) && method.getParameters().length == count) {
    916                     return true;
    917                 }
    918             }
    919             node = node.getSuperClass();
    920         }
    921         while (node != null);
    922         return false;
    923     }
    924
    925     public boolean isInterface(){
    926         return (getModifiers() & Opcodes.ACC_INTERFACE) > 0;
    927     }
    928
    929     public boolean isResolved(){
    930         return redirect().clazz!=null || (componentType != null && componentType.isResolved());
    931     }
    932
    933     public boolean isArray(){
    934         return componentType!=null;
    935     }
    936
    937     public ClassNode getComponentType() {
    938         return componentType;
    939     }
    940
    941     public Class getTypeClass(){
    942         Class c = redirect().clazz;
    943         if (c!=null) return c;
    944         ClassNode component = redirect().componentType;
    945         if (component!=null && component.isResolved()){
    946             ClassNode cn = component.makeArray();
    947             setRedirect(cn);
    948             return redirect().clazz;
    949         }
    950         throw new GroovyBugError("ClassNode#getTypeClass for "+getName()+" is called before the type class is set ");
    951     }
    952
    953     public boolean hasPackageName(){
    954         return redirect().name.indexOf('.')>0;
    955     }
    956 }