Source code: jena/cmdline/CommandLine.java
1 /*
2 * (c) Copyright 2003, 2004, 2005 Hewlett-Packard Development Company, LP
3 * [See end of file]
4 */
5
6 package jena.cmdline ;
7
8 import java.io.* ;
9 import java.util.* ;
10
11 /**
12 * Command line argument processing based on a trigger model.
13 * An action is called whenever an argument is encountered. Example:
14 * <CODE>
15 * public static void main (String[] args)
16 * {
17 * CommandLine cl = new CommandLine() ;
18 * cl.add(false, "verbose")
19 * .add(true, "--file") ;
20 * cl.process(args) ;
21 *
22 * for ( Iterator iter = cl.args() ; iter.hasNext() ; )
23 * ...
24 * }
25 * </CODE>
26 * A gloabl hook is provided to inspect arguments just before the
27 * action. Tracing is enabled by setting this to a suitable function
28 * such as that provided by trace():
29 * <CODE>
30 * cl.setHook(cl.trace()) ;
31 * </CODE>
32 *
33 * <ul>
34 * <li>Neutral as to whether options have - or --</li>
35 * <li>Does not allow multiple single letter options
36 * to be concatenated.</li>
37 * <li>Options may be ended with - or --</li>
38 * </ul>
39 * @author Andy Seaborne
40 * @version $Id: CommandLine.java,v 1.4 2005/02/21 11:48:56 andy_seaborne Exp $
41 */
42
43
44 public class CommandLine
45 {
46 /* Extra processor called before the registered one when set.
47 * Used for tracing.
48 */
49 protected ArgHandler argHook = null ;
50 protected String usage = null ;
51 protected Map argMap = new HashMap() ;
52 protected Set argValue = new HashSet() ;
53 protected PrintStream out = System.err ;
54
55 // Arguments (flags and options) found
56 protected List args = new ArrayList() ;
57 // Rest of the items found on the command line
58 protected List items = new ArrayList() ;
59
60
61 /** Creates new CommandLine */
62 public CommandLine()
63 {
64 }
65
66 /** Set the global argument handler. Called on every valid argument.
67 * @param argHandler Handler
68 */
69 public void setHook(ArgHandler argHandler) { argHook = argHandler ; }
70
71 /** Set the output stream, or null for silent.
72 * Default value is System.err
73 */
74 public void setOutput(PrintStream out) { this.out = out ; }
75 public PrintStream getOutput() { return out ; }
76 public void setUsage(String usageMessage) { usage = usageMessage ; }
77
78 public List args() { return args ; }
79 public List items() { return items ; }
80
81
82 /** Process a set of command line arguments.
83 * @param argv The words of the command line.
84 * @throws IllegalArgumentException Throw when something is wrong (no value found, action fails).
85 */
86 public void process(String[] argv) throws java.lang.IllegalArgumentException
87 {
88 try {
89 int i = 0 ;
90 for ( ; i < argv.length ; i++ )
91 {
92 String argStr = argv[i] ;
93 if ( ! argStr.startsWith("-") || argStr.equals("--") || argStr.equals("-") )
94 break ;
95
96 argStr = ArgDecl.canonicalForm(argStr) ;
97 String val = null ;
98
99 if ( argMap.containsKey(argStr) )
100 {
101 Arg arg = new Arg(argStr) ;
102 ArgDecl argDecl = (ArgDecl)argMap.get(argStr) ;
103
104 if ( argDecl.takesValue() )
105 {
106 if ( i == (argv.length-1) )
107 throw new IllegalArgumentException("No value for argument: "+arg.getName()) ;
108 val = argv[++i] ;
109 arg.setValue(val) ;
110 }
111
112 // Global hook
113 if ( argHook != null )
114 argHook.action(argStr, val) ;
115
116 argDecl.trigger(arg) ;
117 args.add(arg) ;
118 }
119 else
120 // Not recognized
121 throw new IllegalArgumentException("Unknown argument: "+argStr) ;
122 }
123
124 // Remainder.
125 if ( i < argv.length )
126 {
127 if ( argv[i].equals("-") || argv[i].equals("--") )
128 i++ ;
129 for ( ; i < argv.length ; i++ )
130 items.add(argv[i]) ;
131 }
132 } catch (IllegalArgumentException ex)
133 {
134 if ( out != null )
135 {
136 if ( usage != null ) out.println(usage) ;
137 out.println(ex.getMessage()) ;
138 }
139 throw ex ;
140 }
141 }
142
143 /** Test whether an argument was seen.
144 */
145
146 public boolean contains(ArgDecl argDecl) { return getArg(argDecl) != null ; }
147
148 /** Test whether an argument was seen.
149 */
150
151 public boolean contains(String s) { return getArg(s) != null ; }
152
153
154 /** Get the argument associated with the argurment declaration.
155 * Actually retruns the LAST one seen
156 * @param argDecl Argument declaration to find
157 * @return Last argument that matched.
158 */
159
160 public Arg getArg(ArgDecl argDecl)
161 {
162 Arg arg = null ;
163 for ( Iterator iter = args.iterator() ; iter.hasNext() ; )
164 {
165 Arg a = (Arg)iter.next() ;
166 if ( argDecl.matches(a) )
167 arg = a ;
168 }
169 return arg ;
170 }
171
172 public Arg getArg(String s)
173 {
174 s = ArgDecl.canonicalForm(s) ;
175 Arg arg = null ;
176 for ( Iterator iter = args.iterator() ; iter.hasNext() ; )
177 {
178 Arg a = (Arg)iter.next() ;
179 if ( a.getName().equals(s) )
180 arg = a ;
181 }
182 return arg ;
183 }
184
185
186 /** Add an argument to those to be accepted on the command line
187 * @param argName Name
188 * @return The CommandLine processor object
189 */
190
191 public CommandLine add(String argName, boolean hasValue)
192 {
193 return add(new ArgDecl(hasValue, argName)) ;
194 }
195
196 /** Add an argument object
197 * @param arg Argument to add
198 * @return The CommandLine processor object
199 */
200
201 public CommandLine add(ArgDecl arg)
202 {
203 for ( Iterator iter = arg.getNames() ; iter.hasNext() ; )
204 argMap.put(iter.next(), arg) ;
205 return this ;
206 }
207
208 public ArgHandler trace()
209 {
210 final PrintStream _out = out ;
211 return new ArgHandler()
212 {
213 public void action (String arg, String val) //throws java.lang.IllegalArgumentException
214 {
215 if ( _out != null )
216 _out.println("Seen: "+arg+((val!=null)?" = "+val:"")) ;
217 }
218 } ;
219 }
220
221
222 public static void main(String[] argv)
223 {
224 CommandLine cl = new CommandLine() ;
225 cl.setOutput(System.out) ;
226 ArgDecl argA = new ArgDecl(false, "-a") ;
227 cl.add(argA) ;
228 cl.add("-b", false) ;
229 cl.add("-file", true) ;
230
231 ArgDecl argFile = new ArgDecl(false, "-v", "--verbose") ;
232 argFile.addHook(cl.trace()) ;
233 cl.add(argFile) ;
234
235 //cl.setHook(cl.trace()) ;
236
237 String[] a = new String[]{"-a", "--b", "--a", "--file", "value1", "--file", "value2", "--v", "rest"} ;
238 try {
239 cl.process(a) ;
240 System.out.println("PROCESSED") ;
241
242 // Checks
243 if ( cl.getArg("file") == null )
244 System.out.println("No --file seen") ;
245 else
246 System.out.println("--file => "+cl.getArg("file").getValue()) ;
247
248 // Checks
249 if ( cl.getArg(argA) == null )
250 System.out.println("No --a seen") ;
251 else
252 System.out.println("--a seen "+cl.getArg(argFile).getValue()) ;
253
254 System.out.println("DUMP") ;
255 for ( Iterator iter = cl.args().iterator() ; iter.hasNext() ; )
256 {
257 Arg arg = (Arg)iter.next() ;
258 String v = (arg.hasValue()? (" = "+arg.getValue()) : "") ;
259 System.out.println("Arg: "+arg.getName()+v) ;
260 }
261 for ( Iterator iter = cl.items().iterator() ; iter.hasNext() ; )
262 System.out.println("Item: "+(String)iter.next()) ;
263
264 } catch (IllegalArgumentException ex)
265 {
266 System.err.println("Illegal argument: "+ex.getMessage() ) ;
267 }
268 }
269 }
270
271 /*
272 * (c) Copyright 2003, 2004, 2005 Hewlett-Packard Development Company, LP
273 * All rights reserved.
274 *
275 * Redistribution and use in source and binary forms, with or without
276 * modification, are permitted provided that the following conditions
277 * are met:
278 * 1. Redistributions of source code must retain the above copyright
279 * notice, this list of conditions and the following disclaimer.
280 * 2. Redistributions in binary form must reproduce the above copyright
281 * notice, this list of conditions and the following disclaimer in the
282 * documentation and/or other materials provided with the distribution.
283 * 3. The name of the author may not be used to endorse or promote products
284 * derived from this software without specific prior written permission.
285 *
286 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
287 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
288 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
289 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
290 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
291 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
292 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
293 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
294 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
295 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
296 */