View Javadoc

1   /*
2    * $Id: ClassNode.java,v 1.42 2004/07/31 20:32:56 glaforge 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.42 $
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 			// try to resolve Class names
633             answer = tryResolveClassAndInnerClass(type);
634 
635             // try to resolve a public static inner class' name
636             String replacedPointType = type;
637 			while (answer == null && replacedPointType.indexOf('.') > -1) {
638                 int lastPoint = replacedPointType.lastIndexOf('.');
639                 replacedPointType = new StringBuffer()
640                                     .append(replacedPointType.substring(0, lastPoint)).append("$")
641                                     .append(replacedPointType.substring(lastPoint + 1)).toString();
642                 answer = tryResolveClassAndInnerClass(replacedPointType);
643 			}
644         }
645         return answer;
646     }
647 
648 	private String tryResolveClassAndInnerClass(String type) {
649 		String answer = tryResolveClassFromCompileUnit(type);
650 		if (answer == null)
651 		{
652 			// lets try class in same package
653 			String packageName = getPackageName();
654 			if (packageName != null && packageName.length() > 0)
655 			{
656 				answer = tryResolveClassFromCompileUnit(packageName + "." + type);
657 			}
658 		}
659 		if (answer == null)
660 		{
661 			// lets try use the packages imported in the module
662 			if (module != null)
663 			{
664 				//System.out.println("Looking up inside the imported packages: " + module.getImportPackages());
665 
666 				for (Iterator iter = module.getImportPackages().iterator(); iter.hasNext();)
667 				{
668 					String packageName = (String) iter.next();
669 					answer = tryResolveClassFromCompileUnit(packageName + type);
670 					if (answer != null)
671 					{
672 						return answer;
673 					}
674 				}
675 			}
676 		}
677 		if (answer == null)
678 		{
679 			for (int i = 0, size = defaultImports.length; i < size; i++)
680 			{
681 				String packagePrefix = defaultImports[i];
682 				answer = tryResolveClassFromCompileUnit(packagePrefix + "." + type);
683 				if (answer != null)
684 				{
685 					return answer;
686 				}
687 			}
688 		}
689 		return answer;
690 	}
691 
692     /***
693      * @param type
694      * @return
695      */
696     protected String tryResolveClassFromCompileUnit(String type) {
697         CompileUnit theCompileUnit = getCompileUnit();
698         if (theCompileUnit != null) {
699             if (theCompileUnit.getClass(type) != null) {
700                 return type;
701             }
702 
703             try {
704                 theCompileUnit.loadClass(type);
705                 return type;
706             }
707             catch (AccessControlException ace) {
708             	//Percolate this for better diagnostic info
709             	throw ace;
710             }
711             catch (Throwable e) {
712                 // fall through
713             }
714         }
715         return null;
716     }
717 
718     public CompileUnit getCompileUnit() {
719         if (compileUnit == null && module != null) {
720             compileUnit = module.getUnit();
721         }
722         return compileUnit;
723     }
724 
725     /***
726      * @return true if the two arrays are of the same size and have the same contents
727      */
728     protected boolean parametersEqual(Parameter[] a, Parameter[] b) {
729         if (a.length == b.length) {
730             boolean answer = true;
731             for (int i = 0; i < a.length; i++) {
732                 if (!a[i].getType().equals(b[i].getType())) {
733                     answer = false;
734                     break;
735                 }
736             }
737             return answer;
738         }
739         return false;
740     }
741 
742     /***
743      * @return the name of the class for the given identifier if it is a class
744      * otherwise return null
745      */
746     public String getClassNameForExpression(String identifier) {
747         // lets see if it really is a class name
748         String className = null;
749         if (module != null) {
750             className = module.getImport(identifier);
751             if (className == null) {
752                 if (module.getUnit().getClass(identifier) != null) {
753                     className = identifier;
754                 }
755                 else {
756                     // lets prepend the package name to see if its in our
757                     // package
758                     String packageName = getPackageName();
759                     if (packageName != null) {
760                         String guessName = packageName + "." + identifier;
761                         if (module.getUnit().getClass(guessName) != null) {
762                             className = guessName;
763                         }
764                         else if (guessName.equals(name)) {
765                             className = name;
766                         }
767                     }
768                 }
769             }
770         }
771         else {
772             System.out.println("No module for class: " + getName());
773         }
774         return className;
775     }
776 
777     /***
778      * @return the package name of this class
779      */
780     public String getPackageName() {
781         int idx = name.lastIndexOf('.');
782         if (idx > 0) {
783             return name.substring(0, idx);
784         }
785         return null;
786     }
787 
788     public String getNameWithoutPackage() {
789         int idx = name.lastIndexOf('.');
790         if (idx > 0) {
791             return name.substring(idx + 1);
792         }
793         return name;
794     }
795 
796     public void visitContents(GroovyClassVisitor visitor) {
797         // now lets visit the contents of the class
798         for (Iterator iter = getProperties().iterator(); iter.hasNext();) {
799             visitor.visitProperty((PropertyNode) iter.next());
800         }
801 
802         for (Iterator iter = getFields().iterator(); iter.hasNext();) {
803             visitor.visitField((FieldNode) iter.next());
804         }
805 
806         for (Iterator iter = getDeclaredConstructors().iterator(); iter.hasNext();) {
807             visitor.visitConstructor((ConstructorNode) iter.next());
808         }
809 
810         for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
811             visitor.visitMethod((MethodNode) iter.next());
812         }
813     }
814 
815     public MethodNode getGetterMethod(String getterName) {
816         for (Iterator iter = methods.iterator(); iter.hasNext();) {
817             MethodNode method = (MethodNode) iter.next();
818             if (getterName.equals(method.getName())
819                 && !"void".equals(method.getReturnType())
820                 && method.getParameters().length == 0) {
821                 return method;
822             }
823         }
824         return null;
825     }
826 
827     public MethodNode getSetterMethod(String getterName) {
828         for (Iterator iter = methods.iterator(); iter.hasNext();) {
829             MethodNode method = (MethodNode) iter.next();
830             if (getterName.equals(method.getName())
831                 && "void".equals(method.getReturnType())
832                 && method.getParameters().length == 1) {
833                 return method;
834             }
835         }
836         return null;
837     }
838 
839     /***
840      * Is this class delcared in a static method (such as a closure / inner class declared in a static method)
841      * @return
842      */
843     public boolean isStaticClass() {
844         return staticClass;
845     }
846 
847     public void setStaticClass(boolean staticClass) {
848         this.staticClass = staticClass;
849     }
850 
851     /***
852      * @return Returns true if this inner class or closure was declared inside a script body
853      */
854     public boolean isScriptBody() {
855         return scriptBody;
856     }
857 
858     public void setScriptBody(boolean scriptBody) {
859         this.scriptBody = scriptBody;
860     }
861 
862     public boolean isScript() {
863         return script | isDerivedFrom(Script.class.getName());
864     }
865 
866     public void setScript(boolean script) {
867         this.script = script;
868     }
869 
870     public String toString() {
871         return super.toString() + "[name: " + name + "]";
872     }
873 
874    }