001: /*
002: * (c) Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
003: * [See end of file]
004: */
005:
006: package jena.cmdline;
007:
008: import java.io.*;
009: import java.util.*;
010:
011: /**
012: * Command line argument processing based on a trigger model.
013: * An action is called whenever an argument is encountered. Example:
014: * <CODE>
015: * public static void main (String[] args)
016: * {
017: * CommandLine cl = new CommandLine() ;
018: * cl.add(false, "verbose")
019: * .add(true, "--file") ;
020: * cl.process(args) ;
021: *
022: * for ( Iterator iter = cl.args() ; iter.hasNext() ; )
023: * ...
024: * }
025: * </CODE>
026: * A gloabl hook is provided to inspect arguments just before the
027: * action. Tracing is enabled by setting this to a suitable function
028: * such as that provided by trace():
029: * <CODE>
030: * cl.setHook(cl.trace()) ;
031: * </CODE>
032: *
033: * <ul>
034: * <li>Neutral as to whether options have - or --</li>
035: * <li>Does not allow multiple single letter options to be concatenated.</li>
036: * <li>Options may be ended with - or --</li>
037: * <li>Arguments with values can use "="</li>
038: * </ul>
039: * @author Andy Seaborne
040: * @version $Id: CommandLine.java,v 1.14 2008/01/02 12:09:55 andy_seaborne Exp $
041: */
042:
043: public class CommandLine {
044: /* Extra processor called before the registered one when set.
045: * Used for tracing.
046: */
047: protected ArgHandler argHook = null;
048: protected String usage = null;
049: protected Map argMap = new HashMap(); // Map from string name to ArgDecl
050: protected Map args = new HashMap(); // Map from string name to Arg
051: //protected boolean ignoreUnknown = false ;
052:
053: // Rest of the items found on the command line
054: String indirectionMarker = "@";
055: protected boolean allowItemIndirect = false; // Allow @ to mean contents of file
056: boolean ignoreIndirectionMarker = false; // Allow comand line items to have leading @ but strip it.
057: protected List items = new ArrayList();
058:
059: /** Creates new CommandLine */
060: public CommandLine() {
061: }
062:
063: /** Set the global argument handler. Called on every valid argument.
064: * @param argHandler Handler
065: */
066: public void setHook(ArgHandler argHandler) {
067: argHook = argHandler;
068: }
069:
070: public void setUsage(String usageMessage) {
071: usage = usageMessage;
072: }
073:
074: public boolean hasArgs() {
075: return args.size() > 0;
076: }
077:
078: public boolean hasItems() {
079: return items.size() > 0;
080: }
081:
082: public Iterator args() {
083: return args.values().iterator();
084: }
085:
086: // public Map args() { return args ; }
087: // public List items() { return items ; }
088:
089: public int numArgs() {
090: return args.size();
091: }
092:
093: public int numItems() {
094: return items.size();
095: }
096:
097: public void pushItem(String s) {
098: items.add(s);
099: }
100:
101: public boolean isIndirectItem(int i) {
102: return allowItemIndirect
103: && ((String) items.get(i))
104: .startsWith(indirectionMarker);
105: }
106:
107: public String getItem(int i) {
108: return getItem(i, allowItemIndirect);
109: }
110:
111: public String getItem(int i, boolean withIndirect) {
112: if (i < 0 || i >= items.size())
113: return null;
114:
115: String item = (String) items.get(i);
116:
117: if (withIndirect && item.startsWith(indirectionMarker)) {
118: item = item.substring(1);
119: try {
120: item = CmdLineUtils.readWholeFileAsUTF8(item);
121: } catch (Exception ex) {
122: throw new IllegalArgumentException("Failed to read '"
123: + item + "': " + ex.getMessage());
124: }
125: }
126: return item;
127: }
128:
129: /** Process a set of command line arguments.
130: * @param argv The words of the command line.
131: * @throws IllegalArgumentException Throw when something is wrong (no value found, action fails).
132: */
133: public void process(String[] argv)
134: throws java.lang.IllegalArgumentException {
135: List argList = new ArrayList();
136: argList.addAll(Arrays.asList(argv));
137:
138: int i = 0;
139: for (; i < argList.size(); i++) {
140: String argStr = (String) argList.get(i);
141: if (endProcessing(argStr))
142: break;
143:
144: if (ignoreArgument(argStr))
145: continue;
146:
147: // If the flag has a "=" or :, it is long form --arg=value.
148: // Split and insert the arg
149: int j1 = argStr.indexOf('=');
150: int j2 = argStr.indexOf(':');
151: int j = Integer.MAX_VALUE;
152:
153: if (j1 > 0 && j1 < j)
154: j = j1;
155: if (j2 > 0 && j2 < j)
156: j = j2;
157:
158: if (j != Integer.MAX_VALUE) {
159: String a2 = argStr.substring(j + 1);
160: argList.add(i + 1, a2);
161: argStr = argStr.substring(0, j);
162: }
163:
164: argStr = ArgDecl.canonicalForm(argStr);
165: String val = null;
166:
167: if (argMap.containsKey(argStr)) {
168: if (!args.containsKey(argStr))
169: args.put(argStr, new Arg(argStr));
170:
171: Arg arg = (Arg) args.get(argStr);
172: ArgDecl argDecl = (ArgDecl) argMap.get(argStr);
173:
174: if (argDecl.takesValue()) {
175: if (i == (argList.size() - 1))
176: throw new IllegalArgumentException(
177: "No value for argument: "
178: + arg.getName());
179: i++;
180: val = (String) argList.get(i);
181: arg.setValue(val);
182: arg.addValue(val);
183: }
184:
185: // Global hook
186: if (argHook != null)
187: argHook.action(argStr, val);
188:
189: argDecl.trigger(arg);
190: } else
191: handleUnrecognizedArg((String) argList.get(i));
192: // if ( ! getIgnoreUnknown() )
193: // // Not recognized
194: // throw new IllegalArgumentException("Unknown argument: "+argStr) ;
195: }
196:
197: // Remainder.
198: if (i < argList.size()) {
199: if (argList.get(i).equals("-")
200: || argList.get(i).equals("--"))
201: i++;
202: for (; i < argList.size(); i++) {
203: String item = (String) argList.get(i);
204: items.add(item);
205: }
206: }
207: }
208:
209: /** Hook to test whether this argument should be processed further
210: */
211: public boolean ignoreArgument(String argStr) {
212: return false;
213: }
214:
215: /** Answer true if this argument terminates argument processing for the rest
216: * of the command line. Default is to stop just before the first arg that
217: * does not start with "-", or is "-" or "--".
218: */
219: public boolean endProcessing(String argStr) {
220: return !argStr.startsWith("-") || argStr.equals("--")
221: || argStr.equals("-");
222: }
223:
224: /**
225: * Handle an unrecognised argument; default is to throw an exception
226: * @param argStr The string image of the unrecognised argument
227: */
228: public void handleUnrecognizedArg(String argStr) {
229: throw new IllegalArgumentException("Unknown argument: "
230: + argStr);
231: }
232:
233: /** Test whether an argument was seen.
234: */
235:
236: public boolean contains(ArgDecl argDecl) {
237: return getArg(argDecl) != null;
238: }
239:
240: /** Test whether an argument was seen.
241: */
242:
243: public boolean contains(String s) {
244: return getArg(s) != null;
245: }
246:
247: /** Test whether the command line had a particular argument
248: *
249: * @param argName
250: * @return
251: */
252: public boolean hasArg(String argName) {
253: return getArg(argName) != null;
254: }
255:
256: /** Test whether the command line had a particular argument
257: *
258: * @param argDecl
259: * @return
260: */
261:
262: public boolean hasArg(ArgDecl argDecl) {
263: return getArg(argDecl) != null;
264: }
265:
266: /** Get the argument associated with the argument declaration.
267: * Actually returns the LAST one seen
268: * @param argDecl Argument declaration to find
269: * @return Last argument that matched.
270: */
271:
272: public Arg getArg(ArgDecl argDecl) {
273: Arg arg = null;
274: for (Iterator iter = args.values().iterator(); iter.hasNext();) {
275: Arg a = (Arg) iter.next();
276: if (argDecl.matches(a))
277: arg = a;
278: }
279: return arg;
280: }
281:
282: /** Get the argument associated with the arguement name.
283: * Actually returns the LAST one seen
284: * @param argDecl Argument declaration to find
285: * @return Last argument that matched.
286: */
287:
288: public Arg getArg(String s) {
289: s = ArgDecl.canonicalForm(s);
290: return (Arg) args.get(s);
291: }
292:
293: /**
294: * Returns the value (a string) for an argument with a value -
295: * returns null for no argument and no value.
296: * @param argDecl
297: * @return String
298: */
299: public String getValue(ArgDecl argDecl) {
300: Arg arg = getArg(argDecl);
301: if (arg == null)
302: return null;
303: if (arg.hasValue())
304: return arg.getValue();
305: return null;
306: }
307:
308: /**
309: * Returns the value (a string) for an argument with a value -
310: * returns null for no argument and no value.
311: * @param argDecl
312: * @return String
313: */
314: public String getValue(String argName) {
315: Arg arg = getArg(argName);
316: if (arg == null)
317: return null;
318: return arg.getValue();
319: }
320:
321: /**
322: * Returns all the values (0 or more strings) for an argument.
323: * @param argDecl
324: * @return List
325: */
326: public List getValues(ArgDecl argDecl) {
327: Arg arg = getArg(argDecl);
328: if (arg == null)
329: return null;
330: return arg.getValues();
331: }
332:
333: /**
334: * Returns all the values (0 or more strings) for an argument.
335: * @param argDecl
336: * @return List
337: */
338: public List getValues(String argName) {
339: Arg arg = getArg(argName);
340: if (arg == null)
341: return null;
342: return arg.getValues();
343: }
344:
345: /** Add an argument to those to be accepted on the command line.
346: * @param argName Name
347: * @param hasValue True if the command takes a (string) value
348: * @return The CommandLine processor object
349: */
350:
351: public CommandLine add(String argName, boolean hasValue) {
352: return add(new ArgDecl(hasValue, argName));
353: }
354:
355: /** Add an argument to those to be accepted on the command line.
356: * Argument order reflects ArgDecl.
357: * @param hasValue True if the command takes a (string) value
358: * @param argName Name
359: * @return The CommandLine processor object
360: */
361:
362: public CommandLine add(boolean hasValue, String argName) {
363: return add(new ArgDecl(hasValue, argName));
364: }
365:
366: /** Add an argument object
367: * @param arg Argument to add
368: * @return The CommandLine processor object
369: */
370:
371: public CommandLine add(ArgDecl arg) {
372: for (Iterator iter = arg.names(); iter.hasNext();)
373: argMap.put(iter.next(), arg);
374: return this ;
375: }
376:
377: // public boolean getIgnoreUnknown() { return ignoreUnknown ; }
378: // public void setIgnoreUnknown(boolean ign) { ignoreUnknown = ign ; }
379:
380: /**
381: * @return Returns whether items starting "@" have the value of named file.
382: */
383: public boolean allowItemIndirect() {
384: return allowItemIndirect;
385: }
386:
387: /**
388: * @param allowItemIndirect Set whether items starting "@" have the value of named file.
389:
390: */
391: public void setAllowItemIndirect(boolean allowItemIndirect) {
392: this .allowItemIndirect = allowItemIndirect;
393: }
394:
395: /**
396: * @return Returns the ignoreIndirectionMarker.
397: */
398: public boolean isIgnoreIndirectionMarker() {
399: return ignoreIndirectionMarker;
400: }
401:
402: /**
403: * @return Returns the indirectionMarker.
404: */
405: public String getIndirectionMarker() {
406: return indirectionMarker;
407: }
408:
409: /**
410: * @param indirectionMarker The indirectionMarker to set.
411: */
412: public void setIndirectionMarker(String indirectionMarker) {
413: this .indirectionMarker = indirectionMarker;
414: }
415:
416: /**
417: * @param ignoreIndirectionMarker The ignoreIndirectionMarker to set.
418: */
419: public void setIgnoreIndirectionMarker(
420: boolean ignoreIndirectionMarker) {
421: this .ignoreIndirectionMarker = ignoreIndirectionMarker;
422: }
423:
424: public ArgHandler trace() {
425: final PrintStream _out = System.err;
426: return new ArgHandler() {
427: public void action(String arg, String val) //throws java.lang.IllegalArgumentException
428: {
429: if (_out != null)
430: _out.println("Seen: " + arg
431: + ((val != null) ? " = " + val : ""));
432: }
433: };
434: }
435: }
436:
437: /*
438: * (c) Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
439: * All rights reserved.
440: *
441: * Redistribution and use in source and binary forms, with or without
442: * modification, are permitted provided that the following conditions
443: * are met:
444: * 1. Redistributions of source code must retain the above copyright
445: * notice, this list of conditions and the following disclaimer.
446: * 2. Redistributions in binary form must reproduce the above copyright
447: * notice, this list of conditions and the following disclaimer in the
448: * documentation and/or other materials provided with the distribution.
449: * 3. The name of the author may not be used to endorse or promote products
450: * derived from this software without specific prior written permission.
451: *
452: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
453: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
454: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
455: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
456: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
457: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
458: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
459: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
460: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
461: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
462: */
|