View Javadoc

1   /*
2    * $Id: ClassNode.java,v 1.73 2006/06/25 19:58:43 blackdrag Exp $
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: 1.73 $
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.getConstructors();
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             if (result.containsKey(sig)) {
393                 MethodNode inheritedMethod = (MethodNode) result.get(sig);
394                 if (inheritedMethod.isAbstract()) {
395                     result.put(sig, method);
396                 }
397             }
398             else {
399                 result.put(sig, method);
400             }
401         }
402         return result;
403     }
404 
405     public String getName() {
406         return redirect().name;
407     }
408     
409     public String setName(String name) {
410         return redirect().name=name;
411     }
412 
413     public int getModifiers() {
414         return redirect().modifiers;
415     }
416 
417     public List getProperties() {
418         return redirect().properties;
419     }
420 
421     public List getDeclaredConstructors() {
422         if (!lazyInitDone) {
423             lazyClassInit();
424         }
425         return redirect().constructors;
426     }
427 
428     public ModuleNode getModule() {
429         return redirect().module;
430     }
431 
432     public void setModule(ModuleNode module) {
433         redirect().module = module;
434         if (module != null) {
435             redirect().compileUnit = module.getUnit();
436         }
437     }
438 
439     public void addField(FieldNode node) {
440         node.setDeclaringClass(redirect());
441         node.setOwner(redirect());
442         redirect().fields.add(node);
443         redirect().fieldIndex.put(node.getName(), node);
444     }
445 
446     public void addProperty(PropertyNode node) {
447         node.setDeclaringClass(redirect());
448         FieldNode field = node.getField();
449         addField(field);
450 
451         redirect().properties.add(node);
452     }
453 
454     public PropertyNode addProperty(String name,
455                                     int modifiers,
456                                     ClassNode type,
457                                     Expression initialValueExpression,
458                                     Statement getterBlock,
459                                     Statement setterBlock) {
460     	for (Iterator iter = getProperties().iterator(); iter.hasNext();) {
461 			PropertyNode pn = (PropertyNode) iter.next();
462 			if (pn.getName().equals(name)) return pn;
463 		}
464         PropertyNode node =
465                 new PropertyNode(name, modifiers, type, redirect(), initialValueExpression, getterBlock, setterBlock);
466         addProperty(node);
467         return node;
468     }
469 
470     public void addConstructor(ConstructorNode node) {
471         node.setDeclaringClass(this);
472         redirect().constructors.add(node);
473     }
474 
475     public ConstructorNode addConstructor(int modifiers, Parameter[] parameters, ClassNode[] exceptions, Statement code) {
476         ConstructorNode node = new ConstructorNode(modifiers, parameters, exceptions, code);
477         addConstructor(node);
478         return node;
479     }
480 
481     public void addMethod(MethodNode node) {
482         node.setDeclaringClass(this);
483         redirect().methods.add(node);
484     }
485 
486     /***
487      * IF a method with the given name and parameters is already defined then it is returned
488      * otherwise the given method is added to this node. This method is useful for
489      * default method adding like getProperty() or invokeMethod() where there may already
490      * be a method defined in a class and  so the default implementations should not be added
491      * if already present.
492      */
493     public MethodNode addMethod(String name,
494                                 int modifiers,
495                                 ClassNode returnType,
496                                 Parameter[] parameters,
497                                 ClassNode[] exceptions,
498                                 Statement code) {
499         MethodNode other = getDeclaredMethod(name, parameters);
500         // lets not add duplicate methods
501         if (other != null) {
502             return other;
503         }
504         MethodNode node = new MethodNode(name, modifiers, returnType, parameters, exceptions, code);
505         addMethod(node);
506         return node;
507     }
508 
509     /***
510      * Adds a synthetic method as part of the compilation process
511      */
512     public MethodNode addSyntheticMethod(String name,
513                                          int modifiers,
514                                          ClassNode returnType,
515                                          Parameter[] parameters,
516                                          ClassNode[] exceptions,
517                                          Statement code) {
518         MethodNode answer = addMethod(name, modifiers, returnType, parameters, exceptions, code);
519         answer.setSynthetic(true);
520         return answer;
521     }
522 
523     public FieldNode addField(String name, int modifiers, ClassNode type, Expression initialValue) {
524         FieldNode node = new FieldNode(name, modifiers, type, redirect(), initialValue);
525         addField(node);
526         return node;
527     }
528 
529     public void addInterface(ClassNode type) {
530         // lets check if it already implements an interface
531         boolean skip = false;
532         ClassNode[] interfaces = redirect().interfaces;
533         for (int i = 0; i < interfaces.length; i++) {
534             if (type.equals(interfaces[i])) {
535                 skip = true;
536             }
537         }
538         if (!skip) {
539             ClassNode[] newInterfaces = new ClassNode[interfaces.length + 1];
540             System.arraycopy(interfaces, 0, newInterfaces, 0, interfaces.length);
541             newInterfaces[interfaces.length] = type;
542             redirect().interfaces = newInterfaces;
543         }
544     }
545     
546     public boolean equals(Object o) {
547         if (redirect!=null) return redirect().equals(o);
548         ClassNode cn = (ClassNode) o;        
549         return (cn.getName().equals(getName()));
550     }
551 
552     public void addMixin(MixinNode mixin) {
553         // lets check if it already uses a mixin
554         MixinNode[] mixins = redirect().mixins;
555         boolean skip = false;
556         for (int i = 0; i < mixins.length; i++) {
557             if (mixin.equals(mixins[i])) {
558                 skip = true;
559             }
560         }
561         if (!skip) {
562             MixinNode[] newMixins = new MixinNode[mixins.length + 1];
563             System.arraycopy(mixins, 0, newMixins, 0, mixins.length);
564             newMixins[mixins.length] = mixin;
565             redirect().mixins = newMixins;
566         }
567     }
568 
569     public FieldNode getField(String name) {
570         return (FieldNode) redirect().fieldIndex.get(name);
571     }
572 
573     /***
574      * @return the field node on the outer class or null if this is not an
575      *         inner class
576      */
577     public FieldNode getOuterField(String name) {
578         return null;
579     }
580 
581     /***
582      * Helper method to avoid casting to inner class
583      *
584      * @return
585      */
586     public ClassNode getOuterClass() {
587         return null;
588     }
589     
590     public void addObjectInitializerStatements(Statement statements) {
591         objectInitializers.add(statements);
592     }
593     
594     public List getObjectInitializerStatements() {
595         return objectInitializers;
596     }
597 
598     public void addStaticInitializerStatements(List staticStatements, boolean fieldInit) {
599         MethodNode method = null;
600         List declaredMethods = getDeclaredMethods("<clinit>");
601         if (declaredMethods.isEmpty()) {
602             method =
603                     addMethod("<clinit>", ACC_PUBLIC | ACC_STATIC, ClassHelper.VOID_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, new BlockStatement());
604             method.setSynthetic(true);
605         }
606         else {
607             method = (MethodNode) declaredMethods.get(0);
608         }
609         BlockStatement block = null;
610         Statement statement = method.getCode();
611         if (statement == null) {
612             block = new BlockStatement();
613         }
614         else if (statement instanceof BlockStatement) {
615             block = (BlockStatement) statement;
616         }
617         else {
618             block = new BlockStatement();
619             block.addStatement(statement);
620         }
621         
622         // while anything inside a static initializer block is appended 
623         // we don't want to append in the case we have a initialization
624         // expression of a static field. In that case we want to add
625         // before the other statements
626         if (!fieldInit) {
627             block.addStatements(staticStatements);
628         } else {
629             List blockStatements = block.getStatements();
630             staticStatements.addAll(blockStatements);
631             blockStatements.clear();
632             blockStatements.addAll(staticStatements);
633         }
634     }
635 
636     /***
637      * @return a list of methods which match the given name
638      */
639     public List getDeclaredMethods(String name) {
640         List answer = new ArrayList();
641         for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
642             MethodNode method = (MethodNode) iter.next();
643             if (name.equals(method.getName())) {
644                 answer.add(method);
645             }
646         }
647         return answer;
648     }
649 
650     /***
651      * @return a list of methods which match the given name
652      */
653     public List getMethods(String name) {
654         List answer = new ArrayList();
655         ClassNode node = this;
656         do {
657             for (Iterator iter = node.getMethods().iterator(); iter.hasNext();) {
658                 MethodNode method = (MethodNode) iter.next();
659                 if (name.equals(method.getName())) {
660                     answer.add(method);
661                 }
662             }
663             node = node.getSuperClass();
664         }
665         while (node != null);
666         return answer;
667     }
668 
669     /***
670      * @return the method matching the given name and parameters or null
671      */
672     public MethodNode getDeclaredMethod(String name, Parameter[] parameters) {
673         for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
674             MethodNode method = (MethodNode) iter.next();
675             if (name.equals(method.getName()) && parametersEqual(method.getParameters(), parameters)) {
676                 return method;
677             }
678         }
679         return null;
680     }
681 
682     /***
683      * @return true if this node is derived from the given class node
684      */
685     public boolean isDerivedFrom(ClassNode type) {
686         ClassNode node = getSuperClass();
687         while (node != null) {
688             if (type.equals(node)) {
689                 return true;
690             }
691             node = node.getSuperClass();
692         }
693         return false;
694     }
695 
696     /***
697      * @return true if this class is derived from a groovy object
698      *         i.e. it implements GroovyObject
699      */
700     public boolean isDerivedFromGroovyObject() {
701         return implementsInteface(GroovyObject.class.getName());
702     }
703 
704     /***
705      * @param name the fully qualified name of the interface
706      * @return true if this class or any base class implements the given interface
707      */
708     public boolean implementsInteface(String name) {
709         ClassNode node = redirect();
710         do {
711             if (node.declaresInterface(name)) {
712                 return true;
713             }
714             node = node.getSuperClass();
715         }
716         while (node != null);
717         return false;
718     }
719 
720     /***
721      * @param name the fully qualified name of the interface
722      * @return true if this class declares that it implements the given interface
723      */
724     public boolean declaresInterface(String name) {
725         ClassNode[] interfaces = redirect().getInterfaces();
726         int size = interfaces.length;
727         for (int i = 0; i < size; i++) {
728             if (interfaces[i].getName().equals(name)) {
729                 return true;
730             }
731         }
732         return false;
733     }
734 
735     /***
736      * @return the ClassNode of the super class of this type
737      */
738     public ClassNode getSuperClass() {
739         if (!lazyInitDone && !isResolved()) {
740             throw new GroovyBugError("Classnode#getSuperClass for "+getName()+" called before class resolving");
741         }
742         return getUnresolvedSuperClass();
743     }
744     
745     public ClassNode getUnresolvedSuperClass() {
746         if (!lazyInitDone) {
747             lazyClassInit();
748         }
749         return redirect().superClass;
750     }
751 
752     /***
753      * Factory method to create a new MethodNode via reflection
754      */
755     protected MethodNode createMethodNode(Method method) {
756         Parameter[] parameters = createParameters(method.getParameterTypes());
757         return new MethodNode(method.getName(), method.getModifiers(), ClassHelper.make(method.getReturnType()), parameters, ClassHelper.make(method.getExceptionTypes()), EmptyStatement.INSTANCE);
758     }
759 
760     /***
761      * @param types
762      * @return
763      */
764     protected Parameter[] createParameters(Class[] types) {
765         Parameter[] parameters = Parameter.EMPTY_ARRAY;
766         int size = types.length;
767         if (size > 0) {
768             parameters = new Parameter[size];
769             for (int i = 0; i < size; i++) {
770                 parameters[i] = createParameter(types[i], i);
771             }
772         }
773         return parameters;
774     }
775 
776     protected Parameter createParameter(Class parameterType, int idx) {
777         return new Parameter(ClassHelper.make(parameterType), "param" + idx);
778     }
779 
780     public CompileUnit getCompileUnit() {
781         if (redirect!=null) return redirect().getCompileUnit();
782         if (compileUnit == null && module != null) {
783             compileUnit = module.getUnit();
784         }
785         return compileUnit;
786     }
787     
788     protected void setCompileUnit(CompileUnit cu) {
789         if (redirect!=null) redirect().setCompileUnit(cu);
790         if (compileUnit!= null) compileUnit = cu;
791     }
792 
793     /***
794      * @return true if the two arrays are of the same size and have the same contents
795      */
796     protected boolean parametersEqual(Parameter[] a, Parameter[] b) {
797         if (a.length == b.length) {
798             boolean answer = true;
799             for (int i = 0; i < a.length; i++) {
800                 if (!a[i].getType().equals(b[i].getType())) {
801                     answer = false;
802                     break;
803                 }
804             }
805             return answer;
806         }
807         return false;
808     }
809 
810     /***
811      * @return the package name of this class
812      */
813     public String getPackageName() {
814         int idx = getName().lastIndexOf('.');
815         if (idx > 0) {
816             return getName().substring(0, idx);
817         }
818         return null;
819     }
820 
821     public String getNameWithoutPackage() {
822         int idx = getName().lastIndexOf('.');
823         if (idx > 0) {
824             return getName().substring(idx + 1);
825         }
826         return getName();
827     }
828 
829     public void visitContents(GroovyClassVisitor visitor) {
830         
831         // now lets visit the contents of the class
832         for (Iterator iter = getProperties().iterator(); iter.hasNext();) {
833             PropertyNode pn = (PropertyNode) iter.next();
834             visitor.visitProperty(pn);
835         }
836 
837         for (Iterator iter = getFields().iterator(); iter.hasNext();) {
838             FieldNode fn = (FieldNode) iter.next();
839             visitor.visitField(fn);
840         }
841 
842         for (Iterator iter = getDeclaredConstructors().iterator(); iter.hasNext();) {
843             ConstructorNode cn = (ConstructorNode) iter.next();
844             visitor.visitConstructor(cn);
845         }
846 
847         for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
848             MethodNode mn = (MethodNode) iter.next();
849             visitor.visitMethod(mn);
850         }
851     }
852 
853     public MethodNode getGetterMethod(String getterName) {
854         for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
855             MethodNode method = (MethodNode) iter.next();
856             if (getterName.equals(method.getName())
857                     && ClassHelper.VOID_TYPE!=method.getReturnType()
858                     && method.getParameters().length == 0) {
859                 return method;
860             }
861         }
862         return null;
863     }
864 
865     public MethodNode getSetterMethod(String getterName) {
866         for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
867             MethodNode method = (MethodNode) iter.next();
868             if (getterName.equals(method.getName())
869                     && ClassHelper.VOID_TYPE==method.getReturnType()
870                     && method.getParameters().length == 1) {
871                 return method;
872             }
873         }
874         return null;
875     }
876 
877     /***
878      * Is this class delcared in a static method (such as a closure / inner class declared in a static method)
879      *
880      * @return
881      */
882     public boolean isStaticClass() {
883         return redirect().staticClass;
884     }
885 
886     public void setStaticClass(boolean staticClass) {
887         redirect().staticClass = staticClass;
888     }
889 
890     /***
891      * @return Returns true if this inner class or closure was declared inside a script body
892      */
893     public boolean isScriptBody() {
894         return redirect().scriptBody;
895     }
896 
897     public void setScriptBody(boolean scriptBody) {
898         redirect().scriptBody = scriptBody;
899     }
900 
901     public boolean isScript() {
902         return redirect().script || isDerivedFrom(ClassHelper.SCRIPT_TYPE);
903     }
904 
905     public void setScript(boolean script) {
906         redirect().script = script;
907     }
908 
909     public String toString() {
910         return super.toString() + "[name: " + getName() + "]";
911     }
912 
913     /***
914      * Returns true if the given method has a possibly matching method with the given name and arguments
915      */
916     public boolean hasPossibleMethod(String name, Expression arguments) {
917         int count = 0;
918 
919         if (arguments instanceof TupleExpression) {
920             TupleExpression tuple = (TupleExpression) arguments;
921             // TODO this won't strictly be true when using list expension in argument calls
922             count = tuple.getExpressions().size();
923         }
924         ClassNode node = this;
925         do {
926             for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
927                 MethodNode method = (MethodNode) iter.next();
928                 if (name.equals(method.getName()) && method.getParameters().length == count) {
929                     return true;
930                 }
931             }
932             node = node.getSuperClass();
933         }
934         while (node != null);
935         return false;
936     }
937     
938     public boolean isInterface(){
939         return (getModifiers() & Opcodes.ACC_INTERFACE) > 0; 
940     }
941     
942     public boolean isResolved(){
943         return redirect().clazz!=null || (componentType != null && componentType.isResolved());
944     }
945     
946     public boolean isArray(){
947         return componentType!=null;
948     }
949     
950     public ClassNode getComponentType() {
951         return componentType;
952     }
953     
954     public Class getTypeClass(){
955         Class c = redirect().clazz;
956         if (c!=null) return c;
957         ClassNode component = redirect().componentType;
958         if (component!=null && component.isResolved()){
959             ClassNode cn = component.makeArray();
960             setRedirect(cn);
961             return redirect().clazz;
962         }
963         throw new GroovyBugError("ClassNode#getTypeClass for "+getName()+" is called before the type class is set ");
964     }
965     
966     public boolean hasPackageName(){
967         return redirect().name.indexOf('.')>0;
968     }
969 }