Java tutorial
/* * 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); } } }