View Javadoc

1   /*
2    $Id: GroovyMain.java,v 1.9 2004/08/17 22:56:07 jez 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 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.FileNotFoundException;
55  import java.io.FileReader;
56  import java.io.FileWriter;
57  import java.io.IOException;
58  import java.io.InputStreamReader;
59  import java.io.PrintWriter;
60  import java.util.Iterator;
61  import java.util.List;
62  
63  import org.apache.commons.cli.CommandLine;
64  import org.apache.commons.cli.CommandLineParser;
65  import org.apache.commons.cli.HelpFormatter;
66  import org.apache.commons.cli.OptionBuilder;
67  import org.apache.commons.cli.Options;
68  import org.apache.commons.cli.ParseException;
69  import org.apache.commons.cli.PosixParser;
70  import org.apache.commons.cli.Option;
71  import org.codehaus.groovy.control.CompilationFailedException;
72  import org.codehaus.groovy.control.CompilerConfiguration;
73  import org.codehaus.groovy.runtime.InvokerHelper;
74  
75  /***
76   * A Command line to execute groovy.
77   * 
78   * @author Jeremy Rayner
79   * @author Yuri Schimke
80   * @version $Revision: 1.9 $
81   */
82  public class GroovyMain {
83      // arguments to the script
84      private List args;
85  
86      // is this a file on disk
87      private boolean isScriptFile;
88  
89      // filename or content of script
90      private String script;
91  
92      // process args as input files
93      private boolean processFiles;
94  
95      // edit input files in place
96      private boolean editFiles;
97  
98      // automatically output the result of each script
99      private boolean autoOutput;
100     
101     // process sockets
102     private boolean processSockets;
103     
104     // port to listen on when processing sockets
105     private int port;
106     
107     // backup input files with extension
108     private String backupExtension;
109 
110     // Compiler configuration, used to set the encodings of the scripts/classes
111     private CompilerConfiguration conf = new CompilerConfiguration();
112 
113     /***
114      * Main CLI interface.
115      * 
116      * @param args
117      *            all command line args.
118      */
119     public static void main(String args[]) {
120         MetaClass.setUseReflection(true);
121 
122         Options options = buildOptions();
123 
124         try {
125             CommandLine cmd = parseCommandLine(options, args);
126 
127             if (cmd.hasOption('h')) {
128                 HelpFormatter formatter = new HelpFormatter();
129                 formatter.printHelp("groovy", options);
130             }
131             else if (cmd.hasOption('v')) {
132                 String version = InvokerHelper.getVersion();
133                 System.out.println("Groovy Version: " + version + " JVM: " + System.getProperty("java.vm.version"));
134             }
135             else {
136                 process(cmd);
137             }
138         }
139         catch (ParseException pe) {
140             System.out.println("error: " + pe.getMessage());
141             HelpFormatter formatter = new HelpFormatter();
142             formatter.printHelp("groovy", options);
143         }
144     }
145 
146     /***
147      * Parse the command line.
148      * 
149      * @param options
150      *            the options parser.
151      * @param args
152      *            the command line args.
153      * @return parsed command line.
154      * @throws ParseException
155      *             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.
165      * 
166      * @return an options parser.
167      */
168     private static Options buildOptions() {
169         Options options = new Options();
170 
171         options.addOption(OptionBuilder.hasArg(false).withDescription("usage information").withLongOpt("help").create('h'));
172 
173         options.addOption(OptionBuilder.hasArg(false).withDescription("display the Groovy and JVM versions").withLongOpt("version").create('v'));
174 
175         options.addOption(OptionBuilder.withArgName("charset").hasArg().withDescription("specify the encoding of the files").withLongOpt("encoding").create('c'));
176 
177         options.addOption(OptionBuilder.withArgName("script").hasArg().withDescription("specify a command line script").create('e'));
178 
179         options.addOption(OptionBuilder.withArgName("extension").hasOptionalArg().withDescription("modify files in place").create('i'));
180 
181         options.addOption(OptionBuilder.hasArg(false).withDescription("process files line by line").create('n'));
182 
183         options.addOption(OptionBuilder.hasArg(false).withDescription("process files line by line and print result").create('p'));
184         
185         options.addOption(OptionBuilder.withArgName("port").hasOptionalArg().withDescription("listen on a port and process inbound lines").create('l'));
186         return options;
187     }
188 
189     /***
190      * Process the users request.
191      * 
192      * @param line
193      *            the parsed command line.
194      * @throws ParseException
195      *             if invalid options are chosen
196      */
197     private static void process(CommandLine line) throws ParseException {
198         GroovyMain main = new GroovyMain();
199 
200         List args = line.getArgList();
201 
202         // add the ability to parse scripts with a specified encoding
203         if (line.hasOption('c')) {
204             main.conf.setSourceEncoding(line.getOptionValue("encoding"));
205         }
206 
207         main.isScriptFile = !line.hasOption('e');
208         main.processFiles = line.hasOption('p') || line.hasOption('n');
209         main.autoOutput = line.hasOption('p');
210         main.editFiles = line.hasOption('i');
211         if (main.editFiles) {
212             main.backupExtension = line.getOptionValue('i');
213         }
214 
215         if (main.isScriptFile) {
216             if (args.isEmpty())
217                 throw new ParseException("neither -e or filename provided");
218 
219             main.script = (String) args.remove(0);
220             if (main.script.endsWith(".java"))
221                 throw new ParseException("error: cannot compile file with .java extension: " + main.script);
222         }
223         else {
224             main.script = line.getOptionValue('e');
225         }
226         
227         main.processSockets = line.hasOption('l');
228         if (main.processSockets) {
229             String p = line.getOptionValue('l',"1960"); // default port to listen to
230             main.port = new Integer(p).intValue();
231         }
232         main.args = args;
233 
234         main.run();
235     }
236 
237     public GroovyMain() {
238     }
239 
240     /***
241      * Run the script.
242      */
243     private void run() {
244         try {
245             if (processSockets) {
246                 processSockets();
247             } else if (processFiles) {
248                 processFiles();
249             }
250             else {
251                 processOnce();
252             }
253         }
254         catch (Exception e) {
255             System.out.println("Caught: " + e);
256             e.printStackTrace();
257         }
258     }
259     
260     /***
261      * Process Sockets.
262      */
263     private void processSockets() throws CompilationFailedException, IOException {
264         GroovyShell groovy = new GroovyShell(conf);
265         Script s;
266         if (isScriptFile) {
267             s = groovy.parse(new File(script));
268         } else {
269             s = groovy.parse(script, "main");
270         }
271         new GroovySocketServer(groovy,isScriptFile,script,autoOutput,port);
272     }
273     
274     /***
275      * Process the input files.
276      */
277     private void processFiles() throws CompilationFailedException, IOException {
278         GroovyShell groovy = new GroovyShell(conf);
279 
280         Script s = null;
281 
282         if (isScriptFile)
283             s = groovy.parse(new File(script));
284         else
285             s = groovy.parse(script, "main");
286 
287         if (args.isEmpty()) {
288             BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
289             PrintWriter writer = new PrintWriter(System.out);
290 
291             processReader(s, reader, writer);
292         }
293         else {
294             Iterator i = args.iterator();
295             while (i.hasNext()) {
296                 String filename = (String) i.next();
297                 File file = new File(filename);
298                 processFile(s, file);
299             }
300         }
301     }
302 
303     /***
304      * Process a single input file.
305      * 
306      * @param s
307      *            the script to execute.
308      * @param file
309      *            the input file.
310      */
311     private void processFile(Script s, File file) throws IOException {
312         if (!file.exists())
313             throw new FileNotFoundException(file.getName());
314 
315         if (!editFiles) {
316             BufferedReader reader = new BufferedReader(new FileReader(file));
317             try {
318                 PrintWriter writer = new PrintWriter(System.out);
319                 processReader(s, reader, writer);
320                 writer.flush();
321             }
322             finally {
323                 reader.close();
324             }
325         }
326         else {
327             File backup = null;
328             if (backupExtension == null) {
329                 backup = File.createTempFile("groovy_", ".tmp");
330                 backup.deleteOnExit();
331             }
332             else {
333                 backup = new File(file.getPath() + backupExtension);
334                 backup.delete();
335             }
336             if (!file.renameTo(backup))
337                 throw new IOException("unable to rename " + file + " to " + backup);
338 
339             BufferedReader reader = new BufferedReader(new FileReader(backup));
340             try {
341                 PrintWriter writer = new PrintWriter(new FileWriter(file));
342                 try {
343                     processReader(s, reader, writer);
344                 }
345                 finally {
346                     writer.close();
347                 }
348             }
349             finally {
350                 reader.close();
351             }
352         }
353     }
354 
355     /***
356      * Process a script against a single input file.
357      * 
358      * @param s
359      *            script to execute.
360      * @param reader
361      *            input file.
362      * @param pw
363      *            output sink.
364      */
365     private void processReader(Script s, BufferedReader reader, PrintWriter pw) throws IOException {
366         String line = null;
367         s.setProperty("out", pw);
368         while ((line = reader.readLine()) != null) {
369             s.setProperty("line", line);
370             Object o = s.run();
371 
372             if (autoOutput) {
373                 pw.println(o);
374             }
375         }
376     }
377 
378     /***
379      * Process the standard, single script with args.
380      */
381     private void processOnce() throws CompilationFailedException, IOException {
382         GroovyShell groovy = new GroovyShell(conf);
383 
384         if (isScriptFile)
385             groovy.run(new File(script), args);
386         else
387             groovy.run(script, "main", args);
388     }
389 }