View Javadoc

1   /*
2    $Id: ModuleNode.java,v 1.29 2005/11/13 16:42:09 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 <a href="mailto:james@coredevelopers.net">James Strachan</a>
76   * @version $Revision: 1.29 $
77   */
78  public class ModuleNode extends ASTNode implements Opcodes {
79  
80      private BlockStatement statementBlock = new BlockStatement();
81      List classes = new LinkedList();
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 transient 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 boolean hasPackageName(){
181         return</strong> this.packageName != null;
182     }
183 
184     public SourceUnit getContext() {
185         return context;
186     }
187 
188     /***
189      * @return the underlying character stream description
190      */
191     public String getDescription() {
192         if( context != null )
193         {
194             return context.getName();
195         }
196         else
197         {
198             return this.description;
199         }
200     }
201 
202     public void setDescription(String description) {
203         // DEPRECATED -- context.getName() is now sufficient
204         this.description = description;
205     }
206 
207     public CompileUnit getUnit() {
208         return unit;
209     }
210 
211     void setUnit(CompileUnit unit) {
212         this.unit = unit;
213     }
214 
215     protected ClassNode createStatementsClass() {
216         String name = getPackageName();
217         if (name == null) {
218             name = "";
219         }
220         // now lets use the file name to determine the class name
221         if (getDescription() == null) {
222             throw new RuntimeException("Cannot generate main(String[]) class for statements when we have no file description");
223         }
224         name += extractClassFromFileDescription();
225 
226         String baseClassName = null;
227         if (unit != null) baseClassName = unit.getConfig().getScriptBaseClass();
228         ClassNode baseClass = null;
229         if (baseClassName!=null) {
230             baseClass = ClassHelper.make(baseClassName);
231         }
232         if (baseClass == null) {
233             baseClass = ClassHelper.SCRIPT_TYPE;
234         }
235         ClassNode classNode = new ClassNode(name, ACC_PUBLIC, baseClass);
236         classNode.setScript(true);
237 
238         // return new Foo(new ShellContext(args)).run()
239         classNode.addMethod(
240             new MethodNode(
241                 "main",
242                 ACC_PUBLIC | ACC_STATIC,
243                 ClassHelper.VOID_TYPE,
244                 new Parameter[] { new Parameter(ClassHelper.STRING_TYPE.makeArray(), "args")},
245                 new ExpressionStatement(
246                     new MethodCallExpression(
247                         new ClassExpression(ClassHelper.make(InvokerHelper.class)),
248                         "runScript",
249                         new ArgumentListExpression(
250                             new Expression[] {
251                                 new ClassExpression(classNode),
252                                 new VariableExpression("args")})))));
253 
254         classNode.addMethod(
255             new MethodNode("run", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, statementBlock));
256 
257         classNode.addConstructor(ACC_PUBLIC, Parameter.EMPTY_ARRAY, new BlockStatement());
258         Statement stmt = new ExpressionStatement(
259                         new MethodCallExpression(
260                             new VariableExpression("super"),
261             				"setBinding",
262             				new ArgumentListExpression(
263                                     new Expression[] {
264                                         new VariableExpression("context")})));
265 
266         classNode.addConstructor(
267             ACC_PUBLIC,
268             new Parameter[] { new Parameter(ClassHelper.make(Binding.class), "context")},
269 			stmt);
270 
271         for (Iterator iter = methods.iterator(); iter.hasNext();) {
272             MethodNode node = (MethodNode) iter.next();
273             int modifiers = node.getModifiers();
274             if ((modifiers & ACC_ABSTRACT) != 0) {
275                 throw new RuntimeException(
276                     "Cannot use abstract methods in a script, they are only available inside classes. Method: "
277                         + node.getName());
278             }
279             // br: the old logic seems to add static to all def f().... in a script, which makes enclosing
280             // inner classes (including closures) in a def function difficult. Comment it out.
281             node.setModifiers(modifiers /*| ACC_STATIC*/);
282 
283             classNode.addMethod(node);
284         }
285         return classNode;
286     }
287 
288     protected String extractClassFromFileDescription() {
289         // lets strip off everything after the last .
290         String answer = getDescription();
291         int idx = answer.lastIndexOf('.');
292         if (idx > 0) {
293             answer = answer.substring(0, idx);
294         }
295         // new lets trip the path separators
296         idx = answer.lastIndexOf('/');
297         if (idx >= 0) {
298             answer = answer.substring(idx + 1);
299         }
300         idx = answer.lastIndexOf(File.separatorChar);
301         if (idx >= 0) {
302             answer = answer.substring(idx + 1);
303         }
304         return answer;
305     }
306 
307     public boolean isEmpty() {
308         return classes.isEmpty() && statementBlock.getStatements().isEmpty();
309     }
310     
311     public void sortClasses(){
312     	if (isEmpty()) return;
313     	List classes = getClasses();
314     	LinkedList sorted = new LinkedList();
315     	int level=1;
316     	while (!classes.isEmpty()) {
317 	    	for (Iterator cni = classes.iterator(); cni.hasNext();) {
318 				ClassNode cn = (ClassNode) cni.next();
319 				ClassNode sn = cn;
320 				for (int i=0; sn!=null && i<level; i++) sn = sn.getSuperClass();
321 				if (sn!=null && sn.isPrimaryClassNode()) continue;
322 				cni.remove();
323 				sorted.addLast(cn);
324 			}
325 	    	level++;
326     	}
327     	this.classes = sorted;
328     }
329 
330 }