0001: /*
0002: * Copyright (C) 2007 Stephen Ostermiller
0003: * http://ostermiller.org/contact.pl?regarding=Java+Utilities
0004: *
0005: * This program is free software; you can redistribute it and/or modify
0006: * it under the terms of the GNU General Public License as published by
0007: * the Free Software Foundation; either version 2 of the License, or
0008: * (at your option) any later version.
0009: *
0010: * This program is distributed in the hope that it will be useful,
0011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0013: * GNU General Public License for more details.
0014: *
0015: * See COPYING.TXT for details.
0016: */
0017: package com.Ostermiller.util;
0018:
0019: import java.io.*;
0020: import java.util.*;
0021:
0022: /**
0023: * Command line argument parser for Java command line programs.
0024: *
0025: * More information about this class and code samples for suggested use are
0026: * available from <a target="_top" href=
0027: * "http://ostermiller.org/utils/CmdLn.html">ostermiller.org</a>.
0028: *
0029: * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
0030: * @since ostermillerutils 1.07.00
0031: */
0032: public final class CmdLn {
0033:
0034: /**
0035: * The original unparsed command line arguments as
0036: * passed in to the constructor.
0037: *
0038: * @since ostermillerutils 1.07.00
0039: */
0040: private String[] arguments;
0041:
0042: /**
0043: * Description to be put in the help.
0044: *
0045: * @since ostermillerutils 1.07.00
0046: */
0047: private String description;
0048:
0049: /**
0050: * A list of all the options.
0051: * Filled by the public addOptions() methods.
0052: *
0053: * @since ostermillerutils 1.07.00
0054: */
0055: private ArrayList<CmdLnOption> options = new ArrayList<CmdLnOption>();
0056:
0057: /**
0058: * Map the options to their found results.
0059: *
0060: * @since ostermillerutils 1.07.00
0061: */
0062: private HashMap<CmdLnOption, ArrayList<CmdLnResult>> optionsToResults = new HashMap<CmdLnOption, ArrayList<CmdLnResult>>();
0063:
0064: /**
0065: * List of all results that have been found.
0066: *
0067: * @since ostermillerutils 1.07.00
0068: */
0069: private ArrayList<CmdLnResult> results = new ArrayList<CmdLnResult>();
0070:
0071: /**
0072: * A list of the arguments that do not belong to any option.
0073: *
0074: * @since ostermillerutils 1.07.00
0075: */
0076: private ArrayList<String> leftOverArguments = new ArrayList<String>();
0077:
0078: /**
0079: * Mapping of long options to their command line option object.
0080: * Created during the parse method.
0081: *
0082: * @since ostermillerutils 1.07.00
0083: */
0084: private HashMap<String, CmdLnOption> longOptions;
0085:
0086: /**
0087: * Mapping of short options to their command line option object.
0088: * Created during the parse method.
0089: *
0090: * @since ostermillerutils 1.07.00
0091: */
0092: private HashMap<Character, CmdLnOption> shortOptions;
0093:
0094: /**
0095: * An ordered set of what long options start with. Typically contains
0096: * a single entry "--".
0097: *
0098: * @since ostermillerutils 1.07.00
0099: */
0100: private LinkedHashSet<String> longOptionsStart = new LinkedHashSet<String>(
0101: 1);
0102:
0103: /**
0104: * An ordered set of what short options start with. Typically contains
0105: * a single entry "-".
0106: *
0107: * @since ostermillerutils 1.07.00
0108: */
0109: private LinkedHashSet<String> shortOptionsStart = new LinkedHashSet<String>(
0110: 1);
0111:
0112: /**
0113: * A set of strings that indicate that everything following should be regarded
0114: * as non-option arguments.
0115: *
0116: * @since ostermillerutils 1.07.00
0117: */
0118: private HashSet<String> nonOptionSeparators = new HashSet<String>(1);
0119:
0120: /**
0121: * A set of strings that break an option up into an option and an argument.
0122: *
0123: * @since ostermillerutils 1.07.00
0124: */
0125: private HashSet<Character> optionArgumentDelimiters = new HashSet<Character>(
0126: 3);
0127:
0128: /**
0129: * Whether or not the parse method has been called yet.
0130: * Used to determine if setters are appropriate or should throw illegal state exceptions.
0131: *
0132: * @since ostermillerutils 1.07.00
0133: */
0134: private boolean parsed = false;
0135:
0136: /**
0137: * New command line options with the given command line arguments
0138: *
0139: * @param arguments command line arguments from main method
0140: *
0141: * @since ostermillerutils 1.07.00
0142: */
0143: public CmdLn(String[] arguments) {
0144: if (arguments == null)
0145: throw new IllegalArgumentException(
0146: "Command line arguments cannot be null.");
0147: for (String argument : arguments) {
0148: if (argument == null)
0149: throw new IllegalArgumentException(
0150: "Each command line argument cannot be null.");
0151: }
0152: this .arguments = arguments.clone();
0153:
0154: // Defaults
0155: longOptionsStart.add("--");
0156: shortOptionsStart.add("-");
0157: nonOptionSeparators.add("--");
0158: optionArgumentDelimiters.add('=');
0159: optionArgumentDelimiters.add(':');
0160: optionArgumentDelimiters.add(' ');
0161: }
0162:
0163: /**
0164: * Set the delimiters that separate a option name from a value
0165: * within a single argument.
0166: *
0167: * This is to support arguments of the form "--name=value". The
0168: * equals sign can separate the option name ("name") from the
0169: * option argument ("value").
0170: *
0171: * By default, the delimiters are '=', ':', and ' '.
0172: * @param delimiters list of delimiters
0173: * @return this for method chaining.
0174: *
0175: * @since ostermillerutils 1.07.00
0176: */
0177: public CmdLn setOptionArgumentDelimiters(char[] delimiters) {
0178: optionArgumentDelimiters.clear();
0179: for (char c : delimiters) {
0180: optionArgumentDelimiters.add(c);
0181: }
0182: return this ;
0183: }
0184:
0185: /**
0186: * Set what long options and short options start with.
0187: * Typically long options start with "--" and short options with "-"
0188: * (this is the default).
0189: *
0190: * @param longOptionsStart What long options start with (default "--") or null for no long options;
0191: * @param shortOptionsStart What short options start with (default "-") or null for no short options;
0192: * @throws IllegalArgumentException if the long or short start is the empty string
0193: * @return this for method chaining
0194: *
0195: * @since ostermillerutils 1.07.00
0196: */
0197: public CmdLn setOptionStarts(String longOptionsStart,
0198: String shortOptionsStart) {
0199: setOptionStarts(new String[] { longOptionsStart },
0200: new String[] { shortOptionsStart });
0201: return this ;
0202: }
0203:
0204: /**
0205: * Set what long options and short options start with.
0206: * Typically long options start with "--" and short options with "-" and
0207: * this is the default.
0208: * <p>
0209: * The first option start in each array will be the canonical option start
0210: * that is used in the help message.
0211: *
0212: * @param longOptionsStart What long options start with (default "--") or null or empty for no long options;
0213: * @param shortOptionsStart What short options start with (default "-") or null or empty for no short options;
0214: * @throws IllegalArgumentException if the long or short start is the empty string
0215: * @return this for method chaining
0216: *
0217: * @since ostermillerutils 1.07.00
0218: */
0219: public CmdLn setOptionStarts(String[] longOptionsStart,
0220: String[] shortOptionsStart) {
0221: this .longOptionsStart.clear();
0222: if (longOptionsStart != null) {
0223: for (String optionStart : longOptionsStart) {
0224: if (optionStart != null) {
0225: if ("".equals(optionStart))
0226: throw new IllegalArgumentException(
0227: "long option start cannot be the empty string");
0228: this .longOptionsStart.add(optionStart);
0229: }
0230: }
0231: }
0232: this .shortOptionsStart.clear();
0233: if (shortOptionsStart != null) {
0234: for (String optionStart : shortOptionsStart) {
0235: if (optionStart != null) {
0236: if ("".equals(optionStart))
0237: throw new IllegalArgumentException(
0238: "short option start cannot be the empty string");
0239: this .shortOptionsStart.add(optionStart);
0240: }
0241: }
0242: }
0243: return this ;
0244: }
0245:
0246: /**
0247: * Set the "stop" option that causes any following arguments to be treated
0248: * as non-option arguments, even if they look like an option.
0249: * <p>
0250: * The default non-option separator is "--".
0251: *
0252: * @param nonOptionSeparators List of arguments that stop processing options and treat remaining arguments as non option arguments.
0253: * @return this for method chaining
0254: *
0255: * @since ostermillerutils 1.07.00
0256: */
0257: public CmdLn setNonOptionSeparators(String[] nonOptionSeparators) {
0258: this .nonOptionSeparators.clear();
0259: if (nonOptionSeparators != null) {
0260: for (String nonOptionSeparator : nonOptionSeparators) {
0261: this .nonOptionSeparators.add(nonOptionSeparator);
0262: }
0263: }
0264: return this ;
0265: }
0266:
0267: /**
0268: * Set the description for the program. This description will be printed
0269: * on the first line of the help message.
0270: *
0271: * @param description short description about the program
0272: * @return this for method chaining
0273: *
0274: * @since ostermillerutils 1.07.00
0275: */
0276: public CmdLn setDescription(String description) {
0277: this .description = description;
0278: return this ;
0279: }
0280:
0281: /**
0282: * Add options.
0283: *
0284: * @param options options to be added.
0285: * @throws NullPointerException if the options are null.
0286: * @throws NullPointerException if any option in the collection is null.
0287: * @throws IllegalStateException if the command line has already been parsed.
0288: * @return this for method chaining
0289: *
0290: * @since ostermillerutils 1.07.00
0291: */
0292: public CmdLn addOptions(Collection<CmdLnOption> options) {
0293: for (CmdLnOption option : options) {
0294: addOption(option);
0295: }
0296: return this ;
0297: }
0298:
0299: /**
0300: * Add options.
0301: *
0302: * @param options options to be added.
0303: * @throws NullPointerException if the options are null.
0304: * @throws NullPointerException if any option in the collection is null.
0305: * @throws IllegalStateException if the command line has already been parsed.
0306: * @return this for method chaining
0307: *
0308: * @since ostermillerutils 1.07.00
0309: */
0310: public CmdLn addOptions(CmdLnOption[] options) {
0311: for (CmdLnOption option : options) {
0312: addOption(option);
0313: }
0314: return this ;
0315: }
0316:
0317: /**
0318: * Add option.
0319: *
0320: * @param option option to be added.
0321: * @throws NullPointerException if the option is null.
0322: * @throws IllegalStateException if the command line has already been parsed.
0323: * @return this for method chaining
0324: *
0325: * @since ostermillerutils 1.07.00
0326: */
0327: public CmdLn addOption(CmdLnOption option) {
0328: if (parsed)
0329: throw new IllegalStateException("Can no longer add options");
0330: options.add(option);
0331: return this ;
0332: }
0333:
0334: /**
0335: * Get the option associated with the given argument.
0336: * Parses the command line if not already parsed.
0337: *
0338: * @param s long argument
0339: * @return option associated with the given argument.
0340: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
0341: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
0342: * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
0343: *
0344: * @since ostermillerutils 1.07.00
0345: */
0346: private CmdLnOption getOption(String s) {
0347: parse();
0348: return longOptions.get(s);
0349: }
0350:
0351: /**
0352: * Get the option associated with the given argument.
0353: * Parses the command line if not already parsed.
0354: *
0355: * @param c short argument
0356: * @return option associated with the given argument.
0357: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
0358: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
0359: * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
0360: *
0361: * @since ostermillerutils 1.07.00
0362: */
0363: private CmdLnOption getOption(Character c) {
0364: parse();
0365: return shortOptions.get(c);
0366: }
0367:
0368: /**
0369: * Get the option associated with the given argument.
0370: * Parses the command line if not already parsed.
0371: *
0372: * @param c short argument
0373: * @return option associated with the given argument.
0374: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
0375: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
0376: * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
0377: *
0378: * @since ostermillerutils 1.07.00
0379: */
0380: private CmdLnOption getOption(char c) {
0381: parse();
0382: return shortOptions.get(c);
0383: }
0384:
0385: /**
0386: * Get the last result associated with the given argument.
0387: * If a option is in the command line multiple times, typically
0388: * the last one should be the one that wins as an override mechanism.
0389: * Parses the command line if not already parsed.
0390: *
0391: * @param s long argument
0392: * @return result for argument, or null if not associated with an option or not present in the command line.
0393: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
0394: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
0395: * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
0396: *
0397: * @since ostermillerutils 1.07.00
0398: */
0399: public CmdLnResult getResult(String s) {
0400: parse();
0401: return getResult(getOption(s));
0402: }
0403:
0404: /**
0405: * Get the last result associated with the given argument.
0406: * If a option is in the command line multiple times, typically
0407: * the last one should be the one that wins as an override mechanism.
0408: * Parses the command line if not already parsed.
0409: *
0410: * @param c short argument
0411: * @return result for argument, or null if not associated with an option or not present in the command line.
0412: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
0413: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
0414: * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
0415: *
0416: * @since ostermillerutils 1.07.00
0417: */
0418: public CmdLnResult getResult(Character c) {
0419: parse();
0420: return getResult(getOption(c));
0421: }
0422:
0423: /**
0424: * Get the last result associated with the given argument.
0425: * If a option is in the command line multiple times, typically
0426: * the last one should be the one that wins as an override mechanism.
0427: * Parses the command line if not already parsed.
0428: *
0429: * @param c short argument
0430: * @return result for argument, or null if not associated with an option or not present in the command line.
0431: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
0432: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
0433: * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
0434: *
0435: * @since ostermillerutils 1.07.00
0436: */
0437: public CmdLnResult getResult(char c) {
0438: parse();
0439: return getResult(getOption(c));
0440: }
0441:
0442: /**
0443: * Get the last result associated with the option.
0444: * If a option is in the command line multiple times, typically
0445: * the last one should be the one that wins as an override mechanism.
0446: * Parses the command line if not already parsed.
0447: *
0448: * @param option command line option
0449: * @return result for argument, or null if option not added to this command line or not present in the command line.
0450: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
0451: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
0452: * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
0453: *
0454: * @since ostermillerutils 1.07.00
0455: */
0456: public CmdLnResult getResult(CmdLnOption option) {
0457: parse();
0458: if (option == null)
0459: return null;
0460: ArrayList<CmdLnResult> results = optionsToResults.get(option);
0461: if (results == null)
0462: return null;
0463: return results.get(results.size() - 1);
0464: }
0465:
0466: /**
0467: * Get all the results in
0468: * the order in which they appear in the command line.
0469: * Parses the command line if not already parsed.
0470: * @return unmodifiable list of all options and arguments that were specified on the command line
0471: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
0472: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
0473: * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
0474: *
0475: * @since ostermillerutils 1.07.00
0476: */
0477: public List<CmdLnResult> getResults() {
0478: parse();
0479: return Collections.unmodifiableList(results);
0480: }
0481:
0482: /**
0483: * Get all the results associated with the given argument in
0484: * the order in which they appear in the command line.
0485: * If a option is in the command line multiple times, typically
0486: * the last one should be the one that wins as an override mechanism,
0487: * however it may sometimes be useful to know about all of them.
0488: * Parses the command line if not already parsed.
0489: *
0490: * @param s long argument
0491: * @return results for argument, or null if not associated with an option or not present in the command line.
0492: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
0493: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
0494: * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
0495: *
0496: * @since ostermillerutils 1.07.00
0497: */
0498: public List<CmdLnResult> getResults(String s) {
0499: parse();
0500: return getResults(getOption(s));
0501: }
0502:
0503: /**
0504: * Get all the results associated with the given argument in
0505: * the order in which they appear in the command line.
0506: * If a option is in the command line multiple times, typically
0507: * the last one should be the one that wins as an override mechanism,
0508: * however it may sometimes be useful to know about all of them.
0509: * Parses the command line if not already parsed.
0510: *
0511: * @param c short argument
0512: * @return results for argument, or null if not associated with an option or not present in the command line.
0513: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
0514: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
0515: *
0516: * @since ostermillerutils 1.07.00
0517: */
0518: public List<CmdLnResult> getResults(Character c) {
0519: parse();
0520: return getResults(getOption(c));
0521: }
0522:
0523: /**
0524: * Get all the results associated with the given argument in
0525: * the order in which they appear in the command line.
0526: * If a option is in the command line multiple times, typically
0527: * the last one should be the one that wins as an override mechanism,
0528: * however it may sometimes be useful to know about all of them.
0529: * Parses the command line if not already parsed.
0530: *
0531: * @param c short argument
0532: * @return results for argument, or null if not associated with an option or not present in the command line.
0533: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
0534: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
0535: * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
0536: *
0537: * @since ostermillerutils 1.07.00
0538: */
0539: public List<CmdLnResult> getResults(char c) {
0540: parse();
0541: return getResults(getOption(c));
0542: }
0543:
0544: /**
0545: * Get all the results associated with the given argument in
0546: * the order in which they appear in the command line.
0547: * If a option is in the command line multiple times, typically
0548: * the last one should be the one that wins as an override mechanism,
0549: * however it may sometimes be useful to know about all of them.
0550: * Parses the command line if not already parsed.
0551: *
0552: * @param option command line option
0553: * @return results for option, or null if option not added to this command line or not present in the command line.
0554: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
0555: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
0556: * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
0557: *
0558: * @since ostermillerutils 1.07.00
0559: */
0560: public List<CmdLnResult> getResults(CmdLnOption option) {
0561: parse();
0562: if (option == null)
0563: return null;
0564: ArrayList<CmdLnResult> results = optionsToResults.get(option);
0565: if (results == null)
0566: return null;
0567: return Collections.unmodifiableList(results);
0568: }
0569:
0570: /**
0571: * Whether or not the specified option is present in the command line.
0572: * Parses the command line if not already parsed.
0573: *
0574: * @param s long argument
0575: * @return true iff the option (or its synonyms) is present.
0576: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
0577: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
0578: * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
0579: *
0580: * @since ostermillerutils 1.07.00
0581: */
0582: public boolean present(String s) {
0583: parse();
0584: return present(getOption(s));
0585: }
0586:
0587: /**
0588: * Whether or not the specified option is present in the command line.
0589: * Parses the command line if not already parsed.
0590: *
0591: * @param c short argument
0592: * @return true iff the option (or its synonyms) is present.
0593: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
0594: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
0595: * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
0596: *
0597: * @since ostermillerutils 1.07.00
0598: */
0599: public boolean present(Character c) {
0600: parse();
0601: return present(getOption(c));
0602: }
0603:
0604: /**
0605: * Whether or not the specified option is present in the command line.
0606: * Parses the command line if not already parsed.
0607: *
0608: * @param c short argument
0609: * @return true iff the option (or its synonyms) is present.
0610: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
0611: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
0612: * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
0613: *
0614: * @since ostermillerutils 1.07.00
0615: */
0616: public boolean present(char c) {
0617: parse();
0618: return present(getOption(c));
0619: }
0620:
0621: /**
0622: * Whether or not the specified option is present in the command line.
0623: * Parses the command line if not already parsed.
0624: *
0625: * @param option command line option
0626: * @return true iff the option is present.
0627: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
0628: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
0629: * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
0630: *
0631: * @since ostermillerutils 1.07.00
0632: */
0633: public boolean present(CmdLnOption option) {
0634: parse();
0635: if (option == null)
0636: return false;
0637: ArrayList<CmdLnResult> results = optionsToResults.get(option);
0638: return (results != null);
0639: }
0640:
0641: /**
0642: * The number of times the specified option is present in the command line.
0643: * Parses the command line if not already parsed.
0644: *
0645: * @param s long argument
0646: * @return true iff the option (or its synonyms) is present.
0647: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
0648: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
0649: * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
0650: *
0651: * @since ostermillerutils 1.07.00
0652: */
0653: public int occurrences(String s) {
0654: parse();
0655: return occurrences(getOption(s));
0656: }
0657:
0658: /**
0659: * The number of times the specified option is present in the command line.
0660: * Parses the command line if not already parsed.
0661: *
0662: * @param c short argument
0663: * @return true iff the option (or its synonyms) is present.
0664: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
0665: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
0666: * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
0667: *
0668: * @since ostermillerutils 1.07.00
0669: */
0670: public int occurrences(Character c) {
0671: parse();
0672: return occurrences(getOption(c));
0673: }
0674:
0675: /**
0676: * The number of times the specified option is present in the command line.
0677: * Parses the command line if not already parsed.
0678: *
0679: * @param c short argument
0680: * @return true iff the option (or its synonyms) is present.
0681: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
0682: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
0683: * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
0684: *
0685: * @since ostermillerutils 1.07.00
0686: */
0687: public int occurrences(char c) {
0688: parse();
0689: return occurrences(getOption(c));
0690: }
0691:
0692: /**
0693: * The number of times the specified option is present in the command line.
0694: * Parses the command line if not already parsed.
0695: *
0696: * @param option command line option
0697: * @return true iff the option is present.
0698: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
0699: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
0700: * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
0701: *
0702: * @since ostermillerutils 1.07.00
0703: */
0704: public int occurrences(CmdLnOption option) {
0705: parse();
0706: if (option == null)
0707: return 0;
0708: ArrayList<CmdLnResult> results = optionsToResults.get(option);
0709: if (results == null)
0710: return 0;
0711: return results.size();
0712: }
0713:
0714: /**
0715: * Get the left over arguments -- the arguments that are not
0716: * associated with any arguments.
0717: * Parses the command line if not already parsed.
0718: *
0719: * @return unmodifiable list of arguments
0720: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
0721: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
0722: * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
0723: *
0724: * @since ostermillerutils 1.07.00
0725: */
0726: public List<String> getNonOptionArguments() {
0727: parse();
0728: return Collections.unmodifiableList(leftOverArguments);
0729: }
0730:
0731: /**
0732: * Go through the options and sort out the long and the short options
0733: * into their own hash maps.
0734: *
0735: * @since ostermillerutils 1.07.00
0736: */
0737: private void processOptions() {
0738: longOptions = new HashMap<String, CmdLnOption>();
0739: shortOptions = new HashMap<Character, CmdLnOption>();
0740: for (CmdLnOption option : options) {
0741: option.setImmutable();
0742: for (String name : option.getLongNames()) {
0743: if (longOptions.containsKey(name)) {
0744: throw new IllegalArgumentException(
0745: "More than one long option has the name: '"
0746: + name + "'");
0747: }
0748: longOptions.put(name, option);
0749: }
0750: for (Character c : option.getShortNames()) {
0751: if (shortOptions.containsKey(c)) {
0752: throw new IllegalArgumentException(
0753: "More than one short option has the character: '"
0754: + c + "'");
0755: }
0756: shortOptions.put(c, option);
0757: }
0758: }
0759: }
0760:
0761: /**
0762: * Determine whether the given argument is a long option.
0763: * Check all the long argument starts to see if it starts with any of them.
0764: * However, if it *is* a option start, it is not a long option.
0765: *
0766: * @param argument command line argument to check to see if it is a long argument
0767: * @return the argument name (strip off the argument start) or null if not a long argument
0768: *
0769: * @since ostermillerutils 1.07.00
0770: */
0771: private String getLongOptionName(String argument) {
0772: if (shortOptionsStart.contains(argument)
0773: || longOptionsStart.contains(argument)) {
0774: return null;
0775: }
0776: for (String optionStart : longOptionsStart) {
0777: if (argument.startsWith(optionStart)) {
0778: return argument.substring(optionStart.length());
0779: }
0780: }
0781: return null;
0782: }
0783:
0784: /**
0785: * Determine whether the given argument is a short option.
0786: * Check all the short argument starts to see if it starts with any of them.
0787: * However, if it *is* a option start, it is not a short option.
0788: *
0789: * @param argument command line argument to check to see if it is a short argument
0790: * @return the argument name (strip off the argument start) or null if not a short argument
0791: *
0792: * @since ostermillerutils 1.07.00
0793: */
0794: private String getShortOptionName(String sArgument) {
0795: if (shortOptionsStart.contains(sArgument)
0796: || longOptionsStart.contains(sArgument)) {
0797: return null;
0798: }
0799: for (String optionStart : shortOptionsStart) {
0800: if (sArgument.startsWith(optionStart)) {
0801: return sArgument.substring(optionStart.length());
0802: }
0803: }
0804: return null;
0805: }
0806:
0807: /**
0808: * Record the result in all needed places
0809: *
0810: * @param result to record
0811: *
0812: * @since ostermillerutils 1.07.00
0813: */
0814: private void addResult(CmdLnResult result) {
0815: ArrayList<CmdLnResult> results = optionsToResults.get(result
0816: .getOption());
0817: if (results == null) {
0818: results = new ArrayList<CmdLnResult>(1);
0819: optionsToResults.put(result.getOption(), results);
0820: }
0821: results.add(result);
0822: this .results.add(result);
0823: }
0824:
0825: /**
0826: * Find the index of of a option argument delimiter
0827: * @param argument argument to search
0828: * @return index of delimiter or -1 if none found
0829: *
0830: * @since ostermillerutils 1.07.00
0831: */
0832: private int getIndexOfDelimiter(String argument) {
0833: int index = -1;
0834: for (Character delimiter : optionArgumentDelimiters) {
0835: int i = argument.indexOf(delimiter);
0836: if (i > 0 && (index == -1 || i < index))
0837: index = i;
0838: }
0839: return index;
0840: }
0841:
0842: /**
0843: * Look through the arguments in order, see if they are a long option, a short
0844: * option, an argument to an option, or a non-option argument.
0845: *
0846: * @throws UnknownCmdLnOptionException if an unexpected option is encountered.
0847: * @throws ExtraCmdLnArgumentException if a command line option has too many arguments.
0848: *
0849: * @since ostermillerutils 1.07.00
0850: */
0851: private void runThroughArguments() {
0852: CmdLnResult lastResult = null;
0853: String optionName = null;
0854: boolean seenNonOptionSeparator = false;
0855: for (String argument : arguments) {
0856: if (seenNonOptionSeparator) {
0857: // Non-option argument
0858: // No arguments after the separator are options or option arguments
0859: leftOverArguments.add(argument);
0860: } else if (nonOptionSeparators.contains(argument)) {
0861: // No arguments after the separator will be options or option arguments
0862: seenNonOptionSeparator = true;
0863: } else if ((optionName = getLongOptionName(argument)) != null) {
0864: int delimiterIndex = getIndexOfDelimiter(optionName);
0865: String optionArgument = null;
0866: if (delimiterIndex > 0) {
0867: optionArgument = optionName
0868: .substring(delimiterIndex + 1);
0869: optionName = optionName
0870: .substring(0, delimiterIndex);
0871: }
0872: // Long option
0873: CmdLnOption option = longOptions.get(optionName);
0874: if (option == null) {
0875: throw new UnknownCmdLnOptionException()
0876: .setArgument(argument)
0877: .setOption(optionName);
0878: }
0879: lastResult = new CmdLnResult(option);
0880: addResult(lastResult);
0881: if (optionArgument != null) {
0882: if (lastResult.hasAllArguments()) {
0883: throw new ExtraCmdLnArgumentException()
0884: .setResult(lastResult);
0885: }
0886: lastResult.addArgument(optionArgument);
0887: }
0888: } else if ((optionName = getShortOptionName(argument)) != null) {
0889: // Short options
0890: int delimiterIndex = getIndexOfDelimiter(optionName);
0891: for (int i = 0; i < optionName.length(); i++) {
0892: // Short option
0893: Character c = optionName.charAt(i);
0894: CmdLnOption option = shortOptions.get(c);
0895: if (option == null) {
0896: throw new UnknownCmdLnOptionException()
0897: .setArgument(argument).setOption(
0898: Character.toString(c));
0899: }
0900: lastResult = new CmdLnResult(option);
0901: addResult(lastResult);
0902: if (delimiterIndex == i + 1) {
0903: String optionArgument = optionName
0904: .substring(delimiterIndex + 1);
0905: if (lastResult.hasAllArguments()) {
0906: throw new ExtraCmdLnArgumentException()
0907: .setResult(lastResult);
0908: }
0909: lastResult.addArgument(optionArgument);
0910: i = optionName.length(); // break out of loop
0911: }
0912: }
0913: } else {
0914: // Argument
0915: if (lastResult != null && !lastResult.hasAllArguments()) {
0916: // Option argument
0917: lastResult.addArgument(argument);
0918: } else {
0919: // Non-option argument
0920: leftOverArguments.add(argument);
0921: }
0922: }
0923: }
0924: }
0925:
0926: /**
0927: * Parse the command line options if they have not already been parsed.
0928: * Process the options,
0929: * run through the arguments,
0930: * ensure that all the arguments are good,
0931: * call any listeners.
0932: * <p>
0933: * Once this command line has been parsed, options may no longer be added.
0934: *
0935: * @throws UnknownCmdLnOptionException if an unexpected option is encountered
0936: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments.
0937: * @throws ExtraCmdLnArgumentException if a command line option has too many arguments.
0938: *
0939: * @since ostermillerutils 1.07.00
0940: */
0941: public void parse() {
0942: if (parsed)
0943: return;
0944: parsed = true;
0945: processOptions();
0946: runThroughArguments();
0947: ensureOptionsSatisfied();
0948: callListeners();
0949: }
0950:
0951: /**
0952: * Throw an exception if any option doesn't have enough arguments.
0953: *
0954: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments.
0955: *
0956: * @since ostermillerutils 1.07.00
0957: */
0958: private void ensureOptionsSatisfied() {
0959: for (CmdLnResult result : results) {
0960: if (result.requiresMoreArguments()) {
0961: throw new MissingCmdLnArgumentException()
0962: .setResult(result);
0963: }
0964: }
0965: }
0966:
0967: /**
0968: * Call the call back methods in the results
0969: *
0970: * @since ostermillerutils 1.07.00
0971: */
0972: private void callListeners() {
0973: for (CmdLnResult result : results) {
0974: CmdLnListener callback = result.getOption().getListener();
0975: if (callback != null) {
0976: callback.found(result);
0977: }
0978: }
0979: }
0980:
0981: /**
0982: * Get the canonical start of long options. (Usually "--")
0983: *
0984: * @return the first long option start
0985: *
0986: * @since ostermillerutils 1.07.00
0987: */
0988: String getFirstLongOptionsStart() {
0989: if (longOptionsStart.size() == 0)
0990: return null;
0991: return longOptionsStart.iterator().next();
0992: }
0993:
0994: /**
0995: * Get the canonical start of short options. (Usually "-")
0996: *
0997: * @return the first short option start
0998: *
0999: * @since ostermillerutils 1.07.00
1000: */
1001: String getFirstShortOptionsStart() {
1002: if (shortOptionsStart.size() == 0)
1003: return null;
1004: return shortOptionsStart.iterator().next();
1005: }
1006:
1007: /**
1008: * Print help for the command line options. Help will be in this format:
1009: * <pre>
1010: * program description
1011: * --argument -a <?> argument a description
1012: * --another -b argument b description
1013: * --third -c <?> argument c description
1014: * </pre>
1015: * Indentation is the default twenty, and line width is the default 80.
1016: * Parses the command line if not already parsed.
1017: *
1018: * @return Help as a string.
1019: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
1020: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
1021: *
1022: * @since ostermillerutils 1.07.00
1023: */
1024: public String getHelp() {
1025: StringWriter out = new StringWriter();
1026: printHelp(out);
1027: return out.toString();
1028: }
1029:
1030: /**
1031: * Print help for the command line options to standard output. Help will be in this format:
1032: * <pre>
1033: * program description
1034: * --argument -a <?> argument a description
1035: * --another -b argument b description
1036: * --third -c <?> argument c description
1037: * </pre>
1038: * Indentation is the default twenty, and line width is the default eighty.
1039: * Parses the command line if not already parsed.
1040: *
1041: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
1042: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
1043: *
1044: * @since ostermillerutils 1.07.00
1045: */
1046: public void printHelp() {
1047: printHelp(System.out);
1048: }
1049:
1050: /**
1051: * Print help for the command line options.
1052: * Indentation is the default twenty, and line width is the default eighty.
1053: * Parses the command line if not already parsed.
1054: *
1055: * @param out destination to which the help is written.
1056: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
1057: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
1058: *
1059: * @since ostermillerutils 1.07.00
1060: */
1061: public void printHelp(OutputStream out) {
1062: printHelp(new PrintWriter(new OutputStreamWriter(out)));
1063: }
1064:
1065: /**
1066: * Print help for the command line options. Help will be in this format:
1067: * <pre>
1068: * program description
1069: * --argument -a <?> argument a description
1070: * --another -b argument b description
1071: * --third -c <?> argument c description
1072: * </pre>
1073: * Indentation is the default twenty, and line width is the default eighty.
1074: * Parses the command line if not already parsed.
1075: *
1076: * @param out destination to which the help is written.
1077: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
1078: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
1079: *
1080: * @since ostermillerutils 1.07.00
1081: */
1082: public void printHelp(Writer out) {
1083: printHelp(new PrintWriter(out));
1084: }
1085:
1086: /**
1087: * Print help for the command line options. Help will be in this format:
1088: * <pre>
1089: * program description
1090: * --argument -a <?> argument a description
1091: * --another -b argument b description
1092: * --third -c <?> argument c description
1093: * </pre>
1094: * Indentation is the default twenty, and line width is the default eighty.
1095: * Parses the command line if not already parsed.
1096: *
1097: * @param out destination to which the help is written.
1098: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
1099: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
1100: *
1101: * @since ostermillerutils 1.07.00
1102: */
1103: public void printHelp(PrintWriter out) {
1104: printHelp(out, 20, 80);
1105: }
1106:
1107: /**
1108: * Print help for the command line options. Help will be in this format:
1109: * <pre>
1110: * program description
1111: * --argument -a <?> argument a description
1112: * --another -b argument b description
1113: * --third -c <?> argument c description
1114: * </pre>
1115: * Parses the command line if not already parsed.
1116: *
1117: * @return Help as a string.
1118: * @param indent the maximum number of characters to which all descriptions should be indented.
1119: * @param width the number of characters at which text should be wrapped.
1120: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
1121: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
1122: *
1123: * @since ostermillerutils 1.07.00
1124: */
1125: public String getHelp(int indent, int width) {
1126: StringWriter out = new StringWriter();
1127: printHelp(out, indent, width);
1128: return out.toString();
1129: }
1130:
1131: /**
1132: * Print help for the command line options to standard output. Help will be in this format:
1133: * <pre>
1134: * program description
1135: * --argument -a <?> argument a description
1136: * --another -b argument b description
1137: * --third -c <?> argument c description
1138: * </pre>
1139: * Parses the command line if not already parsed.
1140: *
1141: * @param indent the maximum number of characters to which all descriptions should be indented.
1142: * @param width the number of characters at which text should be wrapped.
1143: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
1144: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
1145: *
1146: * @since ostermillerutils 1.07.00
1147: */
1148: public void printHelp(int indent, int width) {
1149: printHelp(System.out, indent, width);
1150: }
1151:
1152: /**
1153: * Print help for the command line options. Help will be in this format:
1154: * <pre>
1155: * program description
1156: * --argument -a <?> argument a description
1157: * --another -b argument b description
1158: * --third -c <?> argument c description
1159: * </pre>
1160: * Parses the command line if not already parsed.
1161: *
1162: * @param out destination to which the help is written.
1163: * @param indent the maximum number of characters to which all descriptions should be indented.
1164: * @param width the number of characters at which text should be wrapped.
1165: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
1166: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
1167: *
1168: * @since ostermillerutils 1.07.00
1169: */
1170: public void printHelp(OutputStream out, int indent, int width) {
1171: printHelp(new PrintWriter(new OutputStreamWriter(out)), indent,
1172: width);
1173: }
1174:
1175: /**
1176: * Print help for the command line options. Help will be in this format:
1177: * <pre>
1178: * program description
1179: * --argument -a <?> argument a description
1180: * --another -b argument b description
1181: * --third -c <?> argument c description
1182: * </pre>
1183: * Parses the command line if not already parsed.
1184: *
1185: * @param out destination to which the help is written.
1186: * @param indent the maximum number of characters to which all descriptions should be indented.
1187: * @param width the number of characters at which text should be wrapped.
1188: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
1189: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
1190: *
1191: * @since ostermillerutils 1.07.00
1192: */
1193: public void printHelp(Writer out, int indent, int width) {
1194: printHelp(new PrintWriter(out), indent, width);
1195: }
1196:
1197: /**
1198: * Print help for the command line options. Help will be in this format:
1199: * <pre>
1200: * program description
1201: * --argument -a <?> argument a description
1202: * --another -b argument b description
1203: * --third -c <?> argument c description
1204: * </pre>
1205: * Parses the command line if not already parsed.
1206: *
1207: * @param out destination to which the help is written.
1208: * @param indent the maximum number of characters to which all descriptions should be indented.
1209: * @param width the number of characters at which text should be wrapped.
1210: * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
1211: * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
1212: *
1213: * @since ostermillerutils 1.07.00
1214: */
1215: public void printHelp(PrintWriter out, int indent, int width) {
1216: parse();
1217: String shortOptionsStart = getFirstShortOptionsStart();
1218: String longOptionsStart = getFirstLongOptionsStart();
1219: if (description != null) {
1220: out.println(description);
1221: }
1222: int maxIndent = 0;
1223: for (CmdLnOption option : options) {
1224: int optionIndent = option.getHelpArgumentsLength(
1225: longOptionsStart, shortOptionsStart);
1226: if (optionIndent > maxIndent) {
1227: maxIndent = optionIndent;
1228: }
1229: }
1230: if (maxIndent < indent) {
1231: indent = maxIndent;
1232: }
1233: for (CmdLnOption option : options) {
1234: String optionHelp = option.getHelp(longOptionsStart,
1235: shortOptionsStart, indent, width);
1236: if (optionHelp != null) {
1237: out.println(optionHelp);
1238: }
1239: }
1240: out.flush();
1241: }
1242: }
|