View Javadoc

    1   /*
    2    $Id: ModuleNode.java 3841 2006-06-15 20:42:01Z blackdrag $
    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
    8    that the following conditions are met:
    9
    10   1. Redistributions of source code must retain copyright
    11      statements and notices.  Redistributions must also contain a
    12      copy of this document.
    13
    14   2. Redistributions in binary form must reproduce the
    15      above copyright notice, this list of conditions and the
    16      following disclaimer in the documentation and/or other
    17      materials provided with the distribution.
    18
    19   3. The name "groovy" must not be used to endorse or promote
    20      products derived from this Software without prior written
    21      permission of The Codehaus.  For written permission,
    22      please contact info@codehaus.org.
    23
    24   4. Products derived from this Software may not be called "groovy"
    25      nor may "groovy" appear in their names without prior written
    26      permission of The Codehaus. "groovy" is a registered
    27      trademark of The Codehaus.
    28
    29   5. Due credit should be given to The Codehaus -
    30      http://groovy.codehaus.org/
    31
    32   THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
    33   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
    34   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    35   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
    36   THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
    37   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    38   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    39   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    40   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    41   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    42   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
    43   OF THE POSSIBILITY OF SUCH DAMAGE.
    44
    45   */
    46  package org.codehaus.groovy.ast;
    47
    48  import groovy.lang.Binding;
    49
    50  import java.io.File;
    51  import java.util.ArrayList;
    52  import java.util.HashMap;
    53  import java.util.Iterator;
    54  import java.util.LinkedList;
    55  import java.util.List;
    56  import java.util.Map;
    57
    58  import org.codehaus.groovy.ast.expr.ArgumentListExpression;
    59  import org.codehaus.groovy.ast.expr.ClassExpression;
    60  import org.codehaus.groovy.ast.expr.Expression;
    61  import org.codehaus.groovy.ast.expr.MethodCallExpression;
    62  import org.codehaus.groovy.ast.expr.VariableExpression;
    63  import org.codehaus.groovy.ast.stmt.BlockStatement;
    64  import org.codehaus.groovy.ast.stmt.ExpressionStatement;
    65  import org.codehaus.groovy.ast.stmt.Statement;
    66  import org.codehaus.groovy.control.SourceUnit;
    67  import org.codehaus.groovy.runtime.InvokerHelper;
    68  import org.objectweb.asm.Opcodes;
    69
    70  /***
    71   * Represents a module, which consists typically of a class declaration
    72   * but could include some imports, some statements and multiple classes
    73   * intermixed with statements like scripts in Python or Ruby
    74   *
    75   * @author Jochen Theodorou
    76   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
    77   * @version $Revision: 3841 $
    78   */
    79  public class ModuleNode extends ASTNode implements Opcodes {
    80
    81      private BlockStatement statementBlock = new BlockStatement();
    82      List classes = new LinkedList();
    83      private List methods = new ArrayList();
    84      private List imports = new ArrayList();
    85      private List importPackages = new ArrayList();
    86      private Map importIndex = new HashMap();
    87      private CompileUnit unit;
    88      privateong> String packageName;
    89      private String description;
    90      private boolean createClassForStatements = true;
    91      private transient SourceUnit context;
    92      private boolean importsResolved = false;
    93
    94
    95      public ModuleNode (SourceUnit context ) {
    96          this.context = context;
    97      }
    98
    99      public ModuleNode (CompileUnit unit) {
    100         this.unit = unit;
    101     }
    102
    103     public BlockStatement getStatementBlock() {
    104         return statementBlock;
    105     }
    106
    107     public List getMethods() {
    108         return methods;
    109     }
    110
    111     public List getClasses() {
    112         if (createClassForStatements && (!statementBlock.isEmpty() || !methods.isEmpty())) {
    113             ClassNode mainClass = createStatementsClass();
    114             createClassForStatements = false;
    115             classes.add(0, mainClass);
    116             mainClass.setModule(this);
    117             addToCompileUnit(mainClass);
    118         }
    119         return classes;
    120     }
    121
    122     public List getImports() {
    123         return imports;
    124     }
    125
    126     public List getImportPackages() {
    127         return importPackages;
    128     }
    129
    130     /***
    131      * @return the class name for the given alias or null if none is available
    132      */
    133     public ClassNode getImport(String alias) {
    134         return (ClassNode) importIndex.get(alias);
    135     }
    136
    137     public void addImport(String alias, ClassNode type) {
    138         imports.add(new ImportNode(type, alias));
    139         importIndex.put(alias, type);
    140     }
    141
    142     publicong> String[]  addImportPackage(String packageName) {
    143         importPackages.add(packageName);
    144         return new String[] { /* class names, not qualified */ };
    145     }
    146
    147     public void addStatement(Statement node) {
    148         statementBlock.addStatement(node);
    149     }
    150
    151     public void addClass(ClassNode node) {
    152         classes.add(node);
    153         node.setModule(this);
    154         addToCompileUnit(node);
    155     }
    156
    157     /***
    158      * @param node
    159      */
    160     private void addToCompileUnit(ClassNode node) {
    161         // register the new class with the compile unit
    162         if (unit != null) {
    163             unit.addClass(node);
    164         }
    165     }
    166
    167     public void addMethod(MethodNode node) {
    168         methods.add(node);
    169     }
    170
    171     public void visit(GroovyCodeVisitor visitor) {
    172     }
    173
    174     public String getPackageName() {
    175         return</strong> packageName;
    176     }
    177
    178     publicong> void setPackageName(String packageName) {
    179         this.packageName = packageName;
    180     }
    181
    182     public boolean hasPackageName(){
    183         return</strong> this.packageName != null;
    184     }
    185
    186     public SourceUnit getContext() {
    187         return context;
    188     }
    189
    190     /***
    191      * @return the underlying character stream description
    192      */
    193     public String getDescription() {
    194         if( context != null )
    195         {
    196             return context.getName();
    197         }
    198         else
    199         {
    200             return this.description;
    201         }
    202     }
    203
    204     public void setDescription(String description) {
    205         // DEPRECATED -- context.getName() is now sufficient
    206         this.description = description;
    207     }
    208
    209     public CompileUnit getUnit() {
    210         return unit;
    211     }
    212
    213     void setUnit(CompileUnit unit) {
    214         this.unit = unit;
    215     }
    216
    217     protected ClassNode createStatementsClass() {
    218         String name = getPackageName();
    219         if (name == null) {
    220             name = "";
    221         }
    222         // now lets use the file name to determine the class name
    223         if (getDescription() == null) {
    224             throw new RuntimeException("Cannot generate main(String[]) class for statements when we have no file description");
    225         }
    226         name += extractClassFromFileDescription();
    227
    228         String baseClassName = null;
    229         if (unit != null) baseClassName = unit.getConfig().getScriptBaseClass();
    230         ClassNode baseClass = null;
    231         if (baseClassName!=null) {
    232             baseClass = ClassHelper.make(baseClassName);
    233         }
    234         if (baseClass == null) {
    235             baseClass = ClassHelper.SCRIPT_TYPE;
    236         }
    237         ClassNode classNode = new ClassNode(name, ACC_PUBLIC, baseClass);
    238         classNode.setScript(true);
    239         classNode.setScriptBody(true);
    240
    241         // return new Foo(new ShellContext(args)).run()
    242         classNode.addMethod(
    243             new MethodNode(
    244                 "main",
    245                 ACC_PUBLIC | ACC_STATIC,
    246                 ClassHelper.VOID_TYPE,
    247                 new Parameter[] { new Parameter(ClassHelper.STRING_TYPE.makeArray(), "args")},
    248                 ClassNode.EMPTY_ARRAY,
    249                 new ExpressionStatement(
    250                     new MethodCallExpression(
    251                         new ClassExpression(ClassHelper.make(InvokerHelper.class)),
    252                         "runScript",
    253                         new ArgumentListExpression(
    254                             new Expression[] {
    255                                 new ClassExpression(classNode),
    256                                 new VariableExpression("args")})))));
    257
    258         classNode.addMethod(
    259             new MethodNode("run", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, statementBlock));
    260
    261         classNode.addConstructor(ACC_PUBLIC, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, new BlockStatement());
    262         Statement stmt = new ExpressionStatement(
    263                         new MethodCallExpression(
    264                             new VariableExpression("super"),
    265             				"setBinding",
    266             				new ArgumentListExpression(
    267                                     new Expression[] {
    268                                         new VariableExpression("context")})));
    269
    270         classNode.addConstructor(
    271             ACC_PUBLIC,
    272             new Parameter[] { new Parameter(ClassHelper.make(Binding.class), "context")},
    273 			ClassNode.EMPTY_ARRAY,
    274             stmt);
    275
    276         for (Iterator iter = methods.iterator(); iter.hasNext();) {
    277             MethodNode node = (MethodNode) iter.next();
    278             int modifiers = node.getModifiers();
    279             if ((modifiers & ACC_ABSTRACT) != 0) {
    280                 throw new RuntimeException(
    281                     "Cannot use abstract methods in a script, they are only available inside classes. Method: "
    282                         + node.getName());
    283             }
    284             // br: the old logic seems to add static to all def f().... in a script, which makes enclosing
    285             // inner classes (including closures) in a def function difficult. Comment it out.
    286             node.setModifiers(modifiers /*| ACC_STATIC*/);
    287
    288             classNode.addMethod(node);
    289         }
    290         return classNode;
    291     }
    292
    293     protected String extractClassFromFileDescription() {
    294         // lets strip off everything after the last .
    295         String answer = getDescription();
    296         int idx = answer.lastIndexOf('.');
    297         if (idx > 0) {
    298             answer = answer.substring(0, idx);
    299         }
    300         // new lets trip the path separators
    301         idx = answer.lastIndexOf('/');
    302         if (idx >= 0) {
    303             answer = answer.substring(idx + 1);
    304         }
    305         idx = answer.lastIndexOf(File.separatorChar);
    306         if (idx >= 0) {
    307             answer = answer.substring(idx + 1);
    308         }
    309         return answer;
    310     }
    311
    312     public boolean isEmpty() {
    313         return classes.isEmpty() && statementBlock.getStatements().isEmpty();
    314     }
    315
    316     public void sortClasses(){
    317     	if (isEmpty()) return;
    318     	List classes = getClasses();
    319     	LinkedList sorted = new LinkedList();
    320     	int level=1;
    321     	while (!classes.isEmpty()) {
    322 	    	for (Iterator cni = classes.iterator(); cni.hasNext();) {
    323 				ClassNode cn = (ClassNode) cni.next();
    324 				ClassNode sn = cn;
    325 				for (int i=0; sn!=null && i<level; i++) sn = sn.getSuperClass();
    326 				if (sn!=null && sn.isPrimaryClassNode()) continue;
    327 				cni.remove();
    328 				sorted.addLast(cn);
    329 			}
    330 	    	level++;
    331     	}
    332     	this.classes = sorted;
    333     }
    334
    335     public boolean hasImportsResolved() {
    336         return importsResolved;
    337     }
    338
    339     public void setImportsResolved(boolean importsResolved) {
    340         this.importsResolved = importsResolved;
    341     }
    342
    343 }