/* 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 se.softhouse.jargo.CommandLineParser.US_BY_DEFAULT;
import java.util.Locale;
import javax.annotation.CheckReturnValue;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.Immutable;
import se.softhouse.common.strings.Describable;
import se.softhouse.jargo.CommandLineParserInstance.ArgumentIterator;
import se.softhouse.jargo.StringParsers.InternalStringParser;
import se.softhouse.jargo.internal.Texts.UsageTexts;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
/**
* <pre>
* {@link Command}s automatically gets an invocation of execute when given on the command line.
* This is particularly useful to avoid a never ending switch statement.
*
* {@link Command}s have a {@link CommandLineParser} themselves (and thereby sub-commands are allowed as well), that is,
* they execute a command and may support contextual arguments as specified by the constructor {@link Command#Command(Argument...)}.
*
* Sub-commands are executed before their parent {@link Command}.
*
* To integrate your {@link Command} into an {@link Argument} use {@link Arguments#command(Command)}
* or {@link CommandLineParser#withCommands(Command...)} if you have several commands.
*
* If you support several commands and a user enters several of them at the same
* time they will be executed in the order given to {@link CommandLineParser#parse(String...)}.
* If any {@link StringParser#parse(String, Locale) parse} errors occurs (for {@link Command#Command(Argument...) command arguments}) the {@link Command}
* will not be executed. However, if given multiple commands and {@link StringParser#parse(String, Locale) parse errors} occurs,
* all {@link Command}s given before the {@link Command} with {@link StringParser#parse(String, Locale) parse errors} will have been executed.
* This is so because {@link Command#Command(Argument...) command arguments} are allowed to be dependent on earlier {@link Command}s being executed.
* So it's recommended to let the user know when you've executed a {@link Command}.
*
* <b>Mutability note:</b> although a {@link Command} should be {@link Immutable}
* the objects it handles doesn't have to be. So repeated invocations of execute
* is allowed to yield different results or to affect external state.
* </pre>
*
* Example subclass:
*
* <pre class="prettyprint">
* <code class="language-java">
* public class Build extends Command
* {
* private static final Argument<File> PATH = Arguments.fileArgument().description("the directory to build").build();
*
* public Build()
* {
* super(PATH);
* }
*
* public String description()
* {
* return "Builds a target";
* }
*
* protected void execute(ParsedArguments parsedArguments)
* {
* File pathToBuild = parsedArguments.get(PATH);
* //Build pathToBuild here
* }
* }
* </code>
* </pre>
*
* And the glue needed to integrate the Build {@link Command} with a {@link CommandLineParser}:
*
* <pre class="prettyprint">
* <code class="language-java">
* CommandLineParser.withCommands(new Build()).parse("build", "some_directory_that_needs_building");
* </code>
* </pre>
*
* As can be seen in the example the command name is the class name in lower case by default. This
* may be overridden with {@link #commandName()}.<br>
* If your commands don't require any arguments you can use
* {@link CommandLineParser#withCommands(Class)} instead.
*/
@Immutable
public abstract class Command extends InternalStringParser<ParsedArguments> implements Describable
{
@Nonnull private final ImmutableList<Argument<?>> commandArguments;
@Nonnull private final Supplier<CommandLineParserInstance> commandArgumentParser = Suppliers.memoize(new Supplier<CommandLineParserInstance>(){
@Override
public CommandLineParserInstance get()
{
return new CommandLineParserInstance(commandArguments, ProgramInformation.AUTO, US_BY_DEFAULT, true);
}
});
/**
* @param commandArguments the arguments that this command supports.
*/
protected Command(Argument<?> ... commandArguments)
{
this.commandArguments = ImmutableList.copyOf(commandArguments);
}
/**
* The name that triggers this command. Defaults to {@link Class#getSimpleName()} in lower
* case. For several names override this with {@link ArgumentBuilder#names(String...)}
*/
@Nonnull
@CheckReturnValue
protected String commandName()
{
return getClass().getSimpleName().toLowerCase(Locale.US);
}
/**
* Called when this command is encountered on the command line
*
* @param parsedArguments a container with parsed values for the command arguments,
* as specified by {@link Command#Command(Argument...)}
*/
protected abstract void execute(ParsedArguments parsedArguments);
/**
* Override to provide a description to print in the usage text for this command.
* This is essentially an alternative to {@link ArgumentBuilder#description(Describable)}
*
* @return the description to use in the usage text
*/
@Override
@Nonnull
public String description()
{
return "";
}
@Override
public String toString()
{
return commandName();
}
@Override
final ParsedArguments parse(final ArgumentIterator arguments, final ParsedArguments previousOccurance, final Argument<?> argumentSettings,
Locale locale) throws ArgumentException
{
arguments.rememberAsCommand();
ParsedArguments parsedArguments = parser().parse(arguments, locale);
arguments.rememberInvocationOfCommand(this, parsedArguments);
return parsedArguments;
}
/**
* The parser for parsing the {@link Argument}s passed to {@link Command#Command(Argument...)}
*/
private CommandLineParserInstance parser()
{
return commandArgumentParser.get();
}
@Override
final ParsedArguments defaultValue()
{
return null;
}
@Override
final String descriptionOfValidValues(Argument<?> argumentSettings, Locale locale)
{
// For commands the validValues is a usage text itself for the command arguments
return parser().usage(locale).toString();
}
@Override
final String describeValue(ParsedArguments value)
{
return null;
}
@Override
final String metaDescription(Argument<?> argumentSettings)
{
return "";
}
@Override
String metaDescriptionInRightColumn(Argument<?> argumentSettings)
{
return UsageTexts.ARGUMENT_HEADER;
}
}