/* Copyright (c) 2012-2014 Boundless and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Distribution License v1.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/org/documents/edl-v10.html * * Contributors: * Gabriel Roldan (Boundless) - initial implementation */ package org.locationtech.geogig.cli; import static com.google.common.base.Preconditions.checkNotNull; import java.io.IOException; import java.util.IllegalFormatException; import javax.annotation.Nullable; import jline.Terminal; import org.fusesource.jansi.Ansi; import org.locationtech.geogig.cli.annotation.RequiresRepository; import org.locationtech.geogig.cli.porcelain.ColorArg; import com.beust.jcommander.JCommander; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import com.google.common.base.Throwables; /** * A template command. * <p> * Services provided to subclasses: * <ul> * <li>Defines {@link RequiresRepository @RequiresRepository(true)} so its inherited by subclasses * and they need only to declare it if they don't require a repository present (which is the least * of the cases). * <li>Out of the box support for the {@code --help} argument * <li>Out of the box support for the hidden {@code --color} argument, allowing any command to * seamlessly support output text coloring or disabling it (see {@link ColorArg}) * <li>The {@link #newAnsi(Terminal)} method provides an {@link Ansi} instance configured to support * coloring or not depending on the {@link Terminal} capabilities and the value of the * {@code --color} argument, if present. * </p> * */ @RequiresRepository(true) public abstract class AbstractCommand implements CLICommand { @Parameter(names = "--help", help = true, hidden = true) public boolean help; @Parameter(hidden = true, names = "--color", description = "Whether to apply colored output. Possible values are auto|never|always.", converter = ColorArg.Converter.class) public ColorArg color = ColorArg.auto; @Override public void run(GeogigCLI cli) throws InvalidParameterException, CommandFailedException { checkNotNull(cli, "No GeogigCLI provided"); if (help) { printUsage(cli); return; } try { runInternal(cli); } catch (IOException e) { throw Throwables.propagate(e); } } protected Ansi newAnsi(Terminal terminal) { boolean useColor; switch (color) { case never: useColor = false; break; case always: useColor = true; break; default: useColor = terminal.isAnsiSupported(); } Ansi ansi = AnsiDecorator.newAnsi(useColor); return ansi; } protected Ansi newAnsi(Terminal terminal, StringBuilder target) { boolean useColor; switch (color) { case never: useColor = false; break; case always: useColor = true; break; default: useColor = terminal.isAnsiSupported(); } Ansi ansi = AnsiDecorator.newAnsi(useColor, target); return ansi; } /** * Subclasses shall implement to do the real work, will not be called if the command was invoked * with {@code --help}. Also, {@link GeogigCLI#getGeogig() cli.getGeogig()} is guaranteed to be * non null (e.g. there's a working repository) if the implementation class is marked with the * {@link RequiresRepository @RequiresRepository} annotation. * * @throws InvalidParameterException as per {@link CLICommand#run(GeogigCLI)} * @throws CommandFailedException as per {@link CLICommand#run(GeogigCLI)} * @throws IOException <b>only</b> propagated back if the IOException was thrown while writing * to the {@link GeogigCLI#getConsole() console}. * @param cli */ protected abstract void runInternal(GeogigCLI cli) throws InvalidParameterException, CommandFailedException, IOException; /** * Prints the JCommander usage for this command. */ public void printUsage(GeogigCLI cli) { JCommander jc = new JCommander(this); String commandName = this.getClass().getAnnotation(Parameters.class).commandNames()[0]; jc.setProgramName("geogig " + commandName); cli.printUsage(jc); } /** * Checks the truth of the boolean expression and throws a {@link InvalidParameterException} if * its {@code false}. * <p> * CLI commands may use this helper method to check the validity of user supplied command * arguments. * * @param expression a boolean expression * @param errorMessage the exception message to use if the check fails; will be converted to a * string using {@link String#valueOf(Object)} * @throws InvalidParameterException if {@code expression} is false */ public static void checkParameter(boolean expression, @Nullable Object errorMessage) { if (!expression) { throw new InvalidParameterException(String.valueOf(errorMessage)); } } /** * /** Checks the truth of the boolean expression and throws a {@link InvalidParameterException} * if its {@code false}. * <p> * CLI commands may use this helper method to check the validity of user supplied command * arguments. * * @param expression a boolean expression * @param errorMessageTemplate a template for the exception message should the check fail. The * message is formed as per {@link String#format(String, Object...)} * @param errorMessageArgs the arguments to be substituted into the message template. * @throws InvalidParameterException if {@code expression} is {@code false} * @throws IllegalFormatException if thrown by {@link String#format(String, Object...)} * @throws NullPointerException If the <tt>format</tt> is {@code null} * */ public static void checkParameter(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { if (!expression) { throw new InvalidParameterException(String.format(errorMessageTemplate, errorMessageArgs)); } } }