org.codeseed.common.config.ext.CommandLineOptionsBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.codeseed.common.config.ext.CommandLineOptionsBuilder.java

Source

/*
 * Copyright 2013 Jeremy Gustie
 *
 * 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 org.codeseed.common.config.ext;

import static com.google.common.base.CaseFormat.LOWER_CAMEL;
import static com.google.common.base.CaseFormat.LOWER_HYPHEN;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionGroup;
import org.apache.commons.cli.Options;
import org.codeseed.common.config.Configuration;
import com.google.common.primitives.Primitives;

/**
 * A builder for Apache Commons CLI {@link Option} instances which uses
 * annotations found on configuration interfaces.
 *
 * @author Jeremy Gustie
 */
public class CommandLineOptionsBuilder {

    /**
     * Methods which contribute command line options.
     */
    private final List<Method> methods = new LinkedList<>();

    /**
     * Resource bundle for descriptions and argument names.
     */
    private ResourceBundle bundle;

    private CommandLineOptionsBuilder() {
    }

    /**
     * Creates a new options builder.
     *
     * @return the new builder
     */
    public static CommandLineOptionsBuilder newBuilder() {
        return new CommandLineOptionsBuilder();
    }

    /**
     * Scans the supplied configuration interface for command line options.
     *
     * @param configuration
     *            the configuration interface to find command line options from
     * @return this builder
     */
    public CommandLineOptionsBuilder addFrom(Class<? extends Configuration> configuration) {
        checkArgument(configuration.isInterface(), "must be an interface: %s", configuration);
        for (Method method : configuration.getMethods()) {
            CommandLine commandLine = method.getAnnotation(CommandLine.class);
            if (commandLine != null) {
                methods.add(method);
            }
        }
        return this;
    }

    /**
     * Provides a source of descriptions and argument names.
     *
     * @param bundle
     *            the localized resource bundle
     * @return this builder
     */
    public CommandLineOptionsBuilder bundle(ResourceBundle bundle) {
        this.bundle = checkNotNull(bundle);
        return this;
    }

    /**
     * Builds the options.
     *
     * @return the built options instance
     */
    public Options build() {
        Options options = new Options();
        Map<String, OptionGroup> groups = new LinkedHashMap<>();
        for (Method method : methods) {
            addOption(options, groups, method);
        }
        for (OptionGroup group : groups.values()) {
            options.addOptionGroup(group);
        }
        return options;
    }

    /**
     * Potentially adds an option to the supplied collection. Used by the
     * {@link #build()} method to populate an options collection.
     *
     * @param options
     *            the current collection of options
     * @param groups
     *            mappings of argument group identifiers to groups
     * @param method
     *            the configuration method to look up
     */
    protected void addOption(Options options, Map<String, OptionGroup> groups, Method method) {
        final CommandLine commandLine = method.getAnnotation(CommandLine.class);

        // Iterate over the triggers; take the first values
        String opt = null;
        String longOpt = null;
        for (String trigger : commandLine.value()) {
            if (!options.hasOption(trigger)) {
                if (opt == null && trigger.length() == 1) {
                    opt = trigger;
                } else if (longOpt == null) {
                    longOpt = trigger;
                }
            }
        }

        // Either we can use the method name or there is no option being added
        if (opt == null && longOpt == null) {
            String methodOpt = LOWER_CAMEL.to(LOWER_HYPHEN, method.getName());
            if (!options.hasOption(methodOpt)) {
                longOpt = methodOpt;
            } else {
                // TODO Warn?
                return;
            }
        }

        // Create a new option
        Option option = new Option(opt, null);
        option.setLongOpt(longOpt);

        // Set the number of arguments based on the return type
        final Class<?> returnType = Primitives.wrap(method.getReturnType());
        if (returnType.equals(Boolean.class)) {
            option.setArgs(0);
        } else if (Iterable.class.isAssignableFrom(returnType)) {
            option.setArgs(commandLine.maximum());
        } else if (Map.class.isAssignableFrom(returnType)) {
            option.setArgs(2);
            option.setValueSeparator('=');
        } else {
            option.setArgs(1);
        }

        // Add some descriptive text
        if (bundle != null) {
            try {
                // TODO Does this make sense?
                String key = option.hasLongOpt() ? option.getLongOpt() : method.getName();
                option.setDescription(bundle.getString(key + ".description"));
            } catch (MissingResourceException e) {
                option.setDescription(null);
            }
        }

        // Set argument names
        if (bundle != null && option.getArgs() > 0) {
            try {
                option.setArgName(bundle.getString(method.getName() + ".argName"));
            } catch (MissingResourceException e) {
                option.setArgName(null);
            }
        }

        // Add to either the collection or to the option groups
        String groupKey = commandLine.groupId();
        if (groupKey.isEmpty()) {
            options.addOption(option);
        } else {
            OptionGroup group = groups.get(groupKey);
            if (group == null) {
                group = new OptionGroup();
                groups.put(groupKey, group);
            }
            group.addOption(option);
        }
    }
}