Ticket #16866: remove-getopt.2.patch
File remove-getopt.2.patch, 120.5 KB (added by , 7 years ago) |
---|
-
src/gnu/getopt/Getopt.java
1 /*2 /* Copyright (c) 1987-1997 Free Software Foundation, Inc.3 /* Java Port Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com)4 /*5 /* This program is free software; you can redistribute it and/or modify6 /* it under the terms of the GNU Library General Public License as published7 /* by the Free Software Foundation; either version 2 of the License or8 /* (at your option) any later version.9 /*10 /* This program is distributed in the hope that it will be useful, but11 /* WITHOUT ANY WARRANTY; without even the implied warranty of12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13 /* GNU Library General Public License for more details.14 /*15 /* You should have received a copy of the GNU Library General Public License16 /* along with this program; see the file COPYING.LIB. If not, write to17 /* the Free Software Foundation Inc., 59 Temple Place - Suite 330,18 /* Boston, MA 02111-1307 USA19 /**************************************************************************/20 21 package gnu.getopt;22 23 import java.text.MessageFormat;24 import java.util.HashMap;25 import java.util.Map;26 import java.util.function.Function;27 28 /**************************************************************************/29 30 /**31 * This is a Java port of GNU getopt, a class for parsing command line32 * arguments passed to programs. It it based on the C getopt() functions33 * in glibc 2.0.6 and should parse options in a 100% compatible manner.34 * If it does not, that is a bug. The programmer's interface is also35 * very compatible.36 * <p>37 * To use Getopt, create a Getopt object with a argv array passed to the38 * main method, then call the getopt() method in a loop. It will return an39 * int that contains the value of the option character parsed from the40 * command line. When there are no more options to be parsed, it41 * returns -1.42 * <p>43 * A command line option can be defined to take an argument. If an44 * option has an argument, the value of that argument is stored in an45 * instance variable called optarg, which can be accessed using the46 * getOptarg() method. If an option that requires an argument is47 * found, but there is no argument present, then an error message is48 * printed. Normally getopt() returns a '?' in this situation, but49 * that can be changed as described below.50 * <p>51 * If an invalid option is encountered, an error message is printed52 * to the standard error and getopt() returns a '?'. The value of the53 * invalid option encountered is stored in the instance variable optopt54 * which can be retrieved using the getOptopt() method. To suppress55 * the printing of error messages for this or any other error, set56 * the value of the opterr instance variable to false using the57 * setOpterr() method.58 * <p>59 * Between calls to getopt(), the instance variable optind is used to60 * keep track of where the object is in the parsing process. After all61 * options have been returned, optind is the index in argv of the first62 * non-option argument. This variable can be accessed with the getOptind()63 * method.64 * <p>65 * Note that this object expects command line options to be passed in the66 * traditional Unix manner. That is, proceeded by a '-' character.67 * Multiple options can follow the '-'. For example "-abc" is equivalent68 * to "-a -b -c". If an option takes a required argument, the value69 * of the argument can immediately follow the option character or be70 * present in the next argv element. For example, "-cfoo" and "-c foo"71 * both represent an option character of 'c' with an argument of "foo"72 * assuming c takes a required argument. If an option takes an argument73 * that is not required, then any argument must immediately follow the74 * option character in the same argv element. For example, if c takes75 * a non-required argument, then "-cfoo" represents option character 'c'76 * with an argument of "foo" while "-c foo" represents the option77 * character 'c' with no argument, and a first non-option argv element78 * of "foo".79 * <p>80 * The user can stop getopt() from scanning any further into a command line81 * by using the special argument "--" by itself. For example:82 * "-a -- -d" would return an option character of 'a', then return -183 * The "--" is discarded and "-d" is pointed to by optind as the first84 * non-option argv element.85 * <p>86 * Here is a basic example of using Getopt:87 * <p>88 * <pre>89 * Getopt g = new Getopt("testprog", argv, "ab:c::d");90 * //91 * int c;92 * String arg;93 * while ((c = g.getopt()) != -1)94 * {95 * switch(c)96 * {97 * case 'a':98 * case 'd':99 * System.out.print("You picked " + (char)c + "\n");100 * break;101 * //102 * case 'b':103 * case 'c':104 * arg = g.getOptarg();105 * System.out.print("You picked " + (char)c +106 * " with an argument of " +107 * ((arg != null) ? arg : "null") + "\n");108 * break;109 * //110 * case '?':111 * break; // getopt() already printed an error112 * //113 * default:114 * System.out.print("getopt() returned " + c + "\n");115 * }116 * }117 * </pre>118 * <p>119 * In this example, a new Getopt object is created with three params.120 * The first param is the program name. This is for printing error121 * messages in the form "program: error message". In the C version, this122 * value is taken from argv[0], but in Java the program name is not passed123 * in that element, thus the need for this parameter. The second param is124 * the argument list that was passed to the main() method. The third125 * param is the list of valid options. Each character represents a valid126 * option. If the character is followed by a single colon, then that127 * option has a required argument. If the character is followed by two128 * colons, then that option has an argument that is not required.129 * <p>130 * Note in this example that the value returned from getopt() is cast to131 * a char prior to printing. This is required in order to make the value132 * display correctly as a character instead of an integer.133 * <p>134 * If the first character in the option string is a colon, for example135 * ":abc::d", then getopt() will return a ':' instead of a '?' when it136 * encounters an option with a missing required argument. This allows the137 * caller to distinguish between invalid options and valid options that138 * are simply incomplete.139 * <p>140 * In the traditional Unix getopt(), -1 is returned when the first non-option141 * charcter is encountered. In GNU getopt(), the default behavior is to142 * allow options to appear anywhere on the command line. The getopt()143 * method permutes the argument to make it appear to the caller that all144 * options were at the beginning of the command line, and all non-options145 * were at the end. For example, calling getopt() with command line args146 * of "-a foo bar -d" returns options 'a' and 'd', then sets optind to147 * point to "foo". The program would read the last two argv elements as148 * "foo" and "bar", just as if the user had typed "-a -d foo bar".149 * <p>150 * The user can force getopt() to stop scanning the command line with151 * the special argument "--" by itself. Any elements occuring before the152 * "--" are scanned and permuted as normal. Any elements after the "--"153 * are returned as is as non-option argv elements. For example,154 * "foo -a -- bar -d" would return option 'a' then -1. optind would point155 * to "foo", "bar" and "-d" as the non-option argv elements. The "--"156 * is discarded by getopt().157 * <p>158 * There are two ways this default behavior can be modified. The first is159 * to specify traditional Unix getopt() behavior (which is also POSIX160 * behavior) in which scanning stops when the first non-option argument161 * encountered. (Thus "-a foo bar -d" would return 'a' as an option and162 * have "foo", "bar", and "-d" as non-option elements). The second is to163 * allow options anywhere, but to return all elements in the order they164 * occur on the command line. When a non-option element is ecountered,165 * an integer 1 is returned and the value of the non-option element is166 * stored in optarg is if it were the argument to that option. For167 * example, "-a foo -d", returns first 'a', then 1 (with optarg set to168 * "foo") then 'd' then -1. When this "return in order" functionality169 * is enabled, the only way to stop getopt() from scanning all command170 * line elements is to use the special "--" string by itself as described171 * above. An example is "-a foo -b -- bar", which would return 'a', then172 * integer 1 with optarg set to "foo", then 'b', then -1. optind would173 * then point to "bar" as the first non-option argv element. The "--"174 * is discarded.175 * <p>176 * The POSIX/traditional behavior is enabled by either setting the177 * property "gnu.posixly_correct" or by putting a '+' sign as the first178 * character of the option string. The difference between the two179 * methods is that setting the gnu.posixly_correct property also forces180 * certain error messages to be displayed in POSIX format. To enable181 * the "return in order" functionality, put a '-' as the first character182 * of the option string. Note that after determining the proper183 * behavior, Getopt strips this leading '+' or '-', meaning that a ':'184 * placed as the second character after one of those two will still cause185 * getopt() to return a ':' instead of a '?' if a required option186 * argument is missing.187 * <p>188 * In addition to traditional single character options, GNU Getopt also189 * supports long options. These are preceeded by a "--" sequence and190 * can be as long as desired. Long options provide a more user-friendly191 * way of entering command line options. For example, in addition to a192 * "-h" for help, a program could support also "--help".193 * <p>194 * Like short options, long options can also take a required or non-required195 * argument. Required arguments can either be specified by placing an196 * equals sign after the option name, then the argument, or by putting the197 * argument in the next argv element. For example: "--outputdir=foo" and198 * "--outputdir foo" both represent an option of "outputdir" with an199 * argument of "foo", assuming that outputdir takes a required argument.200 * If a long option takes a non-required argument, then the equals sign201 * form must be used to specify the argument. In this case,202 * "--outputdir=foo" would represent option outputdir with an argument of203 * "foo" while "--outputdir foo" would represent the option outputdir204 * with no argument and a first non-option argv element of "foo".205 * <p>206 * Long options can also be specified using a special POSIX argument207 * format (one that I highly discourage). This form of entry is208 * enabled by placing a "W;" (yes, 'W' then a semi-colon) in the valid209 * option string. This causes getopt to treat the name following the210 * "-W" as the name of the long option. For example, "-W outputdir=foo"211 * would be equivalent to "--outputdir=foo". The name can immediately212 * follow the "-W" like so: "-Woutputdir=foo". Option arguments are213 * handled identically to normal long options. If a string follows the214 * "-W" that does not represent a valid long option, then getopt() returns215 * 'W' and the caller must decide what to do. Otherwise getopt() returns216 * a long option value as described below.217 * <p>218 * While long options offer convenience, they can also be tedious to type219 * in full. So it is permissible to abbreviate the option name to as220 * few characters as required to uniquely identify it. If the name can221 * represent multiple long options, then an error message is printed and222 * getopt() returns a '?'.223 * <p>224 * If an invalid option is specified or a required option argument is225 * missing, getopt() prints an error and returns a '?' or ':' exactly226 * as for short options. Note that when an invalid long option is227 * encountered, the optopt variable is set to integer 0 and so cannot228 * be used to identify the incorrect option the user entered.229 * <p>230 * Long options are defined by LongOpt objects. These objects are created231 * with a contructor that takes four params: a String representing the232 * object name, a integer specifying what arguments the option takes233 * (the value is one of LongOpt.NO_ARGUMENT, LongOpt.REQUIRED_ARGUMENT,234 * or LongOpt.OPTIONAL_ARGUMENT), a StringBuffer flag object (described235 * below), and an integer value (described below).236 * <p>237 * To enable long option parsing, create an array of LongOpt's representing238 * the legal options and pass it to the Getopt() constructor. WARNING: If239 * all elements of the array are not populated with LongOpt objects, the240 * getopt() method will throw a NullPointerException.241 * <p>242 * When getopt() is called and a long option is encountered, one of two243 * things can be returned. If the flag field in the LongOpt object244 * representing the long option is non-null, then the integer value field245 * is stored there and an integer 0 is returned to the caller. The val246 * field can then be retrieved from the flag field. Note that since the247 * flag field is a StringBuffer, the appropriate String to integer converions248 * must be performed in order to get the actual int value stored there.249 * If the flag field in the LongOpt object is null, then the value field250 * of the LongOpt is returned. This can be the character of a short option.251 * This allows an app to have both a long and short option sequence252 * (say, "-h" and "--help") that do the exact same thing.253 * <p>254 * With long options, there is an alternative method of determining255 * which option was selected. The method getLongind() will return the256 * the index in the long option array (NOT argv) of the long option found.257 * So if multiple long options are configured to return the same value,258 * the application can use getLongind() to distinguish between them.259 * <p>260 * Here is an expanded Getopt example using long options and various261 * techniques described above:262 * <p>263 * <pre>264 * int c;265 * String arg;266 * LongOpt[] longopts = new LongOpt[3];267 * //268 * StringBuffer sb = new StringBuffer();269 * longopts[0] = new LongOpt("help", LongOpt.NO_ARGUMENT, null, 'h');270 * longopts[1] = new LongOpt("outputdir", LongOpt.REQUIRED_ARGUMENT, sb, 'o');271 * longopts[2] = new LongOpt("maximum", LongOpt.OPTIONAL_ARGUMENT, null, 2);272 * //273 * Getopt g = new Getopt("testprog", argv, "-:bc::d:hW;", longopts);274 * g.setOpterr(false); // We'll do our own error handling275 * //276 * while ((c = g.getopt()) != -1)277 * switch (c)278 * {279 * case 0:280 * arg = g.getOptarg();281 * System.out.println("Got long option with value '" +282 * (char)(new Integer(sb.toString())).intValue()283 * + "' with argument " +284 * ((arg != null) ? arg : "null"));285 * break;286 * //287 * case 1:288 * System.out.println("I see you have return in order set and that " +289 * "a non-option argv element was just found " +290 * "with the value '" + g.getOptarg() + "'");291 * break;292 * //293 * case 2:294 * arg = g.getOptarg();295 * System.out.println("I know this, but pretend I didn't");296 * System.out.println("We picked option " +297 * longopts[g.getLongind()].getName() +298 * " with value " +299 * ((arg != null) ? arg : "null"));300 * break;301 * //302 * case 'b':303 * System.out.println("You picked plain old option " + (char)c);304 * break;305 * //306 * case 'c':307 * case 'd':308 * arg = g.getOptarg();309 * System.out.println("You picked option '" + (char)c +310 * "' with argument " +311 * ((arg != null) ? arg : "null"));312 * break;313 * //314 * case 'h':315 * System.out.println("I see you asked for help");316 * break;317 * //318 * case 'W':319 * System.out.println("Hmmm. You tried a -W with an incorrect long " +320 * "option name");321 * break;322 * //323 * case ':':324 * System.out.println("Doh! You need an argument for option " +325 * (char)g.getOptopt());326 * break;327 * //328 * case '?':329 * System.out.println("The option '" + (char)g.getOptopt() +330 * "' is not valid");331 * break;332 * //333 * default:334 * System.out.println("getopt() returned " + c);335 * break;336 * }337 * //338 * for (int i = g.getOptind(); i < argv.length ; i++)339 * System.out.println("Non option argv element: " + argv[i] + "\n");340 * </pre>341 * <p>342 * There is an alternative form of the constructor used for long options343 * above. This takes a trailing boolean flag. If set to false, Getopt344 * performs identically to the example, but if the boolean flag is true345 * then long options are allowed to start with a single '-' instead of346 * "--". If the first character of the option is a valid short option347 * character, then the option is treated as if it were the short option.348 * Otherwise it behaves as if the option is a long option. Note that349 * the name given to this option - long_only - is very counter-intuitive.350 * It does not cause only long options to be parsed but instead enables351 * the behavior described above.352 * <p>353 * Note that the functionality and variable names used are driven from354 * the C lib version as this object is a port of the C code, not a355 * new implementation. This should aid in porting existing C/C++ code,356 * as well as helping programmers familiar with the glibc version to357 * adapt to the Java version even if it seems very non-Java at times.358 * <p>359 * In this release I made all instance variables protected due to360 * overwhelming public demand. Any code which relied on optarg,361 * opterr, optind, or optopt being public will need to be modified to362 * use the appropriate access methods.363 * <p>364 * Please send all bug reports, requests, and comments to365 * <a href="mailto:arenn@urbanophile.com">arenn@urbanophile.com</a>.366 *367 * @version 1.0.7368 *369 * @author Roland McGrath (roland@gnu.ai.mit.edu)370 * @author Ulrich Drepper (drepper@cygnus.com)371 * @author Aaron M. Renn (arenn@urbanophile.com)372 *373 * @see LongOpt374 */375 public class Getopt extends Object376 {377 378 /**************************************************************************/379 380 /*381 * Class Variables382 */383 384 /**385 * Describe how to deal with options that follow non-option ARGV-elements.386 *387 * If the caller did not specify anything,388 * the default is REQUIRE_ORDER if the property389 * gnu.posixly_correct is defined, PERMUTE otherwise.390 *391 * The special argument `--' forces an end of option-scanning regardless392 * of the value of `ordering'. In the case of RETURN_IN_ORDER, only393 * `--' can cause `getopt' to return -1 with `optind' != ARGC.394 *395 * REQUIRE_ORDER means don't recognize them as options;396 * stop option processing when the first non-option is seen.397 * This is what Unix does.398 * This mode of operation is selected by either setting the property399 * gnu.posixly_correct, or using `+' as the first character400 * of the list of option characters.401 */402 protected static final int REQUIRE_ORDER = 1;403 404 /**405 * PERMUTE is the default. We permute the contents of ARGV as we scan,406 * so that eventually all the non-options are at the end. This allows options407 * to be given in any order, even with programs that were not written to408 * expect this.409 */410 protected static final int PERMUTE = 2;411 412 /**413 * RETURN_IN_ORDER is an option available to programs that were written414 * to expect options and other ARGV-elements in any order and that care about415 * the ordering of the two. We describe each non-option ARGV-element416 * as if it were the argument of an option with character code 1.417 * Using `-' as the first character of the list of option characters418 * selects this mode of operation.419 */420 protected static final int RETURN_IN_ORDER = 3;421 422 /**************************************************************************/423 424 /*425 * Instance Variables426 */427 428 /**429 * For communication from `getopt' to the caller.430 * When `getopt' finds an option that takes an argument,431 * the argument value is returned here.432 * Also, when `ordering' is RETURN_IN_ORDER,433 * each non-option ARGV-element is returned here.434 */435 protected String optarg;436 437 /**438 * Index in ARGV of the next element to be scanned.439 * This is used for communication to and from the caller440 * and for communication between successive calls to `getopt'.441 *442 * On entry to `getopt', zero means this is the first call; initialize.443 *444 * When `getopt' returns -1, this is the index of the first of the445 * non-option elements that the caller should itself scan.446 *447 * Otherwise, `optind' communicates from one call to the next448 * how much of ARGV has been scanned so far.449 */450 protected int optind = 0;451 452 /**453 * Callers store false here to inhibit the error message454 * for unrecognized options.455 */456 protected boolean opterr = true;457 458 /**459 * When an unrecognized option is encountered, getopt will return a '?'460 * and store the value of the invalid option here.461 */462 protected int optopt = '?';463 464 /**465 * The next char to be scanned in the option-element466 * in which the last option character we returned was found.467 * This allows us to pick up the scan where we left off.468 *469 * If this is zero, or a null string, it means resume the scan470 * by advancing to the next ARGV-element.471 */472 protected String nextchar;473 474 /**475 * This is the string describing the valid short options.476 */477 protected String optstring;478 479 /**480 * This is an array of LongOpt objects which describ the valid long481 * options.482 */483 protected LongOpt[] long_options;484 485 /**486 * This flag determines whether or not we are parsing only long args487 */488 protected boolean long_only;489 490 /**491 * Stores the index into the long_options array of the long option found492 */493 protected int longind;494 495 /**496 * The flag determines whether or not we operate in strict POSIX compliance497 */498 protected boolean posixly_correct;499 500 /**501 * A flag which communicates whether or not checkLongOption() did all502 * necessary processing for the current option503 */504 protected boolean longopt_handled;505 506 /**507 * The index of the first non-option in argv[]508 */509 protected int first_nonopt = 1;510 511 /**512 * The index of the last non-option in argv[]513 */514 protected int last_nonopt = 1;515 516 /**517 * Flag to tell getopt to immediately return -1 the next time it is518 * called.519 */520 private boolean endparse = false;521 522 /**523 * Saved argument list passed to the program524 */525 protected String[] argv;526 527 /**528 * Determines whether we permute arguments or not529 */530 protected int ordering;531 532 /**533 * Name to print as the program name in error messages. This is necessary534 * since Java does not place the program name in argv[0]535 */536 protected String progname;537 538 /**539 * The localized strings are kept in a separate file540 */541 private OptI18n _messages = new OptI18n(); // ResourceBundle.getBundle("gnu/getopt/MessagesBundle", Locale.getDefault());542 543 /**************************************************************************/544 545 /*546 * Constructors547 */548 549 /**550 * Construct a basic Getopt instance with the given input data. Note that551 * this handles "short" options only.552 *553 * @param progname The name to display as the program name when printing errors554 * @param argv The String array passed as the command line to the program.555 * @param optstring A String containing a description of the valid args for this program556 */557 public558 Getopt(String progname, String[] argv, String optstring)559 {560 this(progname, argv, optstring, null, false);561 }562 563 /**************************************************************************/564 565 /**566 * Construct a Getopt instance with given input data that is capable of567 * parsing long options as well as short.568 *569 * @param progname The name to display as the program name when printing errors570 * @param argv The String array passed as the command ilne to the program571 * @param optstring A String containing a description of the valid short args for this program572 * @param long_options An array of LongOpt objects that describes the valid long args for this program573 */574 public575 Getopt(String progname, String[] argv, String optstring,576 LongOpt[] long_options)577 {578 this(progname, argv, optstring, long_options, false);579 }580 581 /**************************************************************************/582 583 private static Function<String, String> tr = Function.identity();584 585 /**586 * Set the global translation handler for Getopt.587 *588 * This needs to be done before any call to {@link Getopt} or {@link LongOpt}589 * constructor.590 * @param tr function that takes messages in English and returns the localized message591 */592 public static void setI18nHandler(Function<String, String> tr) {593 Getopt.tr = tr;594 }595 596 static class OptI18n {597 598 private final Map<String, String> trns = new HashMap<>();599 600 public OptI18n() {601 add("getopt.ambigious", tr("{0}: option ''{1}'' is ambiguous"));602 add("getopt.arguments1", tr("{0}: option ''--{1}'' does not allow an argument"));603 add("getopt.arguments2", tr("{0}: option ''{1}{2}'' does not allow an argument"));604 add("getopt.requires", tr("{0}: option ''{1}'' requires an argument"));605 add("getopt.unrecognized", tr("{0}: unrecognized option ''--{1}''"));606 add("getopt.unrecognized2", tr("{0}: unrecognized option ''{1}{2}''"));607 add("getopt.illegal", tr("{0}: illegal option -- {1}"));608 add("getopt.invalid", tr("{0}: invalid option -- {1}"));609 add("getopt.requires2", tr("{0}: option requires an argument -- {1}"));610 add("getopt.invalidValue", tr("Invalid value {0} for parameter ''has_arg''"));611 }612 613 private String tr(String s) {614 return Getopt.tr.apply(s);615 }616 617 private void add(String key, String value) {618 trns.put(key, value);619 }620 621 public String getString(String s) {622 String val = trns.get(s);623 if (val == null) throw new IllegalArgumentException();624 return val.replace("'", "''");625 }626 }627 628 /**629 * Construct a Getopt instance with given input data that is capable of630 * parsing long options and short options. Contrary to what you might631 * think, the flag 'long_only' does not determine whether or not we632 * scan for only long arguments. Instead, a value of true here allows633 * long arguments to start with a '-' instead of '--' unless there is a634 * conflict with a short option name.635 *636 * @param progname The name to display as the program name when printing errors637 * @param argv The String array passed as the command ilne to the program638 * @param optstring A String containing a description of the valid short args for this program639 * @param long_options An array of LongOpt objects that describes the valid long args for this program640 * @param long_only true if long options that do not conflict with short options can start with a '-' as well as '--'641 */642 public643 Getopt(String progname, String[] argv, String optstring,644 LongOpt[] long_options, boolean long_only)645 {646 if (optstring.length() == 0)647 optstring = " ";648 649 // This function is essentially _getopt_initialize from GNU getopt650 this.progname = progname;651 this.argv = argv;652 this.optstring = optstring;653 this.long_options = long_options;654 this.long_only = long_only;655 656 // Check for property "gnu.posixly_correct" to determine whether to657 // strictly follow the POSIX standard. This replaces the "POSIXLY_CORRECT"658 // environment variable in the C version659 try {660 if (System.getProperty("gnu.posixly_correct", null) == null)661 posixly_correct = false;662 else663 {664 posixly_correct = true;665 _messages = new OptI18n();//ResourceBundle.getBundle("gnu/getopt/MessagesBundle",666 // Locale.US);667 }668 } catch (SecurityException e) {669 System.err.println(e.getMessage());670 }671 672 // Determine how to handle the ordering of options and non-options673 if (optstring.charAt(0) == '-')674 {675 ordering = RETURN_IN_ORDER;676 if (optstring.length() > 1)677 this.optstring = optstring.substring(1);678 }679 else if (optstring.charAt(0) == '+')680 {681 ordering = REQUIRE_ORDER;682 if (optstring.length() > 1)683 this.optstring = optstring.substring(1);684 }685 else if (posixly_correct)686 {687 ordering = REQUIRE_ORDER;688 }689 else690 {691 ordering = PERMUTE; // The normal default case692 }693 }694 695 /**************************************************************************/696 697 /*698 * Instance Methods699 */700 701 /**702 * In GNU getopt, it is possible to change the string containg valid options703 * on the fly because it is passed as an argument to getopt() each time. In704 * this version we do not pass the string on every call. In order to allow705 * dynamic option string changing, this method is provided.706 *707 * @param optstring The new option string to use708 */709 public void710 setOptstring(String optstring)711 {712 if (optstring.length() == 0)713 optstring = " ";714 715 this.optstring = optstring;716 }717 718 /**************************************************************************/719 720 /**721 * optind it the index in ARGV of the next element to be scanned.722 * This is used for communication to and from the caller723 * and for communication between successive calls to `getopt'.724 *725 * When `getopt' returns -1, this is the index of the first of the726 * non-option elements that the caller should itself scan.727 *728 * Otherwise, `optind' communicates from one call to the next729 * how much of ARGV has been scanned so far.730 */731 public int732 getOptind()733 {734 return(optind);735 }736 737 /**************************************************************************/738 739 /**740 * This method allows the optind index to be set manually. Normally this741 * is not necessary (and incorrect usage of this method can lead to serious742 * lossage), but optind is a public symbol in GNU getopt, so this method743 * was added to allow it to be modified by the caller if desired.744 *745 * @param optind The new value of optind746 */747 public void748 setOptind(int optind)749 {750 this.optind = optind;751 }752 753 /**************************************************************************/754 755 /**756 * Since in GNU getopt() the argument vector is passed back in to the757 * function every time, the caller can swap out argv on the fly. Since758 * passing argv is not required in the Java version, this method allows759 * the user to override argv. Note that incorrect use of this method can760 * lead to serious lossage.761 *762 * @param argv New argument list763 */764 public void765 setArgv(String[] argv)766 {767 this.argv = argv;768 }769 770 /**************************************************************************/771 772 /**773 * For communication from `getopt' to the caller.774 * When `getopt' finds an option that takes an argument,775 * the argument value is returned here.776 * Also, when `ordering' is RETURN_IN_ORDER,777 * each non-option ARGV-element is returned here.778 * No set method is provided because setting this variable has no effect.779 */780 public String781 getOptarg()782 {783 return(optarg);784 }785 786 /**************************************************************************/787 788 /**789 * Normally Getopt will print a message to the standard error when an790 * invalid option is encountered. This can be suppressed (or re-enabled)791 * by calling this method. There is no get method for this variable792 * because if you can't remember the state you set this to, why should I?793 */794 public void795 setOpterr(boolean opterr)796 {797 this.opterr = opterr;798 }799 800 /**************************************************************************/801 802 /**803 * When getopt() encounters an invalid option, it stores the value of that804 * option in optopt which can be retrieved with this method. There is805 * no corresponding set method because setting this variable has no effect.806 */807 public int808 getOptopt()809 {810 return(optopt);811 }812 813 /**************************************************************************/814 815 /**816 * Returns the index into the array of long options (NOT argv) representing817 * the long option that was found.818 */819 public int820 getLongind()821 {822 return(longind);823 }824 825 /**************************************************************************/826 827 /**828 * Exchange the shorter segment with the far end of the longer segment.829 * That puts the shorter segment into the right place.830 * It leaves the longer segment in the right place overall,831 * but it consists of two parts that need to be swapped next.832 * This method is used by getopt() for argument permutation.833 */834 protected void835 exchange(String[] argv)836 {837 int bottom = first_nonopt;838 int middle = last_nonopt;839 int top = optind;840 String tem;841 842 while (top > middle && middle > bottom)843 {844 if (top - middle > middle - bottom)845 {846 // Bottom segment is the short one.847 int len = middle - bottom;848 int i;849 850 // Swap it with the top part of the top segment.851 for (i = 0; i < len; i++)852 {853 tem = argv[bottom + i];854 argv[bottom + i] = argv[top - (middle - bottom) + i];855 argv[top - (middle - bottom) + i] = tem;856 }857 // Exclude the moved bottom segment from further swapping.858 top -= len;859 }860 else861 {862 // Top segment is the short one.863 int len = top - middle;864 int i;865 866 // Swap it with the bottom part of the bottom segment.867 for (i = 0; i < len; i++)868 {869 tem = argv[bottom + i];870 argv[bottom + i] = argv[middle + i];871 argv[middle + i] = tem;872 }873 // Exclude the moved top segment from further swapping.874 bottom += len;875 }876 }877 878 // Update records for the slots the non-options now occupy.879 880 first_nonopt += (optind - last_nonopt);881 last_nonopt = optind;882 }883 884 /**************************************************************************/885 886 /**887 * Check to see if an option is a valid long option. Called by getopt().888 * Put in a separate method because this needs to be done twice. (The889 * C getopt authors just copy-pasted the code!).890 *891 * @param longind A buffer in which to store the 'val' field of found LongOpt892 *893 * @return Various things depending on circumstances894 */895 protected int896 checkLongOption()897 {898 LongOpt pfound = null;899 int nameend;900 boolean ambig;901 boolean exact;902 903 longopt_handled = true;904 ambig = false;905 exact = false;906 longind = -1;907 908 nameend = nextchar.indexOf("=");909 if (nameend == -1)910 nameend = nextchar.length();911 912 // Test all lnog options for either exact match or abbreviated matches913 for (int i = 0; i < long_options.length; i++)914 {915 if (long_options[i].getName().startsWith(nextchar.substring(0, nameend)))916 {917 if (long_options[i].getName().equals(nextchar.substring(0, nameend)))918 {919 // Exact match found920 pfound = long_options[i];921 longind = i;922 exact = true;923 break;924 }925 else if (pfound == null)926 {927 // First nonexact match found928 pfound = long_options[i];929 longind = i;930 }931 else932 {933 // Second or later nonexact match found934 ambig = true;935 }936 }937 } // for938 939 // Print out an error if the option specified was ambiguous940 if (ambig && !exact)941 {942 if (opterr)943 {944 Object[] msgArgs = { progname, argv[optind] };945 System.err.println(MessageFormat.format(946 _messages.getString("getopt.ambigious"),947 msgArgs));948 }949 950 nextchar = "";951 optopt = 0;952 ++optind;953 954 return('?');955 }956 957 if (pfound != null)958 {959 ++optind;960 961 if (nameend != nextchar.length())962 {963 if (pfound.has_arg != LongOpt.NO_ARGUMENT)964 {965 if (nextchar.substring(nameend).length() > 1)966 optarg = nextchar.substring(nameend+1);967 else968 optarg = "";969 }970 else971 {972 if (opterr)973 {974 // -- option975 if (argv[optind - 1].startsWith("--"))976 {977 Object[] msgArgs = { progname, pfound.name };978 System.err.println(MessageFormat.format(979 _messages.getString("getopt.arguments1"),980 msgArgs));981 }982 // +option or -option983 else984 {985 Object[] msgArgs = { progname,986 Character.toString(argv[optind-1].charAt(0)),987 pfound.name };988 System.err.println(MessageFormat.format(989 _messages.getString("getopt.arguments2"),990 msgArgs));991 }992 }993 994 nextchar = "";995 optopt = pfound.val;996 997 return('?');998 }999 } // if (nameend)1000 else if (pfound.has_arg == LongOpt.REQUIRED_ARGUMENT)1001 {1002 if (optind < argv.length)1003 {1004 optarg = argv[optind];1005 ++optind;1006 }1007 else1008 {1009 if (opterr)1010 {1011 Object[] msgArgs = { progname, argv[optind-1] };1012 System.err.println(MessageFormat.format(1013 _messages.getString("getopt.requires"),1014 msgArgs));1015 }1016 1017 nextchar = "";1018 optopt = pfound.val;1019 if (optstring.charAt(0) == ':')1020 return(':');1021 else1022 return('?');1023 }1024 } // else if (pfound)1025 1026 nextchar = "";1027 1028 if (pfound.flag != null)1029 {1030 pfound.flag.setLength(0);1031 pfound.flag.append(pfound.val);1032 1033 return(0);1034 }1035 1036 return(pfound.val);1037 } // if (pfound != null)1038 1039 longopt_handled = false;1040 1041 return(0);1042 }1043 1044 /**************************************************************************/1045 1046 /**1047 * This method returns a char that is the current option that has been1048 * parsed from the command line. If the option takes an argument, then1049 * the internal variable 'optarg' is set which is a String representing1050 * the the value of the argument. This value can be retrieved by the1051 * caller using the getOptarg() method. If an invalid option is found,1052 * an error message is printed and a '?' is returned. The name of the1053 * invalid option character can be retrieved by calling the getOptopt()1054 * method. When there are no more options to be scanned, this method1055 * returns -1. The index of first non-option element in argv can be1056 * retrieved with the getOptind() method.1057 *1058 * @return Various things as described above1059 */1060 public int1061 getopt()1062 {1063 optarg = null;1064 1065 if (endparse == true)1066 return(-1);1067 1068 if ((nextchar == null) || (nextchar.equals("")))1069 {1070 // If we have just processed some options following some non-options,1071 // exchange them so that the options come first.1072 if (last_nonopt > optind)1073 last_nonopt = optind;1074 if (first_nonopt > optind)1075 first_nonopt = optind;1076 1077 if (ordering == PERMUTE)1078 {1079 // If we have just processed some options following some non-options,1080 // exchange them so that the options come first.1081 if ((first_nonopt != last_nonopt) && (last_nonopt != optind))1082 exchange(argv);1083 else if (last_nonopt != optind)1084 first_nonopt = optind;1085 1086 // Skip any additional non-options1087 // and extend the range of non-options previously skipped.1088 while ((optind < argv.length) && (argv[optind].equals("") ||1089 (argv[optind].charAt(0) != '-') || argv[optind].equals("-")))1090 {1091 optind++;1092 }1093 1094 last_nonopt = optind;1095 }1096 1097 // The special ARGV-element `--' means premature end of options.1098 // Skip it like a null option,1099 // then exchange with previous non-options as if it were an option,1100 // then skip everything else like a non-option.1101 if ((optind != argv.length) && argv[optind].equals("--"))1102 {1103 optind++;1104 1105 if ((first_nonopt != last_nonopt) && (last_nonopt != optind))1106 exchange (argv);1107 else if (first_nonopt == last_nonopt)1108 first_nonopt = optind;1109 1110 last_nonopt = argv.length;1111 1112 optind = argv.length;1113 }1114 1115 // If we have done all the ARGV-elements, stop the scan1116 // and back over any non-options that we skipped and permuted.1117 if (optind == argv.length)1118 {1119 // Set the next-arg-index to point at the non-options1120 // that we previously skipped, so the caller will digest them.1121 if (first_nonopt != last_nonopt)1122 optind = first_nonopt;1123 1124 return(-1);1125 }1126 1127 // If we have come to a non-option and did not permute it,1128 // either stop the scan or describe it to the caller and pass it by.1129 if (argv[optind].equals("") || (argv[optind].charAt(0) != '-') ||1130 argv[optind].equals("-"))1131 {1132 if (ordering == REQUIRE_ORDER)1133 return(-1);1134 1135 optarg = argv[optind++];1136 return(1);1137 }1138 1139 // We have found another option-ARGV-element.1140 // Skip the initial punctuation.1141 if (argv[optind].startsWith("--"))1142 nextchar = argv[optind].substring(2);1143 else1144 nextchar = argv[optind].substring(1);1145 }1146 1147 // Decode the current option-ARGV-element.1148 1149 /* Check whether the ARGV-element is a long option.1150 1151 If long_only and the ARGV-element has the form "-f", where f is1152 a valid short option, don't consider it an abbreviated form of1153 a long option that starts with f. Otherwise there would be no1154 way to give the -f short option.1155 1156 On the other hand, if there's a long option "fubar" and1157 the ARGV-element is "-fu", do consider that an abbreviation of1158 the long option, just like "--fu", and not "-f" with arg "u".1159 1160 This distinction seems to be the most useful approach. */1161 if ((long_options != null) && (argv[optind].startsWith("--")1162 || (long_only && ((argv[optind].length() > 2) ||1163 (optstring.indexOf(argv[optind].charAt(1)) == -1)))))1164 {1165 int c = checkLongOption();1166 1167 if (longopt_handled)1168 return(c);1169 1170 // Can't find it as a long option. If this is not getopt_long_only,1171 // or the option starts with '--' or is not a valid short1172 // option, then it's an error.1173 // Otherwise interpret it as a short option.1174 if (!long_only || argv[optind].startsWith("--")1175 || (optstring.indexOf(nextchar.charAt(0)) == -1))1176 {1177 if (opterr)1178 {1179 if (argv[optind].startsWith("--"))1180 {1181 Object[] msgArgs = { progname, nextchar };1182 System.err.println(MessageFormat.format(1183 _messages.getString("getopt.unrecognized"),1184 msgArgs));1185 }1186 else1187 {1188 Object[] msgArgs = { progname,1189 Character.toString(argv[optind].charAt(0)),1190 nextchar };1191 System.err.println(MessageFormat.format(1192 _messages.getString("getopt.unrecognized2"),1193 msgArgs));1194 }1195 }1196 1197 nextchar = "";1198 ++optind;1199 optopt = 0;1200 1201 return('?');1202 }1203 } // if (longopts)1204 1205 // Look at and handle the next short option-character */1206 int c = nextchar.charAt(0); //**** Do we need to check for empty str?1207 if (nextchar.length() > 1)1208 nextchar = nextchar.substring(1);1209 else1210 nextchar = "";1211 1212 String temp = null;1213 if (optstring.indexOf(c) != -1)1214 temp = optstring.substring(optstring.indexOf(c));1215 1216 if (nextchar.equals(""))1217 ++optind;1218 1219 if ((temp == null) || (c == ':'))1220 {1221 if (opterr)1222 {1223 if (posixly_correct)1224 {1225 // 1003.2 specifies the format of this message1226 Object[] msgArgs = { progname,1227 Character.toString((char)c) };1228 System.err.println(MessageFormat.format(1229 _messages.getString("getopt.illegal"), msgArgs));1230 }1231 else1232 {1233 Object[] msgArgs = { progname,1234 Character.toString((char)c) };1235 System.err.println(MessageFormat.format(1236 _messages.getString("getopt.invalid"), msgArgs));1237 }1238 }1239 1240 optopt = c;1241 1242 return('?');1243 }1244 1245 // Convenience. Treat POSIX -W foo same as long option --foo1246 if ((temp.charAt(0) == 'W') && (temp.length() > 1) && (temp.charAt(1) == ';'))1247 {1248 if (!nextchar.equals(""))1249 {1250 optarg = nextchar;1251 }1252 // No further cars in this argv element and no more argv elements1253 else if (optind == argv.length)1254 {1255 if (opterr)1256 {1257 // 1003.2 specifies the format of this message.1258 Object[] msgArgs = { progname,1259 Character.toString((char)c) };1260 System.err.println(MessageFormat.format(1261 _messages.getString("getopt.requires2"), msgArgs));1262 }1263 1264 optopt = c;1265 if (optstring.charAt(0) == ':')1266 return(':');1267 else1268 return('?');1269 }1270 else1271 {1272 // We already incremented `optind' once;1273 // increment it again when taking next ARGV-elt as argument.1274 nextchar = argv[optind];1275 optarg = argv[optind];1276 }1277 1278 c = checkLongOption();1279 1280 if (longopt_handled)1281 return(c);1282 else1283 // Let the application handle it1284 {1285 nextchar = null;1286 ++optind;1287 return('W');1288 }1289 }1290 1291 if ((temp.length() > 1) && (temp.charAt(1) == ':'))1292 {1293 if ((temp.length() > 2) && (temp.charAt(2) == ':'))1294 // This is an option that accepts and argument optionally1295 {1296 if (!nextchar.equals(""))1297 {1298 optarg = nextchar;1299 ++optind;1300 }1301 else1302 {1303 optarg = null;1304 }1305 1306 nextchar = null;1307 }1308 else1309 {1310 if (!nextchar.equals(""))1311 {1312 optarg = nextchar;1313 ++optind;1314 }1315 else if (optind == argv.length)1316 {1317 if (opterr)1318 {1319 // 1003.2 specifies the format of this message1320 Object[] msgArgs = { progname,1321 Character.toString((char)c) };1322 System.err.println(MessageFormat.format(1323 _messages.getString("getopt.requires2"), msgArgs));1324 }1325 1326 optopt = c;1327 1328 if (optstring.charAt(0) == ':')1329 return(':');1330 else1331 return('?');1332 }1333 else1334 {1335 optarg = argv[optind];1336 ++optind;1337 1338 // Ok, here's an obscure Posix case. If we have o:, and1339 // we get -o -- foo, then we're supposed to skip the --,1340 // end parsing of options, and make foo an operand to -o.1341 // Only do this in Posix mode.1342 if ((posixly_correct) && optarg.equals("--"))1343 {1344 // If end of argv, error out1345 if (optind == argv.length)1346 {1347 if (opterr)1348 {1349 // 1003.2 specifies the format of this message1350 Object[] msgArgs = { progname,1351 Character.toString((char)c) };1352 System.err.println(MessageFormat.format(1353 _messages.getString("getopt.requires2"), msgArgs));1354 }1355 1356 optopt = c;1357 1358 if (optstring.charAt(0) == ':')1359 return(':');1360 else1361 return('?');1362 }1363 1364 // Set new optarg and set to end1365 // Don't permute as we do on -- up above since we1366 // know we aren't in permute mode because of Posix.1367 optarg = argv[optind];1368 ++optind;1369 first_nonopt = optind;1370 last_nonopt = argv.length;1371 endparse = true;1372 }1373 }1374 1375 nextchar = null;1376 }1377 }1378 1379 return(c);1380 }1381 1382 } // Class Getopt1383 1384 -
src/gnu/getopt/LongOpt.java
1 /**************************************************************************2 /* LongOpt.java -- Long option object for Getopt3 /*4 /* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com)5 /*6 /* This program is free software; you can redistribute it and/or modify7 /* it under the terms of the GNU Library General Public License as published8 /* by the Free Software Foundation; either version 2 of the License or9 /* (at your option) any later version.10 /*11 /* This program is distributed in the hope that it will be useful, but12 /* WITHOUT ANY WARRANTY; without even the implied warranty of13 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the14 /* GNU Library General Public License for more details.15 /*16 /* You should have received a copy of the GNU Library General Public License17 /* along with this program; see the file COPYING.LIB. If not, write to18 /* the Free Software Foundation Inc., 59 Temple Place - Suite 330,19 /* Boston, MA 02111-1307 USA20 /**************************************************************************/21 22 package gnu.getopt;23 24 import java.text.MessageFormat;25 26 /**************************************************************************/27 28 /**29 * This object represents the definition of a long option in the Java port30 * of GNU getopt. An array of LongOpt objects is passed to the Getopt31 * object to define the list of valid long options for a given parsing32 * session. Refer to the getopt documentation for details on the33 * format of long options.34 *35 * @version 1.0.536 * @author Aaron M. Renn (arenn@urbanophile.com)37 *38 * @see Getopt39 */40 public class LongOpt extends Object41 {42 43 /**************************************************************************/44 45 /*46 * Class Variables47 */48 49 /**50 * Constant value used for the "has_arg" constructor argument. This51 * value indicates that the option takes no argument.52 */53 public static final int NO_ARGUMENT = 0;54 55 /**56 * Constant value used for the "has_arg" constructor argument. This57 * value indicates that the option takes an argument that is required.58 */59 public static final int REQUIRED_ARGUMENT = 1;60 61 /**62 * Constant value used for the "has_arg" constructor argument. This63 * value indicates that the option takes an argument that is optional.64 */65 public static final int OPTIONAL_ARGUMENT = 2;66 67 /**************************************************************************/68 69 /*70 * Instance Variables71 */72 73 /**74 * The name of the long option75 */76 protected String name;77 78 /**79 * Indicates whether the option has no argument, a required argument, or80 * an optional argument.81 */82 protected int has_arg;83 84 /**85 * If this variable is not null, then the value stored in "val" is stored86 * here when this long option is encountered. If this is null, the value87 * stored in "val" is treated as the name of an equivalent short option.88 */89 protected StringBuffer flag;90 91 /**92 * The value to store in "flag" if flag is not null, otherwise the93 * equivalent short option character for this long option.94 */95 protected int val;96 97 /**98 * Localized strings for error messages99 */100 private Getopt.OptI18n _messages = new Getopt.OptI18n(); // ResourceBundle.getBundle("gnu/getopt/MessagesBundle", Locale.getDefault());101 102 /**************************************************************************/103 104 /*105 * Constructors106 */107 108 /**109 * Create a new LongOpt object with the given parameter values. If the110 * value passed as has_arg is not valid, then an exception is thrown.111 *112 * @param name The long option String.113 * @param has_arg Indicates whether the option has no argument (NO_ARGUMENT), a required argument (REQUIRED_ARGUMENT) or an optional argument (OPTIONAL_ARGUMENT).114 * @param flag If non-null, this is a location to store the value of "val" when this option is encountered, otherwise "val" is treated as the equivalent short option character.115 * @param val The value to return for this long option, or the equivalent single letter option to emulate if flag is null.116 *117 * @exception IllegalArgumentException If the has_arg param is not one of NO_ARGUMENT, REQUIRED_ARGUMENT or OPTIONAL_ARGUMENT.118 */119 public120 LongOpt(String name, int has_arg,121 StringBuffer flag, int val) throws IllegalArgumentException122 {123 // Validate has_arg124 if ((has_arg != NO_ARGUMENT) && (has_arg != REQUIRED_ARGUMENT)125 && (has_arg != OPTIONAL_ARGUMENT))126 {127 Object[] msgArgs = { Integer.toString(has_arg) };128 throw new IllegalArgumentException(MessageFormat.format(129 _messages.getString("getopt.invalidValue"), msgArgs));130 }131 132 // Store off values133 this.name = name;134 this.has_arg = has_arg;135 this.flag = flag;136 this.val = val;137 }138 139 /**************************************************************************/140 141 /**142 * Returns the name of this LongOpt as a String143 *144 * @return Then name of the long option145 */146 public String147 getName()148 {149 return(name);150 }151 152 /**************************************************************************/153 154 /**155 * Returns the value set for the 'has_arg' field for this long option156 *157 * @return The value of 'has_arg'158 */159 public int160 getHasArg()161 {162 return(has_arg);163 }164 165 /**************************************************************************/166 167 /**168 * Returns the value of the 'flag' field for this long option169 *170 * @return The value of 'flag'171 */172 public StringBuffer173 getFlag()174 {175 return(flag);176 }177 178 /**179 * Returns the value of the 'val' field for this long option180 *181 * @return The value of 'val'182 */183 public int184 getVal()185 {186 return(val);187 }188 189 /**************************************************************************/190 191 } // Class LongOpt192 -
src/org/openstreetmap/josm/data/projection/ProjectionCLI.java
11 11 import java.nio.file.Files; 12 12 import java.nio.file.Paths; 13 13 import java.util.ArrayList; 14 import java.util.Arrays; 14 15 import java.util.List; 15 16 import java.util.function.Function; 16 17 … … 18 19 import org.openstreetmap.josm.data.coor.EastNorth; 19 20 import org.openstreetmap.josm.data.coor.LatLon; 20 21 import org.openstreetmap.josm.data.coor.conversion.LatLonParser; 21 import org.openstreetmap.josm.tools. I18n;22 import org.openstreetmap.josm.tools.OptionParser; 22 23 import org.openstreetmap.josm.tools.Utils; 23 24 24 import gnu.getopt.Getopt;25 import gnu.getopt.LongOpt;26 27 25 /** 28 26 * Command line interface for projecting coordinates. 29 27 * @since 12792 … … 43 41 44 42 @Override 45 43 public void processArguments(String[] argArray) { 46 Getopt.setI18nHandler(I18n::tr); 47 Getopt getopt = new Getopt("JOSM projection", argArray, "Irh", new LongOpt[] { 48 new LongOpt("help", LongOpt.NO_ARGUMENT, null, 'h')}); 44 List<String> positionalArguments = new OptionParser("JOSM projection") 45 .addFlagParameter("help", this::showHelp) 46 .addShortAlias("help", "h") 47 .addFlagParameter("inverse", () -> argInverse = true) 48 .addShortAlias("inverse", "I") 49 .addFlagParameter("switch-input", () -> argSwitchInput = true) 50 .addShortAlias("switch-input", "r") 51 .addFlagParameter("switch-output", () -> argSwitchOutput = true) 52 .addShortAlias("switch-output", "s") 53 .parseOptionsOrExit(Arrays.asList(argArray)); 49 54 50 int c;51 while ((c = getopt.getopt()) != -1) {52 switch (c) {53 case 'h':54 showHelp();55 System.exit(0);56 case 'I':57 argInverse = true;58 break;59 case 'r':60 argSwitchInput = true;61 break;62 case 's':63 argSwitchOutput = true;64 break;65 default:66 // ignore67 }68 }69 70 55 List<String> projParamFrom = new ArrayList<>(); 71 56 List<String> projParamTo = new ArrayList<>(); 72 57 List<String> otherPositional = new ArrayList<>(); 73 58 boolean toTokenSeen = false; 74 59 // positional arguments: 75 for (int i = getopt.getOptind(); i < argArray.length; ++i) { 76 String arg = argArray[i]; 60 for (String arg: positionalArguments) { 77 61 if (arg.isEmpty()) throw new IllegalArgumentException("non-empty argument expected"); 78 62 if (arg.startsWith("+")) { 79 63 if ("+to".equals(arg)) { … … 99 83 /** 100 84 * Displays help on the console 101 85 */ 102 p ublic staticvoid showHelp() {86 private void showHelp() { 103 87 System.out.println(getHelp()); 88 System.exit(0); 104 89 } 105 90 106 91 private static String getHelp() { -
src/org/openstreetmap/josm/gui/ProgramArguments.java
2 2 package org.openstreetmap.josm.gui; 3 3 4 4 import java.util.ArrayList; 5 import java.util.Arrays; 5 6 import java.util.Collection; 6 7 import java.util.Collections; 7 8 import java.util.EnumMap; … … 13 14 import java.util.logging.Level; 14 15 import java.util.stream.Stream; 15 16 16 import org.openstreetmap.josm.tools.I18n;17 17 import org.openstreetmap.josm.tools.Logging; 18 import org.openstreetmap.josm.tools.OptionParser; 19 import org.openstreetmap.josm.tools.OptionParser.OptionCount; 18 20 19 import gnu.getopt.Getopt;20 import gnu.getopt.LongOpt;21 22 21 /** 23 22 * This class holds the arguments passed on to {@link MainApplication#main}. 24 23 * @author Michael Zangl … … 90 89 public boolean requiresArgument() { 91 90 return requiresArg; 92 91 } 93 94 LongOpt toLongOpt() {95 return new LongOpt(getName(), requiresArgument() ? LongOpt.REQUIRED_ARGUMENT : LongOpt.NO_ARGUMENT, null, 0);96 }97 92 } 98 93 99 94 private final Map<Option, List<String>> argMap = new EnumMap<>(Option.class); … … 113 108 * @param args command-line arguments array 114 109 */ 115 110 private void buildCommandLineArgumentMap(String... args) { 116 Getopt.setI18nHandler(I18n::tr); 117 LongOpt[] los = Stream.of(Option.values()).map(Option::toLongOpt).toArray(LongOpt[]::new); 118 Getopt g = new Getopt("JOSM", args, "hv", los); 119 120 int c; 121 while ((c = g.getopt()) != -1) { 122 Option opt; 123 switch (c) { 124 case 'h': 125 opt = Option.HELP; 126 break; 127 case 'v': 128 opt = Option.VERSION; 129 break; 130 case 0: 131 opt = Option.values()[g.getLongind()]; 132 break; 133 default: 134 opt = null; 111 OptionParser parser = new OptionParser("JOSM"); 112 for (Option o : Option.values()) { 113 if (o.requiresArgument()) { 114 parser.addArgumentParameter(o.getName(), OptionCount.MULTIPLE, p -> addOption(o, p)); 115 } else { 116 parser.addFlagParameter(o.getName(), () -> addOption(o, "")); 135 117 } 136 if (opt != null) {137 addOption(opt, g.getOptarg());138 } else139 throw new IllegalArgumentException("Invalid option: "+ (char) c);140 118 } 119 120 parser.addShortAlias(Option.HELP.getName(), "h"); 121 parser.addShortAlias(Option.VERSION.getName(), "v"); 122 123 List<String> remaining = parser.parseOptionsOrExit(Arrays.asList(args)); 124 141 125 // positional arguments are a shortcut for the --download ... option 142 for ( int i = g.getOptind(); i < args.length; ++i) {143 addOption(Option.DOWNLOAD, arg s[i]);126 for (String arg : remaining) { 127 addOption(Option.DOWNLOAD, arg); 144 128 } 145 129 } 146 130 -
src/org/openstreetmap/josm/gui/mappaint/RenderingCLI.java
11 11 import java.nio.file.Files; 12 12 import java.nio.file.Paths; 13 13 import java.util.ArrayList; 14 import java.util.Arrays; 14 15 import java.util.List; 15 16 import java.util.Locale; 16 17 import java.util.Optional; … … 37 38 import org.openstreetmap.josm.io.OsmReader; 38 39 import org.openstreetmap.josm.spi.preferences.Config; 39 40 import org.openstreetmap.josm.spi.preferences.MemoryPreferences; 40 import org.openstreetmap.josm.tools.I18n;41 41 import org.openstreetmap.josm.tools.JosmDecimalFormatSymbolsProvider; 42 42 import org.openstreetmap.josm.tools.Logging; 43 import org.openstreetmap.josm.tools.OptionParser; 44 import org.openstreetmap.josm.tools.OptionParser.OptionCount; 45 import org.openstreetmap.josm.tools.OptionParser.OptionParseException; 43 46 import org.openstreetmap.josm.tools.RightAndLefthandTraffic; 44 47 45 import gnu.getopt.Getopt;46 import gnu.getopt.LongOpt;47 48 48 /** 49 49 * Command line interface for rendering osm data to an image file. 50 50 * … … 76 76 private String argProjection; 77 77 private Integer argMaxImageSize; 78 78 79 private StyleData argCurrentStyle; 80 79 81 private enum Option { 80 HELP(false, 'h'), 81 DEBUG(false, '*'), 82 TRACE(false, '*'), 83 INPUT(true, 'i'), 84 STYLE(true, 's'), 85 SETTING(true, '*'), 86 OUTPUT(true, 'o'), 87 ZOOM(true, 'z'), 88 SCALE(true, '*'), 89 BOUNDS(true, 'b'), 90 ANCHOR(true, '*'), 91 WIDTH_M(true, '*'), 92 HEIGHT_M(true, '*'), 93 WIDTH_PX(true, '*'), 94 HEIGHT_PX(true, '*'), 95 PROJECTION(true, '*'), 96 MAX_IMAGE_SIZE(true, '*'); 82 HELP(false, 'h'), DEBUG(false, '*'), TRACE(false, '*'), INPUT(true, 'i'), STYLE(true, 's'), SETTING(true, 83 '*'), OUTPUT(true, 'o'), ZOOM(true, 'z'), SCALE(true, '*'), BOUNDS(true, 'b'), ANCHOR(true, 84 '*'), WIDTH_M(true, '*'), HEIGHT_M(true, '*'), WIDTH_PX(true, 85 '*'), HEIGHT_PX(true, '*'), PROJECTION(true, '*'), MAX_IMAGE_SIZE(true, '*'); 97 86 98 87 private final String name; 99 88 private final boolean requiresArg; … … 128 117 public char getShortOption() { 129 118 return shortOption; 130 119 } 131 132 LongOpt toLongOpt() {133 return new LongOpt(getName(), requiresArgument() ? LongOpt.REQUIRED_ARGUMENT : LongOpt.NO_ARGUMENT, null, getShortOption());134 }135 120 } 136 121 137 122 /** … … 187 172 * @param argArray the arguments array 188 173 */ 189 174 void parseArguments(String[] argArray) { 190 Getopt.setI18nHandler(I18n::tr);191 175 Logging.setLogLevel(Level.INFO); 192 176 193 LongOpt[] opts = new LongOpt[Option.values().length]; 194 StringBuilder optString = new StringBuilder(); 177 OptionParser parser = new OptionParser("JOSM rendering"); 195 178 for (Option o : Option.values()) { 196 opts[o.ordinal()] = o.toLongOpt(); 179 if (o.requiresArgument()) { 180 parser.addArgumentParameter(o.getName(), 181 o == Option.SETTING ? OptionCount.MULTIPLE : OptionCount.OPTIONAL, 182 arg -> handleOption(o, arg)); 183 } else { 184 parser.addFlagParameter(o.getName(), () -> handleOption(o)); 185 } 197 186 if (o.getShortOption() != '*') { 198 optString.append(o.getShortOption()); 199 if (o.requiresArgument()) { 200 optString.append(':'); 201 } 187 parser.addShortAlias(o.getName(), o.getShortOption() + ""); 202 188 } 203 189 } 204 190 205 Getopt getopt = new Getopt("JOSM rendering", argArray, optString.toString(), opts); 206 207 StyleData currentStyle = new StyleData(); 191 argCurrentStyle = new StyleData(); 208 192 argStyles = new ArrayList<>(); 209 193 210 int c; 211 while ((c = getopt.getopt()) != -1) { 212 switch (c) { 213 case 'h': 214 showHelp(); 215 System.exit(0); 216 case 'i': 217 argInput = getopt.getOptarg(); 218 break; 219 case 's': 220 if (currentStyle.styleUrl != null) { 221 argStyles.add(currentStyle); 222 currentStyle = new StyleData(); 223 } 224 currentStyle.styleUrl = getopt.getOptarg(); 225 break; 226 case 'o': 227 argOutput = getopt.getOptarg(); 228 break; 229 case 'z': 194 parser.parseOptionsOrExit(Arrays.asList(argArray)); 195 196 if (argCurrentStyle.styleUrl != null) { 197 argStyles.add(argCurrentStyle); 198 } 199 } 200 201 private void handleOption(Option o) { 202 switch (o) { 203 case HELP: 204 showHelp(); 205 System.exit(0); 206 break; 207 case DEBUG: 208 argDebug = true; 209 break; 210 case TRACE: 211 argTrace = true; 212 break; 213 default: 214 throw new AssertionError("Unexpected option index: " + o); 215 } 216 } 217 218 private void handleOption(Option o, String arg) { 219 switch (o) { 220 case INPUT: 221 argInput = arg; 222 break; 223 case STYLE: 224 if (argCurrentStyle.styleUrl != null) { 225 argStyles.add(argCurrentStyle); 226 argCurrentStyle = new StyleData(); 227 } 228 argCurrentStyle.styleUrl = arg; 229 break; 230 case OUTPUT: 231 argOutput = arg; 232 break; 233 case ZOOM: 234 try { 235 argZoom = Integer.valueOf(arg); 236 } catch (NumberFormatException nfe) { 237 throw new OptionParseException( 238 tr("Expected integer number for option {0}, but got ''{1}''", "--zoom", arg), nfe); 239 } 240 if (argZoom < 0) { 241 throw new OptionParseException( 242 tr("Expected integer number >= 0 for option {0}, but got ''{1}''", "--zoom", arg)); 243 } 244 break; 245 case BOUNDS: 246 if (!"auto".equals(arg)) { 230 247 try { 231 arg Zoom = Integer.valueOf(getopt.getOptarg());232 } catch ( NumberFormatException nfe) {233 throw new IllegalArgumentException(234 tr(" Expected integer number for option {0}, but got ''{1}''", "--zoom", getopt.getOptarg()), nfe);248 argBounds = new Bounds(arg, ",", Bounds.ParseMethod.LEFT_BOTTOM_RIGHT_TOP, false); 249 } catch (IllegalArgumentException iae) { // NOPMD 250 throw new OptionParseException( 251 tr("Unable to parse {0} parameter: {1}", "--bounds", iae.getMessage()), iae); 235 252 } 236 if (argZoom < 0)237 throw new IllegalArgumentException(238 tr("Expected integer number >= 0 for option {0}, but got ''{1}''", "--zoom", getopt.getOptarg()));239 break;240 case 'b':241 if (!"auto".equals(getopt.getOptarg())) {242 try {243 argBounds = new Bounds(getopt.getOptarg(), ",", Bounds.ParseMethod.LEFT_BOTTOM_RIGHT_TOP, false);244 } catch (IllegalArgumentException iae) { // NOPMD245 throw new IllegalArgumentException(tr("Unable to parse {0} parameter: {1}", "--bounds", iae.getMessage()), iae);246 }247 }248 break;249 case '*':250 switch (Option.values()[getopt.getLongind()]) {251 case DEBUG:252 argDebug = true;253 break;254 case TRACE:255 argTrace = true;256 break;257 case SETTING:258 String keyval = getopt.getOptarg();259 String[] comp = keyval.split(":");260 if (comp.length != 2)261 throw new IllegalArgumentException(262 tr("Expected key and value, separated by '':'' character for option {0}, but got ''{1}''",263 "--setting", getopt.getOptarg()));264 currentStyle.settings.put(comp[0].trim(), comp[1].trim());265 break;266 case SCALE:267 try {268 argScale = JosmDecimalFormatSymbolsProvider.parseDouble(getopt.getOptarg());269 } catch (NumberFormatException nfe) {270 throw new IllegalArgumentException(271 tr("Expected floating point number for option {0}, but got ''{1}''", "--scale", getopt.getOptarg()), nfe);272 }273 break;274 case ANCHOR:275 String[] parts = getopt.getOptarg().split(",");276 if (parts.length != 2)277 throw new IllegalArgumentException(278 tr("Expected two coordinates, separated by comma, for option {0}, but got ''{1}''",279 "--anchor", getopt.getOptarg()));280 try {281 double lon = LatLonParser.parseCoordinate(parts[0]);282 double lat = LatLonParser.parseCoordinate(parts[1]);283 argAnchor = new LatLon(lat, lon);284 } catch (IllegalArgumentException iae) { // NOPMD285 throw new IllegalArgumentException(tr("In option {0}: {1}", "--anchor", iae.getMessage()), iae);286 }287 break;288 case WIDTH_M:289 try {290 argWidthM = JosmDecimalFormatSymbolsProvider.parseDouble(getopt.getOptarg());291 } catch (NumberFormatException nfe) {292 throw new IllegalArgumentException(293 tr("Expected floating point number for option {0}, but got ''{1}''", "--width-m", getopt.getOptarg()), nfe);294 }295 if (argWidthM <= 0) throw new IllegalArgumentException(296 tr("Expected floating point number > 0 for option {0}, but got ''{1}''", "--width-m", getopt.getOptarg()));297 break;298 case HEIGHT_M:299 try {300 argHeightM = JosmDecimalFormatSymbolsProvider.parseDouble(getopt.getOptarg());301 } catch (NumberFormatException nfe) {302 throw new IllegalArgumentException(303 tr("Expected floating point number for option {0}, but got ''{1}''", "--height-m", getopt.getOptarg()), nfe);304 }305 if (argHeightM <= 0) throw new IllegalArgumentException(306 tr("Expected floating point number > 0 for option {0}, but got ''{1}''", "--width-m", getopt.getOptarg()));307 break;308 case WIDTH_PX:309 try {310 argWidthPx = Integer.valueOf(getopt.getOptarg());311 } catch (NumberFormatException nfe) {312 throw new IllegalArgumentException(313 tr("Expected integer number for option {0}, but got ''{1}''", "--width-px", getopt.getOptarg()), nfe);314 }315 if (argWidthPx <= 0) throw new IllegalArgumentException(316 tr("Expected integer number > 0 for option {0}, but got ''{1}''", "--width-px", getopt.getOptarg()));317 break;318 case HEIGHT_PX:319 try {320 argHeightPx = Integer.valueOf(getopt.getOptarg());321 } catch (NumberFormatException nfe) {322 throw new IllegalArgumentException(323 tr("Expected integer number for option {0}, but got ''{1}''", "--height-px", getopt.getOptarg()), nfe);324 }325 if (argHeightPx <= 0) throw new IllegalArgumentException(326 tr("Expected integer number > 0 for option {0}, but got ''{1}''", "--height-px", getopt.getOptarg()));327 break;328 case PROJECTION:329 argProjection = getopt.getOptarg();330 break;331 case MAX_IMAGE_SIZE:332 try {333 argMaxImageSize = Integer.valueOf(getopt.getOptarg());334 } catch (NumberFormatException nfe) {335 throw new IllegalArgumentException(336 tr("Expected integer number for option {0}, but got ''{1}''", "--max-image-size", getopt.getOptarg()), nfe);337 }338 if (argMaxImageSize < 0) throw new IllegalArgumentException(339 tr("Expected integer number >= 0 for option {0}, but got ''{1}''", "--max-image-size", getopt.getOptarg()));340 break;341 default:342 throw new AssertionError("Unexpected option index: " + getopt.getLongind());343 }344 break;345 case '?':346 throw new IllegalArgumentException(); // getopt error347 default:348 throw new AssertionError("Unrecognized option: " + c);349 253 } 254 break; 255 256 case SETTING: 257 String keyval = arg; 258 String[] comp = keyval.split(":", 2); 259 if (comp.length != 2) { 260 throw new OptionParseException( 261 tr("Expected key and value, separated by '':'' character for option {0}, but got ''{1}''", 262 "--setting", arg)); 263 } 264 argCurrentStyle.settings.put(comp[0].trim(), comp[1].trim()); 265 break; 266 case SCALE: 267 try { 268 argScale = JosmDecimalFormatSymbolsProvider.parseDouble(arg); 269 } catch (NumberFormatException nfe) { 270 throw new OptionParseException( 271 tr("Expected floating point number for option {0}, but got ''{1}''", "--scale", arg), nfe); 272 } 273 break; 274 case ANCHOR: 275 String[] parts = arg.split(","); 276 if (parts.length != 2) 277 throw new OptionParseException( 278 tr("Expected two coordinates, separated by comma, for option {0}, but got ''{1}''", "--anchor", 279 arg)); 280 try { 281 double lon = LatLonParser.parseCoordinate(parts[0]); 282 double lat = LatLonParser.parseCoordinate(parts[1]); 283 argAnchor = new LatLon(lat, lon); 284 } catch (IllegalArgumentException iae) { // NOPMD 285 throw new OptionParseException(tr("In option {0}: {1}", "--anchor", iae.getMessage()), iae); 286 } 287 break; 288 case WIDTH_M: 289 try { 290 argWidthM = JosmDecimalFormatSymbolsProvider.parseDouble(arg); 291 } catch (NumberFormatException nfe) { 292 throw new OptionParseException( 293 tr("Expected floating point number for option {0}, but got ''{1}''", "--width-m", arg), nfe); 294 } 295 if (argWidthM <= 0) 296 throw new OptionParseException( 297 tr("Expected floating point number > 0 for option {0}, but got ''{1}''", "--width-m", arg)); 298 break; 299 case HEIGHT_M: 300 try { 301 argHeightM = JosmDecimalFormatSymbolsProvider.parseDouble(arg); 302 } catch (NumberFormatException nfe) { 303 throw new OptionParseException( 304 tr("Expected floating point number for option {0}, but got ''{1}''", "--height-m", arg), nfe); 305 } 306 if (argHeightM <= 0) 307 throw new OptionParseException( 308 tr("Expected floating point number > 0 for option {0}, but got ''{1}''", "--width-m", arg)); 309 break; 310 case WIDTH_PX: 311 try { 312 argWidthPx = Integer.valueOf(arg); 313 } catch (NumberFormatException nfe) { 314 throw new OptionParseException( 315 tr("Expected integer number for option {0}, but got ''{1}''", "--width-px", arg), nfe); 316 } 317 if (argWidthPx <= 0) 318 throw new OptionParseException( 319 tr("Expected integer number > 0 for option {0}, but got ''{1}''", "--width-px", arg)); 320 break; 321 case HEIGHT_PX: 322 try { 323 argHeightPx = Integer.valueOf(arg); 324 } catch (NumberFormatException nfe) { 325 throw new OptionParseException( 326 tr("Expected integer number for option {0}, but got ''{1}''", "--height-px", arg), nfe); 327 } 328 if (argHeightPx <= 0) { 329 throw new OptionParseException( 330 tr("Expected integer number > 0 for option {0}, but got ''{1}''", "--height-px", arg)); 331 } 332 break; 333 case PROJECTION: 334 argProjection = arg; 335 break; 336 case MAX_IMAGE_SIZE: 337 try { 338 argMaxImageSize = Integer.valueOf(arg); 339 } catch (NumberFormatException nfe) { 340 throw new OptionParseException( 341 tr("Expected integer number for option {0}, but got ''{1}''", "--max-image-size", arg), nfe); 342 } 343 if (argMaxImageSize < 0) { 344 throw new OptionParseException( 345 tr("Expected integer number >= 0 for option {0}, but got ''{1}''", "--max-image-size", arg)); 346 } 347 break; 348 default: 349 throw new AssertionError("Unexpected option index: " + o); 350 350 } 351 if (currentStyle.styleUrl != null) {352 argStyles.add(currentStyle);353 }354 351 } 355 352 356 353 /** … … 361 358 } 362 359 363 360 private static String getHelp() { 364 return tr("JOSM rendering command line interface")+"\n\n"+ 365 tr("Usage")+":\n"+ 366 "\tjava -jar josm.jar render <options>\n\n"+ 367 tr("Description")+":\n"+ 368 tr("Renders data and saves the result to an image file.")+"\n\n"+ 369 tr("Options")+":\n"+ 370 "\t--help|-h "+tr("Show this help")+"\n"+ 371 "\t--input|-i <file> "+tr("Input data file name (.osm)")+"\n"+ 372 "\t--output|-o <file> "+tr("Output image file name (.png); defaults to ''{0}''", "out.png")+"\n"+ 373 "\t--style|-s <file> "+tr("Style file to use for rendering (.mapcss or .zip)")+"\n"+ 374 "\t "+tr("This option can be repeated to load multiple styles.")+"\n"+ 375 "\t--setting <key>:<value> "+tr("Style setting (in JOSM accessible in the style list dialog right click menu)")+"\n"+ 376 "\t "+tr("Applies to the last style loaded with the {0} option.", "--style")+"\n"+ 377 "\t--zoom|-z <lvl> "+tr("Select zoom level to render. (integer value, 0=entire earth, 18=street level)")+"\n"+ 378 "\t--scale <scale> "+tr("Select the map scale")+"\n"+ 379 "\t "+tr("A value of 10000 denotes a scale of 1:10000 (1 cm on the map equals 100 m on the ground; " 380 + "display resolution: 96 dpi)")+"\n"+ 381 "\t "+tr("Options {0} and {1} are mutually exclusive.", "--zoom", "--scale")+"\n"+ 382 "\t--bounds|-b auto|<min_lon>,<min_lat>,<max_lon>,<max_lat>\n"+ 383 "\t "+tr("Area to render, default value is ''{0}''", "auto")+"\n"+ 384 "\t "+tr("With keyword ''{0}'', the downloaded area in the .osm input file will be used (if recorded).", 385 "auto")+"\n"+ 386 "\t--anchor <lon>,<lat> "+tr("Specify bottom left corner of the rendering area")+"\n"+ 387 "\t "+tr("Used in combination with width and height options to determine the area to render.")+"\n"+ 388 "\t--width-m <number> "+tr("Width of the rendered area, in meter")+"\n"+ 389 "\t--height-m <number> "+tr("Height of the rendered area, in meter")+"\n"+ 390 "\t--width-px <number> "+tr("Width of the target image, in pixel")+"\n"+ 391 "\t--height-px <number> "+tr("Height of the target image, in pixel")+"\n"+ 392 "\t--projection <code> "+tr("Projection to use, default value ''{0}'' (web-Mercator)", "epsg:3857")+"\n"+ 393 "\t--max-image-size <number> "+tr("Maximum image width/height in pixel (''{0}'' means no limit), default value: {1}", 394 0, Integer.toString(DEFAULT_MAX_IMAGE_SIZE))+"\n"+ 395 "\n"+ 396 tr("To specify the rendered area and scale, the options can be combined in various ways")+":\n"+ 397 " * --bounds (--zoom|--scale|--width-px|--height-px)\n"+ 398 " * --anchor (--width-m|--width-px) (--height-m|--height-px) (--zoom|--scale)\n"+ 399 " * --anchor --width-m --height-m (--width-px|--height-px)\n"+ 400 " * --anchor --width-px --height-px (--width-m|--height-m)\n"+ 401 tr("If neither ''{0}'' nor ''{1}'' is given, the default value {2} takes effect " 402 + "and the bounds of the download area in the .osm input file are used.", 403 "bounds", "anchor", "--bounds=auto")+"\n\n"+ 404 tr("Examples")+":\n"+ 405 " java -jar josm.jar render -i data.osm -s style.mapcss -z 16\n"+ 406 " josm render -i data.osm -s style.mapcss --scale 5000\n"+ 407 " josm render -i data.osm -s style.mapcss -z 16 -o image.png\n"+ 408 " josm render -i data.osm -s elemstyles.mapcss --setting hide_icons:false -z 16\n"+ 409 " josm render -i data.osm -s style.mapcss -s another_style.mapcss -z 16 -o image.png\n"+ 410 " josm render -i data.osm -s style.mapcss --bounds 21.151,51.401,21.152,51.402 -z 16\n"+ 411 " josm render -i data.osm -s style.mapcss --anchor 21.151,51.401 --width-m 500 --height-m 300 -z 16\n"+ 412 " josm render -i data.osm -s style.mapcss --anchor 21.151,51.401 --width-m 500 --height-m 300 --width-px 1800\n"+ 413 " josm render -i data.osm -s style.mapcss --scale 5000 --projection epsg:4326\n"; 361 return tr("JOSM rendering command line interface") + "\n\n" + tr("Usage") + ":\n" 362 + "\tjava -jar josm.jar render <options>\n\n" + tr("Description") + ":\n" 363 + tr("Renders data and saves the result to an image file.") + "\n\n" + tr("Options") + ":\n" 364 + "\t--help|-h " + tr("Show this help") + "\n" + "\t--input|-i <file> " 365 + tr("Input data file name (.osm)") + "\n" + "\t--output|-o <file> " 366 + tr("Output image file name (.png); defaults to ''{0}''", "out.png") + "\n" 367 + "\t--style|-s <file> " + tr("Style file to use for rendering (.mapcss or .zip)") + "\n" 368 + "\t " + tr("This option can be repeated to load multiple styles.") + "\n" 369 + "\t--setting <key>:<value> " 370 + tr("Style setting (in JOSM accessible in the style list dialog right click menu)") + "\n" 371 + "\t " 372 + tr("Applies to the last style loaded with the {0} option.", "--style") + "\n" 373 + "\t--zoom|-z <lvl> " 374 + tr("Select zoom level to render. (integer value, 0=entire earth, 18=street level)") + "\n" 375 + "\t--scale <scale> " + tr("Select the map scale") + "\n" + "\t " 376 + tr("A value of 10000 denotes a scale of 1:10000 (1 cm on the map equals 100 m on the ground; " 377 + "display resolution: 96 dpi)") 378 + "\n" + "\t " 379 + tr("Options {0} and {1} are mutually exclusive.", "--zoom", "--scale") + "\n" 380 + "\t--bounds|-b auto|<min_lon>,<min_lat>,<max_lon>,<max_lat>\n" + "\t " 381 + tr("Area to render, default value is ''{0}''", "auto") + "\n" + "\t " 382 + tr("With keyword ''{0}'', the downloaded area in the .osm input file will be used (if recorded).", 383 "auto") 384 + "\n" + "\t--anchor <lon>,<lat> " + tr("Specify bottom left corner of the rendering area") + "\n" 385 + "\t " 386 + tr("Used in combination with width and height options to determine the area to render.") + "\n" 387 + "\t--width-m <number> " + tr("Width of the rendered area, in meter") + "\n" 388 + "\t--height-m <number> " + tr("Height of the rendered area, in meter") + "\n" 389 + "\t--width-px <number> " + tr("Width of the target image, in pixel") + "\n" 390 + "\t--height-px <number> " + tr("Height of the target image, in pixel") + "\n" 391 + "\t--projection <code> " 392 + tr("Projection to use, default value ''{0}'' (web-Mercator)", "epsg:3857") + "\n" 393 + "\t--max-image-size <number> " 394 + tr("Maximum image width/height in pixel (''{0}'' means no limit), default value: {1}", 0, 395 Integer.toString(DEFAULT_MAX_IMAGE_SIZE)) 396 + "\n" + "\n" 397 + tr("To specify the rendered area and scale, the options can be combined in various ways") + ":\n" 398 + " * --bounds (--zoom|--scale|--width-px|--height-px)\n" 399 + " * --anchor (--width-m|--width-px) (--height-m|--height-px) (--zoom|--scale)\n" 400 + " * --anchor --width-m --height-m (--width-px|--height-px)\n" 401 + " * --anchor --width-px --height-px (--width-m|--height-m)\n" 402 + tr("If neither ''{0}'' nor ''{1}'' is given, the default value {2} takes effect " 403 + "and the bounds of the download area in the .osm input file are used.", "bounds", "anchor", 404 "--bounds=auto") 405 + "\n\n" + tr("Examples") + ":\n" + " java -jar josm.jar render -i data.osm -s style.mapcss -z 16\n" 406 + " josm render -i data.osm -s style.mapcss --scale 5000\n" 407 + " josm render -i data.osm -s style.mapcss -z 16 -o image.png\n" 408 + " josm render -i data.osm -s elemstyles.mapcss --setting hide_icons:false -z 16\n" 409 + " josm render -i data.osm -s style.mapcss -s another_style.mapcss -z 16 -o image.png\n" 410 + " josm render -i data.osm -s style.mapcss --bounds 21.151,51.401,21.152,51.402 -z 16\n" 411 + " josm render -i data.osm -s style.mapcss --anchor 21.151,51.401 --width-m 500 --height-m 300 -z 16\n" 412 + " josm render -i data.osm -s style.mapcss --anchor 21.151,51.401 --width-m 500 --height-m 300 --width-px 1800\n" 413 + " josm render -i data.osm -s style.mapcss --scale 5000 --projection epsg:4326\n"; 414 414 } 415 415 416 416 /** … … 451 451 Projection proj = ProjectionRegistry.getProjection(); 452 452 Double scale = null; // scale in east-north units per pixel 453 453 if (argZoom != null) { 454 scale = OsmMercator.EARTH_RADIUS * Math.PI * 2 / Math.pow(2, argZoom) / OsmMercator.DEFAUL_TILE_SIZE / proj.getMetersPerUnit(); 454 scale = OsmMercator.EARTH_RADIUS * Math.PI * 2 / Math.pow(2, argZoom) / OsmMercator.DEFAUL_TILE_SIZE 455 / proj.getMetersPerUnit(); 455 456 } 456 457 Bounds bounds = argBounds; 457 458 ProjectionBounds pb = null; … … 463 464 double enPerMeter = Double.NaN; 464 465 DoubleSupplier getEnPerMeter = () -> { 465 466 double shiftMeter = 10; 466 EastNorth projAnchorShifted = projAnchor.add( 467 shiftMeter / proj.getMetersPerUnit() , shiftMeter / proj.getMetersPerUnit());467 EastNorth projAnchorShifted = projAnchor.add(shiftMeter / proj.getMetersPerUnit(), 468 shiftMeter / proj.getMetersPerUnit()); 468 469 LatLon anchorShifted = proj.eastNorth2latlon(projAnchorShifted); 469 470 return projAnchor.distance(projAnchorShifted) / argAnchor.greatCircleDistance(anchorShifted); 470 471 }; … … 481 482 scale = argHeightM / argHeightPx * enPerMeter; 482 483 } else { 483 484 throw new IllegalArgumentException( 484 tr("Argument {0} given, but scale cannot be determined from remaining arguments", "--anchor")); 485 tr("Argument {0} given, but scale cannot be determined from remaining arguments", 486 "--anchor")); 485 487 } 486 488 } 487 489 … … 516 518 bounds.extend(proj.eastNorth2latlon(pb.getMax())); 517 519 } else { 518 520 if (ds.getDataSourceBounds().isEmpty()) { 519 throw new IllegalArgumentException(tr("{0} mode, but no bounds found in osm data input file", "--bounds=auto")); 521 throw new IllegalArgumentException( 522 tr("{0} mode, but no bounds found in osm data input file", "--bounds=auto")); 520 523 } 521 524 bounds = ds.getDataSourceBounds().get(0); 522 525 } … … 530 533 531 534 if (scale == null) { 532 535 if (argScale != null) { 533 double enPerMeter = pb.getMin().distance(pb.getMax()) / bounds.getMin().greatCircleDistance(bounds.getMax()); 536 double enPerMeter = pb.getMin().distance(pb.getMax()) 537 / bounds.getMin().greatCircleDistance(bounds.getMax()); 534 538 scale = argScale * enPerMeter / PIXEL_PER_METER; 535 539 } else if (argWidthPx != null) { 536 540 scale = (pb.maxEast - pb.minEast) / argWidthPx; … … 538 542 scale = (pb.maxNorth - pb.minNorth) / argHeightPx; 539 543 } else { 540 544 throw new IllegalArgumentException( 541 tr("Unable to determine scale, one of the options {0}, {1}, {2} or {3} expected", 542 "-- zoom", "--scale", "--width-px", "--height-px"));545 tr("Unable to determine scale, one of the options {0}, {1}, {2} or {3} expected", "--zoom", 546 "--scale", "--width-px", "--height-px")); 543 547 } 544 548 } 545 549 -
src/org/openstreetmap/josm/tools/OptionParser.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.tools; 3 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 6 import java.util.HashMap; 7 import java.util.LinkedList; 8 import java.util.List; 9 import java.util.Map.Entry; 10 import java.util.Objects; 11 import java.util.function.Consumer; 12 import java.util.stream.Collectors; 13 14 /** 15 * A replacement of getopt. 16 * <p> 17 * Allows parsing command line options 18 * 19 * @author Michael Zangl 20 */ 21 public class OptionParser { 22 23 private HashMap<String, AvailableOption> availableOptions = new HashMap<>(); 24 private final String program; 25 26 /** 27 * Create a new option parser. 28 * @param program The program name. 29 */ 30 public OptionParser(String program) { 31 Objects.requireNonNull(program, "program name must be provided"); 32 this.program = program; 33 } 34 35 /** 36 * Adds an alias for the long option --optionName to the short version -name 37 * @param optionName The long option 38 * @param shortName The short version 39 * @return this {@link OptionParser} 40 */ 41 public OptionParser addShortAlias(String optionName, String shortName) { 42 if (!shortName.matches("\\w")) { 43 throw new IllegalArgumentException("Short name " + shortName + " must be one character"); 44 } 45 if (availableOptions.containsKey("-" + shortName)) { 46 throw new IllegalArgumentException("Short name " + shortName + " is already used"); 47 } 48 AvailableOption longDefinition = availableOptions.get("--" + optionName); 49 if (longDefinition == null) { 50 throw new IllegalArgumentException("No long definition for " + optionName 51 + " was defined. Define the long definition first before creating " + "a short definition for it."); 52 } 53 availableOptions.put("-" + shortName, longDefinition); 54 return this; 55 } 56 57 /** 58 * Adds an option that may be used as a flag, e.g. --debug 59 * @param optionName The parameter name 60 * @param handler The handler that is called when the flag is encountered. 61 * @return this {@link OptionParser} 62 */ 63 public OptionParser addFlagParameter(String optionName, Runnable handler) { 64 checkOptionName(optionName); 65 availableOptions.put("--" + optionName, new AvailableOption() { 66 @Override 67 public void runFor(String parameter) { 68 handler.run(); 69 } 70 }); 71 return this; 72 } 73 74 private void checkOptionName(String optionName) { 75 if (!optionName.matches("\\w([\\w-]*\\w)?")) { 76 throw new IllegalArgumentException("Illegal option name: " + optionName); 77 } 78 if (availableOptions.containsKey("--" + optionName)) { 79 throw new IllegalArgumentException("The option --" + optionName + " is already registered"); 80 } 81 } 82 83 /** 84 * Add a parameter that expects a string attribute. E.g.: --config=/path/to/file 85 * @param optionName The name of the parameter. 86 * @param count The number of times the parameter may occur. 87 * @param handler A function that gets the current object and the parameter. It should throw an {@link OptionParseException} if the parameter cannot be handled / is invalid. 88 * @return this {@link OptionParser} 89 */ 90 public OptionParser addArgumentParameter(String optionName, OptionCount count, Consumer<String> handler) { 91 checkOptionName(optionName); 92 availableOptions.put("--" + optionName, new AvailableOption() { 93 @Override 94 public boolean requiresParameter() { 95 return true; 96 } 97 98 @Override 99 public OptionCount getRequiredCount() { 100 return count; 101 } 102 103 @Override 104 public void runFor(String parameter) { 105 Objects.requireNonNull(parameter, "parameter"); 106 handler.accept(parameter); 107 } 108 }); 109 return this; 110 } 111 112 /** 113 * Same as {@link #parseOptions(List)}, but exits if option parsing fails. 114 * @param arguments The options 115 * @return The remaining program arguments that are no options. 116 */ 117 public List<String> parseOptionsOrExit(List<String> arguments) { 118 try { 119 return parseOptions(arguments); 120 } catch (OptionParseException e) { 121 System.err.println(e.getLocalizedMessage()); 122 System.exit(1); 123 // unreachable, but makes compilers happy 124 throw e; 125 } 126 } 127 128 /** 129 * Parses the options. 130 * <p> 131 * It first checks if all required options are present, if all options are known and validates the option count. 132 * <p> 133 * Then, all option handlers are called in the order in which the options are encountered. 134 * @param arguments Program arguments 135 * @return The remaining program arguments that are no options. 136 * @throws OptionParseException The error to display if option parsing failed. 137 */ 138 public List<String> parseOptions(List<String> arguments) { 139 LinkedList<String> toHandle = new LinkedList<>(arguments); 140 List<String> remainingArguments = new LinkedList<>(); 141 boolean argumentOnlyMode = false; 142 List<FoundOption> options = new LinkedList<>(); 143 144 while (!toHandle.isEmpty()) { 145 String next = toHandle.removeFirst(); 146 if (argumentOnlyMode || !next.matches("-.+")) { 147 // argument found, add it to arguments list 148 remainingArguments.add(next); 149 } else if ("--".equals(next)) { 150 // we are done, the remaining should be used as arguments. 151 argumentOnlyMode = true; 152 } else { 153 if (next.matches("-\\w\\w+")) { 154 // special case: concatenated short options like -hv 155 // We handle them as if the user wrote -h -v by just scheduling the remainder for the next loop. 156 toHandle.addFirst("-" + next.substring(2)); 157 next = next.substring(0, 2); 158 } 159 160 String[] split = next.split("=", 2); 161 String optionName = split[0]; 162 AvailableOption definition = findParameter(optionName); 163 String parameter = null; 164 if (definition.requiresParameter()) { 165 if (split.length > 1) { 166 parameter = split[1]; 167 } else { 168 if (toHandle.isEmpty() || toHandle.getFirst().equals("--")) { 169 throw new OptionParseException(tr("{0}: option ''{1}'' requires an argument", program)); 170 } 171 parameter = toHandle.removeFirst(); 172 } 173 } else if (split.length > 1) { 174 throw new OptionParseException( 175 tr("{0}: option ''{1}'' does not allow an argument", program, optionName)); 176 } 177 options.add(new FoundOption(optionName, definition, parameter)); 178 } 179 } 180 181 // Count how often they are used 182 availableOptions.values().stream().distinct().forEach(def -> { 183 long count = options.stream().filter(p -> def.equals(p.option)).count(); 184 if (count < def.getRequiredCount().min) { 185 // min may be 0 or 1 at the moment 186 throw new OptionParseException(tr("{0}: option ''{1}'' is required")); 187 } else if (count > def.getRequiredCount().max) { 188 // max may be 1 or MAX_INT at the moment 189 throw new OptionParseException(tr("{0}: option ''{1}'' may not appear multiple times")); 190 } 191 }); 192 193 // Actually apply the parameters. 194 for (FoundOption option : options) { 195 try { 196 option.option.runFor(option.parameter); 197 } catch (OptionParseException e) { 198 String message; 199 // Just add a nicer error message 200 if (option.parameter == null) { 201 message = tr("{0}: Error while handling option ''{1}''", program, option.optionName); 202 } else { 203 message = tr("{0}: Invalid value {2} for option ''{1}''", program, option.optionName, 204 option.parameter); 205 } 206 if (!e.getLocalizedMessage().isEmpty()) { 207 message += ": " + e.getLocalizedMessage().isEmpty(); 208 } 209 throw new OptionParseException(message); 210 } 211 } 212 return remainingArguments; 213 } 214 215 private AvailableOption findParameter(String optionName) { 216 AvailableOption exactMatch = availableOptions.get(optionName); 217 if (exactMatch != null) { 218 return exactMatch; 219 } else if (optionName.startsWith("--")) { 220 List<AvailableOption> alternatives = availableOptions.entrySet().stream() 221 .filter(entry -> entry.getKey().startsWith(optionName)).map(Entry::getValue).distinct() 222 .collect(Collectors.toList()); 223 224 if (alternatives.size() == 1) { 225 return alternatives.get(0); 226 } else if (alternatives.size() > 1) { 227 throw new OptionParseException(tr("{0}: option ''{1}'' is ambiguous", program)); 228 } 229 } 230 throw new OptionParseException(tr("{0}: unrecognized option ''{1}''", program, optionName)); 231 } 232 233 /** 234 * How often an option may / must be specified on the command line. 235 * @author Michael Zangl 236 */ 237 public enum OptionCount { 238 /** 239 * The option may be specified once 240 */ 241 OPTIONAL(0, 1), 242 /** 243 * The option is required exactly once 244 */ 245 REQUIRED(1, 1), 246 /** 247 * The option may be specified multiple times 248 */ 249 MULTIPLE(0, Integer.MAX_VALUE); 250 251 private int min; 252 private int max; 253 254 OptionCount(int min, int max) { 255 this.min = min; 256 this.max = max; 257 258 } 259 } 260 261 protected abstract class AvailableOption { 262 263 public boolean requiresParameter() { 264 return false; 265 } 266 267 public OptionCount getRequiredCount() { 268 return OptionCount.OPTIONAL; 269 } 270 271 /** 272 * Called once if the parameter is encountered, afer basic validation. 273 * @param parameter The parameter if {@link #requiresParameter()} is true, <code>null</code> otherwise. 274 */ 275 public abstract void runFor(String parameter); 276 277 } 278 279 private static class FoundOption { 280 private final String optionName; 281 private final AvailableOption option; 282 private final String parameter; 283 284 FoundOption(String optionName, AvailableOption option, String parameter) { 285 this.optionName = optionName; 286 this.option = option; 287 this.parameter = parameter; 288 } 289 } 290 291 /** 292 * @author Michael Zangl 293 */ 294 public static class OptionParseException extends RuntimeException { 295 // Don't rely on JAVA handling this correctly. 296 private final String localizedMessage; 297 298 /** 299 * Create an empty error with no description 300 */ 301 public OptionParseException() { 302 super(); 303 localizedMessage = ""; 304 } 305 306 /** 307 * @param localizedMessage 308 */ 309 public OptionParseException(String localizedMessage) { 310 super(localizedMessage); 311 this.localizedMessage = localizedMessage; 312 } 313 314 /** 315 * @param localizedMessage 316 * @param t 317 */ 318 public OptionParseException(String localizedMessage, Throwable t) { 319 super(localizedMessage, t); 320 this.localizedMessage = localizedMessage; 321 } 322 323 @Override 324 public String getLocalizedMessage() { 325 return localizedMessage; 326 } 327 } 328 } -
test/unit/org/openstreetmap/josm/tools/OptionParserTest.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.tools; 3 4 import static org.junit.Assert.assertEquals; 5 6 import java.util.ArrayList; 7 import java.util.Arrays; 8 import java.util.List; 9 import java.util.concurrent.atomic.AtomicBoolean; 10 import java.util.concurrent.atomic.AtomicReference; 11 12 import org.junit.Test; 13 import org.openstreetmap.josm.tools.OptionParser.OptionParseException; 14 import org.openstreetmap.josm.tools.OptionParser.OptionCount; 15 16 /** 17 * Test for {@link OptionParser} 18 * @author Michael Zangl 19 */ 20 public class OptionParserTest { 21 22 // A reason for moving to jupter... 23 @Test(expected = OptionParseException.class) 24 public void testEmptyParserRejectsLongopt() { 25 new OptionParser("test").parseOptions(Arrays.asList("--long")); 26 } 27 28 @Test(expected = OptionParseException.class) 29 public void testEmptyParserRejectsShortopt() { 30 new OptionParser("test").parseOptions(Arrays.asList("-s")); 31 } 32 33 @Test(expected = OptionParseException.class) 34 public void testParserRejectsWrongShortopt() { 35 new OptionParser("test").addFlagParameter("test", this::nop).addShortAlias("test", "t") 36 .parseOptions(Arrays.asList("-s")); 37 } 38 39 @Test(expected = OptionParseException.class) 40 public void testParserRejectsWrongLongopt() { 41 new OptionParser("test").addFlagParameter("test", this::nop).parseOptions(Arrays.asList("--wrong")); 42 } 43 44 @Test 45 public void testparserOption() { 46 AtomicReference<String> argFound = new AtomicReference<>(); 47 OptionParser parser = new OptionParser("test") 48 .addArgumentParameter("test", OptionCount.REQUIRED, argFound::set); 49 50 parser.parseOptions(Arrays.asList("--test", "arg")); 51 assertEquals("arg", argFound.get()); 52 } 53 54 @Test(expected = OptionParseException.class) 55 public void testparserOptionFailsIfMissing() { 56 AtomicReference<String> argFound = new AtomicReference<>(); 57 OptionParser parser = new OptionParser("test") 58 .addArgumentParameter("test", OptionCount.REQUIRED, argFound::set); 59 60 parser.parseOptions(Arrays.asList("--test2", "arg")); 61 } 62 63 @Test(expected = OptionParseException.class) 64 public void testparserOptionFailsIfMissingArgument() { 65 AtomicReference<String> argFound = new AtomicReference<>(); 66 OptionParser parser = new OptionParser("test") 67 .addArgumentParameter("test", OptionCount.REQUIRED, argFound::set); 68 69 parser.parseOptions(Arrays.asList("--test2", "--other")); 70 } 71 72 @Test(expected = OptionParseException.class) 73 public void testparserOptionFailsIfMissing2() { 74 AtomicReference<String> argFound = new AtomicReference<>(); 75 OptionParser parser = new OptionParser("test") 76 .addArgumentParameter("test", OptionCount.REQUIRED, argFound::set); 77 78 parser.parseOptions(Arrays.asList("--", "--test", "arg")); 79 } 80 81 @Test(expected = OptionParseException.class) 82 public void testparserOptionFailsIfTwice() { 83 AtomicReference<String> argFound = new AtomicReference<>(); 84 OptionParser parser = new OptionParser("test") 85 .addArgumentParameter("test", OptionCount.REQUIRED, argFound::set); 86 87 parser.parseOptions(Arrays.asList("--test", "arg", "--test", "arg")); 88 } 89 90 @Test(expected = OptionParseException.class) 91 public void testparserOptionFailsIfTwiceForAlias() { 92 AtomicReference<String> argFound = new AtomicReference<>(); 93 OptionParser parser = new OptionParser("test") 94 .addArgumentParameter("test", OptionCount.REQUIRED, argFound::set) 95 .addShortAlias("test", "t"); 96 97 parser.parseOptions(Arrays.asList("--test", "arg", "-t", "arg")); 98 } 99 100 @Test(expected = OptionParseException.class) 101 public void testOptionalOptionFailsIfTwice() { 102 OptionParser parser = new OptionParser("test") 103 .addFlagParameter("test", this::nop); 104 parser.parseOptions(Arrays.asList("--test", "--test")); 105 } 106 107 @Test(expected = OptionParseException.class) 108 public void testOptionalOptionFailsIfTwiceForAlias() { 109 OptionParser parser = new OptionParser("test") 110 .addFlagParameter("test", this::nop) 111 .addShortAlias("test", "t"); 112 parser.parseOptions(Arrays.asList("-t", "-t")); 113 } 114 115 @Test(expected = OptionParseException.class) 116 public void testOptionalOptionFailsIfTwiceForAlias2() { 117 OptionParser parser = new OptionParser("test") 118 .addFlagParameter("test", this::nop) 119 .addShortAlias("test", "t"); 120 parser.parseOptions(Arrays.asList("-tt")); 121 } 122 123 @Test 124 public void testLongArgumentsUsingEqualSign() { 125 AtomicReference<String> argFound = new AtomicReference<>(); 126 OptionParser parser = new OptionParser("test") 127 .addArgumentParameter("test", OptionCount.REQUIRED, argFound::set); 128 129 List<String> remaining = parser.parseOptions(Arrays.asList("--test=arg", "value")); 130 131 assertEquals(Arrays.asList("value"), remaining); 132 assertEquals("arg", argFound.get()); 133 134 remaining = parser.parseOptions(Arrays.asList("--test=", "value")); 135 136 assertEquals(Arrays.asList("value"), remaining); 137 assertEquals("", argFound.get()); 138 139 remaining = parser.parseOptions(Arrays.asList("--test=with space and=equals", "value")); 140 141 assertEquals(Arrays.asList("value"), remaining); 142 assertEquals("with space and=equals", argFound.get()); 143 } 144 145 @Test(expected = OptionParseException.class) 146 public void testLongArgumentsMissingOption() { 147 OptionParser parser = new OptionParser("test") 148 .addArgumentParameter("test", OptionCount.REQUIRED, this::nop); 149 150 parser.parseOptions(Arrays.asList("--test")); 151 } 152 153 @Test(expected = OptionParseException.class) 154 public void testLongArgumentsMissingOption2() { 155 OptionParser parser = new OptionParser("test") 156 .addArgumentParameter("test", OptionCount.REQUIRED, this::nop); 157 158 parser.parseOptions(Arrays.asList("--test", "--", "x")); 159 } 160 161 162 @Test(expected = OptionParseException.class) 163 public void testShortArgumentsMissingOption() { 164 OptionParser parser = new OptionParser("test") 165 .addArgumentParameter("test", OptionCount.REQUIRED, this::nop) 166 .addShortAlias("test", "t"); 167 168 parser.parseOptions(Arrays.asList("-t")); 169 } 170 171 @Test(expected = OptionParseException.class) 172 public void testShortArgumentsMissingOption2() { 173 OptionParser parser = new OptionParser("test") 174 .addArgumentParameter("test", OptionCount.REQUIRED, this::nop) 175 .addShortAlias("test", "t"); 176 177 parser.parseOptions(Arrays.asList("-t", "--", "x")); 178 } 179 180 @Test(expected = OptionParseException.class) 181 public void testLongFlagHasOption() { 182 OptionParser parser = new OptionParser("test") 183 .addFlagParameter("test", this::nop); 184 185 parser.parseOptions(Arrays.asList("--test=arg")); 186 } 187 188 @Test(expected = OptionParseException.class) 189 public void testShortFlagHasOption() { 190 OptionParser parser = new OptionParser("test") 191 .addFlagParameter("test", this::nop) 192 .addShortAlias("test", "t"); 193 194 parser.parseOptions(Arrays.asList("-t=arg")); 195 } 196 197 @Test 198 public void testShortArgumentsUsingEqualSign() { 199 AtomicReference<String> argFound = new AtomicReference<>(); 200 OptionParser parser = new OptionParser("test") 201 .addArgumentParameter("test", OptionCount.REQUIRED, argFound::set) 202 .addShortAlias("test", "t"); 203 204 List<String> remaining = parser.parseOptions(Arrays.asList("-t=arg", "value")); 205 206 assertEquals(Arrays.asList("value"), remaining); 207 assertEquals("arg", argFound.get()); 208 } 209 210 @Test 211 public void testMultipleArguments() { 212 AtomicReference<String> argFound = new AtomicReference<>(); 213 List<String> multiFound = new ArrayList<>(); 214 AtomicBoolean usedFlag = new AtomicBoolean(); 215 AtomicBoolean unusedFlag = new AtomicBoolean(); 216 OptionParser parser = new OptionParser("test") 217 .addArgumentParameter("test", OptionCount.REQUIRED, argFound::set) 218 .addShortAlias("test", "t") 219 .addArgumentParameter("multi", OptionCount.MULTIPLE, multiFound::add) 220 .addFlagParameter("flag", () -> usedFlag.set(true)) 221 .addFlagParameter("flag2", () -> unusedFlag.set(true)); 222 223 List<String> remaining = parser.parseOptions(Arrays.asList("-t=arg", "--multi", "m1", "x1", "--flag", "--multi", "m2", "x2", "--", "x3", "--x4", "x5")); 224 225 assertEquals(Arrays.asList("x1", "x2", "x3", "--x4", "x5"), remaining); 226 assertEquals("arg", argFound.get()); 227 assertEquals(Arrays.asList("m1", "m2"), multiFound); 228 assertEquals(true, usedFlag.get()); 229 assertEquals(false, unusedFlag.get()); 230 } 231 232 @Test 233 public void testUseAlternatives() { 234 AtomicReference<String> argFound = new AtomicReference<>(); 235 AtomicBoolean usedFlag = new AtomicBoolean(); 236 AtomicBoolean unusedFlag = new AtomicBoolean(); 237 238 OptionParser parser = new OptionParser("test") 239 .addArgumentParameter("test", OptionCount.REQUIRED, argFound::set) 240 .addFlagParameter("flag", () -> usedFlag.set(true)) 241 .addFlagParameter("fleg", () -> unusedFlag.set(true)); 242 243 List<String> remaining = parser.parseOptions(Arrays.asList("--te=arg", "--fla")); 244 245 assertEquals(Arrays.asList(), remaining); 246 assertEquals("arg", argFound.get()); 247 assertEquals(true, usedFlag.get()); 248 assertEquals(false, unusedFlag.get()); 249 } 250 251 @Test(expected = OptionParseException.class) 252 public void testAbigiousAlternatives() { 253 AtomicReference<String> argFound = new AtomicReference<>(); 254 AtomicBoolean usedFlag = new AtomicBoolean(); 255 AtomicBoolean unusedFlag = new AtomicBoolean(); 256 257 OptionParser parser = new OptionParser("test") 258 .addArgumentParameter("test", OptionCount.REQUIRED, argFound::set) 259 .addFlagParameter("flag", () -> usedFlag.set(true)) 260 .addFlagParameter("fleg", () -> unusedFlag.set(true)); 261 262 parser.parseOptions(Arrays.asList("--te=arg", "--fl")); 263 } 264 265 @Test(expected = OptionParseException.class) 266 public void testMultipleShort() { 267 AtomicReference<String> argFound = new AtomicReference<>(); 268 AtomicBoolean usedFlag = new AtomicBoolean(); 269 AtomicBoolean unusedFlag = new AtomicBoolean(); 270 271 OptionParser parser = new OptionParser("test") 272 .addArgumentParameter("test", OptionCount.REQUIRED, argFound::set) 273 .addShortAlias("test", "t") 274 .addFlagParameter("flag", () -> usedFlag.set(true)) 275 .addShortAlias("flag", "f") 276 .addFlagParameter("fleg", () -> unusedFlag.set(true)); 277 278 List<String> remaining = parser.parseOptions(Arrays.asList("-ft=arg", "x")); 279 280 assertEquals(Arrays.asList("x"), remaining); 281 assertEquals("arg", argFound.get()); 282 assertEquals(true, usedFlag.get()); 283 assertEquals(false, unusedFlag.get()); 284 285 remaining = parser.parseOptions(Arrays.asList("-ft", "arg", "x")); 286 287 assertEquals(Arrays.asList("x"), remaining); 288 assertEquals("arg", argFound.get()); 289 assertEquals(true, usedFlag.get()); 290 assertEquals(false, unusedFlag.get()); 291 292 remaining = parser.parseOptions(Arrays.asList("-f", "-t=arg", "x")); 293 294 assertEquals(Arrays.asList("x"), remaining); 295 assertEquals("arg", argFound.get()); 296 assertEquals(true, usedFlag.get()); 297 assertEquals(false, unusedFlag.get()); 298 } 299 300 301 @Test(expected = IllegalArgumentException.class) 302 public void testIllegalOptionName() { 303 new OptionParser("test").addFlagParameter("", this::nop); 304 } 305 306 @Test(expected = IllegalArgumentException.class) 307 public void testIllegalOptionName2() { 308 new OptionParser("test").addFlagParameter("-", this::nop); 309 } 310 311 @Test(expected = IllegalArgumentException.class) 312 public void testIllegalOptionName3() { 313 new OptionParser("test").addFlagParameter("-test", this::nop); 314 } 315 316 @Test(expected = IllegalArgumentException.class) 317 public void testIllegalOptionName4() { 318 new OptionParser("test").addFlagParameter("$", this::nop); 319 } 320 321 @Test(expected = IllegalArgumentException.class) 322 public void testDupplicateOptionName() { 323 new OptionParser("test").addFlagParameter("test", this::nop).addFlagParameter("test", this::nop); 324 } 325 326 @Test(expected = IllegalArgumentException.class) 327 public void testDupplicateOptionName2() { 328 new OptionParser("test").addFlagParameter("test", this::nop) 329 .addArgumentParameter("test", OptionCount.OPTIONAL, this::nop); 330 } 331 332 @Test(expected = IllegalArgumentException.class) 333 public void testInvalidShortAlias() { 334 new OptionParser("test").addFlagParameter("test", this::nop).addShortAlias("test", "$"); 335 } 336 337 @Test(expected = IllegalArgumentException.class) 338 public void testInvalidShortAlias2() { 339 new OptionParser("test").addFlagParameter("test", this::nop).addShortAlias("test", ""); 340 } 341 342 @Test(expected = IllegalArgumentException.class) 343 public void testInvalidShortAlias3() { 344 new OptionParser("test").addFlagParameter("test", this::nop).addShortAlias("test", "xx"); 345 } 346 347 @Test(expected = IllegalArgumentException.class) 348 public void testDupplicateShortAlias() { 349 new OptionParser("test").addFlagParameter("test", this::nop) 350 .addFlagParameter("test2", this::nop) 351 .addShortAlias("test", "t") 352 .addShortAlias("test2", "t"); 353 } 354 355 @Test(expected = IllegalArgumentException.class) 356 public void testInvalidShortNoLong() { 357 new OptionParser("test").addFlagParameter("test", this::nop).addShortAlias("test2", "t"); 358 } 359 360 private void nop() { 361 // nop 362 } 363 364 private void nop(String arg) { 365 // nop 366 } 367 }