View Javadoc

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