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      privateong> 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     publicong> 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     publicong> 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 }