1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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 private String packageName/package-summary.html">ong> 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 public String[] addImportPackage(String packageName) {/package-summary.html">ong> String[] addImportPackage(String packageName) {
143 importPackages.add(packageName);
144 return new String[] {
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
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 public void setPackageName(String packageName) {/package-summary.html">ong> 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
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
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
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
285
286 node.setModifiers(modifiers
287
288 classNode.addMethod(node);
289 }
290 return classNode;
291 }
292
293 protected String extractClassFromFileDescription() {
294
295 String answer = getDescription();
296 int idx = answer.lastIndexOf('.');
297 if (idx > 0) {
298 answer = answer.substring(0, idx);
299 }
300
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 }