View Javadoc

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