comparison src/main/java/org/monetdb/util/CmdLineOpts.java @ 391:f523727db392

Moved Java classes from packages starting with nl.cwi.monetdb.* to package org.monetdb.* This naming complies to the Java Package Naming convention as MonetDB's main website is www.monetdb.org.
author Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
date Thu, 12 Nov 2020 22:02:01 +0100 (2020-11-12)
parents src/main/java/nl/cwi/monetdb/util/CmdLineOpts.java@54137aeb1f92
children bf9f6b6ecf40
comparison
equal deleted inserted replaced
390:6199e0be3c6e 391:f523727db392
1 /*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 *
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2020 MonetDB B.V.
7 */
8
9 package org.monetdb.util;
10
11 import java.util.ArrayList;
12 import java.util.HashMap;
13 import java.util.Properties;
14
15 public final class CmdLineOpts {
16 /** the arguments we handle */
17 private HashMap<String, OptionContainer> opts = new HashMap<String, OptionContainer>();
18 /** the options themself */
19 private ArrayList<OptionContainer> options = new ArrayList<OptionContainer>();
20
21 /** no arguments */
22 public static final int CAR_ZERO = 0;
23 /** always one argument */
24 public static final int CAR_ONE = 1;
25 /** zero or one argument */
26 public static final int CAR_ZERO_ONE = 2;
27 /** zero or many arguments */
28 public static final int CAR_ZERO_MANY = 3;
29 /** one or many arguments */
30 public static final int CAR_ONE_MANY = 4;
31
32 public CmdLineOpts() {
33 }
34
35 public void addOption(
36 final String shorta,
37 final String longa,
38 final int type,
39 final String defaulta,
40 final String descriptiona)
41 throws OptionsException {
42 OptionContainer oc =
43 new OptionContainer(
44 shorta,
45 longa,
46 type,
47 defaulta,
48 descriptiona
49 );
50 if (shorta != null)
51 opts.put(shorta, oc);
52 if (longa != null)
53 opts.put(longa, oc);
54 }
55
56 public void removeOption(final String name) {
57 OptionContainer oc = opts.get(name);
58 if (oc != null) {
59 opts.remove(oc.shorta);
60 opts.remove(oc.longa);
61 }
62 }
63
64 public OptionContainer getOption(final String key) throws OptionsException {
65 OptionContainer ret = opts.get(key);
66 if (ret == null)
67 throw new OptionsException("No such option: " + key);
68
69 return ret;
70 }
71
72 public void processFile(final java.io.File file) throws OptionsException {
73 // the file is there, parse it and set its settings
74 final Properties prop = new Properties();
75 try {
76 final java.io.FileInputStream in = new java.io.FileInputStream(file);
77 try {
78 prop.load(in);
79 } finally {
80 in.close();
81 }
82
83 for (java.util.Enumeration<?> e = prop.propertyNames(); e.hasMoreElements(); ) {
84 String key = (String) e.nextElement();
85 OptionContainer option = opts.get(key);
86 if (option == null)
87 throw new OptionsException("Unknown option: " + key);
88 option.resetArguments();
89 option.addArgument(prop.getProperty(key));
90 }
91 } catch (java.io.IOException e) {
92 // well... then forget about it
93 }
94 }
95
96 public void processArgs(final String args[]) throws OptionsException {
97 // parse and set the command line arguments
98 OptionContainer option = null;
99 int quant = -1;
100 int qcount = 0;
101 boolean moreData = false;
102 for (int i = 0; i < args.length; i++) {
103 if (option == null) {
104 if (args[i].charAt(0) != '-')
105 throw new OptionsException("Unexpected value: " + args[i]);
106
107 // see what kind of argument we have
108 if (args[i].length() == 1)
109 throw new OptionsException("Illegal argument: '-'");
110
111 if (args[i].charAt(1) == '-') {
112 // we have a long argument
113 // since we don't accept inline values we can take
114 // everything left in the string as argument, unless
115 // there is a = in there...
116 final String tmp = args[i].substring(2);
117 final int pos = tmp.indexOf('=');
118 if (pos == -1) {
119 option = opts.get(tmp);
120 moreData = false;
121 } else {
122 option = opts.get(tmp.substring(0, pos));
123 // modify the option a bit so the code below
124 // handles the moreData correctly
125 args[i] = "-?" + tmp.substring(pos + 1);
126 moreData = true;
127 }
128 } else if (args[i].charAt(1) == 'X') {
129 // extra argument, same as long argument
130 final String tmp = args[i].substring(1);
131 final int pos = tmp.indexOf('=');
132 if (pos == -1) {
133 option = opts.get(tmp);
134 moreData = false;
135 } else {
136 option = opts.get(tmp.substring(0, pos));
137 // modify the option a bit so the code below
138 // handles the moreData correctly
139 args[i] = "-?" + tmp.substring(pos + 1);
140 moreData = true;
141 }
142 } else {
143 // single char argument
144 option = opts.get("" + args[i].charAt(1));
145 // is there more data left in the argument?
146 moreData = args[i].length() > 2 ? true : false;
147 }
148
149 if (option != null) {
150 // make sure we overwrite previously set arguments
151 option.resetArguments();
152 final int card = option.getCardinality();
153 if (card == CAR_ONE) {
154 if (moreData) {
155 option.addArgument(args[i].substring(2));
156 option = null;
157 } else {
158 quant = 1;
159 }
160 } else if (card == CAR_ZERO_ONE) {
161 option.setPresent();
162 qcount = 1;
163 quant = 2;
164 if (moreData) {
165 option.addArgument(args[i].substring(2));
166 option = null;
167 }
168 } else if (card == CAR_ZERO_MANY) {
169 option.setPresent();
170 qcount = 1;
171 quant = -1;
172 if (moreData) {
173 option.addArgument(args[i].substring(2));
174 qcount++;
175 }
176 } else if (card == CAR_ZERO) {
177 option.setPresent();
178 option = null;
179 }
180 } else {
181 throw new OptionsException("Unknown argument: " + args[i]);
182 }
183 } else {
184 // store the `value'
185 option.addArgument(args[i]);
186 if (++qcount == quant) {
187 quant = 0;
188 qcount = 0;
189 option = null;
190 }
191 }
192 }
193 }
194
195 /**
196 * Returns a help message for all options loaded.
197 *
198 * @return the help message
199 */
200 public String produceHelpMessage() {
201 // first calculate how much space is necessary for the options
202 int maxlen = 0;
203 for (OptionContainer oc : options) {
204 final String shorta = oc.getShort();
205 final String longa = oc.getLong();
206 int len = 0;
207 if (shorta != null)
208 len += shorta.length() + 1 + 1;
209 if (longa != null)
210 len += longa.length() + 1 + (longa.charAt(0) == 'X' ? 0 : 1) + 1;
211 // yes, we don't care about branch mispredictions here ;)
212 if (maxlen < len)
213 maxlen = len;
214 }
215
216 // get the individual strings
217 final StringBuilder ret = new StringBuilder();
218 for (OptionContainer oc : options) {
219 ret.append(produceHelpMessage(oc, maxlen));
220 }
221
222 return ret.toString();
223 }
224
225 /**
226 * Returns the help message for the given OptionContainer. The
227 * command line flags will be padded to optwidth spaces to allow for
228 * aligning. The description of the flags will be wrapped at 80
229 * characters.
230 *
231 * @param oc the OptionContainer
232 * @param indentwidth padding width for the command line flags
233 * @return the help message for the option
234 */
235 public String produceHelpMessage(final OptionContainer oc, int indentwidth) {
236 final String desc = oc.getDescription();
237 if (desc == null)
238 return "";
239
240 final String shorta = oc.getShort();
241 final String longa = oc.getLong();
242 int optwidth = 0;
243 if (shorta != null)
244 optwidth += shorta.length() + 1 + 1;
245 if (longa != null)
246 optwidth += longa.length() + 1 + (longa.charAt(0) == 'X' ? 0 : 1) + 1;
247 final int descwidth = 80 - indentwidth;
248
249 // default to with of command line flags if no width given
250 if (indentwidth <= 0)
251 indentwidth = optwidth;
252
253 final StringBuilder ret = new StringBuilder();
254
255 // add the command line flags
256 if (shorta != null)
257 ret.append('-').append(shorta).append(' ');
258 if (longa != null) {
259 ret.append('-');
260 if (longa.charAt(0) != 'X')
261 ret.append('-');
262 ret.append(longa).append(' ');
263 }
264
265 for (int i = optwidth; i < indentwidth; i++)
266 ret.append(' ');
267 // add the description, wrap around words
268 int pos = 0, lastpos = 0;
269 while (pos < desc.length()) {
270 pos += descwidth;
271 if (lastpos != 0) {
272 for (int i = 0; i < indentwidth; i++)
273 ret.append(' ');
274 }
275 if (pos >= desc.length()) {
276 ret.append(desc.substring(lastpos)).append('\n');
277 break;
278 }
279 int space;
280 for (space = pos; desc.charAt(space) != ' '; space--) {
281 if (space == 0) {
282 space = pos;
283 break;
284 }
285 }
286 pos = space;
287 ret.append(desc.substring(lastpos, pos)).append('\n');
288 while (desc.charAt(pos) == ' ')
289 pos++;
290 lastpos = pos;
291 }
292
293 return ret.toString();
294 }
295
296 public final class OptionContainer {
297 private final int cardinality;
298 private final String shorta;
299 private final String longa;
300 private final ArrayList<String> values = new ArrayList<String>();
301 private final String name;
302 private final String defaulta;
303 private final String descriptiona;
304 private boolean present;
305
306 public OptionContainer(
307 final String shorta,
308 final String longa,
309 final int cardinality,
310 final String defaulta,
311 final String descriptiona)
312 throws IllegalArgumentException
313 {
314 this.cardinality = cardinality;
315 this.shorta = shorta;
316 this.longa = longa;
317 this.defaulta = defaulta;
318 this.descriptiona = descriptiona;
319 this.present = false;
320
321 if (cardinality != CAR_ZERO &&
322 cardinality != CAR_ONE &&
323 cardinality != CAR_ZERO_ONE &&
324 cardinality != CAR_ZERO_MANY &&
325 cardinality != CAR_ONE_MANY)
326 throw new IllegalArgumentException("unknown cardinality");
327 if (shorta != null && shorta.length() != 1)
328 throw new IllegalArgumentException("short option should consist of exactly one character");
329 if (shorta == null && longa == null)
330 throw new IllegalArgumentException("either a short or long argument should be given");
331 if ((cardinality == CAR_ZERO ||
332 cardinality == CAR_ZERO_ONE ||
333 cardinality == CAR_ZERO_MANY) &&
334 defaulta != null) {
335 throw new IllegalArgumentException("cannot specify a default for a (possible) zero argument option");
336 }
337
338 name = (longa != null) ? longa : shorta;
339 options.add(this);
340 }
341
342 public final void resetArguments() {
343 values.clear();
344 }
345
346 public void addArgument(final String val) throws OptionsException {
347 if (cardinality == CAR_ZERO) {
348 throw new OptionsException("option " + name + " does not allow arguments");
349 } else if ((cardinality == CAR_ONE ||
350 cardinality == CAR_ZERO_ONE) &&
351 values.size() >= 1) {
352 throw new OptionsException("option " + name + " does at max allow only one argument");
353 }
354 // we can add it
355 values.add(val);
356 setPresent();
357 }
358
359 public final void setPresent() {
360 present = true;
361 }
362
363 public final boolean isPresent() {
364 return present;
365 }
366
367 public final int getCardinality() {
368 return cardinality;
369 }
370
371 public final int getArgumentCount() {
372 return values.size();
373 }
374
375 public final String getArgument() {
376 final String ret = getArgument(1);
377 if (ret == null)
378 return defaulta;
379 return ret;
380 }
381
382 public final String getArgument(final int index) {
383 final String[] args = getArguments();
384 if (index < 1 || index > args.length)
385 return null;
386 return args[index - 1];
387 }
388
389 public final String[] getArguments() {
390 return values.toArray(new String[values.size()]);
391 }
392
393 public final String getShort() {
394 return shorta;
395 }
396
397 public final String getLong() {
398 return longa;
399 }
400
401 public final String getDescription() {
402 return descriptiona;
403 }
404 }
405 }