001    /*
002     * $Id: ClassNode.java 4216 2006-11-13 16:04:23Z blackdrag $
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.HashSet;
054    import java.util.Iterator;
055    import java.util.List;
056    import java.util.Map;
057    
058    /**
059     * Represents a class in the AST.<br/>
060     * A ClassNode should be created using the methods in ClassHelper. 
061     * This ClassNode may be used to represent a class declaration or
062     * any other type. This class uses a proxy meschanism allowing to
063     * create a class for a plain name at ast creation time. In another 
064     * phase of the compiler the real ClassNode for the plain name may be
065     * found. To avoid the need of exchanging this ClassNode with an 
066     * instance of the correct ClassNode the correct ClassNode is set as 
067     * redirect. All method calls are then redirected to that ClassNode.
068     * <br>
069     * Note: the proxy mechanism is only allowed for classes being marked
070     * as primary ClassNode which means they represent no actual class. 
071     * The redirect itself can be any type of ClassNode
072     *
073     * @see org.codehaus.groovy.ast.ClassHelper
074     * 
075     * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
076     * @author Jochen Theodorou
077     * @version $Revision: 4216 $
078     */
079    public class ClassNode extends AnnotatedNode implements Opcodes {
080    
081            public static ClassNode[] EMPTY_ARRAY = new ClassNode[0];
082        
083        public static ClassNode THIS = new ClassNode(Object.class);
084        public static ClassNode SUPER = new ClassNode(Object.class);
085        
086        private String name;
087        private int modifiers;
088        private ClassNode[] interfaces;
089        private MixinNode[] mixins;
090        private List constructors = new ArrayList();
091        private List  objectInitializers = new ArrayList();
092        private List methods = new ArrayList();
093        private List fields = new ArrayList();
094        private List properties = new ArrayList();
095        private Map fieldIndex = new HashMap();
096        private ModuleNode module;
097        private CompileUnit compileUnit;
098        private boolean staticClass = false;
099        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    }