All Downloads are FREE. Search and download functionalities are using the official Maven repository.

co.cask.cdap.common.options.OptionsParser Maven / Gradle / Ivy

There is a newer version: 5.1.2
Show newest version
/*
 * Copyright © 2014 Cask Data, Inc.
 *
 * 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 co.cask.cdap.common.options;

import java.io.PrintStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * 

* OptionsParser looks into the class looking for annotations that * specifies options. It then matches them with the command line arguments passed * to it. If there are any options that are specified on command line and not * present in annotated definition an usage message is automatically generated. * * Following is an simple example of it's usage. *

 *     public class MyClass {
 *       {@literal @}Option(name="name", usage="Specifies the name of a flow")
 *       private String flowName;
 *
 *       {@literal @}Option(name="flowlet", usage="Specifies the name of the flowlet")
 *       private String flowletName;
 *
 *       {@literal @}Option(name="class", usage="Specifies the class associated with the flowlet to be loaded")
 *       private String className;
 *
 *       {@literal @}Option(name="jar", usage="Specifies the path to the jar file that contains the class specified")
 *       private String jarPath;
 *
 *       {@literal @}Option(name="instance", usage="Specifies the instance id of the flowlet")
 *       private int instance;
 *
 *       public static void main(String[] args) {
 *        MyClass myclass = new MyClass();
 *        OptionsParser.init(myclass, args, System.out);
 *       }
 *     }
 *   
*

*/ public final class OptionsParser { private OptionsParser(){} /** * Parses the annotations specified in object and matches them * against the command line arguments that are being passed. If the options * could not be parsed it prints usage. * * @param object instance of class that contains @Option annotations. * @param args command line arguments. * @param out stream available to dump outputs. * @return List of arguments that were not definied by annotations. */ public static List init(Object object, String[] args, String appName, String appVersion, PrintStream out) { List nonOptionArgs = new ArrayList<>(); Map parsedOptions = parseArgs(args, nonOptionArgs); Map declaredOptions = extractDeclarations(object); if (parsedOptions.containsKey("help") && !declaredOptions.containsKey("help")) { printUsage(declaredOptions, appName, appVersion, out); return null; } for (String name : parsedOptions.keySet()) { if (declaredOptions.containsKey(name)) { continue; } throw new UnrecognizedOptionException(name); } for (Map.Entry option : parsedOptions.entrySet()) { try { declaredOptions.get(option.getKey()).setValue(option.getValue()); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } } for (Map.Entry declOption : declaredOptions.entrySet()) { OptionSpec option = declOption.getValue(); if (option.getEnvVar().length() > 0 && !parsedOptions.containsKey(option.getName())) { String envVal = System.getenv(option.getEnvVar()); if (null != envVal) { try { option.setValue(envVal); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } } } } return nonOptionArgs; } /** * Prints usage based on info specified by annotation in the instance * of class specified object. * @param object instance of class containing annotations for Options. * @param out stream to output usage. */ public static void printUsage(Object object, String appName, String appVersion, PrintStream out) { printUsage(extractDeclarations(object), appName, appVersion, out); } /** * Investigates the class specified by object and extracts out * all the fields in the class that are annotated with @Option attributes. * * @param object instance of class that is investigated for presence of @Option attributes * @return map of options to it's definitions. */ private static Map extractDeclarations(Object object) { Map options = new TreeMap<>(); // Get the parent class name. Class clazz = object.getClass(); // Iterate through all the fields specified in the main class // and find out annotations that have Option and construct a table. do { for (Field field : clazz.getDeclaredFields()) { Option option = field.getAnnotation(Option.class); if (option != null) { OptionSpec optionSpec = new OptionSpec(field, option, object); String name = optionSpec.getName(); if (options.containsKey(name)) { throw new DuplicateOptionException(name); } String n = optionSpec.getName(); options.put(n, optionSpec); } } } while (null != (clazz = clazz.getSuperclass())); return options; } private static Map parseArgs(String[] args, List nonOptionArgs) { Map parsedOptions = new TreeMap<>(); boolean ignoreTheRest = false; for (String arg : args) { if (arg.startsWith("-") && !ignoreTheRest) { if (arg.endsWith("--")) { ignoreTheRest = true; break; } String kv = arg.startsWith("--") ? arg.substring(2) : arg.substring(1); String [] splitKV = kv.split("=", 2); String key = splitKV[0]; String value = splitKV.length == 2 ? splitKV[1] : ""; parsedOptions.put(key, value); } else { nonOptionArgs.add(arg); } } return parsedOptions; } static final String NON_DEFAULT_FORMAT_STRING = " --%-20s %-20s %-20s\n"; static final String DEFAULT_FORMAT_STRING = " --%-20s %-20s %-50s\t(Default=%s)\n"; /** * Prints the usage based on declared Options in the class. * @param options extracted options from introspecting a class * @param out Stream to output the usage. */ private static void printUsage(Map options, String appName, String appVersion, PrintStream out) { out.print(String.format("%s - v%s\n", appName, appVersion)); out.println("Options:"); if (!options.containsKey("help")) { out.printf(NON_DEFAULT_FORMAT_STRING, "help", "", "\tDisplay this help message"); } for (OptionSpec option : options.values()) { if (option.isHidden()) { continue; } String usage = option.getUsage(); if (!usage.isEmpty()) { usage = "\t" + usage; } String def = option.getDefaultValue(); if ("null".equals(def)) { out.printf(NON_DEFAULT_FORMAT_STRING, option.getName(), "<" + option.getTypeName() + ">", usage); } else { out.printf(DEFAULT_FORMAT_STRING, option.getName(), "<" + option.getTypeName() + ">", usage, option.getDefaultValue()); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy