1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package jargs.gnu;
17
18 import java.io.Serializable;
19 import java.text.NumberFormat;
20 import java.text.ParseException;
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Locale;
25
26
27
28
29
30
31
32
33
34
35
36
37
38 public class CmdLineParser {
39
40
41 private List<String> remainingArgs;
42
43
44 private HashMap<Object, Option> options = new HashMap<>(10);
45
46
47 private HashMap<String, Object> values = new HashMap<>(10);
48
49
50
51
52 public abstract static class OptionException extends Exception {
53
54
55 private static final long serialVersionUID = 1L;
56
57
58
59
60
61
62
63 OptionException(String msg) {
64 super(msg);
65 }
66 }
67
68
69
70
71
72 public static class UnknownOptionException extends OptionException {
73
74
75 private static final long serialVersionUID = 1L;
76
77
78 private final String optionName;
79
80
81
82
83
84
85
86 UnknownOptionException(String optionName) {
87 this(optionName, "Unknown option '" + optionName + "'");
88 }
89
90
91
92
93
94
95
96
97
98 UnknownOptionException(String optionName, String msg) {
99 super(msg);
100 this.optionName = optionName;
101 }
102
103
104
105
106
107
108 public String getOptionName() {
109 return this.optionName;
110 }
111 }
112
113
114
115
116
117
118
119 public static class UnknownSuboptionException extends UnknownOptionException {
120
121
122 private static final long serialVersionUID = 1L;
123
124
125 private final char suboption;
126
127
128
129
130
131
132
133
134
135 UnknownSuboptionException(String option, char suboption) {
136 super(option, "Illegal option: '" + suboption + "' in '" + option + "'");
137 this.suboption = suboption;
138 }
139
140
141
142
143
144
145 public char getSuboption() {
146 return suboption;
147 }
148 }
149
150
151
152
153
154
155
156 public static class NotFlagException extends UnknownOptionException {
157
158
159 private static final long serialVersionUID = 1L;
160
161
162 private final char notflag;
163
164
165
166
167
168
169
170
171
172 NotFlagException(String option, char unflaggish) {
173 super(option, "Illegal option: '" + option + "', '" + unflaggish + "' requires a value");
174 notflag = unflaggish;
175 }
176
177
178
179
180
181
182 public char getOptionChar() {
183 return notflag;
184 }
185 }
186
187
188
189
190
191 public static class IllegalOptionValueException extends OptionException {
192
193
194 private static final long serialVersionUID = 1L;
195
196
197 private final Option option;
198
199
200 private final String value;
201
202
203
204
205
206
207
208
209
210 public IllegalOptionValueException(Option opt, String value) {
211 super("Illegal value '" + value + "' for option "
212 + (opt.shortForm() != null ? "-" + opt.shortForm() + "/" : "") + "--" + opt.longForm());
213 this.option = opt;
214 this.value = value;
215 }
216
217
218
219
220
221
222 public Option getOption() {
223 return this.option;
224 }
225
226
227
228
229
230
231 public String getValue() {
232 return this.value;
233 }
234
235 }
236
237
238
239
240 public abstract static class Option implements Serializable {
241
242
243 private static final long serialVersionUID = 1L;
244
245
246 private String shortForm;
247
248
249 private String longForm;
250
251
252 private boolean wantsValue;
253
254
255
256
257
258
259
260
261
262 protected Option(String longForm, boolean wantsValue) {
263 this(null, longForm, wantsValue);
264 }
265
266
267
268
269
270
271
272
273
274
275
276 protected Option(char shortForm, String longForm, boolean wantsValue) {
277 this(new String(new char[] { shortForm }), longForm, wantsValue);
278 }
279
280
281
282
283
284
285
286
287
288
289
290 private Option(String shortForm, String longForm, boolean wantsValue) {
291 if (longForm == null)
292 throw new IllegalArgumentException("Null longForm not allowed");
293 this.shortForm = shortForm;
294 this.longForm = longForm;
295 this.wantsValue = wantsValue;
296 }
297
298
299
300
301
302
303 public String shortForm() {
304 return this.shortForm;
305 }
306
307
308
309
310
311
312 public String longForm() {
313 return this.longForm;
314 }
315
316
317
318
319
320
321 public boolean wantsValue() {
322 return this.wantsValue;
323 }
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338 public final Object getValue(String arg, Locale locale) throws IllegalOptionValueException {
339 if (this.wantsValue) {
340 if (arg == null) {
341 throw new IllegalOptionValueException(this, "");
342 }
343 return this.parseValue(arg, locale);
344 } else {
345 return Boolean.TRUE;
346 }
347 }
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362 protected Object parseValue(String arg, Locale locale) throws IllegalOptionValueException {
363 return null;
364 }
365
366
367
368
369 public static class BooleanOption extends Option {
370
371
372 private static final long serialVersionUID = 1L;
373
374
375
376
377
378
379
380
381
382 public BooleanOption(char shortForm, String longForm) {
383 super(shortForm, longForm, false);
384 }
385
386
387
388
389
390
391
392 public BooleanOption(String longForm) {
393 super(longForm, false);
394 }
395 }
396
397
398
399
400 public static class IntegerOption extends Option {
401
402
403 private static final long serialVersionUID = 1L;
404
405
406
407
408
409
410
411
412
413 public IntegerOption(char shortForm, String longForm) {
414 super(shortForm, longForm, true);
415 }
416
417
418
419
420
421
422
423 public IntegerOption(String longForm) {
424 super(longForm, true);
425 }
426
427 @Override
428 protected Object parseValue(String arg, Locale locale) throws IllegalOptionValueException {
429 try {
430 return Integer.valueOf(arg);
431 } catch (NumberFormatException e) {
432 throw new IllegalOptionValueException(this, arg);
433 }
434 }
435 }
436
437
438
439
440 public static class LongOption extends Option {
441
442
443 private static final long serialVersionUID = 1L;
444
445
446
447
448
449
450
451
452
453 public LongOption(char shortForm, String longForm) {
454 super(shortForm, longForm, true);
455 }
456
457
458
459
460
461
462
463 public LongOption(String longForm) {
464 super(longForm, true);
465 }
466
467 @Override
468 protected Object parseValue(String arg, Locale locale) throws IllegalOptionValueException {
469 try {
470 return Long.valueOf(arg);
471 } catch (NumberFormatException e) {
472 throw new IllegalOptionValueException(this, arg);
473 }
474 }
475 }
476
477
478
479
480 public static class DoubleOption extends Option {
481
482
483 private static final long serialVersionUID = 1L;
484
485
486
487
488
489
490
491
492
493 public DoubleOption(char shortForm, String longForm) {
494 super(shortForm, longForm, true);
495 }
496
497
498
499
500
501
502
503 public DoubleOption(String longForm) {
504 super(longForm, true);
505 }
506
507 @Override
508 protected Object parseValue(String arg, Locale locale) throws IllegalOptionValueException {
509 try {
510 NumberFormat format = NumberFormat.getNumberInstance(locale);
511 Number num = format.parse(arg);
512 return num.doubleValue();
513 } catch (ParseException e) {
514 throw new IllegalOptionValueException(this, arg);
515 }
516 }
517 }
518
519
520
521
522 public static class StringOption extends Option {
523
524
525 private static final long serialVersionUID = 1L;
526
527
528
529
530
531
532
533
534
535 public StringOption(char shortForm, String longForm) {
536 super(shortForm, longForm, true);
537 }
538
539
540
541
542
543
544
545 public StringOption(String longForm) {
546 super(longForm, true);
547 }
548
549 @Override
550 protected Object parseValue(String arg, Locale locale) {
551 return arg;
552 }
553 }
554 }
555
556
557
558
559
560
561
562
563
564 public final Option addOption(Option opt) {
565 if (opt.shortForm() != null) {
566 this.options.put("-" + opt.shortForm(), opt);
567 }
568 this.options.put("--" + opt.longForm(), opt);
569 return opt;
570 }
571
572
573
574
575
576
577
578
579
580
581
582 public final Option addStringOption(char shortForm, String longForm) {
583 return addOption(new Option.StringOption(shortForm, longForm));
584 }
585
586
587
588
589
590
591
592
593
594 public final Option addStringOption(String longForm) {
595 return addOption(new Option.StringOption(longForm));
596 }
597
598
599
600
601
602
603
604
605
606
607
608 public final Option addIntegerOption(char shortForm, String longForm) {
609 return addOption(new Option.IntegerOption(shortForm, longForm));
610 }
611
612
613
614
615
616
617
618
619
620 public final Option addIntegerOption(String longForm) {
621 return addOption(new Option.IntegerOption(longForm));
622 }
623
624
625
626
627
628
629
630
631
632
633
634 public final Option addLongOption(char shortForm, String longForm) {
635 return addOption(new Option.LongOption(shortForm, longForm));
636 }
637
638
639
640
641
642
643
644
645
646 public final Option addLongOption(String longForm) {
647 return addOption(new Option.LongOption(longForm));
648 }
649
650
651
652
653
654
655
656
657
658
659
660 public final Option addDoubleOption(char shortForm, String longForm) {
661 return addOption(new Option.DoubleOption(shortForm, longForm));
662 }
663
664
665
666
667
668
669
670
671
672 public final Option addDoubleOption(String longForm) {
673 return addOption(new Option.DoubleOption(longForm));
674 }
675
676
677
678
679
680
681
682
683
684
685
686 public final Option addBooleanOption(char shortForm, String longForm) {
687 return addOption(new Option.BooleanOption(shortForm, longForm));
688 }
689
690
691
692
693
694
695
696
697
698 public final Option addBooleanOption(String longForm) {
699 return addOption(new Option.BooleanOption(longForm));
700 }
701
702
703
704
705
706
707
708
709
710 public final Object getOptionValue(Option o) {
711 return getOptionValue(o, null);
712 }
713
714
715
716
717
718
719
720
721
722
723
724 @SuppressWarnings("unchecked")
725 public final Object getOptionValue(Option o, Object def) {
726 List<String> v = (ArrayList<String>) values.get(o.longForm());
727
728 if (v == null) {
729 return def;
730 } else if (v.isEmpty()) {
731 return null;
732 } else {
733 Object result = v.get(0);
734 v.remove(0);
735 return result;
736 }
737 }
738
739
740
741
742
743
744
745
746
747
748 public final List<String> getOptionValues(Option option) {
749 List<String> result = new ArrayList<>();
750
751 while (true) {
752 Object o = getOptionValue(option, null);
753
754 if (o == null) {
755 return result;
756 } else {
757 result.add((String) o);
758 }
759 }
760 }
761
762
763
764
765
766
767 public final List<String> getRemainingArgs() {
768 return this.remainingArgs;
769 }
770
771
772
773
774
775
776
777
778
779
780
781
782
783 public final void parse(String[] argv) throws IllegalOptionValueException, UnknownOptionException {
784
785
786
787
788
789 parse(argv, Locale.getDefault());
790 }
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806 public final void parse(String[] argv, Locale locale) throws IllegalOptionValueException, UnknownOptionException {
807
808
809
810
811
812 List<String> otherArgs = new ArrayList<>();
813 int position = 0;
814 this.values = new HashMap<>(10);
815 while (position < argv.length) {
816 String curArg = argv[position];
817 if (curArg.startsWith("-")) {
818 if ("--".equals(curArg)) {
819
820 position += 1;
821 break;
822 }
823 String valueArg = null;
824 if (curArg.startsWith("--")) {
825
826 int equalsPos = curArg.indexOf('=');
827 if (equalsPos != -1) {
828 valueArg = curArg.substring(equalsPos + 1);
829 curArg = curArg.substring(0, equalsPos);
830 }
831 } else if (curArg.length() > 2) {
832
833 for (int i = 1; i < curArg.length(); i++) {
834 Option opt = this.options.get("-" + curArg.charAt(i));
835 if (opt == null)
836 throw new UnknownSuboptionException(curArg, curArg.charAt(i));
837 if (opt.wantsValue())
838 throw new NotFlagException(curArg, curArg.charAt(i));
839 addValue(opt, opt.getValue(null, locale));
840
841 }
842 position++;
843 continue;
844 }
845
846 Option opt = this.options.get(curArg);
847 if (opt == null) {
848 throw new UnknownOptionException(curArg);
849 }
850 Object value;
851 if (opt.wantsValue()) {
852 if (valueArg == null) {
853 position += 1;
854 if (position < argv.length) {
855 valueArg = argv[position];
856 }
857 }
858 value = opt.getValue(valueArg, locale);
859 } else {
860 value = opt.getValue(null, locale);
861 }
862
863 addValue(opt, value);
864
865 position += 1;
866 } else {
867 otherArgs.add(curArg);
868 position += 1;
869 }
870 }
871 for (; position < argv.length; ++position) {
872 otherArgs.add(argv[position]);
873 }
874
875 this.remainingArgs = new ArrayList<>(otherArgs.size());
876 otherArgs.addAll(remainingArgs);
877 }
878
879
880
881
882
883
884
885
886
887 @SuppressWarnings("unchecked")
888 private void addValue(Option opt, Object value) {
889 String lf = opt.longForm();
890
891 List<Object> v = (ArrayList<Object>) values.get(lf);
892
893 if (v == null) {
894 v = new ArrayList<>();
895 values.put(lf, v);
896 }
897
898 v.add(value);
899 }
900
901 }