1 | package dmg.util ; |
2 | |
3 | import java.util.* ; |
4 | import java.io.Serializable; |
5 | |
6 | /** |
7 | * TODO: Write JavaDoc! |
8 | */ |
9 | public class Args |
10 | implements Serializable, Cloneable |
11 | { |
12 | |
13 | static final long serialVersionUID = -8950082352156787965L; |
14 | private final Map<String, String> _optHash = CollectionFactory.newHashMap(); |
15 | private final List<String> _optv = new Vector<String>(); |
16 | private final List<String> _argv = new Vector<String>(); |
17 | private String _oneChar; |
18 | public Args( String args ) { |
19 | |
20 | new Scanner(args).scan(); |
21 | } |
22 | public Args( String [] args ) { |
23 | |
24 | StringBuilder sb = new StringBuilder() ; |
25 | for( int i = 0 ; i < args.length ; i++ ) |
26 | sb.append(args[i]).append(" "); |
27 | |
28 | new Scanner(sb).scan(); |
29 | } |
30 | Args( Args in ){ |
31 | _argv.addAll(in._argv); |
32 | _optv.addAll( in._optv ); |
33 | _optHash.putAll( in._optHash ); |
34 | _oneChar = in._oneChar; |
35 | } |
36 | public boolean isOneCharOption( char c ){ |
37 | return _oneChar.indexOf(c) > -1 ; |
38 | } |
39 | public int argc(){ return _argv.size() ; } |
40 | public int optc(){ return _optv.size() ; } |
41 | public String getOpt( String optName ){ return _optHash.get( optName ) ; } |
42 | public String argv( int i ){ |
43 | |
44 | String value = null; |
45 | if( i < _argv.size() ) { |
46 | value = _argv.get(i) ; |
47 | } |
48 | |
49 | return value; |
50 | |
51 | } |
52 | public String optv( int i ){ |
53 | String value = null; |
54 | |
55 | if( i < _optv.size() ){ |
56 | value = _optv.get(i) ; |
57 | } |
58 | |
59 | return value; |
60 | } |
61 | public void shift(){ |
62 | |
63 | if( !_argv.isEmpty() ) { |
64 | _argv.remove(0); |
65 | } |
66 | |
67 | } |
68 | |
69 | public Map<String, String> options() |
70 | { |
71 | return Collections.unmodifiableMap(_optHash); |
72 | } |
73 | |
74 | public Object clone(){ return new Args( this ) ; } |
75 | |
76 | public boolean equals(Object other) |
77 | { |
78 | if (other == this) { |
79 | return true; |
80 | } |
81 | if (!(other instanceof Args)) { |
82 | return false; |
83 | } |
84 | |
85 | Args args = (Args) other; |
86 | return args._optHash.equals(_optHash) |
87 | && args._optv.equals(_optv) |
88 | && args._argv.equals(_argv) |
89 | && args._oneChar.equals(_oneChar); |
90 | } |
91 | |
92 | public int hashCode() |
93 | { |
94 | return _optHash.hashCode() ^ _argv.hashCode(); |
95 | } |
96 | |
97 | private void quote(String in, StringBuilder out) |
98 | { |
99 | for (int i = 0; i < in.length(); i++) { |
100 | switch (in.charAt(i)) { |
101 | case '\\': |
102 | out.append("\\\\"); |
103 | break; |
104 | case '"': |
105 | out.append("\\\""); |
106 | break; |
107 | case '\'': |
108 | out.append("\\'"); |
109 | break; |
110 | case '=': |
111 | out.append("\\="); |
112 | break; |
113 | case ' ': |
114 | out.append("\\ "); |
115 | break; |
116 | default: |
117 | out.append(in.charAt(i)); |
118 | break; |
119 | } |
120 | } |
121 | } |
122 | |
123 | public String toString() |
124 | { |
125 | StringBuilder s = new StringBuilder(); |
126 | |
127 | for (Map.Entry<String,String> e: _optHash.entrySet()) { |
128 | String key = e.getKey(); |
129 | String value = e.getValue(); |
130 | |
131 | s.append('-'); |
132 | quote(key, s); |
133 | if (value.length() > 0) { |
134 | s.append('='); |
135 | quote(value, s); |
136 | } |
137 | s.append(' '); |
138 | } |
139 | |
140 | if (s.length() > 0) { |
141 | s.append("-- "); |
142 | } |
143 | |
144 | for (int i = 0; i < argc(); i++) { |
145 | quote(argv(i), s); |
146 | s.append(' '); |
147 | } |
148 | |
149 | return s.toString(); |
150 | } |
151 | |
152 | public String getInfo(){ |
153 | StringBuilder sb = new StringBuilder() ; |
154 | |
155 | sb.append( "Positional :\n" ); |
156 | for( int i= 0 ; i < _argv.size() ; i++ ){ |
157 | sb.append(i).append(" -> ").append(_argv.get(i)).append("\n") ; |
158 | } |
159 | sb.append( "Options :\n" ); |
160 | for( int i= 0 ; i < _optv.size() ; i++ ){ |
161 | String key = _optv.get(i) ; |
162 | String val = _optHash.get(key) ; |
163 | sb.append(key) ; |
164 | if( val != null ) |
165 | sb.append( " -> " ).append(val) ; |
166 | sb.append("\n") ; |
167 | } |
168 | |
169 | return sb.toString() ; |
170 | } |
171 | |
172 | public static void main( String [] args )throws Exception { |
173 | if( args.length < 1 ){ |
174 | System.err.println( "Usage : ... <parseString>" ) ; |
175 | System.exit(4); |
176 | } |
177 | Args lineArgs = null ; |
178 | if( args.length == 1 ) |
179 | lineArgs = new Args( args[0] ) ; |
180 | else |
181 | lineArgs = new Args( args ); |
182 | System.out.print( lineArgs.getInfo() ) ; |
183 | System.out.println( "pvr="+lineArgs.getOpt( "pvr" ) ) ; |
184 | |
185 | } |
186 | |
187 | /** |
188 | * Scanner for parsing strings of white space separated |
189 | * words. Characters may be escaped with a backslash and character |
190 | * sequences may be quoted. Options begin with an unescaped dash. |
191 | |
192 | * A -- signals the end of options and disables further option |
193 | * processing. Any arguments after the -- are treated as regular |
194 | * arguments. |
195 | */ |
196 | class Scanner |
197 | { |
198 | private final CharSequence _line; |
199 | private int _position; |
200 | |
201 | public Scanner(CharSequence line) |
202 | { |
203 | _line = line; |
204 | } |
205 | |
206 | private char peek() |
207 | { |
208 | return isEof() ? (char) 0 : _line.charAt(_position); |
209 | } |
210 | |
211 | private char readChar() |
212 | { |
213 | char c = peek(); |
214 | _position++; |
215 | return c; |
216 | } |
217 | |
218 | private boolean isEof() |
219 | { |
220 | return (_position >= _line.length()); |
221 | } |
222 | |
223 | private boolean isWhitespace() |
224 | { |
225 | return Character.isWhitespace(peek()); |
226 | } |
227 | |
228 | private void scanWhitespace() |
229 | { |
230 | while (isWhitespace()) { |
231 | readChar(); |
232 | } |
233 | } |
234 | |
235 | public void scan() |
236 | { |
237 | StringBuilder oneChar = new StringBuilder(); |
238 | boolean isAtEndOfOptions = false; |
239 | scanWhitespace(); |
240 | while (!isEof()) { |
241 | if (!isAtEndOfOptions && peek() == '-') { |
242 | readChar(); |
243 | String key = scanKey(); |
244 | if (key.isEmpty()) { |
245 | _argv.add("-"); |
246 | } else if (peek() == '=') { |
247 | readChar(); |
248 | _optv.add(key) ; |
249 | _optHash.put(key, scanWord()); |
250 | } else if (key.equals("-")) { |
251 | isAtEndOfOptions = true; |
252 | } else { |
253 | _optv.add(key) ; |
254 | _optHash.put(key, ""); |
255 | oneChar.append(key); |
256 | } |
257 | } else { |
258 | _argv.add(scanWord()); |
259 | } |
260 | scanWhitespace(); |
261 | } |
262 | _oneChar = oneChar.toString(); |
263 | } |
264 | |
265 | /** |
266 | * Scans an option key. An option key is terminated by an |
267 | * unescaped white space character or - for non-empty keys - |
268 | * by an unescaped equal sign. |
269 | */ |
270 | private String scanKey() |
271 | { |
272 | StringBuilder key = new StringBuilder(); |
273 | do { |
274 | scanWordElement(key); |
275 | } while (!isEof() && !isWhitespace() && peek() != '='); |
276 | return key.toString(); |
277 | } |
278 | |
279 | /** |
280 | * Scans the next word. A word is a sequence of non-white |
281 | * space characters and escaped or quoted white space |
282 | * characters. The unescaped and unquoted word is returned. |
283 | */ |
284 | private String scanWord() |
285 | { |
286 | StringBuilder word = new StringBuilder(); |
287 | while (!isEof() && !isWhitespace()) { |
288 | scanWordElement(word); |
289 | } |
290 | return word.toString(); |
291 | } |
292 | |
293 | /** |
294 | * Scans the next element of a word. Elements of a word are |
295 | * non-white space characters, escaped characters and quoted |
296 | * strings. The unescaped and unquoted element is added to word. |
297 | */ |
298 | private void scanWordElement(StringBuilder word) |
299 | { |
300 | if (!isEof() && !isWhitespace()) { |
301 | switch (peek()) { |
302 | case '\'': |
303 | scanSingleQuotedString(word); |
304 | break; |
305 | case '"': |
306 | scanDoubleQuotedString(word); |
307 | break; |
308 | case '\\': |
309 | scanEscapedCharacter(word); |
310 | break; |
311 | default: |
312 | word.append(readChar()); |
313 | break; |
314 | } |
315 | } |
316 | } |
317 | |
318 | /** |
319 | * Scans a single quoted string. Escaped characters are not |
320 | * recognized. The unquoted string is added to word. |
321 | */ |
322 | private void scanSingleQuotedString(StringBuilder word) |
323 | { |
324 | if (readChar() != '\'') { |
325 | throw new IllegalStateException("Parse failure"); |
326 | } |
327 | |
328 | while (!isEof()) { |
329 | char c = readChar(); |
330 | switch (c) { |
331 | case '\'': |
332 | return; |
333 | default: |
334 | word.append(c); |
335 | break; |
336 | } |
337 | } |
338 | } |
339 | |
340 | /** |
341 | * Scans a double quoted string. Escaped characters are |
342 | * recognized. The unquoted and unescaped string is added to |
343 | * word. |
344 | */ |
345 | private void scanDoubleQuotedString(StringBuilder word) |
346 | { |
347 | if (readChar() != '"') { |
348 | throw new IllegalStateException("Parse failure"); |
349 | } |
350 | |
351 | while (!isEof()) { |
352 | switch (peek()) { |
353 | case '\\': |
354 | scanEscapedCharacter(word); |
355 | break; |
356 | case '"': |
357 | readChar(); |
358 | return; |
359 | default: |
360 | word.append(readChar()); |
361 | break; |
362 | } |
363 | } |
364 | } |
365 | |
366 | /** |
367 | * Scans a backslash escaped character. The escaped character |
368 | * without the escape symbol is added to word. |
369 | */ |
370 | private void scanEscapedCharacter(StringBuilder word) |
371 | { |
372 | if (readChar() != '\\') { |
373 | throw new IllegalStateException("Parse failure"); |
374 | } |
375 | |
376 | if (!isEof()) { |
377 | word.append(readChar()); |
378 | } |
379 | } |
380 | } |
381 | } |