Parcourir la source

start with data specification and validation part
added temporary test.cc and two data files for immediate testing


git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jelte-datadef@291 e5f2f494-b856-4b98-b285-d166d9295462

Jelte Jansen il y a 15 ans
Parent
commit
805d4406fd

+ 4 - 1
src/lib/cc/cpp/Makefile.am

@@ -1,4 +1,7 @@
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib/cc/cpp -I$(top_srcdir)/ext
 
+bin_PROGRAMS = test
+test_SOURCES = test.cc
+test_LDADD = libcc.a
 lib_LIBRARIES = libcc.a
-libcc_a_SOURCES = data.cc data.h session.cc session.h
+libcc_a_SOURCES = data.cc data.h data_def.h data_def.cc session.cc session.h

+ 3 - 4
src/lib/cc/cpp/data.cc

@@ -289,7 +289,9 @@ from_stringstream_map(std::istream &in, int& line, int& pos)
         in.get();
         pos++;
         p.second = Element::create_from_string(in, line, pos);
-        if (!p.second) { return ElementPtr(); };
+        if (!p.second) {
+            throw ParseError(std::string("missing map value for ") + p.first, line, pos);
+        };
         m.insert(p);
         skip_to(in, line, pos, ",}", " \t\n");
         c = in.get();
@@ -298,8 +300,6 @@ from_stringstream_map(std::istream &in, int& line, int& pos)
     return Element::create(m);
 }
 
-//ElementPtr
-//Element::create_from_string(std::stringstream &in)
 ElementPtr
 Element::create_from_string(std::istream &in) throw(ParseError)
 {
@@ -364,7 +364,6 @@ Element::create_from_string(std::istream &in, int& line, int& pos) throw(ParseEr
     if (el_read) {
         return element;
     } else {
-        // throw exception?
         throw ParseError("nothing read");
     }
 }

+ 2 - 0
src/lib/cc/cpp/data.h

@@ -104,6 +104,7 @@ namespace ISC { namespace Data {
         virtual ElementPtr get(const std::string& name) { throw TypeError(); } ;
         virtual void set(const std::string& name, ElementPtr element) { throw TypeError(); };
         virtual void remove(const std::string& name) { throw TypeError(); };
+        virtual bool contains(const std::string& s) { throw TypeError(); }
         virtual ElementPtr find(const std::string& identifier) { throw TypeError(); };
         virtual bool find(const std::string& id, ElementPtr& t) { return false; };
 
@@ -248,6 +249,7 @@ namespace ISC { namespace Data {
         ElementPtr get(const std::string& s) { return m[s]; };
         void set(const std::string& s, ElementPtr p) { m[s] = p; };
         void remove(const std::string& s) { m.erase(s); }
+        bool contains(const std::string& s) { return m.find(s) != m.end(); }
         std::string str();
         std::string str_xml(size_t prefix = 0);
         std::string to_wire(int omit_length = 1);

+ 120 - 0
src/lib/cc/cpp/data_def.cc

@@ -0,0 +1,120 @@
+
+#include "data_def.h"
+
+#include <sstream>
+
+#include <boost/foreach.hpp>
+
+
+using namespace ISC::Data;
+
+DataDefinition::DataDefinition(std::istream& in) throw(ParseError) {
+    definition = Element::create_from_string(in);
+    // TODO, make sure the whole structure is complete and valid
+    if (!definition->contains("data_specification")) {
+        throw ParseError("Data specification does not contain data_specification element");
+    }
+}
+
+//
+// helper functions for validation
+//
+static bool
+check_type(ElementPtr spec, ElementPtr element)
+{
+    std::string cur_item_type;
+    cur_item_type = spec->get("item_type")->string_value();
+    if (cur_item_type == "any") {
+        return true;
+    }
+    switch (element->get_type()) {
+        case Element::integer:
+            return cur_item_type == "integer";
+            break;
+        case Element::real:
+            return cur_item_type == "real";
+            break;
+        case Element::boolean:
+            return cur_item_type == "boolean";
+            break;
+        case Element::string:
+            return cur_item_type == "string";
+            break;
+        case Element::list:
+            return cur_item_type == "list";
+            break;
+        case Element::map:
+            return cur_item_type == "map";
+            break;
+    }
+    return false;
+}
+
+bool
+DataDefinition::validate_item(const ElementPtr spec, const ElementPtr data) {
+    std::cout << "Validating type of " << data << std::endl;
+    if (!check_type(spec, data)) {
+        std::cout << "type mismatch; not " << spec->get("item_type") << ": " << data << std::endl;
+        std::cout << spec << std::endl;
+        return false;
+    }
+    if (data->get_type() == Element::list) {
+        BOOST_FOREACH(ElementPtr list_el, data->list_value()) {
+            if (!validate_spec(spec->get("list_item_spec"), list_el)) {
+                return false;
+            }
+        }
+    }
+    if (data->get_type() == Element::map) {
+        if (!validate_spec_list(spec->get("map_item_spec"), data)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+// spec is a map with item_name etc, data is a map
+bool
+DataDefinition::validate_spec(const ElementPtr spec, const ElementPtr data) {
+    std::string item_name = spec->get("item_name")->string_value();
+    bool optional = spec->get("item_optional")->bool_value();
+    ElementPtr data_el;
+    
+    std::cout << "check for item with name " << item_name << std::endl;
+    data_el = data->get(item_name);
+    if (data_el) {
+        if (!validate_item(spec, data_el)) {
+            return false;
+        }
+    } else {
+        if (!optional) {
+            std::cout << "non-optional value not found" << std::endl;
+            return false;
+        }
+    }
+    return true;
+}
+
+// spec is a list of maps, data is a map
+bool
+DataDefinition::validate_spec_list(const ElementPtr spec, const ElementPtr data) {
+    ElementPtr cur_data_el;
+    std::string cur_item_name;
+    bool optional;
+    BOOST_FOREACH(ElementPtr cur_spec_el, spec->list_value()) {
+        if (!validate_spec(cur_spec_el, data)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+// TODO
+// this function does *not* check if the specification is in correct
+// form, we should do that in the constructor
+bool
+DataDefinition::validate(const ElementPtr data) {
+    ElementPtr spec = definition->find("data_specification/config_data");
+    return validate_spec_list(spec, data);
+}
+

+ 31 - 0
src/lib/cc/cpp/data_def.h

@@ -0,0 +1,31 @@
+#ifndef _DATA_DEF_H
+#define _DATA_DEF_H 1
+
+#include "data.h"
+
+#include <sstream>
+
+namespace ISC { namespace Data {
+
+    class DataDefinition {
+    public:
+        explicit DataDefinition() {};
+        explicit DataDefinition(ElementPtr e) : definition(e) {};
+        explicit DataDefinition(std::istream& in) throw(ParseError);
+
+        const ElementPtr getDefinition() { return definition; };
+        // returns true if the given element conforms to this data
+        // definition scheme
+        bool validate(const ElementPtr data);
+
+    private:
+        bool validate_item(const ElementPtr spec, const ElementPtr data);
+        bool validate_spec(const ElementPtr spec, const ElementPtr data);
+        bool validate_spec_list(const ElementPtr spec, const ElementPtr data);
+
+        ElementPtr definition;
+    };
+
+} }
+
+#endif // _DATA_DEF_H

+ 12 - 0
src/lib/cc/cpp/parkinglot.data

@@ -0,0 +1,12 @@
+{
+  "port": 5300,
+  "zones": [
+    {
+      "zone_name": "tjeb.nl"
+    },
+    {
+      "zone_name": "jinmei.org"
+    }
+  ]
+}
+

+ 46 - 0
src/lib/cc/cpp/parkinglot.spec

@@ -0,0 +1,46 @@
+{
+  "data_specification": {
+    "module_name": "parkinglot",
+    "config_data": [
+      {
+        "item_name": "port",
+        "item_type": "integer",
+        "item_optional": false,
+        "item_default": 5300
+      },
+      {
+        "item_name": "zones",
+        "item_type": "list",
+        "item_optional": false,
+        "item_default": [ ],
+        "list_item_spec": {
+          "item_name": "zone_name",
+          "item_type": "string",
+          "item_optional": false,
+          "item_default": ""
+        }
+      }
+    ],
+    "commands": [
+      {
+        "command_name": "zone_add",
+        "command_args": {
+          "item_name": "zone_name",
+          "item_type": "string",
+          "item_optional": false,
+          "item_default": ""
+        }
+      },
+      {
+        "command_name": "zone_delete",
+        "command_args": {
+          "item_name": "zone_name",
+          "item_type": "string",
+          "item_optional": false,
+          "item_default": ""
+        }
+      }
+    ]
+  }
+}
+

+ 53 - 0
src/lib/cc/cpp/test.cc

@@ -0,0 +1,53 @@
+
+#include "data.h"
+#include "data_def.h"
+
+#include <fstream>
+
+using namespace std;
+using namespace ISC::Data;
+
+int
+main(int argc, char **argv) {
+    std::ifstream file;
+    std::stringstream ss;
+    DataDefinition def;
+    ElementPtr data;
+
+    file.open("parkinglot.spec");
+    if (!file) {
+        cout << "error opening parkinglot.spec" << endl;
+        return 1;
+    }
+
+    try {
+        def = DataDefinition(file);
+        cout << "Definition: " << endl;
+        cout << def.getDefinition() << endl;
+    } catch (ParseError pe) {
+        cout << "Error parsing definition file: " << pe.what() << endl;
+        return 1;
+    }
+    file.close();
+
+    file.open("parkinglot.data");
+    if (!file) {
+        cout << "error opening parkinglot.data" << endl;
+        return 1;
+    }
+    try {
+        data = Element::create_from_string(file);
+        cout << "Data: " << endl;
+        cout << data << endl;
+    } catch (ParseError pe) {
+        cout << "Error parsing data file: " << pe.what() << endl;
+        return 1;
+    }
+
+    if (def.validate(data)) {
+        cout << "Data validated" << endl;
+    } else {
+        cout << "Error in data validation" << endl;
+    }
+    file.close();
+}