package org.ovirt.engine.core.config; import java.util.HashMap; import org.apache.log4j.Logger; import org.ovirt.engine.core.config.validation.ConfigActionType; /** * The <code>EngineConfigCLIParser</code> class represents a parser for the EngineConfig tool. It parses the given * arguments into meaningful keys and values. The parser assumes the '=' char can only be used in the format k=v, and * not as a char that is actually part of a key/value. */ public class EngineConfigCLIParser { private static final Logger log = Logger.getLogger(EngineConfigCLIParser.class); private HashMap<String, String> argsMap = new HashMap<String, String>(); private EngineConfigMap engineConfigMap = new EngineConfigMap(); public EngineConfigCLIParser() { } /** * Parses the given arguments, identifies the desired action, and the different keys and values. * @param args * The arguments that need to be parsed. * @throws IllegalArgumentException * If there are no arguments, if a legal action was not identified, or if second argument has '=' char, * but action is not 'set'. */ public void parse(String[] args) { log.debug("parse: beginning to parse arguments."); validateNonEmpty(args); parseAction(args); parseArguments(args); log.debug("parse: Finished parsing arguments."); } private void validateNonEmpty(String[] args) { if (args.length == 0) { log.debug("parse error: no arguments given."); throw (new IllegalArgumentException("Error: at least 1 argument needed for configuration utility to run.")); } } /** * Parses the argument in the currentIndex of args, into a key and its value. Can also parse version, properties * file, config file. If the argument in the currentIndex does not have a value, assumes the value is in the * following argument. The argsMap helps to parse the arguments which are eventually set into the engineConfigMap. * @param args * @param currentIndex * @return whether or not the next argument is to be skipped */ private boolean parseKeyValue (String[] args, int currentIndex) { boolean fShouldSkip = false; int delimiterIndex = args[currentIndex].indexOf("="); String key = getStringBeforeEqualChar(args[currentIndex], delimiterIndex); // includes '-' String value = getStringAfterEqualChar(args[currentIndex], delimiterIndex); if (!key.isEmpty()) { if (!value.isEmpty()) { argsMap.put(key, value); } else { if (args.length > currentIndex + 1) { // To make sure there is another argument argsMap.put(key, args[currentIndex + 1]); fShouldSkip = true; } else { log.debug("parsing error: missing pair for key " + args[currentIndex] + ". Skipping argument."); } } } else { log.debug("parsing error: illegal argument " + args[currentIndex] + ", starts with '='. Skipping argument."); } return fShouldSkip; } /** * Parses the second argument in case it does not start with a '-'. There are two valid scenarios for this. First, * when the argument has been given as the key in the 'get' action, in the format: "-g key". Second, in the 'set' * action. For set action, we require key=value, or parsing will fail. * * @param arg */ private void parseSecondArgWithoutDash(String arg) { int delimiterIndex = arg.indexOf("="); if (getConfigAction().equals(ConfigActionType.ACTION_SET) && delimiterIndex == -1) { throw new IllegalArgumentException("Argument for set action must be in format of key=value."); } String key = getStringBeforeEqualChar(arg, delimiterIndex); String value = getStringAfterEqualChar(arg, delimiterIndex); if (!key.isEmpty()) { if (!value.isEmpty()) { parseSecondArgWithKeyValue(arg, key, value); } else if (getConfigAction().equals(ConfigActionType.ACTION_SET) && getKey() == null) { engineConfigMap.setKey(key); engineConfigMap.setValue(value); } else if (getConfigAction().equals(ConfigActionType.ACTION_GET) && getKey() == null) { engineConfigMap.setKey(arg); // sets the key in 'get' action with format: "-g key" } else { log.debug("parsing error: illegal argument " + arg + ". Skipping argument."); } } else { log.debug("parsing error: illegal argument " + arg + ", starts with '='. Skipping argument."); } } /** * Parses second argument with a key and a value. Is only valid in the 'set' action. * @param arg */ private void parseSecondArgWithKeyValue(String arg, String key, String value) { if (getConfigAction().equals(ConfigActionType.ACTION_SET)) { engineConfigMap.setKey(key); engineConfigMap.setValue(value); } else { log.debug("parseArguments error: second argument '" + arg + "' has an '=' char but action is not 'set'."); throw new IllegalArgumentException("Illegal second argument: " + arg + "."); } } /** * Parses all arguments except for the first argument which is assumed to be the action. The argsMap member helps to * parse the arguments which are eventually set into the engineConfigMap. * @param args * The arguments that needs to be parsed. */ private void parseArguments(String[] args) { boolean fShouldSkip = true; // So the first arg which is the action will be skipped for (int currentIndex = 0; currentIndex < args.length; currentIndex++) { if (fShouldSkip) { fShouldSkip = false; continue; } if (args[currentIndex].startsWith("-")) { fShouldSkip = parseKeyValue(args, currentIndex); } else if (currentIndex == 1) { parseSecondArgWithoutDash(args[currentIndex]); } else { log.debug("parseArguments error: Skipping argument " + args[currentIndex] + "."); } } fillEngineConfigMap(); } /** * Parses the action from the given arguments. * @param args * @throws IllegalArgumentException * If the first argument is not a legal action */ private void parseAction(String[] args) { validateArgStartsWithDash(args[0]); int delimiterIndex = args[0].indexOf("="); String action = getStringBeforeEqualChar(args[0], delimiterIndex); String key = getStringAfterEqualChar(args[0], delimiterIndex); if (!action.isEmpty()) { if (!key.isEmpty()) { handleActionWithKey(action, key); } else { handleActionWithoutKey(action); } } else { log.debug("parseAction error: Illegal first argument: '" + args[0] + "' - not a legal action."); throw new IllegalArgumentException("Action verb must come first, and '" + args[0] + "' is not an action.\nPlease tell me what to do: list? get? set? get-all?"); } } /** * Returns the first part of the given arg, until the delimiterIndex, excluding. Did not use split() because of * problematic handling of empty parts. */ private String getStringAfterEqualChar(String arg, int delimiterIndex) { String value; if (delimiterIndex > 0) { value = arg.substring(delimiterIndex + 1); } else { value = ""; } return value; } /** * Returns the second part of the given arg, starting from the delimiterIndex, excluding. Did not use split() * because of problematic handling of empty parts. */ private String getStringBeforeEqualChar(String arg, int delimiterIndex) { String key; if (delimiterIndex > 0) { key = arg.substring(0, delimiterIndex); } else { key = arg; } return key; } /** * Handles an action without a key. * @param action */ private void handleActionWithoutKey(String action) { engineConfigMap.setConfigAction(ConfigActionType.getActionType(action)); if (getConfigAction() == null) { log.debug("parseAction error: Illegal first argument: '" + action + "' - not a legal action."); throw new IllegalArgumentException("Action verb must come first, and '" + action + "' is not an action.\nPlease tell me what to do: list? get? set? get-all?"); } } /** * Handles an action with a key. The only valid action with a key in the first argument is the 'get' action, in the * format: "--get=key". * @param action * @param key */ private void handleActionWithKey(String action, String key) { engineConfigMap.setConfigAction(ConfigActionType.getActionType(action)); if (action.equals("--get")) { // Since this is the only valid case for having a key in the first arg engineConfigMap.setKey(key); } else { log.debug("parseAction error: first argument is illegal."); throw new IllegalArgumentException("Action verb must come first, and '" + action + '=' + key + "' is not an action.\nPlease tell me what to do: list? get? set? get-all?"); } } /** * Makes sure the first argument starts with a '-', since all actions do. * @param arg */ private void validateArgStartsWithDash(String arg) { if (!arg.startsWith("-")) { log.debug("parseAction error: first argument '" + arg + "' did not start with '-' or '--'."); throw (new IllegalArgumentException("First argument must be an action, and start with '-' or '--'")); } } private String parseAlternateConfigFile() { for (String configKeyName : AlternateFileType.OPTION_CONFIG.getOptionalStrings()) { if (argsMap.containsKey(configKeyName)) { return argsMap.get(configKeyName); } } return null; } private String parseAlternatePropertiesFile() { for (String propertyKeyName : AlternateFileType.OPTION_PROPERTIES.getOptionalStrings()) { if (argsMap.containsKey(propertyKeyName)) { return argsMap.get(propertyKeyName); } } return null; } private void fillEngineConfigMap() { engineConfigMap.setVersion(argsMap.get("--cver")); engineConfigMap.setAlternateConfigFile(parseAlternateConfigFile()); engineConfigMap.setAlternatePropertiesFile(parseAlternatePropertiesFile()); } public EngineConfigMap getEngineConfigMap() { return engineConfigMap; } public String getVersion() { return engineConfigMap.getVersion(); } public ConfigActionType getConfigAction() { return engineConfigMap.getConfigAction(); } public String getKey() { return engineConfigMap.getKey(); } public String getValue() { return engineConfigMap.getValue(); } public String getAlternateConfigFile() { return engineConfigMap.getAlternateConfigFile(); } public String getAlternatePropertiesFile() { return engineConfigMap.getAlternatePropertiesFile(); } public String engineConfigMapToString() { return engineConfigMap.toString(); } }