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 <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 private String packageName/package-summary.html">ong> 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 public String[] addImportPackage(String packageName) {/package-summary.html">ong> String[] addImportPackage(String packageName) {
141 importPackages.add(packageName);
142 return new String[] {
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
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 void setPackageName(String packageName) {/package-summary.html">ong> 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
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
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
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
280
281 node.setModifiers(modifiers
282
283 classNode.addMethod(node);
284 }
285 return classNode;
286 }
287
288 protected String extractClassFromFileDescription() {
289
290 String answer = getDescription();
291 int idx = answer.lastIndexOf('.');
292 if (idx > 0) {
293 answer = answer.substring(0, idx);
294 }
295
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 }