View Javadoc

1   /*
2    $Id: ModuleNode.java,v 1.32 2006/06/15 20:42:01 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
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: 1.32 $
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 }