View Javadoc

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