Add Grouped behaviour.
authorRafał Długołęcki <rafal@dlugolecki.net.pl>
Thu, 14 May 2015 21:58:15 +0000 (23:58 +0200)
committerRafał Długołęcki <rafal@dlugolecki.net.pl>
Thu, 14 May 2015 21:58:15 +0000 (23:58 +0200)
include/argument.h
include/command.h
include/grouped.h [new file with mode: 0644]
include/multiValue.h
include/option.h
include/parameter.h
include/required.h
src/main.cpp

index 80deabb9c22f903aa4f518b89dc940342b179d23..81a7cd4186e8c4be1c755b74d42829ab7e0fb25b 100644 (file)
@@ -67,7 +67,7 @@ namespace command {
         virtual bool understand(const std::string & argv) {
             std::stringstream ss;
 
-            ss << argv;
+            ss << std::fixed << argv;
             ss >> value;
 
             if (!ss.fail()) {
index 15d9a6be73ebfc0f0d1bda0239120a593400cc6c..7d01cb800cb9464a505fab1a957a9a5ef8698c00 100644 (file)
@@ -4,31 +4,32 @@
 #include <string>
 #include <vector>
 #include <typeinfo>
+#include <iostream>
 
 #include "parameter.h"
-#include "exception/missingRequiredParameter.h"
+#include "grouped.h"
 
 namespace command {
     /**
      * Main class for handling user passed parameters from command line.
      */
-    class Command {
-    protected:
-        std::vector<Parameter *> parameters;
+    class Command : protected Grouped {
     public:
         /**
          * Default constructor.
          *
-         * @param argc passed to the main function
-         * @param argv passed to the main function
+         * @param argc from the main function
+         * @param argv from the main function
          * @param params initializer_list containing Parameter handlers
          *      responsible for correctly handle user data.
          */
         Command(unsigned int argc, char *argv[], std::initializer_list<Parameter *> params)
-            : parameters(params) {
-
+            : Grouped(params, "Command") {
             try {
-                matchArguments(argc, argv);
+                for (unsigned int i = 1; i < argc; i++) {
+                    this->understand(argv[i]);
+                }
+                handle();
             }
             catch(const std::invalid_argument & exception) {
                 releaseMemory();
@@ -46,38 +47,6 @@ namespace command {
         ~Command() {
             releaseMemory();
         }
-    protected:
-        /**
-         * Matches user passed arguments with available parameter handlers.
-         */
-        void matchArguments(unsigned int argc, char *argv[]) {
-            for (unsigned int i = 1; i < argc; i++) {
-                for(Parameter *param : parameters) {
-                    if (!param->isUsed() && param->understand(argv[i])) {
-                        param->handle();
-                        break;
-                    }
-                }
-            }
-            for(Parameter *param : parameters) {
-                if (param->isRequired() && !param->isUsed()) {
-                    throw MissingRequiredParameter(param->describe() + " is required but it was not passed");
-                }
-            }
-        }
-
-        /**
-         * Releases acquired memory
-         */
-        void releaseMemory() {
-            for (Parameter * parameter : parameters) {
-                if (parameter != NULL) {
-                    delete parameter;
-                }
-            }
-            parameters.clear();
-            parameters.shrink_to_fit();
-        }
     };
 }
 
diff --git a/include/grouped.h b/include/grouped.h
new file mode 100644 (file)
index 0000000..613943f
--- /dev/null
@@ -0,0 +1,159 @@
+#ifndef __COMMAND_GROUPED_H
+#define __COMMAND_GROUPED_H
+
+#include <iostream>
+#include <vector>
+#include <string>
+
+#include "parameter.h"
+#include "exception/missingRequiredParameter.h"
+
+namespace command {
+    /**
+     * Grouped Parameters decorator. Allows Parameters to understand be grouped
+     * together.
+     *
+     * Example usage:
+     *  - ./myprog [ARGUMENT OPTION] [ARGUMENT OPTION] ...
+     */
+    class Grouped : public Parameter {
+    protected:
+        /**
+         * Parameters which will be treated as grouped together
+         */
+        std::vector<Parameter *> parameters;
+
+        std::vector<std::string> values;
+
+        std::vector<bool> understood;
+        std::vector<bool> all_used;
+
+    public:
+        /**
+         * Default constructor.
+         *
+         * @param params Parameters which will be treated as grouped together
+         */
+        Grouped(std::initializer_list<Parameter *> params, std::string description = "Grouped")
+            : Parameter(description), parameters(params) {
+            for (unsigned int i = 0; i < parameters.size(); i++) {
+                understood.push_back(false);
+                values.push_back("");
+            }
+        }
+
+        /**
+         * Default destructor. Releases allocated memory
+         */
+        virtual ~Grouped() {
+            releaseMemory();
+        }
+
+        /**
+         * Wrapper method around passed Parameter::handle().
+         *
+         * \inheritdoc
+         */
+        virtual void handle() {
+//                 std::cout << this->describe() << " handles" << "\n";
+//             if (!isUsed()) {
+                for (unsigned int i = 0; i < parameters.size(); i++) {
+                    Parameter *param = parameters[i];
+                    if (understood[i] ||  param->hungryForValue()) {
+//                     std::cout << param->describe() << "=" << values[i] << "\n";
+                        param->handle();
+                    }
+                }
+//             }
+            // @TODO: Nested required
+            for(Parameter *param : parameters) {
+                if (param->isRequired() && !param->isUsed()) {
+                    throw MissingRequiredParameter(param->describe() + " is required");
+                }
+            }
+        }
+
+        /**
+         * 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) {
+//             std::cout << "[+] Grouped [" << this->describe() << "]\n";
+
+            bool _understand = false;
+
+//             std::cout << "GMD\n" ;
+            for (unsigned int i = 0; i < parameters.size(); i++) {
+                Parameter *param = parameters[i];
+
+//                 std::cout << " > Grouped:Param [" << param->describe() << ", " << value << "]\n";
+                if (!understood[i]) {
+                    _understand = param->understand(value);
+                    if (_understand) {
+//                         std::cout << " > Understood [" << param->describe() << ", " << value << "]\n";
+                        understood[i] = _understand;
+                        values[i] = value;
+                        break;
+                    }
+                }
+            }
+
+            bool all_understood = (understood.size() == parameters.size());
+            for (bool u : understood) {
+                all_understood &= u;
+            }
+
+//             std::cout << this->describe() << " understand: " << std::boolalpha << all_understood << "\n";
+
+            return all_understood;
+        }
+
+        /**
+         * Indicates if current Parameter has been already used
+         *
+         * @return true if current Parameter has been already used. False otherwise.
+         */
+//         virtual bool isUsed() {
+//             if (!used) {
+// //                 bool used 
+//                 for(Parameter *param : parameters) {
+//                     param->isUsed();
+//                 }
+//             }
+//         }
+
+        /**
+         * \inheritdoc
+         */
+        virtual bool hungryForValue() {
+            return true;
+        }
+
+        /**
+         * Wrapper method around passed Parameter::valuePosition().
+         *
+         * \inheritdoc
+         */
+        virtual unsigned int valuePosition(const std::string &) {
+            throw std::logic_error("Group does not have value part");
+        }
+
+        /**
+         * Releases acquired memory
+         */
+        void releaseMemory() {
+            for (Parameter * parameter : parameters) {
+                if (parameter != NULL) {
+                    delete parameter;
+                }
+            }
+            parameters.clear();
+            parameters.shrink_to_fit();
+        }
+    };
+}
+
+#endif /* __COMMAND_GROUPED_H */
index 62fbf4d150c0dce495cabf6037d5a236586a6946..26738b5c54e6a7c2461d9a8e88edb4355365aee9 100644 (file)
@@ -68,11 +68,17 @@ namespace command {
          * \inheritdoc
          */
         virtual bool understand(const std::string & value) {
-            size_t start = parameter->valuePosition(value);
+            size_t start = 0;
             size_t pos = 0;
             bool _understand = true;
             std::string prefix = "";
 
+            start = parameter->valuePosition(value);
+
+            if (start > value.size()) {
+                return false;
+            }
+
             if (start > 0) {
                 prefix = value.substr(0, ++start);// always count: "="
             }
@@ -82,8 +88,12 @@ namespace command {
                 values.push_back(prefix + value.substr(start, pos-start));
                 _understand &= parameter->understand(values.back());
                 start = pos + 1;
-            } while ((pos != std::string::npos) && (start < value.size()));
 
+                if (!_understand) {
+                    values.clear();
+                    break;
+                }
+            } while ((pos != std::string::npos) && (start < value.size()));
             return _understand;
         }
 
@@ -117,4 +127,4 @@ namespace command {
     };
 }
 
-#endif /* __COMMAND_PARAMETER_H */
+#endif /* __COMMAND_MULTIVALUE_H */
index d66164d38d6f622e7f81440c5ba4845bcd4e6a7a..7e9917b589c7021a0c5852695e90eecb5fa36908 100644 (file)
@@ -89,8 +89,7 @@ namespace command {
          *  to ParameterType
          */
         virtual bool understand(const std::string & argv) {
-
-            if (argv.find(name) == 0) {
+            if (this->hasName(argv)) {
                 std::size_t pos = this->valuePosition(argv);
 
                 if (pos != name.size()) {
@@ -98,11 +97,11 @@ namespace command {
                 }
 
                 std::stringstream ss;
-                ss << argv.substr(pos + 1);
+                ss << std::fixed << argv.substr(pos + 1);
                 ss >> value;
 
                 if (ss.fail()) {
-                    throw OptionFailedConversion("Value for option: " + name + " failed conversion to the required type");
+                    throw OptionFailedConversion("Option: " + name + " failed value conversion to the required type");
                 }
 
                 return true;
@@ -116,12 +115,17 @@ namespace command {
         virtual unsigned int valuePosition(const std::string & value) {
             std::size_t pos = value.find("=");
 
-            if (pos == std::string::npos) {
-                throw OptionValueNotSpecified("Option: " + name + " requires value to be specified using equal sign");
+            if ((this->hasName(value)) && (pos == std::string::npos)) {
+                throw OptionValueNotSpecified("Option: " + name + " requires value to be specified after equal sign, but no equal sign was found");
             }
 
             return pos;
         }
+
+    protected:
+        bool hasName(const std::string & argv) {
+            return argv.find(name) == 0;
+        }
     };
 
     /**
index 1bbfa88739b55aa1c8445feef05ff9d36c7597a7..87c1590ccc85fbcdbe9d6f69ce01f0d5fd2253a6 100644 (file)
@@ -2,6 +2,7 @@
 #define __COMMAND_PARAMETER_H
 
 #include <string>
+#include <sstream>
 
 #include "descriptive.h"
 #include "callable.h"
@@ -64,6 +65,12 @@ namespace command {
             return used;
         }
 
+        /**
+         */
+        virtual bool hungryForValue() {
+            return false;
+        }
+
         /**
          * @return position where value starts in passed string
          */
index 726f6ac983640e8e7cab7baf09a998de39039890..f88d2ae15051c3a9bf71949ba7ba90b7c661dc9d 100644 (file)
@@ -82,4 +82,4 @@ namespace command {
     };
 }
 
-#endif /* __COMMAND_PARAMETER_H */
+#endif /* __COMMAND_REQUIRED_H */
index 06fbf1e14bd7befddfa64ccff38b62e5402edc19..5ad96a22520a2e9f5c1547e8e184d2f3f3a3e674 100644 (file)
@@ -5,6 +5,7 @@
 #include "argument.h"
 #include "required.h"
 #include "multiValue.h"
+#include "grouped.h"
 #include "command.h"
 
 using namespace command;
@@ -24,12 +25,12 @@ void void_function(void) {
 int main(int argc, char *argv[]) {
     try {
         Command command(argc, argv, {
-//             new Argument<std::string>("File path", [](std::string value)->void { std::cout << "Hello from lambda " << value << std::endl; }),
-            new Required(new MultiValue("-", new Argument<bool>("Input values", argument_function))),
-            new MultiValue(",", new Option<std::string>("f", "Optional file", option_function)),
+            new Grouped({
+                new Required(new MultiValue("-", new Argument<bool>("Input values", argument_function))),
+                new MultiValue(",", new Option<std::string>("f", "Optional file", option_function))
+            }),
             new Option<void>("h", "Help", void_function)
         });
-
     }
     catch(const std::exception & e) {
         std::cout << e.what() << std::endl;