View Javadoc

    1   /*
    2    $Id: ModuleNode.java,v 1.25 2004/07/10 03:31:38 bran 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
    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.Script;
    49  import groovy.lang.Binding;
    50
    51  import java.io.File;
    52  import java.util.ArrayList;
    53  import java.util.HashMap;
    54  import java.util.Iterator;
    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.Constants;
    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 <a href="mailto:james@coredevelopers.net">James Strachan</a>
    76   * @version $Revision: 1.25 $
    77   */
    78  public class ModuleNode extends ASTNode implements Constants {
    79
    80      private BlockStatement statementBlock = new BlockStatement();
    81      List classes = new ArrayList();
    82      private List methods = new ArrayList();
    83      private List imports = new ArrayList();
    84      private List importPackages = new ArrayList();
    85      private Map importIndex = new HashMap();
    86      private CompileUnit unit;
    87      private</str<a href="../../../../ong> String packageName/package-summary.html">ong> String packageName;
    88      private String description;
    89      private boolean createClassForStatements = true;
    90      private SourceUnit context;
    91
    92
    93      public ModuleNode( SourceUnit context ) {
    94          this.context = context;
    95      }
    96
    97      public ModuleNode(CompileUnit unit) {
    98          this.unit = unit;
    99      }
    100
    101     public BlockStatement getStatementBlock() {
    102         return statementBlock;
    103     }
    104
    105     public List getMethods() {
    106         return methods;
    107     }
    108
    109     public List getClasses() {
    110         if (createClassForStatements && (!statementBlock.isEmpty() || !methods.isEmpty())) {
    111             ClassNode mainClass = createStatementsClass();
    112             createClassForStatements = false;
    113             classes.add(0, mainClass);
    114             mainClass.setModule(this);
    115             addToCompileUnit(mainClass);
    116         }
    117         return classes;
    118     }
    119
    120     public List getImports() {
    121         return imports;
    122     }
    123
    124     public List getImportPackages() {
    125         return importPackages;
    126     }
    127
    128     /***
    129      * @return the class name for the given alias or null if none is available
    130      */
    131     public String getImport(String alias) {
    132         return (String) importIndex.get(alias);
    133     }
    134
    135     public void addImport(String alias, String className) {
    136         imports.add(new ImportNode(className, alias));
    137         importIndex.put(alias, className);
    138     }
    139
    140     public</str<a href="../../../../ong> String[]  addImportPackage(String packageName) {/package-summary.html">ong> String[]  addImportPackage(String packageName) {
    141         importPackages.add(packageName);
    142         return new String[] { /* class names, not qualified */ };
    143     }
    144
    145     public void addStatement(Statement node) {
    146         statementBlock.addStatement(node);
    147     }
    148
    149     public void addClass(ClassNode node) {
    150         classes.add(node);
    151         node.setModule(this);
    152         addToCompileUnit(node);
    153     }
    154
    155     /***
    156      * @param node
    157      */
    158     private void addToCompileUnit(ClassNode node) {
    159         // register the new class with the compile unit
    160         if (unit != null) {
    161             unit.addClass(node);
    162         }
    163     }
    164
    165     public void addMethod(MethodNode node) {
    166         methods.add(node);
    167     }
    168
    169     public void visit(GroovyCodeVisitor visitor) {
    170     }
    171
    172     public String getPackageName() {
    173         return</strong> packageName;
    174     }
    175
    176     public</str<a href="../../../../ong> <strong>void</strong> setPackageName(String packageName) {/package-summary.html">ong> void setPackageName(String packageName) {
    177         this.packageName = packageName;
    178     }
    179
    180     public SourceUnit getContext() {
    181         return context;
    182     }
    183
    184     /***
    185      * @return the underlying character stream description
    186      */
    187     public String getDescription() {
    188         if( context != null )
    189         {
    190             return context.getName();
    191         }
    192         else
    193         {
    194             return this.description;
    195         }
    196     }
    197
    198     public void setDescription(String description) {
    199         // DEPRECATED -- context.getName() is now sufficient
    200         this.description = description;
    201     }
    202
    203     public CompileUnit getUnit() {
    204         return unit;
    205     }
    206
    207     void setUnit(CompileUnit unit) {
    208         this.unit = unit;
    209     }
    210
    211     protected ClassNode createStatementsClass() {
    212         String name = getPackageName();
    213         if (name == null) {
    214             name = "";
    215         }
    216         else {
    217             name = name + ".";
    218         }
    219         // now lets use the file name to determine the class name
    220         if (getDescription() == null) {
    221             throw new RuntimeException("Cannot generate main(String[]) class for statements when we have no file description");
    222         }
    223         name += extractClassFromFileDescription();
    224
    225         String baseClass = null;
    226         if (unit != null) {
    227             baseClass = unit.getConfig().getScriptBaseClass();
    228         }
    229         if (baseClass == null) {
    230             baseClass = Script.class.getName();
    231         }
    232         ClassNode classNode = new ClassNode(name, ACC_PUBLIC, baseClass);
    233         classNode.setScript(true);
    234
    235         // return new Foo(new ShellContext(args)).run()
    236         classNode.addMethod(
    237             new MethodNode(
    238                 "main",
    239                 ACC_PUBLIC | ACC_STATIC,
    240                 "void",
    241                 new Parameter[] { new Parameter("java.lang.String[]", "args")},
    242                 new ExpressionStatement(
    243                     new MethodCallExpression(
    244                         new ClassExpression(InvokerHelper.class.getName()),
    245                         "runScript",
    246                         new ArgumentListExpression(
    247                             new Expression[] {
    248                                 new ClassExpression(classNode.getName()),
    249                                 new VariableExpression("args")})))));
    250
    251         classNode.addMethod(
    252             new MethodNode("run", ACC_PUBLIC, Object.class.getName(), Parameter.EMPTY_ARRAY, statementBlock));
    253
    254         classNode.addConstructor(ACC_PUBLIC, Parameter.EMPTY_ARRAY, new BlockStatement());
    255         Statement stmt = new ExpressionStatement(
    256                         new MethodCallExpression(
    257                             new VariableExpression("super"),
    258             				"setBinding",
    259             				new ArgumentListExpression(
    260                                     new Expression[] {
    261                                         new VariableExpression("context")})));
    262
    263         classNode.addConstructor(
    264             ACC_PUBLIC,
    265             new Parameter[] { new Parameter(Binding.class.getName(), "context")},
    266 			stmt);
    267
    268         for (Iterator iter = methods.iterator(); iter.hasNext();) {
    269             MethodNode node = (MethodNode) iter.next();
    270             int modifiers = node.getModifiers();
    271             if ((modifiers & ACC_ABSTRACT) != 0) {
    272                 throw new RuntimeException(
    273                     "Cannot use abstract methods in a script, they are only available inside classes. Method: "
    274                         + node.getName());
    275             }
    276             // br: the old logic seems to add static to all def f().... in a script, which makes enclosing
    277             // inner classes (including closures) in a def function difficult. Comment it out.
    278             node.setModifiers(modifiers /*| ACC_STATIC*/);
    279
    280             classNode.addMethod(node);
    281         }
    282         return classNode;
    283     }
    284
    285     protected String extractClassFromFileDescription() {
    286         // lets strip off everything after the last .
    287         String answer = getDescription();
    288         int idx = answer.lastIndexOf('.');
    289         if (idx > 0) {
    290             answer = answer.substring(0, idx);
    291         }
    292         // new lets trip the path separators
    293         idx = answer.lastIndexOf('/');
    294         if (idx >= 0) {
    295             answer = answer.substring(idx + 1);
    296         }
    297         idx = answer.lastIndexOf(File.separatorChar);
    298         if (idx >= 0) {
    299             answer = answer.substring(idx + 1);
    300         }
    301         return answer;
    302     }
    303
    304     public boolean isEmpty() {
    305         return classes.isEmpty() && statementBlock.getStatements().isEmpty();
    306     }
    307
    308 }