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