/* Copyright 2013 Jonatan Jönsson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package se.softhouse.jargo; import static com.google.common.collect.Lists.asList; import static se.softhouse.common.strings.Describers.booleanAsEnabledDisabled; import static se.softhouse.common.strings.Describers.characterDescriber; import static se.softhouse.common.strings.Describers.fileDescriber; import static se.softhouse.common.strings.Describers.numberDescriber; import static se.softhouse.jargo.StringParsers.bigDecimalParser; import static se.softhouse.jargo.StringParsers.bigIntegerParser; import static se.softhouse.jargo.StringParsers.booleanParser; import static se.softhouse.jargo.StringParsers.byteParser; import static se.softhouse.jargo.StringParsers.charParser; import static se.softhouse.jargo.StringParsers.enumParser; import static se.softhouse.jargo.StringParsers.fileParser; import static se.softhouse.jargo.StringParsers.integerParser; import static se.softhouse.jargo.StringParsers.longParser; import static se.softhouse.jargo.StringParsers.shortParser; import static se.softhouse.jargo.StringParsers.stringParser; import java.io.File; import java.math.BigDecimal; import java.math.BigInteger; import javax.annotation.CheckReturnValue; import javax.annotation.Nonnull; import javax.annotation.concurrent.Immutable; import se.softhouse.common.strings.Describer; import se.softhouse.common.strings.Describers; import se.softhouse.jargo.ArgumentBuilder.CommandBuilder; import se.softhouse.jargo.ArgumentBuilder.DefaultArgumentBuilder; import se.softhouse.jargo.ArgumentBuilder.OptionArgumentBuilder; import se.softhouse.jargo.ArgumentBuilder.SimpleArgumentBuilder; import se.softhouse.jargo.StringParsers.HelpParser; /** * <pre> * Used as a starting point to create {@link Argument} instances. * * The produced arguments can be passed to * {@link CommandLineParser#withArguments(Argument...)} to group several * {@link Argument}s together. If only one argument should be * parsed, {@link ArgumentBuilder#parse(String...)} can be used instead. * </pre> */ @Immutable public final class Arguments { private Arguments() { } /** * <pre> * Makes the {@link CommandLineParser} it's passed to throw a helpful {@link ArgumentException} * with usage explaining either the full program or a specific {@link Argument}/{@link Command}. * The resulting {@link Usage} is currently only accessible through the {@link ArgumentException#getMessageAndUsage()} method. * This suits most use-cases as there's usually a catch block for {@link ArgumentException} anyway where the {@link Usage} is printed * * Supported usages: * <ul> * <li>"program -h" - Gives {@link Usage} for the whole program</li> * <li>"program -h --number" Gives {@link Usage} for the "--number"{@link Argument}</li> * <li>"program commit -h" Gives {@link Usage} for the <code>commit</code> {@link Command}</li> * <li>"program -h commit" Also gives {@link Usage} for the <code>commit</code> {@link Command}</li> * <li>"program commit -h --amend" Gives {@link Usage} for the <code>--amend</code> {@link Argument} for the <code>commit</code> {@link Command}</li> * * </ul> * * If given an unknown argument name, an {@link ArgumentException} is thrown. * * @param mandatoryName "-h" in the above example * @param optionalNames for example "--help" * @return an {@link Argument} that can be passed to {@link CommandLineParser#withArguments(Argument...)} * </pre> */ public static Argument<?> helpArgument(final String mandatoryName, final String ... optionalNames) { return new SimpleArgumentBuilder<String>(HelpParser.INSTANCE).names(asList(mandatoryName, optionalNames)).build(); } /** * Creates an {@link Argument} that uses {@link StringParsers#booleanParser()} * * @param names the {@link ArgumentBuilder#names(String...)} to use */ @CheckReturnValue @Nonnull public static DefaultArgumentBuilder<Boolean> booleanArgument(final String ... names) { return withParser(booleanParser()).names(names); } /** * Creates an {@link Argument} that uses {@link StringParsers#byteParser()} * * @param names the {@link ArgumentBuilder#names(String...)} to use */ @CheckReturnValue @Nonnull public static DefaultArgumentBuilder<Byte> byteArgument(final String ... names) { return withParser(byteParser()).defaultValueDescriber(numberDescriber()).names(names); } /** * Creates an {@link Argument} that uses {@link StringParsers#shortParser()} * * @param names the {@link ArgumentBuilder#names(String...)} to use */ @CheckReturnValue @Nonnull public static DefaultArgumentBuilder<Short> shortArgument(final String ... names) { return withParser(shortParser()).defaultValueDescriber(numberDescriber()).names(names); } /** * Creates an {@link Argument} that uses {@link StringParsers#integerParser()} * * @param names the {@link ArgumentBuilder#names(String...)} to use */ @CheckReturnValue @Nonnull public static DefaultArgumentBuilder<Integer> integerArgument(final String ... names) { return withParser(integerParser()).defaultValueDescriber(numberDescriber()).names(names); } /** * Creates an {@link Argument} that uses {@link StringParsers#longParser()} * * @param names the {@link ArgumentBuilder#names(String...)} to use */ @CheckReturnValue @Nonnull public static DefaultArgumentBuilder<Long> longArgument(final String ... names) { return withParser(longParser()).defaultValueDescriber(numberDescriber()).names(names); } /** * Creates an {@link Argument} that uses {@link StringParsers#bigIntegerParser()} * * @param names the {@link ArgumentBuilder#names(String...)} to use */ @CheckReturnValue @Nonnull public static DefaultArgumentBuilder<BigInteger> bigIntegerArgument(final String ... names) { return withParser(bigIntegerParser()).defaultValueDescriber(numberDescriber()).names(names); } /** * Creates an {@link Argument} that uses {@link StringParsers#bigIntegerParser()} * * @param names the {@link ArgumentBuilder#names(String...)} to use */ @CheckReturnValue @Nonnull public static DefaultArgumentBuilder<BigDecimal> bigDecimalArgument(final String ... names) { return withParser(bigDecimalParser()).defaultValueDescriber(numberDescriber()).names(names); } /** * Creates an {@link Argument} that uses {@link StringParsers#charParser()}.<br> * Describes default {@link Character}s with {@link Describers#characterDescriber()} in the * usage. * * @param names the {@link ArgumentBuilder#names(String...)} to use */ @CheckReturnValue @Nonnull public static DefaultArgumentBuilder<Character> charArgument(final String ... names) { return withParser(charParser()).defaultValueDescriber(characterDescriber()).names(names); } /** * Creates an {@link Argument} that uses {@link StringParsers#stringParser()} * * @param names the {@link ArgumentBuilder#names(String...)} to use */ @CheckReturnValue @Nonnull public static DefaultArgumentBuilder<String> stringArgument(final String ... names) { return withParser(stringParser()).names(names); } /** * Creates an {@link Argument} that uses {@link StringParsers#fileParser()}.<br> * Describes default {@link File}s with {@link Describers#fileDescriber()} in the usage. * * @param names the {@link ArgumentBuilder#names(String...)} to use */ @CheckReturnValue @Nonnull public static DefaultArgumentBuilder<File> fileArgument(final String ... names) { return withParser(fileParser()).defaultValueDescriber(fileDescriber()).names(names); } /** * <pre> * Creates an optional (flag) {@link Argument} where the sole existence of the name on the command * line matters. This is the only place to have an {@link ArgumentBuilder#arity(int)} of zero. * * The default value is printed with {@link Describers#booleanAsEnabledDisabled()}. * This can be changed with {@link ArgumentBuilder#defaultValueDescriber(Describer)}. * * @see Describers#booleanAsOnOff() * * @param mandatoryName the first name that enables this option * @param optionalNames aliases that also enables this option * </pre> */ @CheckReturnValue @Nonnull public static OptionArgumentBuilder optionArgument(final String mandatoryName, final String ... optionalNames) { return new OptionArgumentBuilder().defaultValueDescriber(booleanAsEnabledDisabled()).names(asList(mandatoryName, optionalNames)); } /** * <pre> * Creates an {@link Argument} that uses an {@link StringParsers#enumParser(Class)} to parse * arguments. * * If you end up with a big switch statement for your enum consider using {@link Command}s * instead. * * </pre> * * @param enumToHandle the {@link Enum} to retrieve enum values from * @param names the {@link ArgumentBuilder#names(String...)} to use */ @CheckReturnValue @Nonnull public static <T extends Enum<T>> DefaultArgumentBuilder<T> enumArgument(final Class<T> enumToHandle, final String ... names) { return withParser(enumParser(enumToHandle)).names(names); } /** * <pre> * Creates a {@link CommandBuilder} for {@code command}. Used for setting up custom things like * {@link ArgumentBuilder#ignoreCase()} for a {@link Command#commandName()}. Typically not needed * as {@link CommandLineParser#andCommands(Command...)} or {@link CommandLineParser#withCommands(Command...)} * can be used. * </pre> */ @CheckReturnValue @Nonnull public static CommandBuilder command(final Command command) { return new CommandBuilder(command).names(command.commandName()).description(command); } /** * Used to create {@link Argument} instances with a custom {@link StringParser}. * A custom {@link StringParser} is one which isn't available through {@link StringParsers}. * Consider setting {@link ArgumentBuilder#names(String...) names} on the returned * {@link ArgumentBuilder builder}. */ @CheckReturnValue @Nonnull public static <T> DefaultArgumentBuilder<T> withParser(final StringParser<T> parser) { return new DefaultArgumentBuilder<T>(parser); } }