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 groovy.ui;
47
48 import groovy.lang.GroovyShell;
49 import groovy.lang.MetaClass;
50 import groovy.lang.Script;
51
52 import java.io.BufferedReader;
53 import java.io.File;
54 import java.io.FileInputStream;
55 import java.io.FileNotFoundException;
56 import java.io.FileReader;
57 import java.io.FileWriter;
58 import java.io.IOException;
59 import java.io.InputStreamReader;
60 import java.io.PrintWriter;
61 import java.util.Iterator;
62 import java.util.List;
63
64 import org.apache.commons.cli.CommandLine;
65 import org.apache.commons.cli.CommandLineParser;
66 import org.apache.commons.cli.HelpFormatter;
67 import org.apache.commons.cli.OptionBuilder;
68 import org.apache.commons.cli.Options;
69 import org.apache.commons.cli.ParseException;
70 import org.apache.commons.cli.PosixParser;
71 import org.codehaus.groovy.control.CompilationFailedException;
72 import org.codehaus.groovy.control.CompilerConfiguration;
73 import org.codehaus.groovy.runtime.InvokerHelper;
74 import org.codehaus.groovy.runtime.InvokerInvocationException;
75
76 /***
77 * A Command line to execute groovy.
78 *
79 * @author Jeremy Rayner
80 * @author Yuri Schimke
81 * @version $Revision: 1.25 $
82 */
83 public class GroovyMain {
84
85 private List args;
86
87
88 private boolean isScriptFile;
89
90
91 private String script;
92
93
94 private boolean processFiles;
95
96
97 private boolean editFiles;
98
99
100 private boolean autoOutput;
101
102
103 private boolean processSockets;
104
105
106 private int port;
107
108
109 private String backupExtension;
110
111
112 private boolean debug = false;
113
114
115 private CompilerConfiguration conf = new CompilerConfiguration();
116
117 /***
118 * Main CLI interface.
119 *
120 * @param args all command line args.
121 */
122 public static void main(String args[]) {
123 MetaClass.setUseReflection(true);
124
125 Options options = buildOptions();
126
127 try {
128 CommandLine cmd = parseCommandLine(options, args);
129
130 if (cmd.hasOption('h')) {
131 HelpFormatter formatter = new HelpFormatter();
132 formatter.printHelp("groovy", options);
133 } else if (cmd.hasOption('v')) {
134 String version = InvokerHelper.getVersion();
135 System.out.println("Groovy Version: " + version + " JVM: " + System.getProperty("java.vm.version"));
136 } else {
137
138 if (!process(cmd)) {
139 System.exit(1);
140 }
141 }
142 } catch (ParseException pe) {
143 System.out.println("error: " + pe.getMessage());
144 HelpFormatter formatter = new HelpFormatter();
145 formatter.printHelp("groovy", options);
146 }
147 }
148
149 /***
150 * Parse the command line.
151 *
152 * @param options the options parser.
153 * @param args the command line args.
154 * @return parsed command line.
155 * @throws ParseException if there was a problem.
156 */
157 private static CommandLine parseCommandLine(Options options, String[] args) throws ParseException {
158 CommandLineParser parser = new PosixParser();
159 CommandLine cmd = parser.parse(options, args, true);
160 return cmd;
161 }
162
163 /***
164 * Build the options parser. Has to be synchronized because of the way Options are constructed.
165 *
166 * @return an options parser.
167 */
168 private static synchronized Options buildOptions() {
169 Options options = new Options();
170
171 options.addOption(
172 OptionBuilder.hasArg(false)
173 .withDescription("usage information")
174 .withLongOpt("help")
175 .create('h'));
176 options.addOption(
177 OptionBuilder.hasArg(false)
178 .withDescription("debug mode will print out full stack traces")
179 .withLongOpt("debug")
180 .create('d'));
181 options.addOption(
182 OptionBuilder.hasArg(false)
183 .withDescription("display the Groovy and JVM versions")
184 .withLongOpt("version")
185 .create('v'));
186 options.addOption(
187 OptionBuilder.withArgName("charset")
188 .hasArg()
189 .withDescription("specify the encoding of the files")
190 .withLongOpt("encoding")
191 .create('c'));
192 options.addOption(
193 OptionBuilder.withArgName("script")
194 .hasArg()
195 .withDescription("specify a command line script")
196 .create('e'));
197 options.addOption(
198 OptionBuilder.withArgName("extension")
199 .hasOptionalArg()
200 .withDescription("modify files in place, create backup if extension is given (e.g. \'.bak\')")
201 .create('i'));
202 options.addOption(
203 OptionBuilder.hasArg(false)
204 .withDescription("process files line by line")
205 .create('n'));
206 options.addOption(
207 OptionBuilder.hasArg(false)
208 .withDescription("process files line by line and print result")
209 .create('p'));
210 options.addOption(
211 OptionBuilder.withArgName("port")
212 .hasOptionalArg()
213 .withDescription("listen on a port and process inbound lines")
214 .create('l'));
215 return options;
216 }
217
218 /***
219 * Process the users request.
220 *
221 * @param line the parsed command line.
222 * @throws ParseException if invalid options are chosen
223 */
224 private static boolean process(CommandLine line) throws ParseException {
225 GroovyMain main = new GroovyMain();
226
227 List args = line.getArgList();
228
229
230 if (line.hasOption('c')) {
231 main.conf.setSourceEncoding(line.getOptionValue("encoding"));
232 }
233
234 main.isScriptFile = !line.hasOption('e');
235 main.debug = line.hasOption('d');
236 main.conf.setDebug(main.debug);
237 main.processFiles = line.hasOption('p') || line.hasOption('n');
238 main.autoOutput = line.hasOption('p');
239 main.editFiles = line.hasOption('i');
240 if (main.editFiles) {
241 main.backupExtension = line.getOptionValue('i');
242 }
243
244 if (main.isScriptFile) {
245 if (args.isEmpty())
246 throw new ParseException("neither -e or filename provided");
247
248 main.script = (String) args.remove(0);
249 if (main.script.endsWith(".java"))
250 throw new ParseException("error: cannot compile file with .java extension: " + main.script);
251 } else {
252 main.script = line.getOptionValue('e');
253 }
254
255 main.processSockets = line.hasOption('l');
256 if (main.processSockets) {
257 String p = line.getOptionValue('l', "1960");
258 main.port = new Integer(p).intValue();
259 }
260 main.args = args;
261
262 return main.run();
263 }
264
265
266 /***
267 * Run the script.
268 */
269 private boolean run() {
270 try {
271 if (processSockets) {
272 processSockets();
273 } else if (processFiles) {
274 processFiles();
275 } else {
276 processOnce();
277 }
278 return true;
279 } catch (CompilationFailedException e) {
280 System.err.println(e);
281 return false;
282 } catch (Throwable e) {
283 if (e instanceof InvokerInvocationException) {
284 InvokerInvocationException iie = (InvokerInvocationException) e;
285 e = iie.getCause();
286 }
287 System.err.println("Caught: " + e);
288 if (debug) {
289 e.printStackTrace();
290 } else {
291 StackTraceElement[] stackTrace = e.getStackTrace();
292 for (int i = 0; i < stackTrace.length; i++) {
293 StackTraceElement element = stackTrace[i];
294 String fileName = element.getFileName();
295 if (fileName!=null && !fileName.endsWith(".java")) {
296 System.err.println("\tat " + element);
297 }
298 }
299 }
300 return false;
301 }
302 }
303
304 /***
305 * Process Sockets.
306 */
307 private void processSockets() throws CompilationFailedException, IOException {
308 GroovyShell groovy = new GroovyShell(conf);
309
310 if (isScriptFile) {
311 groovy.parse(new FileInputStream(huntForTheScriptFile(script)));
312 } else {
313 groovy.parse(script);
314 }
315 new GroovySocketServer(groovy, isScriptFile, script, autoOutput, port);
316 }
317
318 /***
319 * Hunt for the script file, doesn't bother if it is named precisely.
320 *
321 * Tries in this order:
322 * - actual supplied name
323 * - name.groovy
324 * - name.gvy
325 * - name.gy
326 * - name.gsh
327 */
328 public File huntForTheScriptFile(String scriptFileName) {
329 File scriptFile = new File(scriptFileName);
330 String[] standardExtensions = {".groovy",".gvy",".gy",".gsh"};
331 int i = 0;
332 while (i < standardExtensions.length && !scriptFile.exists()) {
333 scriptFile = new File(scriptFileName + standardExtensions[i]);
334 i++;
335 }
336
337 if (!scriptFile.exists()) {
338 scriptFile = new File(scriptFileName);
339 }
340 return scriptFile;
341 }
342
343 /***
344 * Process the input files.
345 */
346 private void processFiles() throws CompilationFailedException, IOException {
347 GroovyShell groovy = new GroovyShell(conf);
348
349 Script s = null;
350
351 if (isScriptFile) {
352 s = groovy.parse(huntForTheScriptFile(script));
353 } else {
354 s = groovy.parse(script, "main");
355 }
356
357 if (args.isEmpty()) {
358 BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
359 PrintWriter writer = new PrintWriter(System.out);
360
361 try {
362 processReader(s, reader, writer);
363 } finally {
364 reader.close();
365 writer.close();
366 }
367
368 } else {
369 Iterator i = args.iterator();
370 while (i.hasNext()) {
371 String filename = (String) i.next();
372 File file = huntForTheScriptFile(filename);
373 processFile(s, file);
374 }
375 }
376 }
377
378 /***
379 * Process a single input file.
380 *
381 * @param s the script to execute.
382 * @param file the input file.
383 */
384 private void processFile(Script s, File file) throws IOException {
385 if (!file.exists())
386 throw new FileNotFoundException(file.getName());
387
388 if (!editFiles) {
389 BufferedReader reader = new BufferedReader(new FileReader(file));
390 try {
391 PrintWriter writer = new PrintWriter(System.out);
392 processReader(s, reader, writer);
393 writer.flush();
394 } finally {
395 reader.close();
396 }
397 } else {
398 File backup = null;
399 if (backupExtension == null) {
400 backup = File.createTempFile("groovy_", ".tmp");
401 backup.deleteOnExit();
402 } else {
403 backup = new File(file.getPath() + backupExtension);
404 }
405 backup.delete();
406 if (!file.renameTo(backup))
407 throw new IOException("unable to rename " + file + " to " + backup);
408
409 BufferedReader reader = new BufferedReader(new FileReader(backup));
410 try {
411 PrintWriter writer = new PrintWriter(new FileWriter(file));
412 try {
413 processReader(s, reader, writer);
414 } finally {
415 writer.close();
416 }
417 } finally {
418 reader.close();
419 }
420 }
421 }
422
423 /***
424 * Process a script against a single input file.
425 *
426 * @param s script to execute.
427 * @param reader input file.
428 * @param pw output sink.
429 */
430 private void processReader(Script s, BufferedReader reader, PrintWriter pw) throws IOException {
431 String line = null;
432 s.setProperty("out", pw);
433 while ((line = reader.readLine()) != null) {
434 s.setProperty("line", line);
435 Object o = s.run();
436
437 if (autoOutput) {
438 pw.println(o);
439 }
440 }
441 }
442
443 private static ClassLoader getLoader(ClassLoader cl) {
444 if (cl!=null) return cl;
445 cl = Thread.currentThread().getContextClassLoader();
446 if (cl!=null) return cl;
447 cl = GroovyMain.class.getClassLoader();
448 if (cl!=null) return cl;
449 return null;
450 }
451
452 /***
453 * Process the standard, single script with args.
454 */
455 private void processOnce() throws CompilationFailedException, IOException {
456 GroovyShell groovy = new GroovyShell(conf);
457
458 if (isScriptFile)
459 groovy.run(huntForTheScriptFile(script), args);
460 else
461 groovy.run(script, "script_from_command_line", args);
462 }
463 }