/* 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.base.Objects.toStringHelper;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.copyOf;
import static com.google.common.collect.Iterables.isEmpty;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static se.softhouse.common.guavaextensions.Functions2.listTransformer;
import static se.softhouse.common.guavaextensions.Predicates2.listPredicate;
import static se.softhouse.common.strings.Describables.EMPTY_STRING;
import static se.softhouse.common.strings.Describables.withString;
import static se.softhouse.jargo.StringParsers.optionParser;
import static se.softhouse.jargo.StringParsers.stringParser;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.annotation.CheckReturnValue;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.OverridingMethodsMustInvokeSuper;
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.NotThreadSafe;
import se.softhouse.common.guavaextensions.Functions2;
import se.softhouse.common.guavaextensions.Predicates2;
import se.softhouse.common.guavaextensions.Suppliers2;
import se.softhouse.common.strings.Describable;
import se.softhouse.common.strings.Describer;
import se.softhouse.common.strings.Describers;
import se.softhouse.jargo.ForwardingStringParser.SimpleForwardingStringParser;
import se.softhouse.jargo.StringParsers.FixedArityParser;
import se.softhouse.jargo.StringParsers.InternalStringParser;
import se.softhouse.jargo.StringParsers.KeyValueParser;
import se.softhouse.jargo.StringParsers.RepeatedArgumentParser;
import se.softhouse.jargo.StringParsers.StringParserBridge;
import se.softhouse.jargo.StringParsers.StringSplitterParser;
import se.softhouse.jargo.StringParsers.VariableArityParser;
import se.softhouse.jargo.internal.Texts.ProgrammaticErrors;
import se.softhouse.jargo.internal.Texts.UserErrors;
import com.google.common.annotations.Beta;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.Maps;
import com.google.common.collect.Range;
/**
* <pre>
* Responsible for configuring and building {@link Argument} instances.
* Example builders can be created with {@link Arguments}.
*
* <b>Note:</b>The code examples (for each method) assumes that all methods in {@link Arguments} have been statically imported.
*
* <b>Note:</b>Some methods needs to be called in a specific order
* (to make the generic type system produce the correct type) and to guide the
* caller, such invalid orders are documented with {@link Deprecated}. If those warnings
* are ignored {@link IllegalStateException} will be thrown at the offending call.
*
* @param <SELF> the type of the subclass extending this class.
* Concept borrowed from: <a href="http://passion.forco.de/content/emulating-self-types-using-java-generics-simplify-fluent-api-implementation">Ansgar.Konermann's blog</a>
* The pattern also resembles the <a href="http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern">Curiously recurring template pattern</a>
* @param <T> the type of arguments the built {@link Argument} instance should handle,
* such as {@link Integer} in the case of {@link Arguments#integerArgument(String...)}
* </pre>
*/
@NotThreadSafe
public abstract class ArgumentBuilder<SELF extends ArgumentBuilder<SELF, T>, T>
{
static final String DEFAULT_SEPARATOR = " ";
// ArgumentSetting variables, think about #copy() when adding new ones
@Nonnull private List<String> names = emptyList();
@Nonnull private Describable description = EMPTY_STRING;
private boolean required = false;
@Nonnull private String separator = DEFAULT_SEPARATOR;
private boolean ignoreCase = false;
private boolean isAllowedToRepeat = false;
@Nonnull private Optional<String> metaDescription = Optional.absent();
private boolean hideFromUsage = false;
private boolean isPropertyMap = false;
// Members that uses the T type, think about
// ListArgumentBuilder#copyAsListBuilder() when adding new ones
@Nullable private Supplier<? extends T> defaultValueSupplier = null;
@Nullable private Describer<? super T> defaultValueDescriber = null;
@Nonnull private Function<T, T> finalizer = Functions.identity();
@Nonnull private Predicate<? super T> limiter = Predicates.alwaysTrue();
@Nullable private final InternalStringParser<T> internalStringParser;
@Nonnull private final SELF myself;
/**
* Creates a default {@link ArgumentBuilder} that produces arguments that:
* <ul>
* <li>doesn't have any {@link #names(String...)}</li>
* <li>has an empty {@link #description(String)}</li>
* <li>isn't {@link #required()}</li>
* <li>uses {@link StringParser#defaultValue()} on {@link #parser()} to produce default values</li>
* <li>uses {@link Object#toString()} to describe the default value</li>
* <li>uses {@link StringParser#metaDescription()} on {@link #parser()} to produce meta
* descriptions</li>
* <li>doesn't have any {@link #limitTo(Predicate) limits}</li>
* </ul>
* Typically invoked implicitly by subclasses.
*/
protected ArgumentBuilder()
{
this(null);
}
ArgumentBuilder(final InternalStringParser<T> stringParser)
{
this.internalStringParser = stringParser;
myself = self();
}
@SuppressWarnings("unchecked")
private SELF self()
{
// SELF is passed in by subclasses as a type-variable, so type-safety
// is up to them
return (SELF) this;
}
/**
* Returns an {@link Immutable} {@link Argument} which can be passed to
* {@link CommandLineParser#withArguments(Argument...)} <br>
* When the parsing is done the parsed value for this
* argument can be fetched with {@link ParsedArguments#get(Argument)}.
*
* @throws IllegalStateException if it's not possible to construct an {@link Argument} with the
* current settings
*/
@CheckReturnValue
@Nonnull
public final Argument<T> build()
{
return new Argument<T>(this);
}
/**
* <pre>
* Parses command line arguments and returns the value of the argument built
* by {@link #build()}, providing a simple one liner shortcut when faced with only one argument.
* </pre>
*
* <pre class="prettyprint">
* <code class="language-java">
* String[] args = {"--listen-port", "8090"};
*
* int port = integerArgument("-p", "--listen-port").defaultValue(8080).description("The port clients should connect to.").parse(args);
* assertThat(port).isEqualTo(8090);
* </code>
* </pre>
*
* This is a shorthand method that should be used if only one {@link Argument} is expected as it
* will throw if unexpected arguments are encountered. If several arguments are expected use
* {@link CommandLineParser#withArguments(Argument...)} instead.
*
* @param actualArguments the arguments from the command line
* @return the parsed value from the {@code actualArguments}
* @throws ArgumentException if actualArguments isn't compatible with this
* argument
* </pre>
*/
@Nullable
public final T parse(String ... actualArguments) throws ArgumentException
{
return build().parse(actualArguments);
}
/**
* Returns a usage string for the argument that this builder builds.
* Should only be used if one argument is supported,
* otherwise the {@link CommandLineParser#usage()} method should be used instead.
*/
public final Usage usage()
{
return build().usage();
}
/**
* <pre>
* Returns a customized parser that the {@link Argument} will use to parse values.
*
* This is a suitable place to verify the configuration of your parser.
*
* If your {@link StringParser} doesn't support any configuration you can use
* {@link Arguments#withParser(StringParser)} directly instead of subclassing
* {@link ArgumentBuilder}
*
* @return the {@link StringParser} that performs the actual parsing of an argument value
* @throws IllegalStateException if the parser have been configured wrongly
* </pre>
*/
@Nonnull
protected abstract StringParser<T> parser();
@Nonnull
InternalStringParser<T> internalParser()
{
StringParser<T> parser = parser();
if(parser != InternalArgumentBuilder.MARKER)
return new StringParserBridge<T>(parser);
return internalStringParser;
}
/**
* Specifies the argument names that triggers the argument being built. As commands sometimes
* gets long and hard to understand it's recommended to also support long named arguments,
* making the commands even longer but more readable instead <br>
*
* @param argumentNames <ul>
* <li>"-o" for a short named option/argument</li>
* <li>"--option-name" for a long named option/argument</li>
* <li>"-o", "--option-name" to give the user both choices</li>
* <li>zero elements: makes this an indexed argument, meaning that the argument must
* be given at the same position on the command line as it is given to
* {@link CommandLineParser#withArguments(Argument...)} (not counting named
* arguments).</li>
* </ul>
* @return this builder
*/
public SELF names(final String ... argumentNames)
{
checkNoSpaces(asList(argumentNames));
names = copyOf(argumentNames);
return myself;
}
/**
* Works just like {@link #names(String...)} but it takes an {@link Iterable} instead.
*
* @param argumentNames the list to use as argument names
* @return this builder
*/
public SELF names(final Iterable<String> argumentNames)
{
checkNoSpaces(argumentNames);
names = copyOf(argumentNames);
return myself;
}
/**
* Ignores the case of the argument names set by {@link ArgumentBuilder#names(String...)}
*
* @return this builder
*/
public final SELF ignoreCase()
{
ignoreCase = true;
return myself;
}
/**
* <pre>
* Sets {@code theDescription} of what this argument does/means. Printed with
* {@link CommandLineParser#usage()}.
*
* For instance, in:
* -l, --enable-logging Output debug information to standard out
* Default: disabled
* "Output debug information to standard out" is {@code theDescription}
* @return this builder
* </pre>
*/
public final SELF description(final String theDescription)
{
description = withString(theDescription);
return myself;
}
/**
* <pre>
* {@link Describable} version of {@link #description(String)}.
* @return this builder
* </pre>
*/
public final SELF description(final Describable theDescription)
{
description = checkNotNull(theDescription);
return myself;
}
/**
* <pre>
* Makes {@link CommandLineParser#parse(String...)} throw
* {@link ArgumentException} if this argument isn't given.
* If possible, it's preferred to use a {@link #defaultValue(Object) default value} instead.
*
* The {@link Argument#toString()} will be used to print each missing argument.
*
* <b>Note</b>: If you choose to use multiple {@link #required()} indexed arguments all of them
* must have unique {@link #metaDescription(String)}s. This ensures that error messages
* can point out erroneous arguments better
*
* @return this builder
* @throws IllegalStateException if {@link #defaultValue(Object)} (or
* {@link #defaultValueSupplier(Supplier)}) has been
* called, because these are mutually exclusive with {@link #required()}
* </pre>
*/
public SELF required()
{
checkState(defaultValueSupplier == null, ProgrammaticErrors.DEFAULT_VALUE_AND_REQUIRED);
required = true;
return myself;
}
/**
* <pre>
* Sets a default value to use for this argument. Overrides {@link StringParser#defaultValue()} which is used by default.
* Returned by {@link ParsedArguments#get(Argument)} when no argument {@link ParsedArguments#wasGiven(Argument) was given}.
* To create default values lazily see {@link ArgumentBuilder#defaultValueSupplier(Supplier)}.
*
* <b>Mutability</b>:Remember that as {@link Argument} is {@link Immutable}
* this value should be so too if multiple argument parsings is going to take place.
* If mutability is wanted {@link ArgumentBuilder#defaultValueSupplier(Supplier)} should be used instead.
*
* @return this builder
* @throws IllegalStateException if {@link #required()} has been called,
* because these two methods are mutually exclusive
*/
public SELF defaultValue(@Nullable final T value)
{
checkState(!required, ProgrammaticErrors.DEFAULT_VALUE_AND_REQUIRED);
defaultValueSupplier = Suppliers.ofInstance(value);
return myself;
}
/**
* <pre>
* Sets a {@link Supplier} that can supply default values in the absence of this argument
*
* <b>Note:</b> Even if {@link #limitTo(Predicate)} is used, the {@link Supplier#get()} isn't called
* until the default value is actually needed ({@link ParsedArguments#get(Argument)}. If the
* default value is deemed non-allowed at that point an {@link IllegalStateException} is thrown.
*
* <b>Note:</b> May be removed in the future if Guava is removed as a dependency
*
* Wrap your supplier with {@link Suppliers#memoize(Supplier)} if you want to cache created values.
*
* @return this builder
* @throws IllegalStateException if {@link #required()} has been called,
* because these two methods are mutually exclusive
*/
@Beta
public SELF defaultValueSupplier(final Supplier<? extends T> aDefaultValueSupplier)
{
checkState(!required, ProgrammaticErrors.DEFAULT_VALUE_AND_REQUIRED);
defaultValueSupplier = checkNotNull(aDefaultValueSupplier);
return myself;
}
/**
* Provides a way to give the usage texts a better explanation of a default
* value than {@link Object#toString()} provides. Always prints {@code aDescription} regardless
* of what the default value is.
*
* @param aDescription the description
* @return this builder
* @see Describers#withConstantString(String)
*/
public SELF defaultValueDescription(final String aDescription)
{
this.defaultValueDescriber = Describers.withConstantString(aDescription);
return myself;
}
/**
* {@link Describer} version of {@link #defaultValueDescription(String)}
*
* @param describer a describer
* @return this builder
*/
public SELF defaultValueDescriber(final Describer<? super T> describer)
{
this.defaultValueDescriber = checkNotNull(describer);
return myself;
}
/**
* <pre>
* By default {@link StringParser}s provides a meta description (by implementing {@link StringParser#metaDescription()}
* that describes the type of data they expect. For instance, if you're writing a music player,
* a user of your application would (most probably) rather see:
*
* --track-nr <track nr> The track number to play (using {@code metaDescription("<track nr>") })
*
* instead of
*
* --track-nr <integer> The track number to play (the default provided by {@link StringParser#metaDescription()})
*
* So when using general data type parsers such as {@link StringParsers#integerParser()} you're better of if
* you provide a meta description that explains what the {@code integer} represents in the context of this argument.
*
* <b>Note:</b> empty meta descriptions aren't allowed
* <b>Note:</b> the surrounding < & > aren't enforced or added automatically but it's preferred
* to have them because it makes a clear distinction between {@link #names(String...) argument names} and their parameters.
*
* @param aMetaDescription "<track nr>" in the above example
* @return this builder
* @throws IllegalArgumentException if aMetaDescription is empty
* </pre>
*/
public final SELF metaDescription(final String aMetaDescription)
{
checkArgument(aMetaDescription.length() > 0, ProgrammaticErrors.INVALID_META_DESCRIPTION);
this.metaDescription = Optional.of(aMetaDescription);
return myself;
}
/**
* Hides this argument so that it's not displayed in the usage texts.<br>
* It's recommended that hidden arguments have a reasonable {@link #defaultValue(Object)} and
* aren't {@link #required()}, in fact this is recommended for all arguments.
*
* @return this builder
*/
public final SELF hideFromUsage()
{
this.hideFromUsage = true;
return myself;
}
/**
* For instance, "=" in --author=jjonsson
*
* @param aSeparator the character that separates the argument name and
* argument value, defaults to a space
* @return this builder
*/
public SELF separator(final String aSeparator)
{
separator = checkNotNull(aSeparator);
return myself;
}
/**
* <pre>
* Limits values parsed so that they conform to some specific rule.
* Only values for which {@link Predicate#apply(Object)} returns true, will be accepted.
* Other values will cause an {@link ArgumentException}.
* For example {@link Range#closed(Comparable, Comparable)} only allows values within a range.
*
* To override the default error message that is generated with {@link UserErrors#DISALLOWED_VALUE}
* you can throw {@link IllegalArgumentException} from {@link Predicate#apply(Object)}. The detail
* message of that exception will be used by {@link ArgumentException#getMessageAndUsage()}.
* When this is needed it's generally recommended to write a parser of its own instead.
*
* <b>Note:</b>{@link Object#toString() toString()} on {@code aLimiter} will replace {@link StringParser#descriptionOfValidValues(Locale)} in the usage
*
* <b>Note:</b>The validity of any {@link #defaultValueSupplier(Supplier) default value} isn't checked until
* it's actually needed when {@link ParsedArguments#get(Argument)} is called. This is so
* because {@link Supplier#get()} (or {@link StringParser#defaultValue()}) could take an arbitrary long time.
*
* <b>Note:</b>Any previously set limiter will be {@link Predicates2#and(Predicate, Predicate)
* and'ed} together with {@code aLimiter}.
* </pre>
*
* @param aLimiter a limiter
* @return this builder
*/
@Beta
public SELF limitTo(Predicate<? super T> aLimiter)
{
limiter = Predicates2.<T>and(limiter, aLimiter);
return myself;
}
/**
* <pre>
* Makes this argument handle "property like" arguments:
* -Dproperty_name=value
* where
* "-D" is the string supplied to {@link #names(String...)},
* "property_name" is the {@link String} key in the resulting {@link Map},
* "=" is the {@link #separator(String)} (set to "=" if it hasn't been overridden already),
* "value" is decoded by the previously set {@link StringParser}.
*
*
* Tip: You can pass {@link #defaultValueDescriber(Describer)} a
* {@link Describers#mapDescriber(Map)} to describe each property that you
* support (given that you've used {@link ArgumentBuilder#defaultValue(Object)}
* to use sane defaults for your properties).
*
* @return a new (more specific) builder
*
* @see #asKeyValuesWithKeyParser(StringParser) to use other types as keys than {@link String}
* </pre>
*/
@CheckReturnValue
public final MapArgumentBuilder<String, T> asPropertyMap()
{
return new MapArgumentBuilder<String, T>(this, stringParser());
}
/**
* <pre>
* Makes this argument handle properties like arguments:
* -Dproperty_name=value
* where "-D" is one of the strings supplied to {@link #names(String...)},
* "property_name" is decoded by {@code keyParser} and
* "value" is decoded by the {@link StringParser} previously passed to the constructor.
*
* For example:
* </pre>
*
* <pre class="prettyprint">
* <code class="language-java">
* Map<Integer, Integer> numberMap = Arguments.integerArgument("-N")
* .asKeyValuesWithKeyParser(StringParsers.integerParser())
* .parse("-N1=5", "-N2=10");
* assertThat(numberMap.get(1)).isEqualTo(5);
* </code>
* </pre>
*
* For this to work correctly it's paramount that {@code K} implements a
* proper {@link Object#hashCode()} because it's going to be used a key in a {@link Map}.
*
* @return a new (more specific) builder
*/
@CheckReturnValue
public final <K> MapArgumentBuilder<K, T> asKeyValuesWithKeyParser(StringParser<K> keyParser)
{
return new MapArgumentBuilder<K, T>(this, checkNotNull(keyParser));
}
/**
* <pre>
* When given a "," this allows for
* arguments such as:
* -numbers 1,2,3
* where the resulting {@code List<Integer>} would contain 1, 2 & 3.
*
* Doesn't allow empty lists.
* </pre>
*
* @param valueSeparator the string to split the input with
* @return a new (more specific) builder
*/
@CheckReturnValue
public SplitterArgumentBuilder<T> splitWith(final String valueSeparator)
{
return new SplitterArgumentBuilder<T>(this, valueSeparator);
}
/**
* Useful for handling a variable amount of parameters in the end of a
* command. Uses this argument to parse values but assumes that all the following
* parameters are of the same type, integer in the following example:
*
* <pre class="prettyprint">
* <code class="language-java">
* String[] threeArgs = {"--numbers", "1", "2", "3"};
* List<Integer> numbers = integerArgument("--numbers").variableArity().parse(threeArgs);
* assertThat(numbers).isEqualTo(asList(1, 2, 3));
*
* String[] twoArgs = {"--numbers", "1", "2"};
* List<Integer> numbers = integerArgument("--numbers").variableArity().parse(twoArgs);
* assertThat(numbers).isEqualTo(asList(1, 2));
* </code>
* </pre>
*
* <b>Note:</b> if {@link #defaultValue(Object) default value} has been set before
* {@link #variableArity()} is called the {@link #defaultValue(Object) default value} will be a
* list with that one element in it, otherwise it will be empty.
*
* @return a new (more specific) builder
*/
@CheckReturnValue
public ArityArgumentBuilder<T> variableArity()
{
return new ArityArgumentBuilder<T>(this);
}
/**
* <pre>
* Uses this argument to parse values but assumes that {@code numberOfParameters} of the
* following parameters are of the same type.
*
* {@link Integer} in the following example:
* </pre>
*
* <pre class="prettyprint">
* <code class="language-java">
* List<Integer> numbers = integerArgument("--numbers").arity(2).parse("--numbers", "1", "2");
* assertThat(numbers).isEqualTo(asList(1, 2));
* </code>
* </pre>
*
* <b>Note:</b>If the argument isn't {@link #required()} the default value
* will be a list that contains {@code numberOfParameters} elements
* of {@link StringParser#defaultValue()}, in the above example that would be two zeros.
* If this isn't wanted use {@link #defaultValue(Object)} to override it.
*
* @return a new (more specific) builder
* @throws IllegalArgumentException if {@code numberOfParameters} is less than 2
*/
@CheckReturnValue
public ArityArgumentBuilder<T> arity(final int numberOfParameters)
{
checkArgument(numberOfParameters > 1, ProgrammaticErrors.TO_SMALL_ARITY, numberOfParameters);
return new ArityArgumentBuilder<T>(this, numberOfParameters);
}
/**
* <pre>
* Makes it possible to enter several values for the same argument. Such as this:
*
* <pre class="prettyprint">
* <code class="language-java">
* String[] arguments = {"--number", "1", "--number", "2"};
* List<Integer> numbers = integerArgument("--number").repeated().parse(arguments);
* assertThat(numbers).isEqualTo(asList(1, 2));
* </code>
* </pre>
*
* If you want to combine {@link #repeated()} with a specific {@link #arity(int)} then call
* {@link #arity(int)} before calling {@link #repeated()}:
*
* <pre class="prettyprint">
* <code class="language-java">
* String[] arguments = {"--numbers", "1", "2", "--numbers", "3", "4"};
* List<List<Integer>> numberLists = integerArgument("--numbers").arity(2).repeated().parse(arguments);
* assertThat(numberLists).isEqualTo(asList(asList(1, 2), asList(3, 4)));
* </code>
* </pre>
*
* For repeated values in a property map such as this:
*
* <pre class="prettyprint">
* <code class="language-java">
* String[] arguments = {"-Nnumber=1", "-Nnumber=2"};
* Map<String, List<Integer>> numberMap = integerArgument("-N").repeated().asPropertyMap().parse(arguments);
* assertThat(numberMap.get("number")).isEqualTo(asList(1, 2));
* </code>
* </pre>
*
* {@link #repeated()} should be called before {@link #asPropertyMap()}.
* For arguments without a name (indexed arguments) use {@link #variableArity()} instead.
*
* @return a new (more specific) builder
* </pre>
*/
@CheckReturnValue
public RepeatedArgumentBuilder<T> repeated()
{
return new RepeatedArgumentBuilder<T>(this);
}
@Override
public String toString()
{
return toStringHelper(this).omitNullValues().add("names", names).add("description", description).add("metaDescription", metaDescription)
.add("hideFromUsage", hideFromUsage).add("ignoreCase", ignoreCase).add("limiter", limiter).add("required", required)
.add("separator", separator).add("defaultValueDescriber", defaultValueDescriber).add("defaultValueSupplier", defaultValueSupplier)
.add("internalStringParser", internalStringParser).toString();
}
/**
* <pre>
* Copies all values from the given copy into this one, except for:
* {@link #parser()}, {@link #defaultValueSupplier()} & {@link #defaultValueDescriber()}
* as they may change between different builders
* (e.g the default value for Argument<Boolean> and Argument<List<Boolean> are not compatible)
* @param copy the ArgumentBuilder to copy from
*/
@OverridingMethodsMustInvokeSuper
void copy(final ArgumentBuilder<?, ?> copy)
{
this.names = copy.names;
this.description = copy.description;
this.required = copy.required;
this.separator = copy.separator;
this.ignoreCase = copy.ignoreCase;
this.isAllowedToRepeat = copy.isAllowedToRepeat;
this.metaDescription = copy.metaDescription;
this.hideFromUsage = copy.hideFromUsage;
}
/**
* <pre>
* {@code aFinalizer} is called after {@link StringParser#parse(String, Locale)}
* but before any predicates given to {@link #limitTo(Predicate)} are tested.
*
* Is used internally to finalize values produced by {@link StringParser#parse(String, Locale)}.
*
* For example {@link RepeatedArgumentParser} uses this to make the resulting {@link List} {@link Immutable}.
*
* For regular {@link StringParser}s it's recommended to use {@link SimpleForwardingStringParser}
* and decorate your {@link StringParser} with any finalization there instead.
*
* <b>Note:</b> If {@link #finalizeWith(Function)} have been called before,
* the given {@code aFinalizer} will be run after that finalizer.
*
* </pre>
*
* @param aFinalizer a finalizer
* @return this builder
*/
final SELF finalizeWith(Function<T, T> aFinalizer)
{
finalizer = Functions2.compound(finalizer, aFinalizer);
return myself;
}
final void allowRepeatedArguments()
{
isAllowedToRepeat = true;
}
final void setAsPropertyMap()
{
isPropertyMap = true;
}
/**
* @formatter.off
*/
@Nonnull final List<String> names(){ return names; }
@Nullable Describer<? super T> defaultValueDescriber(){ return defaultValueDescriber; }
@Nonnull Describable description(){ return description; }
final boolean isRequired(){ return required; }
@Nullable final String separator(){ return separator; }
final boolean isIgnoringCase(){ return ignoreCase; }
final boolean isPropertyMap(){ return isPropertyMap; }
final boolean isAllowedToRepeat(){ return isAllowedToRepeat; }
@Nullable final Optional<String> metaDescription(){ return metaDescription; }
final boolean isHiddenFromUsage(){ return hideFromUsage; }
@Nullable final Supplier<? extends T> defaultValueSupplier(){ return defaultValueSupplier; }
@Nonnull final Function<T, T> finalizer(){ return finalizer; }
@Nonnull final Predicate<? super T> limiter(){ return limiter; }
private void checkNoSpaces(Iterable<String> argumentNames)
{
for(String name : argumentNames)
{
checkArgument(!name.contains(" "), "Detected a space in %s, argument names must not have spaces in them", name);
}
}
/**
* @formatter.on
*/
/**
* A very simple version of an {@link ArgumentBuilder}, it's exposed mainly to
* lessen the exposure of the {@code SELF_TYPE} argument of the {@link ArgumentBuilder}.
*/
@NotThreadSafe
public static final class DefaultArgumentBuilder<T> extends ArgumentBuilder<DefaultArgumentBuilder<T>, T>
{
private final StringParser<T> parser;
DefaultArgumentBuilder(final StringParser<T> aParser)
{
parser = checkNotNull(aParser);
}
@Override
protected StringParser<T> parser()
{
return parser;
}
}
// Non-Interesting builders below, most declarations under here handles
// (by deprecating) invalid invariants between different argument properties
private abstract static class InternalArgumentBuilder<BUILDER extends InternalArgumentBuilder<BUILDER, T>, T> extends ArgumentBuilder<BUILDER, T>
{
/**
* Only used to flag that this builder is an internal one, not used for parsing
*/
static final StringParser<?> MARKER = new StringParserBridge<String>(StringParsers.stringParser());
InternalArgumentBuilder()
{
}
InternalArgumentBuilder(InternalStringParser<T> parser)
{
super(parser);
}
@SuppressWarnings("unchecked")
@Override
protected StringParser<T> parser()
{
return (StringParser<T>) MARKER;
}
}
/**
* The {@link InternalStringParser} equivalent to
* {@link se.softhouse.jargo.ArgumentBuilder.DefaultArgumentBuilder}
*/
static final class SimpleArgumentBuilder<T> extends InternalArgumentBuilder<SimpleArgumentBuilder<T>, T>
{
SimpleArgumentBuilder(InternalStringParser<T> parser)
{
super(parser);
}
}
/**
* Builder for {@link Command}s. Created with {@link Arguments#command(Command)}.
*/
@NotThreadSafe
public static final class CommandBuilder extends InternalArgumentBuilder<CommandBuilder, ParsedArguments>
{
CommandBuilder(final Command command)
{
super(command);
}
/**
* @deprecated Commands shouldn't be required
*/
@Deprecated
@Override
public CommandBuilder required()
{
throw new IllegalStateException();
}
/**
* @deprecated Commands shouldn't have default values
*/
@Deprecated
@Override
public CommandBuilder defaultValue(ParsedArguments defaultValue)
{
throw new IllegalStateException();
}
/**
* @deprecated Commands shouldn't have default values
*/
@Deprecated
@Override
public CommandBuilder defaultValueSupplier(Supplier<? extends ParsedArguments> defaultValueSupplier)
{
throw new IllegalStateException();
}
/**
* @deprecated Commands can't have default values, so no description can be useful
*/
@Deprecated
@Override
public CommandBuilder defaultValueDescription(String defaultValueDescription)
{
throw new IllegalStateException();
}
/**
* @deprecated Commands can't have default values, so no description can be useful
*/
@Deprecated
@Override
public CommandBuilder defaultValueDescriber(Describer<? super ParsedArguments> defaultValueDescriber)
{
throw new IllegalStateException();
}
/**
* @deprecated Commands can't be limited
*/
@Deprecated
@Override
public CommandBuilder limitTo(Predicate<? super ParsedArguments> limiter)
{
throw new IllegalStateException();
}
/**
* @deprecated Commands can't be split
*/
@Deprecated
@Override
public SplitterArgumentBuilder<ParsedArguments> splitWith(String valueSeparator)
{
throw new IllegalStateException();
}
/**
* @deprecated pass any {@link Argument}s to your {@link Command} to
* {@link Command#Command(Argument...)}
*/
@Deprecated
@Override
public ArityArgumentBuilder<ParsedArguments> arity(int arity)
{
throw new IllegalStateException();
}
/**
* @deprecated pass any {@link Argument}s to your {@link Command} to
* {@link Command#Command(Argument...)}
*/
@Deprecated
@Override
public ArityArgumentBuilder<ParsedArguments> variableArity()
{
throw new IllegalStateException();
}
}
private static class ListArgumentBuilder<BUILDER extends ListArgumentBuilder<BUILDER, T>, T> extends InternalArgumentBuilder<BUILDER, List<T>>
{
ListArgumentBuilder(InternalStringParser<List<T>> parser)
{
super(parser);
}
void copyAsListBuilder(ArgumentBuilder<?, T> builder, int nrOfElementsInDefaultValue)
{
finalizeWith(listTransformer(builder.finalizer));
finalizeWith(Functions2.<T>unmodifiableList());
limitTo(listPredicate(builder.limiter));
if(builder.defaultValueSupplier != null)
{
defaultValueSupplier(Suppliers2.ofRepeatedElements(builder.defaultValueSupplier, nrOfElementsInDefaultValue));
}
if(builder.defaultValueDescriber != null)
{
defaultValueDescriber(Describers.listDescriber(builder.defaultValueDescriber));
}
// TODO(jontejj): should meta description <foo> be expanded to <foo> <foo>?
}
}
/**
* An intermediate builder used by {@link #arity(int)} and {@link #variableArity()}. It's mainly
* used to switch the T argument of the previous builder to List<T> and to indicate
* invalid call
* orders.
*/
@NotThreadSafe
public static final class ArityArgumentBuilder<T> extends ListArgumentBuilder<ArityArgumentBuilder<T>, T>
{
private ArityArgumentBuilder(final ArgumentBuilder<? extends ArgumentBuilder<?, T>, T> builder, final int arity)
{
super(new FixedArityParser<T>(builder.internalParser(), arity));
init(builder, arity);
}
private ArityArgumentBuilder(final ArgumentBuilder<? extends ArgumentBuilder<?, T>, T> builder)
{
super(new VariableArityParser<T>(builder.internalParser()));
init(builder, 1);
}
private void init(final ArgumentBuilder<? extends ArgumentBuilder<?, T>, T> builder, int nrOfElementsInDefaultValue)
{
copy(builder);
copyAsListBuilder(builder, nrOfElementsInDefaultValue);
}
/**
* @deprecated
* <pre>
* This doesn't work with {@link ArgumentBuilder#arity(int)} or {@link ArgumentBuilder#variableArity()}
* I.e --foo 1,2 3,4
* is currently unsupported
* </pre>
*/
@Deprecated
@Override
public SplitterArgumentBuilder<List<T>> splitWith(final String valueSeparator)
{
throw new IllegalStateException("splitWith(...) doesn't work with arity/variableArity()");
}
}
/**
* An intermediate builder used by {@link #repeated()}. It's mainly used to switch the T
* argument of the previous builder to List<T> and to indicate invalid call orders.
*/
@NotThreadSafe
public static final class RepeatedArgumentBuilder<T> extends ListArgumentBuilder<RepeatedArgumentBuilder<T>, T>
{
private RepeatedArgumentBuilder(final ArgumentBuilder<? extends ArgumentBuilder<?, T>, T> builder)
{
super(new RepeatedArgumentParser<T>(builder.internalParser()));
copy(builder);
copyAsListBuilder(builder, 1);
allowRepeatedArguments();
}
/**
* @deprecated this method should be called before {@link #repeated()}
*/
@Deprecated
@Override
public ArityArgumentBuilder<List<T>> arity(final int numberOfParameters)
{
throw new IllegalStateException("Programmer Error. Call arity(...) before repeated()");
}
/**
* @deprecated this method should be called before {@link #repeated()}
*/
@Override
@Deprecated
public ArityArgumentBuilder<List<T>> variableArity()
{
throw new IllegalStateException("Programmer Error. Call variableArity(...) before repeated()");
}
/**
* @deprecated call {@link #splitWith(String)} before {@link #repeated()}
*/
@Deprecated
@Override
public SplitterArgumentBuilder<List<T>> splitWith(final String valueSeparator)
{
throw new IllegalStateException("call splitWith(String) before repeated()");
}
}
/**
* An intermediate builder used by {@link Arguments#optionArgument(String, String...)}
*/
@NotThreadSafe
public static final class OptionArgumentBuilder extends InternalArgumentBuilder<OptionArgumentBuilder, Boolean>
{
OptionArgumentBuilder()
{
defaultValue(false);
}
@Override
InternalStringParser<Boolean> internalParser()
{
return optionParser(defaultValueSupplier().get());
}
@Override
public OptionArgumentBuilder defaultValue(@Nonnull Boolean value)
{
checkNotNull(value, ProgrammaticErrors.OPTION_DOES_NOT_ALLOW_NULL_AS_DEFAULT);
return super.defaultValue(value);
}
@Override
public OptionArgumentBuilder names(Iterable<String> argumentNames)
{
checkArgument(!isEmpty(argumentNames), ProgrammaticErrors.OPTIONS_REQUIRES_AT_LEAST_ONE_NAME);
return super.names(argumentNames);
}
@Override
public OptionArgumentBuilder names(String ... argumentNames)
{
checkArgument(argumentNames.length >= 1, ProgrammaticErrors.OPTIONS_REQUIRES_AT_LEAST_ONE_NAME);
return super.names(argumentNames);
}
/**
* @deprecated an optional flag can't be required
*/
@Deprecated
@Override
public OptionArgumentBuilder required()
{
throw new IllegalStateException("An optional flag can't be requried");
}
/**
* @deprecated a separator is useless since an optional flag can't be
* assigned a value
*/
@Deprecated
@Override
public OptionArgumentBuilder separator(final String aSeparator)
{
throw new IllegalStateException("A seperator for an optional flag isn't supported as an optional flag can't be assigned a value");
}
/**
* @deprecated an optional flag can only have an arity of zero
*/
@Deprecated
@Override
public ArityArgumentBuilder<Boolean> arity(final int numberOfParameters)
{
throw new IllegalStateException("An optional flag can't have any other arity than zero");
}
/**
* @deprecated an optional flag can only have an arity of zero
*/
@Deprecated
@Override
public ArityArgumentBuilder<Boolean> variableArity()
{
throw new IllegalStateException("An optional flag can't have any other arity than zero");
}
/**
* @deprecated an optional flag can't be split by anything
*/
@Deprecated
@Override
public SplitterArgumentBuilder<Boolean> splitWith(final String valueSeparator)
{
throw new IllegalStateException("An optional flag can't be split as it has no value that is parsed");
}
}
/**
* An intermediate builder used by {@link #asPropertyMap()}. It's mainly used to switch the T
* argument of the previous builder to Map<K, T> and to indicate invalid call orders.
*/
@NotThreadSafe
public static final class MapArgumentBuilder<K, V> extends InternalArgumentBuilder<MapArgumentBuilder<K, V>, Map<K, V>>
{
private final ArgumentBuilder<?, V> valueBuilder;
private final StringParser<K> keyParser;
private MapArgumentBuilder(final ArgumentBuilder<?, V> builder, StringParser<K> keyParser)
{
this.valueBuilder = builder;
this.keyParser = keyParser;
copy(builder);
finalizeWith(Functions2.<K, V>mapValueTransformer(builder.finalizer));
finalizeWith(Functions2.<K, V>unmodifiableMap());
setAsPropertyMap();
}
@Override
InternalStringParser<Map<K, V>> internalParser()
{
checkState(names().size() > 0, ProgrammaticErrors.NO_NAME_FOR_PROPERTY_MAP);
if(separator().equals(DEFAULT_SEPARATOR))
{
separator("=");
}
else
{
checkState(separator().length() > 0, ProgrammaticErrors.EMPTY_SEPARATOR);
}
return new KeyValueParser<K, V>(keyParser,
valueBuilder.internalParser(),
valueBuilder.limiter,
defaultValueSupplier(),
valueBuilder.defaultValueSupplier);
}
// A Describer<? super V> is also a describer for a V
@SuppressWarnings("unchecked")
@Override
Describer<? super Map<K, V>> defaultValueDescriber()
{
Describer<? super Map<K, V>> overriddenDescriber = super.defaultValueDescriber();
if(overriddenDescriber != null)
return overriddenDescriber;
Describer<? super V> valueDescriber = valueBuilder.defaultValueDescriber();
if(valueDescriber != null)
{
Describer<?> mapDescriber = Describers.mapDescriber(valueDescriber, separator());
return (Describer<? super Map<K, V>>) mapDescriber;
}
return Describers.mapDescriber(Describers.<V>toStringDescriber(), separator());
}
/**
* @deprecated because {@link #repeated()} should be called before {@link #asPropertyMap()}
*/
@Deprecated
@Override
public RepeatedArgumentBuilder<Map<K, V>> repeated()
{
throw new IllegalStateException("You'll need to call repeated before asPropertyMap");
}
/**
* @deprecated because {@link #splitWith(String)} should be
* called before {@link #asPropertyMap()}.
* This is to make generic work its magic and produce the
* correct type, for example {@code Map<String, List<Integer>>}.
*/
@Deprecated
@Override
public SplitterArgumentBuilder<Map<K, V>> splitWith(final String valueSeparator)
{
throw new IllegalStateException("You'll need to call splitWith before asPropertyMap");
}
/**
* @deprecated because {@link #arity(int)} should be
* called before {@link #asPropertyMap()}.
*/
@Deprecated
@Override
public ArityArgumentBuilder<Map<K, V>> arity(final int nrOfParameters)
{
throw new IllegalStateException("You'll need to call arity before asPropertyMap");
}
/**
* @deprecated because {@link #asPropertyMap()} is already of variable arity
*/
@Deprecated
@Override
public ArityArgumentBuilder<Map<K, V>> variableArity()
{
throw new IllegalStateException("asPropertyMap is already of variable arity");
}
@Override
public MapArgumentBuilder<K, V> defaultValue(Map<K, V> defaultKeyValues)
{
return super.defaultValue(Maps.newLinkedHashMap(defaultKeyValues));
}
}
/**
* An intermediate builder used by {@link #splitWith(String)}. It's mainly used to switch the T
* argument of the previous builder to List<T> and to indicate invalid call orders.
*/
@NotThreadSafe
public static final class SplitterArgumentBuilder<T> extends ListArgumentBuilder<SplitterArgumentBuilder<T>, T>
{
private SplitterArgumentBuilder(final ArgumentBuilder<?, T> builder, final String valueSeparator)
{
super(new StringSplitterParser<T>(valueSeparator, builder.internalParser()));
copy(builder);
copyAsListBuilder(builder, 1);
}
/**
* @deprecated you can't use both {@link #splitWith(String)} and {@link #arity(int)}
*/
@Deprecated
@Override
public ArityArgumentBuilder<List<T>> arity(final int numberOfParameters)
{
throw new IllegalStateException("You can't use both splitWith and arity");
}
/**
* @deprecated you can't use both {@link #splitWith(String)} and {@link #variableArity()}
*/
@Deprecated
@Override
public ArityArgumentBuilder<List<T>> variableArity()
{
throw new IllegalStateException("You can't use both splitWith and variableArity");
}
}
}