org.pantsbuild.args4j.Parser.java Source code

Java tutorial

Introduction

Here is the source code for org.pantsbuild.args4j.Parser.java

Source

// Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).

package org.pantsbuild.args4j;

import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.List;

import com.google.common.base.Optional;
import com.google.common.collect.Lists;

import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.OptionHandlerRegistry;
import org.kohsuke.args4j.ParserProperties;

/**
 * Encapsulates args4j command line parsing and makes it behave similar to com.twitter.common.args.
 */
public class Parser {

    /**
     * Represents the result of a command line argument parse.
     */
    public static final class Result {
        static Result failure(CmdLineParser parser, String message, Object... parameters) {
            return new Result(parser, Optional.of(String.format(message, parameters)));
        }

        static Result success(CmdLineParser cmdLineParser) {
            return new Result(cmdLineParser, Optional.<String>absent());
        }

        private final CmdLineParser parser;
        private final Optional<String> failure;

        private Result(CmdLineParser parser, Optional<String> failure) {
            this.parser = parser;
            this.failure = failure;
        }

        /**
         * @return {@code true} if the command line parse failed.
         */
        public boolean isFailure() {
            return failure.isPresent();
        }

        /**
         * <p>
         * Prints command line usage.
         * </p>
         * <p>
         * The usage will include failure details if the command line parse was a failure according to
         * {@link #isFailure()}.
         * </p>
         *
         * @param out The stream to print command line usage to.
         */
        public void printUsage(OutputStream out) {
            PrintStream helpPrinter = new PrintStream(out);
            if (failure.isPresent()) {
                helpPrinter.println(failure.get());
                helpPrinter.println();
            }

            helpPrinter.println("Usage:");
            parser.printSingleLineUsage(helpPrinter);
            helpPrinter.println(); // printSingleLineUsage does not include a newline

            helpPrinter.println();
            parser.printUsage(helpPrinter);
        }
    }

    /**
     * Parses command line arguments and populates the given {@code option} bean with the values if
     * the parse is successful.
     *
     * @param options The options bean to populate with the parsed command line options.
     * @param args The command line arguments to parse.
     * @return The result of the parse.
     */
    public static Result parse(Object options, String... args) {
        OptionHandlerRegistry.getRegistry().registerHandler(boolean.class, BooleanOptionHandler.class);
        OptionHandlerRegistry.getRegistry().registerHandler(Boolean.class, BooleanOptionHandler.class);

        ParserProperties parserProperties = ParserProperties.defaults()
                // The @ syntax here is global, @argfile alone is a file one or more options in it.
                // The jar-tool traditionally accepted limited @argfile switch values, ie: -f=@argfile,
                // and the contents of the argfile was the single option's value.
                // As such we turn off args4j @ syntax explicitly and implement a custom OptionHandler
                // to retain the traditional jar-tool @ semantics.
                .withAtSyntax(false).withOptionValueDelimiter("=").withShowDefaults(true);

        // Args4j expects positional arguments come 1st whereas pants traditionally expects them to come
        // last.  Fixup the order to suit Args4j if needed.
        // NB: This only works because we set the option value delimiter to non-whitespace above!
        List<String> arguments = Lists.newArrayList(args);
        if (arguments.size() > 1) {
            List<String> positionalArgs = Lists.newArrayList();
            Iterator<String> reverseArgIterator = Lists.reverse(arguments).iterator();
            while (reverseArgIterator.hasNext()) {
                String arg = reverseArgIterator.next();
                if (!arg.startsWith("-")) {
                    reverseArgIterator.remove();
                    positionalArgs.add(arg);
                } else {
                    break;
                }
            }
            arguments.addAll(0, Lists.reverse(positionalArgs));
        }

        CmdLineParser cmdLineParser = new CmdLineParser(options, parserProperties);
        try {
            cmdLineParser.parseArgument(arguments);
            return Result.success(cmdLineParser);
        } catch (CmdLineException e) {
            return Result.failure(cmdLineParser, "Invalid command line:\n\t%s", e.getLocalizedMessage());
        } catch (InvalidCmdLineArgumentException e) {
            return Result.failure(cmdLineParser, "Invalid command line parameter:\n\t%s", e.getMessage());
        }
    }
}