All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.freeutils.util.Arguments Maven / Gradle / Ivy

The newest version!
/*
 *  Copyright © 2003-2024 Amichai Rothman
 *
 *  This file is part of JElementary - the Java Elementary Utilities package.
 *
 *  JElementary 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.
 *
 *  JElementary 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.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with JElementary.  If not, see .
 *
 *  For additional info see https://www.freeutils.net/source/jelementary/
 */

package net.freeutils.util;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Parses command-line arguments and options.
 */
public class Arguments {

    /**
     * A supported command-line option's configuration.
     *
     * @param  the option value type (if it has a value)
     */
    protected class Option {

        protected final String name;
        protected final String abbrev;
        protected final Class type;
        protected final boolean required;
        protected final T def;

        /**
         * Constructs an option with the given configuration details.
         *
         * @param name the full name (long option form)
         * @param abbrev the abbreviation (short name or letter, i.e. short option form)
         * @param type the option value type (if it has a value)
         * @param required whether the option is required or not
         * @param def a default value if the option is not specified
         */
        public Option(String name, String abbrev, Class type, boolean required, T def) {
            this.name = name;
            this.abbrev = abbrev;
            this.type = type;
            this.required = required;
            this.def = def;
        }

        /**
         * Returns whether this option has the given name or abbreviation.
         *
         * @param name an option name, or null
         * @param abbrev an option abbreviation, or null
         * @return true if this option has the given name or abbreviation
         */
        public boolean is(String name, String abbrev) {
            return name != null && name.equals(this.name)
                || abbrev != null && abbrev.equals(this.abbrev);
        }

        /**
         * Parses a value for this option.
         *
         * @param value the option value string
         * @return the parsed option value, or the default value if the value is null
         *         and the option is not required
         * @throws IllegalArgumentException if value is null and the option is required
         */
        public T parse(String value) {
            if (value != null)
                return Reflect.convert(value, type);
            if (!required)
                return def;
            throw new IllegalArgumentException(
                "missing required option --" + name + " or -" + abbrev);
        }
    }

    protected List> options = new ArrayList<>();
    protected Map, String> values;
    protected List positionals;

    /**
     * Constructs a new Arguments instance.
     */
    public Arguments() {}

    /**
     * Finds a supported option with the given name or abbreviation.
     *
     * @param name an option name, or null
     * @param abbrev an option abbreviation, or null
     * @param throwIfNotFound specifies whether to throw an exception if the option is not found,
     *        or simply return null
     * @param  the option value type
     * @return the supported option with the given name or abbreviation,
     *         or null if the option was not found and throwIfNotFound is false
     * @throws IllegalArgumentException if throwIfNotFound is true and
     *         there is no option with the given name or abbreviation
     */
    @SuppressWarnings("unchecked")
    protected  Option find(String name, String abbrev, boolean throwIfNotFound) {
        for (Option opt : options)
            if (opt.is(name, abbrev))
                return (Option)opt;
        if (throwIfNotFound)
            throw new IllegalArgumentException("unknown option: " + (name != null ? name : abbrev));
        return null;
    }

    /**
     * Finds the supported option specified by the given command-line argument.
     *
     * @param arg the command-line argument (including prefix dashes)
     * @param  the option value type
     * @return the supported option specified by the given argument,
     *         or null if the given argument is a positional argument
     *         and does not reference an option (i.e. it does not start with a dash)
     * @throws IllegalArgumentException if there is no supported option matching
     *         the given argument
     */
    protected  Option find(String arg) {
        if (arg.startsWith("--"))
            return find(arg.substring(2), null, true);
        if (arg.startsWith("-"))
            return find(null, arg.substring(1), true);
        return null;
    }

    /**
     * Adds a command-line option to support.
     *
     * @param option the option to add
     * @return this Arguments instance
     * @throws IllegalStateException if arguments have already been parsed
     * @throws IllegalArgumentException if an option with the same name or abbreviation already exists
     */
    protected Arguments add(Option option) {
        if (values != null || positionals != null)
            throw new IllegalStateException("cannot add options after arguments were parsed");
        if (find(option.name, option.abbrev, false) != null)
            throw new IllegalArgumentException("an option with the given name or abbreviation already exists");
        options.add(option);
        return this;
    }

    /**
     * Adds a command-line option to support.
     *
     * @param name the option full name
     * @param abbrev the option abbreviated name (short name or letter)
     * @param type the option's value type
     * @param required whether the option is required or optional
     * @param def a default value to use if the option is not specified
     * @param  the option value type
     * @return this Arguments instance
     */
    protected  Arguments add(String name, String abbrev, Class type, boolean required, T def) {
        return add(new Option<>(name, abbrev, type, required, def));
    }

    /**
     * Adds a command-line option to support.
     *
     * @param name the option full name
     * @param abbrev the option abbreviated name (short name or letter)
     * @param type the option's value type
     * @param required whether the option is required or optional
     * @param  the option value type
     * @return this Arguments instance
     */
    public  Arguments add(String name, String abbrev, Class type, boolean required) {
        return add(name, abbrev, type, required, null);
    }

    /**
     * Adds a command-line option to support.
     *
     * @param name the option full name
     * @param abbrev the option abbreviated name (short name or letter)
     * @param type the option's value type
     * @param def a default value to use if the option is not specified
     * @param  the option value type
     * @return this Arguments instance
     */
    public  Arguments add(String name, String abbrev, Class type, T def) {
        return add(name, abbrev, type, false, def);
    }

    /**
     * Adds a command-line option to support.
     * The option is optional and has no default value.
     *
     * @param name the option full name
     * @param abbrev the option abbreviated name (short name or letter)
     * @param type the option's value type
     * @param  the option value type
     * @return this Arguments instance
     */
    public  Arguments add(String name, String abbrev, Class type) {
        return add(name, abbrev, type, null);
    }

    /**
     * Adds a command-line option to support.
     * The option is optional, and its value is of type String.
     *
     * @param name the option full name
     * @param abbrev the option abbreviated name (short name or letter)
     * @param def a default value to use if the option is not specified
     * @return this Arguments instance
     */
    public Arguments add(String name, String abbrev, String def) {
        return add(name, abbrev, String.class, def);
    }

    /**
     * Adds a command-line option to support.
     * The option is optional, and its value is of type String.
     *
     * @param name the option full name
     * @param abbrev the option abbreviated name (short name or letter)
     * @return this Arguments instance
     */
    public Arguments add(String name, String abbrev) {
        return add(name, abbrev, String.class);
    }

    /**
     * Parses the given command line arguments according to the configured options.
     *
     * @param args the command-line arguments
     * @return this Arguments instance
     * @throws IllegalArgumentException if any of the arguments are invalid,
     *         such as unsupported options or wrong value types
     */
    public Arguments parse(String... args) throws IllegalArgumentException {
        values = new LinkedHashMap<>();
        positionals = new ArrayList<>();
        for (int i = 0; i < args.length; i++) {
            String arg = args[i];
            Option opt = find(arg);
            if (opt == null) {
                positionals.add(arg);
            } else if (opt.type == boolean.class) {
                values.put(opt, "true");
            } else if (i == args.length - 1) {
                throw new IllegalArgumentException("missing value for option " + arg);
            } else {
                values.put(opt, args[++i]);
            }
        }
        return this;
    }

    /**
     * Ensures that arguments were parsed.
     *
     * @throws IllegalStateException if no arguments were parsed
     */
    protected void ensureParsed() {
        if (values == null || positionals == null)
            throw new IllegalStateException("no arguments were parsed");
    }

    /**
     * Returns the number of positional (non-option) arguments.
     *
     * @return  the number of positional (non-option) arguments
     */
    public int count() {
        return positionals.size();
    }

    /**
     * Returns the value of the named option.
     *
     * @param name the name of the option
     * @param  the type of the value
     * @return the value of the option
     * @throws IllegalStateException if no arguments were parsed
     * @throws IllegalArgumentException if there is no supported option with the given name
     */
    public  T get(String name) {
        ensureParsed();
        Option opt = find(name, null, true);
        return opt.parse(values.get(opt));
    }

    /**
     * Returns the i-th positional argument.
     *
     * @param i the zero-based index of the positional argument
     * @return the i-th positional argument
     * @throws IllegalStateException if no arguments were parsed
     * @throws IllegalArgumentException if no i-th positional argument was provided
     */
    public String get(int i) {
        ensureParsed();
        if (i >= positionals.size())
            throw new IllegalArgumentException("missing argument #" + i
                + ", only " + positionals.size() + " arguments provided");
        return positionals.get(i);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy