CmdLn.java Source Code

  • CmdLn Documentation and Examples
  • CmdLn Javadoc
    /*
     * Copyright (C) 2007-2010 Stephen Ostermiller
     * http://ostermiller.org/contact.pl?regarding=Java+Utilities
     *
     * This program is free software; you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation; either version 2 of the License, or
     * (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     * GNU General Public License for more details.
     *
     * See LICENSE.txt for details.
     */
    package com.Ostermiller.util;
    
    import java.io.*;
    import java.util.*;
    
    /**
     * Command line argument parser for Java command line programs.
     *
     * More information about this class and code samples for suggested use are
     * available from <a target="_top" href=
     * "http://ostermiller.org/utils/CmdLn.html">ostermiller.org</a>.
     *
     * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
     * @since ostermillerutils 1.07.00
     */
    public final class CmdLn {
    
    	/**
    	 * The original unparsed command line arguments as
    	 * passed in to the constructor.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private String[] arguments;
    
    	/**
    	 * Description to be put in the help.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private String description;
    
    	/**
    	 * A list of all the options.
    	 * Filled by the public addOptions() methods.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private ArrayList<CmdLnOption> options = new ArrayList<CmdLnOption>();
    
    	/**
    	 * Map the options to their found results.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private HashMap<CmdLnOption, ArrayList<CmdLnResult>> optionsToResults = new HashMap<CmdLnOption, ArrayList<CmdLnResult>>();
    
    	/**
    	 * List of all results that have been found.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private ArrayList<CmdLnResult> results = new ArrayList<CmdLnResult>();
    
    	/**
    	 * A list of the arguments that do not belong to any option.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private ArrayList<String> leftOverArguments = new ArrayList<String>();
    
    	/**
    	 * Mapping of long options to their command line option object.
    	 * Created during the parse method.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private HashMap<String,CmdLnOption> longOptions;
    
    	/**
    	 * Mapping of short options to their command line option object.
    	 * Created during the parse method.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private HashMap<Character,CmdLnOption> shortOptions;
    
    	/**
    	 * An ordered set of what long options start with. Typically contains
    	 * a single entry "--".
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private LinkedHashSet<String> longOptionsStart = new LinkedHashSet<String>(1);
    
    	/**
    	 * An ordered set of what short options start with. Typically contains
    	 * a single entry "-".
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private LinkedHashSet<String> shortOptionsStart = new LinkedHashSet<String>(1);
    
    	/**
    	 * A set of strings that indicate that everything following should be regarded
    	 * as non-option arguments.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private HashSet<String> nonOptionSeparators = new HashSet<String>(1);
    
    	/**
    	 * A set of strings that break an option up into an option and an argument.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private HashSet<Character> optionArgumentDelimiters = new HashSet<Character>(3);
    
    	/**
    	 * Whether or not the parse method has been called yet.
    	 * Used to determine if setters are appropriate or should throw illegal state exceptions.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private boolean parsed = false;
    
    	/**
    	 * New command line options with the given command line arguments
    	 *
    	 * @param arguments command line arguments from main method
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public CmdLn(String[] arguments){
    		if (arguments == null) throw new IllegalArgumentException("Command line arguments cannot be null.");
    		for(String argument: arguments){
    			if (argument == null) throw new IllegalArgumentException("Each command line argument cannot be null.");
    		}
    		this.arguments = arguments.clone();
    
    		// Defaults
    		longOptionsStart.add("--");
    		shortOptionsStart.add("-");
    		nonOptionSeparators.add("--");
    		optionArgumentDelimiters.add('=');
    		optionArgumentDelimiters.add(':');
    		optionArgumentDelimiters.add(' ');
    	}
    
    	/**
    	 * Set the delimiters that separate a option name from a value
    	 * within a single argument.
    	 *
    	 * This is to support arguments of the form "--name=value".  The
    	 * equals sign can separate the option name ("name") from the
    	 * option argument ("value").
    	 *
    	 * By default, the delimiters are '=', ':', and ' '.
    	 * @param delimiters list of delimiters
    	 * @return this for method chaining.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public CmdLn setOptionArgumentDelimiters(char[] delimiters){
    		optionArgumentDelimiters.clear();
    		for (char c: delimiters){
    			optionArgumentDelimiters.add(c);
    		}
    		return this;
    	}
    
    	/**
    	 * Set what long options and short options start with.
    	 * Typically long options start with "--" and short options with "-"
    	 * (this is the default).
    	 *
    	 * @param longOptionsStart What long options start with (default "--") or null for no long options;
    	 * @param shortOptionsStart What short options start with (default "-") or null for no short options;
    	 * @throws IllegalArgumentException if the long or short start is the empty string
    	 * @return this for method chaining
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public CmdLn setOptionStarts(String longOptionsStart, String shortOptionsStart){
    		setOptionStarts(
    			new String[]{longOptionsStart},
    			new String[]{shortOptionsStart}
    		);
    		return this;
    	}
    
    	/**
    	 * Set what long options and short options start with.
    	 * Typically long options start with "--" and short options with "-" and
    	 * this is the default.
    	 * <p>
    	 * The first option start in each array will be the canonical option start
    	 * that is used in the help message.
    	 *
    	 * @param longOptionsStart What long options start with (default "--") or null or empty for no long options;
    	 * @param shortOptionsStart What short options start with (default "-") or null or empty for no short options;
    	 * @throws IllegalArgumentException if the long or short start is the empty string
    	 * @return this for method chaining
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public CmdLn setOptionStarts(String[] longOptionsStart, String[] shortOptionsStart){
    		this.longOptionsStart.clear();
    		if (longOptionsStart != null){
    			for (String optionStart: longOptionsStart){
    				if (optionStart != null){
    					if ("".equals(optionStart)) throw new IllegalArgumentException("long option start cannot be the empty string");
    					this.longOptionsStart.add(optionStart);
    				}
    			}
    		}
    		this.shortOptionsStart.clear();
    		if (shortOptionsStart != null){
    			for (String optionStart: shortOptionsStart){
    				if (optionStart != null){
    					if ("".equals(optionStart)) throw new IllegalArgumentException("short option start cannot be the empty string");
    					this.shortOptionsStart.add(optionStart);
    				}
    			}
    		}
    		return this;
    	}
    
    	/**
    	 * Set the "stop" option that causes any following arguments to be treated
    	 * as non-option arguments, even if they look like an option.
    	 * <p>
    	 * The default non-option separator is "--".
    	 *
    	 * @param nonOptionSeparators List of arguments that stop processing options and treat remaining arguments as non option arguments.
    	 * @return this for method chaining
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public CmdLn setNonOptionSeparators(String[] nonOptionSeparators){
    		this.nonOptionSeparators.clear();
    		if (nonOptionSeparators != null){
    			for (String nonOptionSeparator: nonOptionSeparators){
    				this.nonOptionSeparators.add(nonOptionSeparator);
    			}
    		}
    		return this;
    	}
    
    	/**
    	 * Set the description for the program. This description will be printed
    	 * on the first line of the help message.
    	 *
    	 * @param description short description about the program
    	 * @return this for method chaining
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public CmdLn setDescription(String description){
    		this.description = description;
    		return this;
    	}
    
    	/**
    	 * Add options.
    	 *
    	 * @param options options to be added.
    	 * @throws NullPointerException if the options are null.
    	 * @throws NullPointerException if any option in the collection is null.
    	 * @throws IllegalStateException if the command line has already been parsed.
    	 * @return this for method chaining
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public CmdLn addOptions(Collection<CmdLnOption> options){
    		for(CmdLnOption option: options){
    			addOption(option);
    		}
    		return this;
    	}
    
    	/**
    	 * Add options.
    	 *
    	 * @param options options to be added.
    	 * @throws NullPointerException if the options are null.
    	 * @throws NullPointerException if any option in the collection is null.
    	 * @throws IllegalStateException if the command line has already been parsed.
    	 * @return this for method chaining
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public CmdLn addOptions(CmdLnOption[] options){
    		for(CmdLnOption option: options){
    			addOption(option);
    		}
    		return this;
    	}
    
    	/**
    	 * Add option.
    	 *
    	 * @param option option to be added.
    	 * @throws NullPointerException if the option is null.
    	 * @throws IllegalStateException if the command line has already been parsed.
    	 * @return this for method chaining
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public CmdLn addOption(CmdLnOption option){
    		if (parsed) throw new IllegalStateException("Can no longer add options");
    		options.add(option);
    		return this;
    	}
    
    	/**
    	 * Get the option associated with the given argument.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param s long argument
    	 * @return option associated with the given argument.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private CmdLnOption getOption(String s){
    		parse();
    		return longOptions.get(s);
    	}
    
    	/**
    	 * Get the option associated with the given argument.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param c short argument
    	 * @return option associated with the given argument.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private CmdLnOption getOption(Character c){
    		parse();
    		return shortOptions.get(c);
    	}
    
    	/**
    	 * Get the option associated with the given argument.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param c short argument
    	 * @return option associated with the given argument.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private CmdLnOption getOption(char c){
    		parse();
    		return shortOptions.get(c);
    	}
    
    	/**
    	 * Get the last result associated with the given argument.
    	 * If a option is in the command line multiple times, typically
    	 * the last one should be the one that wins as an override mechanism.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param s long argument
    	 * @return result for argument, or null if not associated with an option or not present in the command line.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public CmdLnResult getResult(String s){
    		parse();
    		return getResult(getOption(s));
    	}
    
    	/**
    	 * Get the last result associated with the given argument.
    	 * If a option is in the command line multiple times, typically
    	 * the last one should be the one that wins as an override mechanism.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param c short argument
    	 * @return result for argument, or null if not associated with an option or not present in the command line.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public CmdLnResult getResult(Character c){
    		parse();
    		return getResult(getOption(c));
    	}
    
    	/**
    	 * Get the last result associated with the given argument.
    	 * If a option is in the command line multiple times, typically
    	 * the last one should be the one that wins as an override mechanism.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param c short argument
    	 * @return result for argument, or null if not associated with an option or not present in the command line.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public CmdLnResult getResult(char c){
    		parse();
    		return getResult(getOption(c));
    	}
    
    	/**
    	 * Get the last result associated with the option.
    	 * If a option is in the command line multiple times, typically
    	 * the last one should be the one that wins as an override mechanism.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param option command line option
    	 * @return result for argument, or null if option not added to this command line or not present in the command line.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public CmdLnResult getResult(CmdLnOption option){
    		parse();
    		if (option == null) return null;
    		ArrayList<CmdLnResult> results = optionsToResults.get(option);
    		if (results == null) return null;
    		return results.get(results.size()-1);
    	}
    
    	/**
    	 * Get all the results in
    	 * the order in which they appear in the command line.
    	 * Parses the command line if not already parsed.
    	 * @return unmodifiable list of all options and arguments that were specified on the command line
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public List<CmdLnResult> getResults(){
    		parse();
    		return Collections.unmodifiableList(results);
    	}
    
    	/**
    	 * Get all the results associated with the given argument in
    	 * the order in which they appear in the command line.
    	 * If a option is in the command line multiple times, typically
    	 * the last one should be the one that wins as an override mechanism,
    	 * however it may sometimes be useful to know about all of them.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param s long argument
    	 * @return results for argument, or null if not associated with an option or not present in the command line.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public List<CmdLnResult> getResults(String s){
    		parse();
    		return getResults(getOption(s));
    	}
    
    	/**
    	 * Get all the results associated with the given argument in
    	 * the order in which they appear in the command line.
    	 * If a option is in the command line multiple times, typically
    	 * the last one should be the one that wins as an override mechanism,
    	 * however it may sometimes be useful to know about all of them.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param c short argument
    	 * @return results for argument, or null if not associated with an option or not present in the command line.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public List<CmdLnResult> getResults(Character c){
    		parse();
    		return getResults(getOption(c));
    	}
    
    	/**
    	 * Get all the results associated with the given argument in
    	 * the order in which they appear in the command line.
    	 * If a option is in the command line multiple times, typically
    	 * the last one should be the one that wins as an override mechanism,
    	 * however it may sometimes be useful to know about all of them.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param c short argument
    	 * @return results for argument, or null if not associated with an option or not present in the command line.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public List<CmdLnResult> getResults(char c){
    		parse();
    		return getResults(getOption(c));
    	}
    
    	/**
    	 * Get all the results associated with the given argument in
    	 * the order in which they appear in the command line.
    	 * If a option is in the command line multiple times, typically
    	 * the last one should be the one that wins as an override mechanism,
    	 * however it may sometimes be useful to know about all of them.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param option command line option
    	 * @return results for option, or null if option not added to this command line or not present in the command line.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public List<CmdLnResult> getResults(CmdLnOption option){
    		parse();
    		if (option == null) return null;
    		ArrayList<CmdLnResult> results = optionsToResults.get(option);
    		if (results == null) return null;
    		return Collections.unmodifiableList(results);
    	}
    
    	/**
    	 * Whether or not the specified option is present in the command line.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param s long argument
    	 * @return true iff the option (or its synonyms) is present.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public boolean present(String s){
    		parse();
    		return present(getOption(s));
    	}
    
    	/**
    	 * Whether or not the specified option is present in the command line.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param c short argument
    	 * @return true iff the option (or its synonyms) is present.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public boolean present(Character c){
    		parse();
    		return present(getOption(c));
    	}
    
    	/**
    	 * Whether or not the specified option is present in the command line.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param c short argument
    	 * @return true iff the option (or its synonyms) is present.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public boolean present(char c){
    		parse();
    		return present(getOption(c));
    	}
    
    	/**
    	 * Whether or not the specified option is present in the command line.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param option command line option
    	 * @return true iff the option is present.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public boolean present(CmdLnOption option){
    		parse();
    		if (option == null) return false;
    		ArrayList<CmdLnResult> results = optionsToResults.get(option);
    		return (results != null);
    	}
    
    	/**
    	 * The number of times the specified option is present in the command line.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param s long argument
    	 * @return true iff the option (or its synonyms) is present.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public int occurrences(String s){
    		parse();
    		return occurrences(getOption(s));
    	}
    
    	/**
    	 * The number of times the specified option is present in the command line.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param c short argument
    	 * @return true iff the option (or its synonyms) is present.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public int occurrences(Character c){
    		parse();
    		return occurrences(getOption(c));
    	}
    
    	/**
    	 * The number of times the specified option is present in the command line.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param c short argument
    	 * @return true iff the option (or its synonyms) is present.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public int occurrences(char c){
    		parse();
    		return occurrences(getOption(c));
    	}
    
    	/**
    	 * The number of times the specified option is present in the command line.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param option command line option
    	 * @return true iff the option is present.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public int occurrences(CmdLnOption option){
    		parse();
    		if (option == null) return 0;
    		ArrayList<CmdLnResult> results = optionsToResults.get(option);
    		if (results == null) return 0;
    		return results.size();
    	}
    
    	/**
    	 * Get the left over arguments -- the arguments that are not
    	 * associated with any arguments.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @return unmodifiable list of arguments
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 * @throws ExtraCmdLnArgumentException if a command line option has too many arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public List<String> getNonOptionArguments(){
    		parse();
    		return Collections.unmodifiableList(leftOverArguments);
    	}
    
    	/**
    	 * Go through the options and sort out the long and the short options
    	 * into their own hash maps.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private void processOptions(){
    		longOptions = new HashMap<String,CmdLnOption>();
    		shortOptions= new HashMap<Character,CmdLnOption>();
    		for (CmdLnOption option: options){
    			option.setImmutable();
    			for (String name: option.getLongNames()){
    				if (longOptions.containsKey(name)){
    					throw new IllegalArgumentException("More than one long option has the name: '" + name + "'");
    				}
    				longOptions.put(name, option);
    			}
    			for (Character c: option.getShortNames()){
    				if (shortOptions.containsKey(c)){
    					throw new IllegalArgumentException("More than one short option has the character: '" + c + "'");
    				}
    				shortOptions.put(c, option);
    			}
    		}
    	}
    
    	/**
    	 * Determine whether the given argument is a long option.
    	 * Check all the long argument starts to see if it starts with any of them.
    	 * However, if it *is* a option start, it is not a long option.
    	 *
    	 * @param argument command line argument to check to see if it is a long argument
    	 * @return the argument name (strip off the argument start) or null if not a long argument
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private String getLongOptionName(String argument){
    		if (shortOptionsStart.contains(argument) || longOptionsStart.contains(argument)){
    			return null;
    		}
    		for (String optionStart: longOptionsStart){
    			if (argument.startsWith(optionStart)){
    				return argument.substring(optionStart.length());
    			}
    		}
    		return null;
    	}
    
    	/**
    	 * Determine whether the given argument is a short option.
    	 * Check all the short argument starts to see if it starts with any of them.
    	 * However, if it *is* a option start, it is not a short option.
    	 *
    	 * @param argument command line argument to check to see if it is a short argument
    	 * @return the argument name (strip off the argument start) or null if not a short argument
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private String getShortOptionName(String sArgument){
    		if (shortOptionsStart.contains(sArgument) || longOptionsStart.contains(sArgument)){
    			return null;
    		}
    		for (String optionStart: shortOptionsStart){
    			if (sArgument.startsWith(optionStart)){
    				return sArgument.substring(optionStart.length());
    			}
    		}
    		return null;
    	}
    
    	/**
    	 * Record the result in all needed places
    	 *
    	 * @param result to record
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private void addResult(CmdLnResult result){
    		ArrayList<CmdLnResult> results = optionsToResults.get(result.getOption());
    		if (results == null){
    			results = new ArrayList<CmdLnResult>(1);
    			optionsToResults.put(result.getOption(), results);
    		}
    		results.add(result);
    		this.results.add(result);
    	}
    
    	/**
    	 * Find the index of of a option argument delimiter
    	 * @param argument argument to search
    	 * @return index of delimiter or -1 if none found
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private int getIndexOfDelimiter(String argument){
    		int index = -1;
    		for(Character delimiter: optionArgumentDelimiters){
    			int i = argument.indexOf(delimiter);
    			if (i > 0 && (index==-1 || i<index)) index = i;
    		}
    		return index;
    	}
    
    	/**
    	 * Look through the arguments in order, see if they are a long option, a short
    	 * option, an argument to an option, or a non-option argument.
    	 *
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered.
    	 * @throws ExtraCmdLnArgumentException if a command line option has too many arguments.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private void runThroughArguments(){
    		CmdLnResult lastResult = null;
    		String optionName = null;
    		boolean seenNonOptionSeparator = false;
    		for(String argument: arguments){
    			if (seenNonOptionSeparator){
    				// Non-option argument
    				// No arguments after the separator are options or option arguments
    				leftOverArguments.add(argument);
    			} else if (nonOptionSeparators.contains(argument)){
    				// No arguments after the separator will be options or option arguments
    				seenNonOptionSeparator = true;
    			} else if ((optionName = getLongOptionName(argument)) != null){
    				int delimiterIndex = getIndexOfDelimiter(optionName);
    				String optionArgument = null;
    				if (delimiterIndex > 0){
    					optionArgument = optionName.substring(delimiterIndex+1);
    					optionName = optionName.substring(0, delimiterIndex);
    				}
    				// Long option
    				CmdLnOption option = longOptions.get(optionName);
    				if (option == null){
    					throw new UnknownCmdLnOptionException().setArgument(argument).setOption(optionName);
    				}
    				lastResult = new CmdLnResult(option);
    				addResult(lastResult);
    				if (optionArgument != null){
    					if (lastResult.hasAllArguments()){
    						throw new ExtraCmdLnArgumentException().setResult(lastResult);
    					}
    					lastResult.addArgument(optionArgument);
    				}
    			} else if ((optionName = getShortOptionName(argument)) != null){
    				// Short options
    				int delimiterIndex = getIndexOfDelimiter(optionName);
    				for (int i=0; i<optionName.length(); i++){
    					// Short option
    					Character c = optionName.charAt(i);
    					CmdLnOption option = shortOptions.get(c);
    					if (option == null){
    						throw new UnknownCmdLnOptionException().setArgument(argument).setOption(Character.toString(c));
    					}
    					lastResult = new CmdLnResult(option);
    					addResult(lastResult);
    					if (delimiterIndex == i+1){
    						String optionArgument = optionName.substring(delimiterIndex+1);
    						if (lastResult.hasAllArguments()){
    							throw new ExtraCmdLnArgumentException().setResult(lastResult);
    						}
    						lastResult.addArgument(optionArgument);
    						i = optionName.length(); // break out of loop
    					}
    				}
    			} else {
    				// Argument
    				if (lastResult != null && !lastResult.hasAllArguments()){
    					// Option argument
    					lastResult.addArgument(argument);
    				} else {
    					// Non-option argument
    					leftOverArguments.add(argument);
    				}
    			}
    		}
    	}
    
    	/**
    	 * Parse the command line options if they have not already been parsed.
    	 * Process the options,
    	 * run through the arguments,
    	 * ensure that all the arguments are good,
    	 * call any listeners.
    	 * <p>
    	 * Once this command line has been parsed, options may no longer be added.
    	 *
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments.
    	 * @throws ExtraCmdLnArgumentException if a command line option has too many arguments.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public void parse(){
    		if (parsed) return;
    		parsed = true;
    		processOptions();
    		runThroughArguments();
    		ensureOptionsSatisfied();
    		callListeners();
    	}
    
    	/**
    	 * Throw an exception if any option doesn't have enough arguments.
    	 *
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private void ensureOptionsSatisfied(){
    		for (CmdLnResult result: results){
    			if (result.requiresMoreArguments()){
    				throw new MissingCmdLnArgumentException().setResult(result);
    			}
    		}
    	}
    
    	/**
    	 * Call the call back methods in the results
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	private void callListeners() {
    		for (CmdLnResult result: results){
    			CmdLnListener callback = result.getOption().getListener();
    			if (callback != null){
    				callback.found(result);
    			}
    		}
    	}
    
    	/**
    	 * Get the canonical start of long options. (Usually "--")
    	 *
    	 * @return the first long option start
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	String getFirstLongOptionsStart(){
    		if (longOptionsStart.size() == 0) return null;
    		return longOptionsStart.iterator().next();
    	}
    
    	/**
    	 * Get the canonical start of short options. (Usually "-")
    	 *
    	 * @return the first short option start
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	String getFirstShortOptionsStart(){
    		if (shortOptionsStart.size() == 0) return null;
    		return shortOptionsStart.iterator().next();
    	}
    
    	/**
    	 * Print help for the command line options. Help will be in this format:
    	 * <pre>
    	 * program description
    	 *   --argument -a <?>  argument a description
    	 *   --another -b       argument b description
    	 *   --third -c <?>     argument c description
    	 * </pre>
    	 * Indentation is the default twenty, and line width is the default 80.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @return Help as a string.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public String getHelp(){
    		StringWriter out = new StringWriter();
    		printHelp(out);
    		return out.toString();
    	}
    
    	/**
    	 * Print help for the command line options to standard output. Help will be in this format:
    	 * <pre>
    	 * program description
    	 *   --argument -a <?>  argument a description
    	 *   --another -b       argument b description
    	 *   --third -c <?>     argument c description
    	 * </pre>
    	 * Indentation is the default twenty, and line width is the default eighty.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public void printHelp(){
    		printHelp(System.out);
    	}
    
    	/**
    	 * Print help for the command line options.
    	 * Indentation is the default twenty, and line width is the default eighty.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param out destination to which the help is written.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public void printHelp(OutputStream out){
    		printHelp(new PrintWriter(new OutputStreamWriter(out)));
    	}
    
    	/**
    	 * Print help for the command line options. Help will be in this format:
    	 * <pre>
    	 * program description
    	 *   --argument -a <?>  argument a description
    	 *   --another -b       argument b description
    	 *   --third -c <?>     argument c description
    	 * </pre>
    	 * Indentation is the default twenty, and line width is the default eighty.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param out destination to which the help is written.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public void printHelp(Writer out){
    		printHelp(new PrintWriter(out));
    	}
    
    	/**
    	 * Print help for the command line options. Help will be in this format:
    	 * <pre>
    	 * program description
    	 *   --argument -a <?>  argument a description
    	 *   --another -b       argument b description
    	 *   --third -c <?>     argument c description
    	 * </pre>
    	 * Indentation is the default twenty, and line width is the default eighty.
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param out destination to which the help is written.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public void printHelp(PrintWriter out){
    		 printHelp(out, 20, 80);
    	}
    
    	/**
    	 * Print help for the command line options. Help will be in this format:
    	 * <pre>
    	 * program description
    	 *   --argument -a <?>  argument a description
    	 *   --another -b       argument b description
    	 *   --third -c <?>     argument c description
    	 * </pre>
    	 * Parses the command line if not already parsed.
    	 *
    	 * @return Help as a string.
    	 * @param indent the maximum number of characters to which all descriptions should be indented.
    	 * @param width the number of characters at which text should be wrapped.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public String getHelp(int indent, int width){
    		StringWriter out = new StringWriter();
    		printHelp(out, indent, width);
    		return out.toString();
    	}
    
    	/**
    	 * Print help for the command line options to standard output. Help will be in this format:
    	 * <pre>
    	 * program description
    	 *   --argument -a <?>  argument a description
    	 *   --another -b       argument b description
    	 *   --third -c <?>     argument c description
    	 * </pre>
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param indent the maximum number of characters to which all descriptions should be indented.
    	 * @param width the number of characters at which text should be wrapped.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public void printHelp(int indent, int width){
    		printHelp(System.out, indent, width);
    	}
    
    	/**
    	 * Print help for the command line options. Help will be in this format:
    	 * <pre>
    	 * program description
    	 *   --argument -a <?>  argument a description
    	 *   --another -b       argument b description
    	 *   --third -c <?>     argument c description
    	 * </pre>
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param out destination to which the help is written.
    	 * @param indent the maximum number of characters to which all descriptions should be indented.
    	 * @param width the number of characters at which text should be wrapped.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public void printHelp(OutputStream out, int indent, int width){
    		printHelp(new PrintWriter(new OutputStreamWriter(out)), indent, width);
    	}
    
    	/**
    	 * Print help for the command line options. Help will be in this format:
    	 * <pre>
    	 * program description
    	 *   --argument -a <?>  argument a description
    	 *   --another -b       argument b description
    	 *   --third -c <?>     argument c description
    	 * </pre>
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param out destination to which the help is written.
    	 * @param indent the maximum number of characters to which all descriptions should be indented.
    	 * @param width the number of characters at which text should be wrapped.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public void printHelp(Writer out, int indent, int width){
    		printHelp(new PrintWriter(out), indent, width);
    	}
    
    	/**
    	 * Print help for the command line options. Help will be in this format:
    	 * <pre>
    	 * program description
    	 *   --argument -a <?>  argument a description
    	 *   --another -b       argument b description
    	 *   --third -c <?>     argument c description
    	 * </pre>
    	 * Parses the command line if not already parsed.
    	 *
    	 * @param out destination to which the help is written.
    	 * @param indent the maximum number of characters to which all descriptions should be indented.
    	 * @param width the number of characters at which text should be wrapped.
    	 * @throws UnknownCmdLnOptionException if an unexpected option is encountered during parsing.
    	 * @throws MissingCmdLnArgumentException if a command line option does not have enough arguments during parsing.
    	 *
    	 * @since ostermillerutils 1.07.00
    	 */
    	public void printHelp(PrintWriter out, int indent, int width){
    		parse();
    		String shortOptionsStart = getFirstShortOptionsStart();
    		String longOptionsStart = getFirstLongOptionsStart();
    		if (description != null){
    			out.println(description);
    		}
    		int maxIndent = 0;
    		for(CmdLnOption option: options){
    			int optionIndent = option.getHelpArgumentsLength(longOptionsStart, shortOptionsStart);
    			if (optionIndent > maxIndent){
    				maxIndent = optionIndent;
    			}
    		}
    		if (maxIndent < indent){
    			indent = maxIndent;
    		}
    		for(CmdLnOption option: options){
    			String optionHelp = option.getHelp(longOptionsStart, shortOptionsStart, indent, width);
    			if (optionHelp != null){
    				out.println(optionHelp);
    			}
    		}
    		out.flush();
    	}
    }