-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathArgParser.java
156 lines (137 loc) · 5.45 KB
/
ArgParser.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Utility class for parsing command-line arguments. The allowed optional
* arguments are registered via calls to {@code addOption}. Then, a call to
* {@code parseArgs} will parse the actual arguments. It returns the
* non-optimal arguments that were discovered.
* <p>
* Optional arguments are those of the form {@code --X} or {@code --X=Y}.
*/
public class ArgParser {
/** Stores the supported types of options. */
private static Set<Class<?>> ALLOWED_TYPES = new HashSet<Class<?>>();
static {
ALLOWED_TYPES.add(Boolean.class);
ALLOWED_TYPES.add(Integer.class);
ALLOWED_TYPES.add(Double.class);
ALLOWED_TYPES.add(String.class);
}
/** Stores the name of the program. */
private final String programName;
/** Stores the registered optional arguments and their types. */
private final Map<String, Class<?>> optionTypes;
/** Stores the values of any optional arguments seen or null before parse. */
private Map<String, Object> optionValues;
/** Creates a parser with no optimal arguments, initially. */
public ArgParser(String programName) {
this.programName = programName;
this.optionTypes = new HashMap<String, Class<?>>();
}
/**
* Registers an optional argument that takes a value of the given type,
* unless {@code clazz == Boolean.class}, in which case it takes none. The
* provided class must be {@code Boolean.class}, {@code Integer.class},
* {@code Double.class}, or {@code String.class}.
*/
public void addOption(String name, Class<?> clazz) {
assert !optionTypes.containsKey(name);
assert ALLOWED_TYPES.contains(clazz);
optionTypes.put(name, clazz);
}
/**
* Parses the provided arguments, recording the optional ones and returning
* the non-optional ones.
*/
public String[] parseArgs(String[] allArgs, int minArgs, int maxArgs) {
assert optionValues == null;
optionValues = new HashMap<String, Object>();
List<String> arguments = new ArrayList<String>();
for (int i = 0; i < allArgs.length; i++) {
if (allArgs[i].startsWith("--")) {
int index = allArgs[i].indexOf('=');
if (index > 0) {
String name = allArgs[i].substring(2, index);
String value = allArgs[i].substring(index + 1);
if (!optionTypes.containsKey(name))
usage(minArgs, maxArgs);
if (optionTypes.get(name) == Boolean.class) {
usage(minArgs, maxArgs); // no value allowed
} else if (optionTypes.get(name) == Integer.class) {
try { optionValues.put(name, Integer.parseInt(value)); }
catch (NumberFormatException ex) { usage(minArgs, maxArgs); }
} else if (optionTypes.get(name) == Double.class) {
try { optionValues.put(name, Double.parseDouble(value)); }
catch (NumberFormatException ex) { usage(minArgs, maxArgs); }
} else if (optionTypes.get(name) == String.class) {
optionValues.put(name, value);
} else {
assert false : "impossible";
}
} else if (optionTypes.get(allArgs[i].substring(2)) == Boolean.class) {
optionValues.put(allArgs[i].substring(2), Boolean.TRUE);
} else {
usage(minArgs, maxArgs);
}
} else {
arguments.add(allArgs[i]);
}
}
if (arguments.size() < minArgs || maxArgs < arguments.size())
usage(minArgs, maxArgs);
return arguments.toArray(new String[arguments.size()]);
}
/** Determines whether the given option was found when parsing. */
public boolean hasOption(String name) {
assert optionValues != null;
return optionValues.containsKey(name);
}
/** Returns the value of integer option found during parsing. */
public int getIntegerOption(String name) {
assert optionTypes.get(name) == Integer.class;
assert optionValues.containsKey(name);
return (Integer) optionValues.get(name);
}
/** Returns the value of double option found during parsing. */
public double getDoubleOption(String name) {
assert optionTypes.get(name) == Double.class;
assert optionValues.containsKey(name);
return (Double) optionValues.get(name);
}
/** Returns the value of string option found during parsing. */
public String getStringOption(String name) {
assert optionTypes.get(name) == String.class;
assert optionValues.containsKey(name);
return (String) optionValues.get(name);
}
/** Prints out a usage message and exits. */
private void usage(int minArgs, int maxArgs) {
StringBuilder options = new StringBuilder();
for (String name : optionTypes.keySet()) {
if (options.length() > 0)
options.append(' ');
options.append("--");
options.append(name);
if (optionTypes.get(name) == Boolean.class) {
// nothing
} else if (optionTypes.get(name) == Integer.class) {
options.append("=<int>");
} else if (optionTypes.get(name) == Double.class) {
options.append("=<double>");
} else if (optionTypes.get(name) == String.class) {
options.append("=..");
} else {
assert false : "impossible";
}
}
System.err.printf("Usage: %s %s ... (+%s arguments)\n", programName,
options.toString(), minArgs, maxArgs,
(minArgs == maxArgs) ? String.format("%d", minArgs) :
String.format("%d-%d", minArgs, maxArgs));
System.exit(1);
}
}