Add Possibility to set MultiValue Parameters.
authorRafał Długołęcki <rafal@dlugolecki.net.pl>
Tue, 12 May 2015 15:58:31 +0000 (17:58 +0200)
committerRafał Długołęcki <rafal@dlugolecki.net.pl>
Tue, 12 May 2015 15:58:31 +0000 (17:58 +0200)
include/argument.h
include/command.h
include/multiValue.h [new file with mode: 0644]
include/option.h
tests/Makefile.am
tests/multiValue/should_extract_values_by_separator.cpp [new file with mode: 0644]

index 5690e5dbf6ba2973147535ce32d66459ad23dd95..a32cf82064893f9f78677aea1a9e3c4bb3d4a3f7 100644 (file)
@@ -45,6 +45,7 @@ namespace command {
          */
         virtual void handle() {
             this->call(value);
+            this->used = true;
         }
 
         /**
@@ -64,16 +65,13 @@ namespace command {
          *  against next value.
          */
         virtual bool understand(const std::string & argv) {
-            if (!isUsed()) {
-                std::stringstream ss;
+            std::stringstream ss;
 
-                ss << argv;
-                ss >> value;
+            ss << argv;
+            ss >> value;
 
-                if (!ss.fail()) {
-                    this->used = true;
-                    return true;
-                }
+            if (!ss.fail()) {
+                return true;
             }
 
             return false;
index 0cbb3d6eba84d4a02c1391f316f710de189c1514..15d9a6be73ebfc0f0d1bda0239120a593400cc6c 100644 (file)
@@ -53,7 +53,7 @@ namespace command {
         void matchArguments(unsigned int argc, char *argv[]) {
             for (unsigned int i = 1; i < argc; i++) {
                 for(Parameter *param : parameters) {
-                    if (param->understand(argv[i])) {
+                    if (!param->isUsed() && param->understand(argv[i])) {
                         param->handle();
                         break;
                     }
diff --git a/include/multiValue.h b/include/multiValue.h
new file mode 100644 (file)
index 0000000..e84b855
--- /dev/null
@@ -0,0 +1,105 @@
+#ifndef __COMMAND_MULTIVALUE_H
+#define __COMMAND_MULTIVALUE_H
+
+#include <iostream>
+
+#include "parameter.h"
+
+namespace command {
+    /**
+     * Multiple Value Parameter decorator. Allows Parameters to understand
+     * many values.
+     *
+     * Example:
+     *  - separator: ","
+     *  - multiValue: "0,1,2,3,4,5,6,7,8,9"
+     *
+     * Example usage:
+     *  - ./myprog ARGUMENT,ARGUMENT,ARGUMENT,ARGUMENT
+     *  - ./myprog OPTION_NAME=VALUE,VALUE,VALUE,VALUE
+     */
+    class MultiValue : public Parameter {
+    protected:
+        /**
+         * Parameter which will be treated as containing multiple values
+         */
+        Parameter * parameter;
+
+        std::vector<std::string> values;
+
+        std::string separator;
+
+    public:
+        /**
+         * Default constructor.
+         *
+         * @param parameter Parameter which will be treated as containing multiple values
+         */
+        MultiValue(std::string separator, Parameter * parameter)
+            : Parameter(parameter->describe()), parameter(parameter),
+                separator(separator) {
+        }
+
+        /**
+         * Default destructor. Releases allocated memory
+         */
+        virtual ~MultiValue() {
+            delete parameter;
+        }
+
+        /**
+         * Wrapper method around passed Parameter::handle().
+         *
+         * \inheritdoc
+         */
+        virtual void handle() {
+            for (std::string value : values) {
+                parameter->understand(value);
+                parameter->handle();
+            }
+        }
+
+        /**
+         * Wrapper method around passed Parameter::understand()
+         *
+         * @param argv command line value against which test will be made
+         *
+         * \inheritdoc
+         */
+        virtual bool understand(const std::string & value) {
+            size_t start = 0;
+            size_t pos = 0;
+            bool _understand = true;
+
+            do {
+                pos = value.find(separator, start);
+                values.push_back(value.substr(start, pos-start));
+                _understand &= parameter->understand(values.back());
+                start = pos + 1;
+            } while ((pos != std::string::npos) && (start < value.size()));
+
+            return _understand;
+        }
+
+        /**
+         * Wrapper method around passed Parameter::isRequired().
+         * Indicates if current Parameter is required.
+         *
+         * @return true if Parameter is required, false otherwise
+         */
+        virtual bool isRequired() {
+            return parameter->isRequired();
+        };
+
+        /**
+         * Wrapper method around passed Parameter::isUsed().
+         *
+         * \inheritdoc
+         */
+        virtual bool isUsed() {
+            return parameter->isUsed();
+        };
+    };
+}
+
+#endif /* __COMMAND_PARAMETER_H */
index 21f814e105f9f3e6b1fffc91d50864109700c749..59de3957b9d9f97cdfb651b57b470ce03dc057b6 100644 (file)
@@ -57,6 +57,7 @@ namespace command {
          */
         virtual void handle() {
             this->call(value);
+            used = true;
         }
 
         /**
@@ -88,7 +89,7 @@ namespace command {
          */
         virtual bool understand(const std::string & argv) {
 
-            if ((!isUsed()) && (argv.find(name) == 0)) {
+            if (argv.find(name) == 0) {
                 std::size_t pos = argv.find("=");
 
                 if (pos != name.size()) {
@@ -103,7 +104,6 @@ namespace command {
                     throw OptionFailedConversion("Value for option: " + name + " failed conversion to the required type");
                 }
 
-                used = true;
                 return true;
             }
             return false;
@@ -149,6 +149,7 @@ namespace command {
          */
         virtual void handle() {
             this->call();
+            used = true;
         }
 
         /**
@@ -167,9 +168,7 @@ namespace command {
          *  used to check against next value.
          */
         virtual bool understand(const std::string & argv) {
-            if ((!isUsed()) &&
-                (argv == name)) {
-                used = true;
+            if (argv == name) {
                 return true;
             }
             return false;
index f338994ea03a2cccde4a1681f7edd8d0ce576c2b..d7b072a9b6a377bcc11e07a3af707c01f85b733c 100644 (file)
@@ -21,7 +21,8 @@ TESTS = \
        option_handles_void_value.test \
        option_should_match_exact_name.test \
        option_should_throw_exception_on_missing_value.test \
-       required_should_be_required.test
+       required_should_be_required.test \
+       multivalue_should_extract_values_by_separator.test
 
 noinst_PROGRAMS = $(TESTS)
 
@@ -53,3 +54,5 @@ option_should_match_exact_name_test_SOURCES  = option/should_match_exact_name.cp
 option_should_throw_exception_on_missing_value_test_SOURCES = option/should_throw_exception_on_missing_value.cpp
 
 required_should_be_required_test_SOURCES = required/should_be_required.cpp
+
+multivalue_should_extract_values_by_separator_test_SOURCES = multiValue/should_extract_values_by_separator.cpp
diff --git a/tests/multiValue/should_extract_values_by_separator.cpp b/tests/multiValue/should_extract_values_by_separator.cpp
new file mode 100644 (file)
index 0000000..8b5e5e3
--- /dev/null
@@ -0,0 +1,54 @@
+#include <iostream>
+#include <vector>
+
+#include "argument.h"
+#include "multiValue.h"
+
+using namespace std;
+using namespace command;
+
+#define VALUE "0,1,2,3,4,5,6,7,8,9"
+
+typedef int ArgumentType;
+
+std::vector<ArgumentType> input;
+
+void function(ArgumentType value) {
+    input.push_back(value);
+    cout << "Catched value: " << value << "\n";
+}
+
+int main() {
+    Parameter * argument = new MultiValue(",", new Argument<ArgumentType>("Argument as multiValue int", function));
+
+    try {
+        if (argument->understand(VALUE)) {
+            argument->handle();
+        }
+        else {
+            cout << argument->describe() << " should understand multiple int values\n";
+            return 1;
+        }
+    }
+    catch (...) {
+        delete argument;
+        cout << argument->describe() << " thrown unknown exception\n";
+        return 1;
+    }
+
+    bool test = true;
+    for (int i = 0; i < 10; i++) {
+        test &= (input[i] == i);
+        cout << i << ") input: " << input[i] << "\n";
+    }
+
+    if (test) {
+        cout << argument->describe() << " handles boolean (TRUE) values\n";
+        delete argument;
+        return 0;
+    }
+
+    cout << argument->describe() << " do not handle multiple int values\n";
+    delete argument;
+    return 1;
+}