View Javadoc

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