Source code: org/pqt/cloptions/OptionProcessor.java
1 //AutoRIB
2 // Copyright © 1998 - 2002, P W Quint
3 //
4 // Contact: autorib00@aol.com
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public
8 // License as published by the Free Software Foundation; either
9 // version 2 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20 package org.pqt.cloptions;
21
22 import java.util.*;
23
24
25
26 /**
27
28 a class for handling command line options and parameters. The class contains one
29
30 central routine, called process, which processes command line arguments. The class
31
32 must be initialized using an array of items which are subclasses of the class Option.
33
34 As the command line arguments are processed routines in these option classes are
35
36 called, and when the process is finished it is the values in these classes which
37
38 represent the command line options that have been set. The OptionProcessor class
39
40 also contains various utility routines for reading items from the command line.
41
42 Options can have either a long form (a word made up of alphabetic characters and
43
44 '_') or a short form (a single letter). On the command line a single dash ('-')
45
46 may be followed by one or more options in short form (providing there are no spaces
47
48 in between the options), while a double dash ('--') is followed by a (single) option
49
50 in long form.
51
52 */
53
54 public class OptionProcessor extends Object
55
56 {
57
58
59
60 Option[] clOptionArray = null;
61
62 StringBuffer argString = null;
63
64 int argPosn = -1;
65
66 int savedArgPosn = -1;
67
68 boolean matchCaseShort = true;
69
70 boolean afterDash = false;
71
72 /** contains any filenames (words not preceeded by a '-') read on the command
73
74 line, in the order in which they appear */
75
76 public Vector fileNames = new Vector(3,3);
77
78 /** the number of options read*/
79
80 public int optionsRead = 0;
81
82
83
84 /** initialize using a given array of Option(s)
85
86 @param optionArray the array of Options to use*/
87
88 public OptionProcessor(Option[] optionArray)
89
90 {
91
92 this.clOptionArray = optionArray;
93
94 }
95
96
97
98 /**process the given array of string arguments
99
100 @param argArray an array of String arguments - such as those passed to the
101
102 'main' routine
103
104 @return the number of parameters read
105
106 @exception OptionException will be thrown if some error occurs during
107
108 processing of an option or if an unrecognized option is found*/
109
110 public int process(String[] argArray) throws OptionException
111
112 {
113
114 argString = new StringBuffer(255);
115
116 optionsRead = 0;
117
118 if (argArray.length == 0) return 0;
119
120 //first convert the argArray into a single long string
121
122 for (int i = 0; i < argArray.length; i++)
123
124 {
125
126 //if the current arg string contains space, put quotes around it
127
128 boolean containsSpace = false;
129
130 for (int j = 0; j < argArray[i].length(); j++)
131
132 if (argArray[i].charAt(j) <= ' ')
133
134 {
135
136 containsSpace = true;
137
138 break;
139
140 }
141
142 if (containsSpace)
143
144 argString.append('"');
145
146 argString.append(argArray[i]);
147
148 if (containsSpace)
149
150 argString.append('"');
151
152 argString.append(' ');
153
154 }
155
156 //remove the extra space at the end
157
158 argString.setLength(argString.length() - 1);
159
160 argPosn = 0;
161
162 //afterDash = true when we are processing multiple short options after a
163
164 //single -
165
166 afterDash = false;
167
168 while (argPosn < argString.length())
169
170 //the main loop
171
172 {
173
174 int c = peekCharNoSpace();
175
176 //check if it is an option, else read in a file name (see below)
177
178 if (c == '-')
179
180 {
181
182 String optionName = null;
183
184 int optionChar = 0;
185
186 boolean matched = false;
187
188 c = getCharNoSpace(); //read the initial -
189
190 c = peekChar();
191
192 //now either process a single long option or multiple short ones
193
194 if (c == '-')
195
196 {
197
198 if (afterDash)
199
200 throw new OptionException("Misplaced '-' on Command Line");
201
202 afterDash = false;
203
204 optionName = getLongName();
205
206 for (int i = 0; i < clOptionArray.length; i++)
207
208 {
209
210 //check each option in turn for a match
211
212 matched = longCheck(clOptionArray[i], optionName);
213
214 if (matched)
215
216 {
217
218 optionsRead++;
219
220 //let the option read any extra parameters
221
222 clOptionArray[i].read(this);
223
224 break;
225
226 }
227
228 }
229
230 if (!matched)
231
232 throw new OptionException("Unrecognized Command Line Option --"
233
234 + optionName);
235
236 }
237
238 else
239
240 { //multiple short options
241
242 afterDash = true;
243
244 optionChar = getChar(); //returns -1 if end of line
245
246 while (afterDash && (optionChar != ' ') &&
247
248 (optionChar >= 0))
249
250 {
251
252 for (int i = 0; i < clOptionArray.length; i++)
253
254 {
255
256 //check the option array
257
258 matched = shortCheck(clOptionArray[i], optionChar);
259
260 if (matched)
261
262 {
263
264 optionsRead++;
265
266 //read in any extra params
267
268 clOptionArray[i].read(this);
269
270 break;
271
272 }
273
274 }
275
276 if (!matched)
277
278 throw new OptionException("Unrecognized Command Line Option -"
279
280 + (char) optionChar);
281
282 if (afterDash)
283
284 optionChar = getChar();
285
286 if ((optionChar < 0) || (optionChar == ' '))
287
288 afterDash = false;
289
290 }
291
292 }
293
294 }
295
296 else
297
298 {
299
300 //read in a filename
301
302 String fileName = getStringValue();
303
304 fileNames.addElement(fileName);
305
306 }
307
308 }
309
310 return optionsRead + fileNames.size();
311
312 }
313
314
315
316 /** sets whether short options are case sensitive (long options never are)
317
318 @param flag true if short options are case sensitive, false otherwise
319
320 */
321
322
323
324 public void matchCase(boolean flag)
325
326 {
327
328 matchCaseShort = flag;
329
330 }
331
332
333
334 /**
335
336 get a single character from the command line options string
337
338 @return the character read, or -1 if the end of the string is reached
339
340 */
341
342 public int getChar()
343
344 {
345
346 int rval = -1;
347
348 if (argString != null)
349
350 {
351
352 if (argPosn < argString.length())
353
354 rval = argString.charAt(argPosn);
355
356 argPosn++;
357
358 }
359
360 return rval;
361
362 }
363
364
365
366
367
368 /**
369
370 get a single character from the command line options string, ignoring any
371
372 white space
373
374 @return the character read, or -1 if the end of the string is reached
375
376 */
377
378 public int getCharNoSpace()
379
380 {
381
382 int rval = getChar();
383
384 while ((rval >= 0) && (rval <= ' '))
385
386 {
387
388 rval = getChar();
389
390 afterDash = false;
391
392 }
393
394 return rval;
395
396 }
397
398
399
400 /**
401
402 get a single character from the command line options string, without changing
403
404 the current position in the string (ie a subsequent getChar will return the same
405
406 character)
407
408 @return the character read, or -1 if the end of the string is reached
409
410 */
411
412 public int peekChar()
413
414 {
415
416 int oldArgPosn = argPosn;
417
418 int rval = getChar();
419
420 argPosn = oldArgPosn;
421
422 return rval;
423
424 }
425
426 /**
427
428 get a single character from the command line options string, without changing
429
430 the current position in the string (ie a subsequent getChar will return the same
431
432 character) ignore any white space
433
434 @return the character read, or -1 if the end of the string is reached
435
436 */
437
438 public int peekCharNoSpace()
439
440 {
441
442 int oldArgPosn = argPosn;
443
444 int rval = getCharNoSpace();
445
446 argPosn = oldArgPosn;
447
448 return rval;
449
450 }
451
452
453
454 //is c a valid character for a long option name
455
456 private boolean isIdChar(int c)
457
458 {
459
460 return (((c >= 'a') && (c <= 'z')) ||
461
462 ((c >= 'A') && (c <= 'Z')) ||
463
464 (c == '_'));
465
466 }
467
468
469
470 /**
471
472 read a long option name from the command line string. A long option name consists
473
474 of alphabetic characters and '_' with no spaces
475
476 @return the name read */
477
478 public String getLongName()
479
480 {
481
482 StringBuffer rval = new StringBuffer(15);
483
484 int c = getChar();
485
486 while (c == '-')
487
488 c = getChar();
489
490 while (isIdChar(c))
491
492 {
493
494 rval.append((char) c);
495
496 c = getChar();
497
498 }
499
500 if (c >= 0) argPosn--;
501
502 return rval.toString();
503
504 }
505
506
507
508 /**
509
510 read in a string (enclosed in double quotes) from the command line string
511
512 @return the string read, or null if there is an error in the string
513
514 */
515
516
517
518 protected String getString()
519
520 {
521
522 int c = getCharNoSpace();
523
524 String rval = null;
525
526 if (c == '"')
527
528 rval = getString_();
529
530 return rval;
531
532 }
533
534
535
536 //get a string where the initial " has already been read
537
538 protected String getString_()
539
540 {
541
542 StringBuffer rval = new StringBuffer(25);
543
544 int c = getChar();
545
546 while ((c >= 0) && (c != '"'))
547
548 {
549
550 rval.append((char) c);
551
552 c = getChar();
553
554 }
555
556 if (c < 0) ; //throw an unclosed string error
557
558 return rval.toString();
559
560 }
561
562
563
564 /**
565
566 read a string, either enclosed in quotes (in which case the string can
567
568 contain spaces) or not enclosed in quotes (in which case spaces are not
569
570 allowed). Initial whitespace is skipped
571
572 @return the string read, or null if an error occurs*/
573
574 public String getStringValue()
575
576 {
577
578 afterDash = false;
579
580 String rval = null;
581
582 int c = getCharNoSpace();
583
584 if (c == '"')
585
586 rval = getString_();
587
588 else
589
590 {
591
592 StringBuffer s = new StringBuffer(25);
593
594 while ((c != ' ') && (c >= 0))
595
596 {
597
598 s.append((char) c);
599
600 c = getChar();
601
602 }
603
604 if (c >= 0) argPosn--;
605
606 rval = s.toString();
607
608 }
609
610 return rval;
611
612 }
613
614
615
616 /**
617
618 return true if the character c could be part of a floating point number#
619
620 */
621
622 public boolean isNumChar(int c)
623
624 {
625
626 return (((c >= '0') && (c <= '9')) ||
627
628 (c == '+') || (c == '-') || (c == '.') ||
629
630 (c == 'E') || (c == 'e'));
631
632 }
633
634
635
636 /**
637
638 read a numeric value (which can be in full floating point form) from the
639
640 command line string
641
642 @return the value read, or NaN if an error has occurred */
643
644 public float getNumValue()
645
646 {
647
648 float rval;
649
650 StringBuffer s = new StringBuffer(10);
651
652 int c = getCharNoSpace();
653
654 while (isNumChar(c))
655
656 {
657
658 s.append((char) c);
659
660 c = getChar();
661
662 }
663
664 if (c >= 0) argPosn--;
665
666 try
667
668 {
669
670 rval = Float.valueOf(s.toString()).floatValue();
671
672 }
673
674 catch (NumberFormatException e)
675
676 {
677
678 rval = Float.NaN;
679
680 }
681
682 return rval;
683
684 }
685
686
687
688 /**
689
690 if the next character in the command string is a '+' or '-' read it
691
692 @return true if '+' is read, or no character is read, false otherwise
693
694 */
695
696
697
698 public boolean getBoolean()
699
700 {
701
702 boolean rval = true;
703
704 int c = getChar();
705
706 if (c == '-')
707
708 rval = false;
709
710 else if (c == '+')
711
712 rval = true;
713
714 else if (c >= 0) argPosn--;
715
716 return rval;
717
718 }
719
720
721
722 /**
723
724 check a character against an option
725
726 @param op the Option to check
727
728 @param the character to check
729
730 @return true if the character is one of those used as a short name for
731
732 the given option
733
734 */
735
736 public boolean shortCheck(Option op, int c)
737
738 {
739
740 boolean rval = false;
741
742 if ((op != null) && (c > 0))
743
744 {
745
746 for (int i = 0; (i < op.shortNames.length()) && !rval; i++)
747
748 {
749
750 char opc = op.shortNames.charAt(i);
751
752 if (matchCaseShort)
753
754 rval = (c == opc);
755
756 else
757
758 rval = (Character.toLowerCase((char) c) ==
759
760 Character.toLowerCase(opc));
761
762 }
763
764 }
765
766 return rval;
767
768 }
769
770 /**
771
772 check a string against an option
773
774 @param op the option to check
775
776 @param the long name to check
777
778 @return true if the string is one of the long names used by this option
779
780 */
781
782 public boolean longCheck(Option op, String s)
783
784 {
785
786 int lnpos = 0;
787
788 boolean rval = false;
789
790 if ((op != null) && (s != null) && (s.length() > 0))
791
792 while ((lnpos < op.longNames.length()) && !rval)
793
794 {
795
796 int start = lnpos;
797
798 while ((lnpos < op.longNames.length()) &&
799
800 (isIdChar(op.longNames.charAt(lnpos))))
801
802 lnpos++;
803
804 rval = (op.longNames.substring(start, lnpos).equalsIgnoreCase(s));
805
806 while ((lnpos < op.longNames.length()) &&
807
808 (!isIdChar(op.longNames.charAt(lnpos))))
809
810 lnpos++;
811
812 }
813
814 return rval;
815
816 }
817
818
819
820 /**
821
822 save the current position in the command string
823
824 */
825
826 public void savePosn()
827
828 {
829
830 savedArgPosn = argPosn;
831
832 }
833
834
835
836 /**
837
838 restore the current position in the command string, as saved by savePosn
839
840 */
841
842
843
844 public void restorePosn()
845
846 {
847
848 argPosn = savedArgPosn;
849
850 }
851
852
853
854
855
856
857
858 }
859
860
861