Implement ArgumentsParser::parseArguments and add tests.

Bug: 10059681
Change-Id: I6511a46c879d7a52d0bb4fcab445a66bc40db98c
diff --git a/native/dicttoolkit/src/command_executors/makedict_executor.cpp b/native/dicttoolkit/src/command_executors/makedict_executor.cpp
index 8a84e80..4b0a5ae 100644
--- a/native/dicttoolkit/src/command_executors/makedict_executor.cpp
+++ b/native/dicttoolkit/src/command_executors/makedict_executor.cpp
@@ -24,6 +24,12 @@
 const char *const MakedictExecutor::COMMAND_NAME = "makedict";
 
 /* static */ int MakedictExecutor::run(const int argc, char **argv) {
+    const ArgumentsAndOptions argumentsAndOptions =
+            getArgumentsParser().parseArguments(argc, argv, true /* printErrorMessages */);
+    if (!argumentsAndOptions.isValid()) {
+        printUsage();
+        return 1;
+    }
     fprintf(stderr, "Command '%s' has not been implemented yet.\n", COMMAND_NAME);
     return 0;
 }
diff --git a/native/dicttoolkit/src/utils/arguments_and_options.h b/native/dicttoolkit/src/utils/arguments_and_options.h
index d8f5985..2d81b1e 100644
--- a/native/dicttoolkit/src/utils/arguments_and_options.h
+++ b/native/dicttoolkit/src/utils/arguments_and_options.h
@@ -42,6 +42,29 @@
         return mOptions.find(optionName) != mOptions.end();
     }
 
+    const std::string &getOptionValue(const std::string &optionName) const {
+        const auto &it = mOptions.find(optionName);
+        ASSERT(it != mOptions.end());
+        return it->second;
+    }
+
+    bool hasArgument(const std::string &name) const {
+        const auto &it = mArguments.find(name);
+        return it != mArguments.end() && !it->second.empty();
+    }
+
+    const std::string &getSingleArgument(const std::string &name) const {
+        const auto &it = mArguments.find(name);
+        ASSERT(it != mArguments.end() && !it->second.empty());
+        return it->second.front();
+    }
+
+    const std::vector<std::string> &getVariableLengthArguments(const std::string &name) const {
+        const auto &it = mArguments.find(name);
+        ASSERT(it != mArguments.end());
+        return it->second;
+    }
+
  private:
     DISALLOW_ASSIGNMENT_OPERATOR(ArgumentsAndOptions);
 
diff --git a/native/dicttoolkit/src/utils/arguments_parser.cpp b/native/dicttoolkit/src/utils/arguments_parser.cpp
index 52cc7b2..1451284 100644
--- a/native/dicttoolkit/src/utils/arguments_parser.cpp
+++ b/native/dicttoolkit/src/utils/arguments_parser.cpp
@@ -21,7 +21,7 @@
 namespace latinime {
 namespace dicttoolkit {
 
-const int ArgumentSpec::UNLIMITED_COUNT = -1;
+const size_t ArgumentSpec::UNLIMITED_COUNT = S_INT_MAX;
 
 bool ArgumentsParser::validateSpecs() const {
     std::unordered_set<std::string> argumentNameSet;
@@ -53,7 +53,7 @@
         const std::string &optionName = option.first;
         const OptionSpec &spec = option.second;
         printf(" [-%s", optionName.c_str());
-        if (spec.takeValue()) {
+        if (spec.needsValue()) {
             printf(" <%s>", spec.getValueName().c_str());
         }
         printf("]");
@@ -74,11 +74,11 @@
         const std::string &optionName = option.first;
         const OptionSpec &spec = option.second;
         printf(" -%s", optionName.c_str());
-        if (spec.takeValue()) {
+        if (spec.needsValue()) {
             printf(" <%s>", spec.getValueName().c_str());
         }
         printf("\t\t\t%s", spec.getDescription().c_str());
-        if (spec.takeValue() && !spec.getDefaultValue().empty()) {
+        if (spec.needsValue() && !spec.getDefaultValue().empty()) {
             printf("\tdefault: %s", spec.getDefaultValue().c_str());
         }
         printf("\n");
@@ -89,9 +89,76 @@
     printf("\n\n");
 }
 
-const ArgumentsAndOptions ArgumentsParser::parseArguments(const int argc, char **argv) const {
-    // TODO: Implement
-    return ArgumentsAndOptions();
+const ArgumentsAndOptions ArgumentsParser::parseArguments(const int argc, char **argv,
+        const bool printErrorMessage) const {
+    if (argc <= 0) {
+        AKLOGE("Invalid argc (%d).", argc);
+        ASSERT(false);
+        return ArgumentsAndOptions();
+    }
+    std::unordered_map<std::string, std::string> options;
+    for (const auto &entry : mOptionSpecs) {
+        const std::string &optionName = entry.first;
+        const OptionSpec &optionSpec = entry.second;
+        if (optionSpec.needsValue() && !optionSpec.getDefaultValue().empty()) {
+            // Set default value.
+            options[optionName] = optionSpec.getDefaultValue();
+        }
+    }
+    std::unordered_map<std::string, std::vector<std::string>> arguments;
+    auto argumentSpecIt = mArgumentSpecs.cbegin();
+    for (int i = 1; i < argc; ++i) {
+        const std::string arg = argv[i];
+        if (arg.length() > 1 && arg[0] == '-') {
+            // option
+            const std::string optionName = arg.substr(1);
+            const auto it = mOptionSpecs.find(optionName);
+            if (it == mOptionSpecs.end()) {
+                if (printErrorMessage) {
+                    fprintf(stderr, "Unknown option: '%s'\n", optionName.c_str());
+                }
+                return ArgumentsAndOptions();
+            }
+            std::string optionValue;
+            if (it->second.needsValue()) {
+                ++i;
+                if (i >= argc) {
+                    if (printErrorMessage) {
+                        fprintf(stderr, "Missing argument for option '%s'\n", optionName.c_str());
+                    }
+                    return ArgumentsAndOptions();
+                }
+                optionValue = argv[i];
+            }
+            options[optionName] = optionValue;
+        } else {
+            // argument
+            if (argumentSpecIt == mArgumentSpecs.end()) {
+                if (printErrorMessage) {
+                    fprintf(stderr, "Too many arguments.\n");
+                }
+                return ArgumentsAndOptions();
+            }
+            arguments[argumentSpecIt->getName()].push_back(arg);
+            if (arguments[argumentSpecIt->getName()].size() >= argumentSpecIt->getMaxCount()) {
+                ++argumentSpecIt;
+            }
+        }
+    }
+
+    if (argumentSpecIt != mArgumentSpecs.end()) {
+        const auto &it = arguments.find(argumentSpecIt->getName());
+        const size_t minCount = argumentSpecIt->getMinCount();
+        const size_t actualcount = it == arguments.end() ? 0 : it->second.size();
+        if (minCount > actualcount) {
+            if (printErrorMessage) {
+                fprintf(stderr, "Not enough arguments. %zd argumant(s) required for <%s>\n",
+                        minCount, argumentSpecIt->getName().c_str());
+            }
+            return ArgumentsAndOptions();
+        }
+    }
+    return ArgumentsAndOptions(std::move(options), std::move(arguments));
 }
 
 } // namespace dicttoolkit
diff --git a/native/dicttoolkit/src/utils/arguments_parser.h b/native/dicttoolkit/src/utils/arguments_parser.h
index 510a872..32bd328 100644
--- a/native/dicttoolkit/src/utils/arguments_parser.h
+++ b/native/dicttoolkit/src/utils/arguments_parser.h
@@ -35,29 +35,29 @@
 
     static OptionSpec keyValueOption(const std::string &valueName, const std::string &defaultValue,
             const std::string &description) {
-        return OptionSpec(true /* takeValue */, valueName, defaultValue, description);
+        return OptionSpec(true /* needsValue */, valueName, defaultValue, description);
     }
 
     static OptionSpec switchOption(const std::string &description) {
-        return OptionSpec(false /* takeValue */, "" /* valueName */, "" /* defaultValue */,
+        return OptionSpec(false /* needsValue */, "" /* valueName */, "" /* defaultValue */,
                 description);
     }
 
-    bool takeValue() const { return mTakeValue; }
+    bool needsValue() const { return mNeedsValue; }
     const std::string &getValueName() const { return mValueName; }
     const std::string &getDefaultValue() const { return mDefaultValue; }
     const std::string &getDescription() const { return mDescription; }
 
  private:
-    OptionSpec(const bool takeValue, const std::string &valueName, const std::string &defaultValue,
+    OptionSpec(const bool needsValue, const std::string &valueName, const std::string &defaultValue,
             const std::string &description)
-            : mTakeValue(takeValue), mValueName(valueName), mDefaultValue(defaultValue),
+            : mNeedsValue(needsValue), mValueName(valueName), mDefaultValue(defaultValue),
               mDescription(description) {}
 
     // Whether the option have to be used with a value or just a switch.
-    // e.g. 'f' in "command -f /path/to/file" is mTakeValue == true.
-    //      'f' in "command -f -t" is mTakeValue == false.
-    bool mTakeValue;
+    // e.g. 'f' in "command -f /path/to/file" is mNeedsValue == true.
+    //      'f' in "command -f -t" is mNeedsValue == false.
+    bool mNeedsValue;
     // Name of the value used to show usage.
     std::string mValueName;
     std::string mDefaultValue;
@@ -66,32 +66,32 @@
 
 class ArgumentSpec {
  public:
-    static const int UNLIMITED_COUNT;
+    static const size_t UNLIMITED_COUNT;
 
     static ArgumentSpec singleArgument(const std::string &name, const std::string &description) {
         return ArgumentSpec(name, 1 /* minCount */, 1 /* maxCount */, description);
     }
 
-    static ArgumentSpec variableLengthArguments(const std::string &name, const int minCount,
-            const int maxCount, const std::string &description) {
+    static ArgumentSpec variableLengthArguments(const std::string &name, const size_t minCount,
+            const size_t maxCount, const std::string &description) {
         return ArgumentSpec(name, minCount, maxCount, description);
     }
 
     const std::string &getName() const { return mName; }
-    int getMinCount() const { return mMinCount; }
-    int getMaxCount() const { return mMaxCount; }
+    size_t getMinCount() const { return mMinCount; }
+    size_t getMaxCount() const { return mMaxCount; }
     const std::string &getDescription() const { return mDescription; }
 
  private:
     DISALLOW_DEFAULT_CONSTRUCTOR(ArgumentSpec);
 
-    ArgumentSpec(const std::string &name, const int minCount, const int maxCount,
+    ArgumentSpec(const std::string &name, const size_t minCount, const size_t maxCount,
             const std::string &description)
             : mName(name), mMinCount(minCount), mMaxCount(maxCount), mDescription(description) {}
 
     const std::string mName;
-    const int mMinCount;
-    const int mMaxCount;
+    const size_t mMinCount;
+    const size_t mMaxCount;
     const std::string mDescription;
 };
 
@@ -101,7 +101,8 @@
             const std::vector<ArgumentSpec> &&argumentSpecs)
             : mOptionSpecs(std::move(optionSpecs)), mArgumentSpecs(std::move(argumentSpecs)) {}
 
-    const ArgumentsAndOptions parseArguments(const int argc, char **argv) const;
+    const ArgumentsAndOptions parseArguments(const int argc, char **argv,
+            const bool printErrorMessage) const;
     bool validateSpecs() const;
     void printUsage(const std::string &commandName, const std::string &description) const;
 
diff --git a/native/dicttoolkit/tests/utils/arguments_parser_test.cpp b/native/dicttoolkit/tests/utils/arguments_parser_test.cpp
index e79425b..58b4998 100644
--- a/native/dicttoolkit/tests/utils/arguments_parser_test.cpp
+++ b/native/dicttoolkit/tests/utils/arguments_parser_test.cpp
@@ -68,6 +68,80 @@
     }
 }
 
+int initArgv(char *mutableCommandLine, char **argv) {
+    bool readingSeparator = false;
+    int argc = 1;
+    argv[0] = mutableCommandLine;
+    const size_t length = strlen(mutableCommandLine);
+    for (size_t i = 0; i < length; ++i) {
+        if (mutableCommandLine[i] != ' ' && readingSeparator) {
+            readingSeparator = false;
+            argv[argc] = mutableCommandLine + i;
+            ++argc;
+        } else if (mutableCommandLine[i] == ' ' && !readingSeparator) {
+            readingSeparator = true;
+            mutableCommandLine[i] = '\0';
+        }
+    }
+    argv[argc] = nullptr;
+    return argc;
+}
+
+TEST(ArgumentsParserTests, TestParseArguments) {
+    std::unordered_map<std::string, OptionSpec> optionSpecs;
+    optionSpecs["a"] = OptionSpec::switchOption("description");
+    optionSpecs["b"] = OptionSpec::keyValueOption("valueName", "default", "description");
+    const std::vector<ArgumentSpec> argumentSpecs = {
+        ArgumentSpec::singleArgument("arg0", "description"),
+        ArgumentSpec::variableLengthArguments("arg1", 0 /* minCount */,  2 /* maxCount */,
+                "description"),
+    };
+    const ArgumentsParser parser =
+            ArgumentsParser(std::move(optionSpecs), std::move(argumentSpecs));
+
+    {
+        char kMutableCommandLine[1024] = "command arg";
+        char *argv[128] = {};
+        const int argc = initArgv(kMutableCommandLine, argv);
+        ASSERT_EQ(2, argc);
+        const ArgumentsAndOptions argumentsAndOptions = parser.parseArguments(
+                argc, argv, false /* printErrorMessages */);
+        EXPECT_FALSE(argumentsAndOptions.hasOption("a"));
+        EXPECT_EQ("default", argumentsAndOptions.getOptionValue("b"));
+        EXPECT_EQ("arg", argumentsAndOptions.getSingleArgument("arg0"));
+        EXPECT_FALSE(argumentsAndOptions.hasArgument("arg1"));
+    }
+    {
+        char kArgumentBuffer[1024] = "command -a arg arg";
+        char *argv[128] = {};
+        const int argc = initArgv(kArgumentBuffer, argv);
+        ASSERT_EQ(4, argc);
+        const ArgumentsAndOptions argumentsAndOptions = parser.parseArguments(
+                argc, argv, false /* printErrorMessages */);
+        EXPECT_TRUE(argumentsAndOptions.hasOption("a"));
+        EXPECT_EQ("default", argumentsAndOptions.getOptionValue("b"));
+        EXPECT_EQ("arg", argumentsAndOptions.getSingleArgument("arg0"));
+        EXPECT_TRUE(argumentsAndOptions.hasArgument("arg1"));
+        EXPECT_EQ(1u, argumentsAndOptions.getVariableLengthArguments("arg1").size());
+    }
+    {
+        char kArgumentBuffer[1024] = "command -b value arg arg1 arg2";
+        char *argv[128] = {};
+        const int argc = initArgv(kArgumentBuffer, argv);
+        ASSERT_EQ(6, argc);
+        const ArgumentsAndOptions argumentsAndOptions = parser.parseArguments(
+                argc, argv, false /* printErrorMessages */);
+        EXPECT_FALSE(argumentsAndOptions.hasOption("a"));
+        EXPECT_EQ("value", argumentsAndOptions.getOptionValue("b"));
+        EXPECT_EQ("arg", argumentsAndOptions.getSingleArgument("arg0"));
+        const std::vector<std::string> &arg1 =
+                argumentsAndOptions.getVariableLengthArguments("arg1");
+        EXPECT_EQ(2u, arg1.size());
+        EXPECT_EQ("arg1", arg1[0]);
+        EXPECT_EQ("arg2", arg1[1]);
+    }
+}
+
 } // namespace
 } // namespace dicttoolkit
 } // namespace latinime