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 }