+++ /dev/null
-/*--------------------------------------------------------------------------\r
- * Copyright 2008 Taro L. Saito\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- *--------------------------------------------------------------------------*/\r
-//--------------------------------------\r
-// XerialJ\r
-//\r
-// OptionParser.java\r
-// Since: Oct 27, 2008 11:11:28 AM\r
-//\r
-// $URL: http://www.xerial.org/svn/project/XerialJ/trunk/xerial-core/src/main/java/org/xerial/util/opt/OptionParser.java $\r
-// $Author: leo $\r
-//--------------------------------------\r
-package org.xerial.util.opt;\r
-\r
-import java.io.OutputStream;\r
-import java.io.StringWriter;\r
-import java.io.Writer;\r
-import java.util.ArrayList;\r
-import java.util.HashSet;\r
-import java.util.List;\r
-\r
-import org.xerial.core.XerialError;\r
-import org.xerial.core.XerialErrorCode;\r
-import org.xerial.core.XerialException;\r
-import org.xerial.util.bean.TypeInfo;\r
-\r
-/**\r
- * A command-line option and argument parser\r
- * \r
- * @author leo\r
- * \r
- */\r
-public class OptionParser {\r
- private final OptionSchema schema;\r
- private final Object optionHolder;\r
-\r
- private boolean ignoreUnknownOption = false;\r
- private HashSet<Option> activatedOption = new HashSet<Option>();\r
- private HashSet<Argument> activatedArgument = new HashSet<Argument>();\r
- private List<String> unusedArgument = new ArrayList<String>();\r
-\r
- private boolean ignoreAfterTheFirstArgument = false;\r
-\r
- public <T> OptionParser(T optionHolder) {\r
- this.optionHolder = optionHolder;\r
- schema = OptionSchema.newOptionSchema(optionHolder);\r
- }\r
-\r
- public <T> OptionParser(Class<T> optionHolderType) {\r
- try {\r
- this.optionHolder = TypeInfo.createInstance(optionHolderType);\r
- }\r
- catch (XerialException e) {\r
- throw new XerialError(XerialErrorCode.INVALID_ARGUMENT, e);\r
- }\r
-\r
- schema = OptionSchema.newOptionSchema(optionHolder);\r
- }\r
-\r
- @SuppressWarnings("unchecked")\r
- public <T> T getOptionHolder() {\r
- return (T) optionHolder;\r
- }\r
-\r
- OptionItem findOptionItem(OptionSchema schema, String optionName) throws OptionParserException {\r
- OptionItem optionItem = schema.getOption(optionName);\r
- if (optionItem == null) {\r
- if (!ignoreUnknownOption) {\r
- throw new OptionParserException(XerialErrorCode.SYNTAX_ERROR, "unknown option: "\r
- + optionName);\r
- }\r
- }\r
- return optionItem;\r
- }\r
-\r
- public void printUsage() {\r
- printUsage(System.out);\r
- }\r
-\r
- public void printUsage(Writer out) {\r
- assert schema != null;\r
- schema.printUsage(out);\r
- }\r
-\r
- public void printUsage(OutputStream out) {\r
- assert schema != null;\r
- schema.printUsage(out);\r
- }\r
-\r
- public String getUsage() {\r
- StringWriter buf = new StringWriter();\r
- printUsage(buf);\r
- return buf.toString();\r
- }\r
-\r
- /**\r
- * Return the unused option arguments after the invocation of the\r
- * {@link #parse(String[])} method\r
- * \r
- * @return\r
- */\r
- public String[] getUnusedArguments() {\r
- String[] result = new String[unusedArgument.size()];\r
- for (int i = 0; i < unusedArgument.size(); ++i)\r
- result[i] = unusedArgument.get(i);\r
- return result;\r
- }\r
-\r
- /**\r
- * Parse the command-line arguments and bind them to the field value of the\r
- * optionHolder\r
- * \r
- * @param args\r
- * @param ignoreUnknownOption\r
- * when true, the option parser ignore unknown commands. When\r
- * false, OptionParserException will be thrown when unknown\r
- * options are found.\r
- * @throws OptionParserException\r
- */\r
- public void parse(String[] args, boolean ignoreUnknownOption) throws OptionParserException {\r
- setIgnoreUnknownOption(ignoreUnknownOption);\r
- parse(args);\r
- }\r
-\r
- /**\r
- * Parse until the first argument is found. This method is useful to\r
- * implement sub commands, e.g.,;\r
- * \r
- * <pre>\r
- * > [global option] (sub command name) [sub command option]\r
- * </pre>\r
- * \r
- * @param <OptionHolder>\r
- * @param args\r
- * @return\r
- * @throws OptionParserException\r
- */\r
- public void parseUntilTheFirstArgument(String[] args) throws OptionParserException {\r
- ignoreAfterTheFirstArgument = true;\r
- try {\r
- parse(args);\r
- }\r
- finally {\r
- ignoreAfterTheFirstArgument = false;\r
- }\r
- }\r
-\r
- /**\r
- * Parse the command-line arguments and bind them to the field value of the\r
- * optionHolder\r
- * \r
- * @param args\r
- * @throws OptionParserException\r
- */\r
- public void parse(String[] args) throws OptionParserException {\r
- // clear\r
- unusedArgument.clear();\r
- activatedOption.clear();\r
- activatedArgument.clear();\r
-\r
- // initialize collections in the OptionHolder\r
- for (OptionItem each : schema.getOptionItemList()) {\r
- each.initialize(optionHolder);\r
- }\r
- for (ArgumentItem each : schema.getArgumentItemList()) {\r
- each.initialize(optionHolder);\r
- }\r
-\r
- int index = 0; // index in the args array\r
- int argIndex = 0; // argument index\r
- for (; index < args.length; index++) {\r
- String currentArg = args[index];\r
-\r
- if (currentArg.startsWith("--")) {\r
- // long name option\r
- int splitPos = currentArg.indexOf('=');\r
- if (splitPos == -1) {\r
- // no value is found\r
- String longOptionName = currentArg.substring(2);\r
- OptionItem optionItem = findOptionItem(schema, longOptionName);\r
- if (optionItem == null) {\r
- unusedArgument.add(currentArg);\r
- continue;\r
- }\r
-\r
- if (optionItem.needsArgument())\r
- throw new OptionParserException(XerialErrorCode.SYNTAX_ERROR,\r
- "parameter value is required for --" + longOptionName);\r
-\r
- setOption(optionItem, "true");\r
-\r
- if (!optionItem.takesMultipleArguments()\r
- && activatedOption.contains(optionItem.getOption()))\r
- throw new OptionParserException(XerialErrorCode.DUPLICATE_OPTION,\r
- optionItem.getOption().toString());\r
-\r
- activatedOption.add(optionItem.getOption());\r
- }\r
- else {\r
- // option is a (key, value) pair\r
- String longOptionName = currentArg.substring(2, splitPos);\r
- String value = currentArg.substring(splitPos + 1);\r
- OptionItem optionItem = findOptionItem(schema, longOptionName);\r
- if (optionItem == null) {\r
- unusedArgument.add(currentArg);\r
- continue;\r
- }\r
-\r
- if (!optionItem.needsArgument()) {\r
- throw new OptionParserException(XerialErrorCode.SYNTAX_ERROR,\r
- "syntax error --" + longOptionName);\r
- }\r
-\r
- setOption(optionItem, value);\r
- if (!optionItem.takesMultipleArguments()\r
- && activatedOption.contains(optionItem.getOption()))\r
- throw new OptionParserException(XerialErrorCode.DUPLICATE_OPTION,\r
- optionItem.getOption().toString());\r
-\r
- activatedOption.add(optionItem.getOption());\r
- }\r
-\r
- }\r
- else if (currentArg.startsWith("-")) {\r
- // option with a leading hyphen (e.g. "-txvf" is equivalent to "-t", "-x", "-v" and "-f")\r
- String shortOptionList = currentArg.substring(1);\r
- for (int i = 0; i < shortOptionList.length(); i++) {\r
- String shortOptionName = shortOptionList.substring(i, i + 1);\r
- OptionItem optionItem = findOptionItem(schema, shortOptionName);\r
- if (optionItem == null) {\r
- unusedArgument.add(currentArg);\r
- continue;\r
- }\r
-\r
- if (optionItem.needsArgument()) {\r
- if (shortOptionList.length() != 1)\r
- throw new OptionParserException(\r
- XerialErrorCode.SYNTAX_ERROR,\r
- String\r
- .format(\r
- "short name option -%s with an arguments must be a single notation",\r
- shortOptionName));\r
-\r
- setOption(optionItem, args[++index]);\r
- }\r
- else\r
- setOption(optionItem, "true");\r
-\r
- if (!optionItem.takesMultipleArguments()\r
- && activatedOption.contains(optionItem.getOption()))\r
- throw new OptionParserException(XerialErrorCode.DUPLICATE_OPTION,\r
- optionItem.getOption().toString());\r
-\r
- activatedOption.add(optionItem.getOption());\r
- }\r
- }\r
- else {\r
- // general argument\r
- ArgumentItem argItem = schema.getArgument(argIndex);\r
- if (argItem == null) {\r
- if (ignoreUnknownOption) {\r
- unusedArgument.add(currentArg);\r
- }\r
- else\r
- throw new OptionParserException(XerialErrorCode.SYNTAX_ERROR,\r
- "unused argument: " + currentArg);\r
- }\r
- else {\r
- try {\r
- argItem.set(optionHolder, currentArg);\r
- }\r
- catch (XerialException e) {\r
- throw new OptionParserException(XerialErrorCode.INVALID_ARGUMENT, e\r
- .getMessage());\r
- }\r
-\r
- // when duplicate values for the argument variable have found\r
- if (!argItem.takesMultipleArguments()\r
- && activatedArgument.contains(argItem.getArgumentDescriptor()))\r
- throw new OptionParserException(XerialErrorCode.DUPLICATE_OPTION, argItem\r
- .getArgumentDescriptor().toString());\r
-\r
- activatedArgument.add(argItem.getArgumentDescriptor());\r
- argIndex++;\r
- }\r
-\r
- if (ignoreAfterTheFirstArgument) {\r
- // put all the arguments after the current argument to the list, and exits the loop\r
- index++;\r
- for (; index < args.length; index++) {\r
- unusedArgument.add(args[index]);\r
- }\r
- }\r
- }\r
-\r
- }\r
-\r
- // verify missing options & arguments\r
- for (ArgumentItem argItem : schema.getArgumentItemList()) {\r
- if (argItem.getArgumentDescriptor().required()\r
- && !activatedArgument.contains(argItem.getArgumentDescriptor()))\r
- throw new OptionParserException(XerialErrorCode.MISSING_ARGUMENT, argItem\r
- .toString());\r
- }\r
-\r
- }\r
-\r
- private void setOption(OptionItem item, String value) throws OptionParserException {\r
- try {\r
- item.setOption(optionHolder, value);\r
- }\r
- catch (XerialException e) {\r
- XerialErrorCode be = e.getErrorCode();\r
- switch (be) {\r
- case InvalidFormat:\r
- throw new OptionParserException(XerialErrorCode.INVALID_ARGUMENT, String.format(\r
- "cannot set %s to %s", value, item.toString()));\r
- default:\r
- throw new OptionParserException(e.<XerialErrorCode> getErrorCode(), e.getMessage());\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Set this true when ignoring unknown options and arguments that match the\r
- * input arguments\r
- * \r
- * @param ignore\r
- */\r
- public void setIgnoreUnknownOption(boolean ignore) {\r
- ignoreUnknownOption = ignore;\r
- }\r
-\r
-}\r