001: /*
002: * Copyright 2002,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.commons.jelly.util;
018:
019: import java.io.File;
020: import java.io.FileWriter;
021: import java.net.URL;
022: import java.util.Arrays;
023: import java.util.ArrayList;
024: import java.util.List;
025: import java.util.Properties;
026:
027: import org.apache.commons.cli.CommandLine;
028: import org.apache.commons.cli.Options;
029: import org.apache.commons.cli.ParseException;
030: import org.apache.commons.cli.Parser;
031: import org.apache.commons.cli.HelpFormatter;
032: import org.apache.commons.jelly.Jelly;
033: import org.apache.commons.jelly.JellyContext;
034: import org.apache.commons.jelly.JellyException;
035: import org.apache.commons.jelly.Script;
036: import org.apache.commons.jelly.XMLOutput;
037:
038: /**
039: * Utility class to parse command line options using CLI.
040: * Using a separate class allows us to run Jelly without
041: * CLI in the classpath when the command line interface
042: * is not in use.
043: *
044: * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
045: * @author Morgan Delagrange
046: * @version $Revision: 155420 $
047: */
048: public class CommandLineParser {
049:
050: protected static CommandLineParser _instance = new CommandLineParser();
051:
052: private Options cmdLineOptions = null;
053:
054: public static CommandLineParser getInstance() {
055: return _instance;
056: }
057:
058: /**
059: * Parse out the command line options and configure
060: * the give Jelly instance.
061: *
062: * @param args options from the command line
063: * @exception JellyException
064: * if the command line could not be parsed
065: */
066: public void invokeCommandLineJelly(String[] args)
067: throws JellyException {
068: CommandLine cmdLine = null;
069: try {
070: cmdLine = parseCommandLineOptions(args);
071: } catch (ParseException e) {
072: throw new JellyException(e);
073: }
074:
075: // check for -h or -v
076: if (cmdLine.hasOption("h")) {
077: new HelpFormatter()
078: .printHelp(
079: "jelly [scriptFile] [-script scriptFile] [-o outputFile] [-Dsysprop=syspropval] [-awt]",
080: cmdLineOptions);
081: System.exit(1);
082: }
083: if (cmdLine.hasOption("v")) {
084: System.err.println("Jelly " + Jelly.getJellyVersion());
085: System.err.println(" compiled: "
086: + Jelly.getJellyBuildDate());
087: System.err.println("");
088: System.exit(1);
089: }
090:
091: // get the -script option. If there isn't one then use args[0]
092: String scriptFile = null;
093: if (cmdLine.hasOption("script")) {
094: scriptFile = cmdLine.getOptionValue("script");
095: } else {
096: scriptFile = args[0];
097: }
098:
099: // check the -awt option.
100: boolean runInSwingThread = cmdLine.hasOption("awt")
101: || cmdLine.hasOption("swing");
102:
103: //
104: // Use classloader to find file
105: //
106: URL url = ClassLoaderUtils.getClassLoader(getClass())
107: .getResource(scriptFile);
108: // check if the script file exists
109: if (url == null && !(new File(scriptFile)).exists()) {
110: throw new JellyException("Script file " + scriptFile
111: + " not found");
112: }
113:
114: try {
115: // extract the -o option for the output file to use
116: final XMLOutput output = cmdLine.hasOption("o") ? XMLOutput
117: .createXMLOutput(new FileWriter(cmdLine
118: .getOptionValue("o"))) : XMLOutput
119: .createXMLOutput(System.out);
120:
121: Jelly jelly = new Jelly();
122: jelly.setScript(scriptFile);
123:
124: final Script script = jelly.compileScript();
125:
126: // add the system properties and the command line arguments
127: final JellyContext context = jelly.getJellyContext();
128: context.setVariable("args", args);
129: context.setVariable("commandLine", cmdLine);
130: if (runInSwingThread) {
131: javax.swing.SwingUtilities
132: .invokeAndWait(new Runnable() {
133: public void run() {
134: try {
135: script.run(context, output);
136: } catch (Exception ex) {
137: ex.printStackTrace();
138: }
139: }
140: });
141: } else {
142: script.run(context, output);
143: }
144:
145: // now lets wait for all threads to close
146: Runtime.getRuntime().addShutdownHook(new Thread() {
147: public void run() {
148: try {
149: output.close();
150: } catch (Exception e) {
151: // ignore errors
152: }
153: }
154: });
155:
156: } catch (Exception e) {
157: throw new JellyException(e);
158: }
159:
160: }
161:
162: /**
163: * Parse the command line using CLI. -o and -script are reserved for Jelly.
164: * -Dsysprop=sysval is support on the command line as well.
165: */
166: public CommandLine parseCommandLineOptions(String[] args)
167: throws ParseException {
168: // create the expected options
169: cmdLineOptions = new Options();
170: cmdLineOptions.addOption("o", true, "Output file");
171: cmdLineOptions.addOption("script", true, "Jelly script to run");
172: cmdLineOptions.addOption("h", "help", false,
173: "Give this help message");
174: cmdLineOptions.addOption("v", "version", false,
175: "prints Jelly's version and exits");
176: cmdLineOptions.addOption("script", true, "Jelly script to run");
177: cmdLineOptions.addOption("awt", false,
178: "Wether to run in the AWT thread.");
179: cmdLineOptions
180: .addOption("swing", false, "Synonym of \"-awt\".");
181: List builtinOptionNames = Arrays.asList(new String[] { "-o",
182: "-script", "-h", "--help", "-v", "--version", "-awt",
183: "-swing" });
184:
185: // -D options will be added to the system properties
186: Properties sysProps = System.getProperties();
187:
188: // filter the system property setting from the arg list
189: // before passing it to the CLI parser
190: ArrayList filteredArgList = new ArrayList();
191:
192: for (int i = 0; i < args.length; i++) {
193: String arg = args[i];
194:
195: // if this is a -D property parse it and add it to the system properties.
196: // -D args will not be copied into the filteredArgList.
197: if (arg.startsWith("-D") && (arg.length() > 2)) {
198: arg = arg.substring(2);
199: int ePos = arg.indexOf("=");
200: if (ePos == -1 || ePos == 0 || ePos == arg.length() - 1)
201: System.err.println("Invalid system property: \""
202: + arg + "\".");
203: sysProps.setProperty(arg.substring(0, ePos), arg
204: .substring(ePos + 1));
205: } else {
206: // add this to the filtered list of arguments
207: filteredArgList.add(arg);
208:
209: // add additional "-?" options to the options object. if this is not done
210: // the only options allowed would be the builtin-ones.
211: if (arg.startsWith("-") && arg.length() > 1) {
212: if (!(builtinOptionNames.contains(arg))) {
213: cmdLineOptions.addOption(arg.substring(1, arg
214: .length()), true, "dynamic option");
215: }
216: }
217: }
218: }
219:
220: // make the filteredArgList into an array
221: String[] filterArgs = new String[filteredArgList.size()];
222: filteredArgList.toArray(filterArgs);
223:
224: // parse the command line
225: Parser parser = new org.apache.commons.cli.GnuParser();
226: return parser.parse(cmdLineOptions, filterArgs);
227: }
228:
229: }
|