From d80d096ee8743fd6f7382d274272b0b6d7faf9bf Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 18 Oct 2010 11:17:51 +0200 Subject: Support for schema evolution using substitution groups New examples: hybrid/evolution/ignore and hybrid/evolution/passthrough. --- libxsde/xsde/cxx/hybrid/any-type-pskel.cxx | 44 ++++--- libxsde/xsde/cxx/hybrid/parser-map.cxx | 17 +-- libxsde/xsde/cxx/hybrid/serializer-map.cxx | 17 +-- libxsde/xsde/cxx/parser/context.hxx | 3 + libxsde/xsde/cxx/parser/context.ixx | 7 ++ libxsde/xsde/cxx/parser/non-validating/parser.cxx | 12 +- .../xsde/cxx/parser/substitution-map-callback.hxx | 29 +++++ libxsde/xsde/cxx/parser/substitution-map.cxx | 47 +++++++- libxsde/xsde/cxx/parser/substitution-map.hxx | 13 ++ libxsde/xsde/cxx/parser/substitution-map.ixx | 26 ++-- .../xsde/cxx/parser/validating/inheritance-map.cxx | 5 + .../xsde/cxx/parser/validating/inheritance-map.hxx | 1 - libxsde/xsde/cxx/ro-string.ixx | 2 +- libxsde/xsde/cxx/serializer/genx/document.cxx | 2 +- .../cxx/serializer/substitution-map-callback.hxx | 26 ++++ libxsde/xsde/cxx/serializer/substitution-map.cxx | 133 ++++++++++++--------- libxsde/xsde/cxx/serializer/substitution-map.hxx | 23 +++- libxsde/xsde/cxx/serializer/substitution-map.ixx | 13 +- 18 files changed, 308 insertions(+), 112 deletions(-) create mode 100644 libxsde/xsde/cxx/parser/substitution-map-callback.hxx create mode 100644 libxsde/xsde/cxx/serializer/substitution-map-callback.hxx (limited to 'libxsde') diff --git a/libxsde/xsde/cxx/hybrid/any-type-pskel.cxx b/libxsde/xsde/cxx/hybrid/any-type-pskel.cxx index d8434b2..bb5bb7f 100644 --- a/libxsde/xsde/cxx/hybrid/any-type-pskel.cxx +++ b/libxsde/xsde/cxx/hybrid/any-type-pskel.cxx @@ -13,15 +13,16 @@ namespace xsde { #ifdef XSDE_POLYMORPHIC bool any_type_pskel:: - _start_element_impl (const ro_string& ns, - const ro_string& name, - const char* type) + _start_element_impl ( +#ifdef XSDE_PARSER_VALIDATION + const ro_string& ns, const ro_string& name, const char* type +#else + const ro_string&, const ro_string&, const char* +#endif + ) { #ifdef XSDE_PARSER_VALIDATION - parser::context& ctx = _context (); - ctx.current_.any_ = true; - ctx.current_.depth_++; - + _context ().start_wildcard_content (); _start_any_element (ns, name, type); return true; #else @@ -30,13 +31,16 @@ namespace xsde } #else bool any_type_pskel:: - _start_element_impl (const ro_string& ns, const ro_string& name) + _start_element_impl ( +#ifdef XSDE_PARSER_VALIDATION + const ro_string& ns, const ro_string& name +#else + const ro_string&, const ro_string& +#endif + ) { #ifdef XSDE_PARSER_VALIDATION - parser::context& ctx = _context (); - ctx.current_.any_ = true; - ctx.current_.depth_++; - + _context ().start_wildcard_content (); _start_any_element (ns, name); return true; #else @@ -46,7 +50,13 @@ namespace xsde #endif bool any_type_pskel:: - _end_element_impl (const ro_string& ns, const ro_string& name) + _end_element_impl ( +#ifdef XSDE_PARSER_VALIDATION + const ro_string& ns, const ro_string& name +#else + const ro_string&, const ro_string& +#endif + ) { #ifdef XSDE_PARSER_VALIDATION _end_any_element (ns, name); @@ -76,7 +86,13 @@ namespace xsde #endif bool any_type_pskel:: - _characters_impl (const ro_string& s) + _characters_impl ( +#ifdef XSDE_PARSER_VALIDATION + const ro_string& s +#else + const ro_string& +#endif + ) { #ifdef XSDE_PARSER_VALIDATION _any_characters (s); diff --git a/libxsde/xsde/cxx/hybrid/parser-map.cxx b/libxsde/xsde/cxx/hybrid/parser-map.cxx index ae67b94..33b0a53 100644 --- a/libxsde/xsde/cxx/hybrid/parser-map.cxx +++ b/libxsde/xsde/cxx/hybrid/parser-map.cxx @@ -21,23 +21,26 @@ namespace xsde if (size_ == 0) return 0; + int r (1); + size_t m; size_t l = 0; size_t h = size_ - 1; while (l <= h) { - size_t m = l + (h - l)/2; - int r = strcmp (entries_[m].type_id, tid); + m = l + (h - l)/2; + r = strcmp (entries_[m].type_id, tid); - if (r > 0) - h = m - 1; - else if (r < 0) + if (r == 0 || l == h) + break; + + if (r < 0) l = m + 1; else - return entries_[m].parser; + h = (m == 0 ? 0 : m - 1); } - return 0; + return r == 0 ? entries_[m].parser : 0; } void parser_map_impl:: diff --git a/libxsde/xsde/cxx/hybrid/serializer-map.cxx b/libxsde/xsde/cxx/hybrid/serializer-map.cxx index d20f8d1..be32cc4 100644 --- a/libxsde/xsde/cxx/hybrid/serializer-map.cxx +++ b/libxsde/xsde/cxx/hybrid/serializer-map.cxx @@ -23,23 +23,26 @@ namespace xsde if (size_ == 0) return 0; + int r (1); + size_t m; size_t l = 0; size_t h = size_ - 1; while (l <= h) { - size_t m = l + (h - l)/2; - int r = strcmp (entries_[m].type_id, tid); + m = l + (h - l)/2; + r = strcmp (entries_[m].type_id, tid); - if (r > 0) - h = m - 1; - else if (r < 0) + if (r == 0 || l == h) + break; + + if (r < 0) l = m + 1; else - return entries_[m].serializer; + h = (m == 0 ? 0 : m - 1); } - return 0; + return r == 0 ? entries_[m].serializer : 0; } void serializer_map_impl:: diff --git a/libxsde/xsde/cxx/parser/context.hxx b/libxsde/xsde/cxx/parser/context.hxx index 6019f34..34ded3e 100644 --- a/libxsde/xsde/cxx/parser/context.hxx +++ b/libxsde/xsde/cxx/parser/context.hxx @@ -140,6 +140,9 @@ namespace xsde public: void + start_wildcard_content (); + + void reset (XML_Parser); void diff --git a/libxsde/xsde/cxx/parser/context.ixx b/libxsde/xsde/cxx/parser/context.ixx index b3828f5..5e38ae2 100644 --- a/libxsde/xsde/cxx/parser/context.ixx +++ b/libxsde/xsde/cxx/parser/context.ixx @@ -47,6 +47,13 @@ namespace xsde } inline void context:: + start_wildcard_content () + { + current_.any_ = true; + current_.depth_++; + } + + inline void context:: reset (XML_Parser parser) { xml_parser_ = parser; diff --git a/libxsde/xsde/cxx/parser/non-validating/parser.cxx b/libxsde/xsde/cxx/parser/non-validating/parser.cxx index aec43e0..1bd20a5 100644 --- a/libxsde/xsde/cxx/parser/non-validating/parser.cxx +++ b/libxsde/xsde/cxx/parser/non-validating/parser.cxx @@ -195,11 +195,7 @@ namespace xsde { if (!_start_element_impl (ns, name, type)) { - context& c = _context (); - - c.current_.any_ = true; - c.current_.depth_++; - + _context ().start_wildcard_content (); _start_any_element (ns, name, type); } } @@ -210,11 +206,7 @@ namespace xsde { if (!_start_element_impl (ns, name)) { - context& c = _context (); - - c.current_.any_ = true; - c.current_.depth_++; - + _context ().start_wildcard_content (); _start_any_element (ns, name); } } diff --git a/libxsde/xsde/cxx/parser/substitution-map-callback.hxx b/libxsde/xsde/cxx/parser/substitution-map-callback.hxx new file mode 100644 index 0000000..97d22c9 --- /dev/null +++ b/libxsde/xsde/cxx/parser/substitution-map-callback.hxx @@ -0,0 +1,29 @@ +// file : xsde/cxx/parser/substitution-map-callback.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2005-2010 Code Synthesis Tools CC +// license : GNU GPL v2 + exceptions; see accompanying LICENSE file + +#ifndef XSDE_CXX_PARSER_SUBSTITUTION_MAP_CALLBACK_HXX +#define XSDE_CXX_PARSER_SUBSTITUTION_MAP_CALLBACK_HXX + +#include + +namespace xsde +{ + namespace cxx + { + namespace parser + { + void + parser_smap_callback ( + bool (*callback) ( + const ro_string& root_ns, + const ro_string& root_name, + const ro_string& member_ns, + const ro_string& member_name, + const char*& type)); + } + } +} + +#endif // XSDE_CXX_PARSER_SUBSTITUTION_MAP_CALLBACK_HXX diff --git a/libxsde/xsde/cxx/parser/substitution-map.cxx b/libxsde/xsde/cxx/parser/substitution-map.cxx index 5cbe4a1..d58ac84 100644 --- a/libxsde/xsde/cxx/parser/substitution-map.cxx +++ b/libxsde/xsde/cxx/parser/substitution-map.cxx @@ -5,7 +5,7 @@ #include -#include // strlen, strcmp, strncmp +#include // strlen, strcmp, strncmp, strchr #ifndef XSDE_EXCEPTIONS # include // assert @@ -17,6 +17,7 @@ #endif #include +#include #include namespace xsde @@ -47,6 +48,22 @@ namespace xsde *type = v->type_; } + // Call the callback. + // + if (!r && callback_ != 0) + { + const char* t; + const char* p = strchr (root, ' '); + + ro_string rname (p ? root : 0, p ? p - root : 0); + ro_string rns (p ? p + 1 : root); + + r = callback_ (rns, rname, ns, name, t); + + if (r && type != 0 && *type == 0) + *type = t; + } + return r; } @@ -78,6 +95,20 @@ namespace xsde *type = v->type_; } + // Call the callback. + // + if (!r && callback_ != 0) + { + const char* t; + ro_string rns (root_ns); + ro_string rname (root_name); + + r = callback_ (rns, rname, ns, name, t); + + if (r && type != 0 && *type == 0) + *type = t; + } + return r; } @@ -249,7 +280,21 @@ namespace xsde #endif } + // Callback. // + void + parser_smap_callback ( + bool (*callback) ( + const ro_string& root_ns, + const ro_string& root_name, + const ro_string& member_ns, + const ro_string& member_name, + const char*& type)) + { + substitution_map_instance ().callback (callback); + } + + // Load. // size_t parser_smap_elements () diff --git a/libxsde/xsde/cxx/parser/substitution-map.hxx b/libxsde/xsde/cxx/parser/substitution-map.hxx index a6df80f..1e50c0e 100644 --- a/libxsde/xsde/cxx/parser/substitution-map.hxx +++ b/libxsde/xsde/cxx/parser/substitution-map.hxx @@ -27,6 +27,16 @@ namespace xsde const char* root, const char* type); + typedef bool (*callback_func) ( + const ro_string& root_ns, + const ro_string& root_name, + const ro_string& member_ns, + const ro_string& member_name, + const char*& type); + + void + callback (callback_func); + // Check and have the type set if found. // bool @@ -81,6 +91,9 @@ namespace xsde const value* find_ (const ro_string& member_ns, const ro_string& member_name) const; + + private: + callback_func callback_; }; diff --git a/libxsde/xsde/cxx/parser/substitution-map.ixx b/libxsde/xsde/cxx/parser/substitution-map.ixx index ddd0b4d..995da4e 100644 --- a/libxsde/xsde/cxx/parser/substitution-map.ixx +++ b/libxsde/xsde/cxx/parser/substitution-map.ixx @@ -11,7 +11,7 @@ namespace xsde { inline substitution_map:: substitution_map (size_t buckets) - : hashmap (buckets, sizeof (value)) + : hashmap (buckets, sizeof (value)), callback_ (0) { } @@ -26,6 +26,12 @@ namespace xsde hashmap::insert (member, &v); } + inline void substitution_map:: + callback (callback_func c) + { + callback_ = c; + } + inline bool substitution_map:: check (const ro_string& member_ns, const ro_string& member_name, @@ -33,9 +39,9 @@ namespace xsde const char*& type) const { - return empty () - ? false - : check_ (member_ns, member_name, root, &type); + return !empty () || callback_ != 0 + ? check_ (member_ns, member_name, root, &type) + : false; } inline bool substitution_map:: @@ -45,9 +51,9 @@ namespace xsde const char* root_name, const char*& type) const { - return empty () - ? false - : check_ (member_ns, member_name, root_ns, root_name, &type); + return !empty () || callback_ != 0 + ? check_ (member_ns, member_name, root_ns, root_name, &type) + : false; } inline bool substitution_map:: @@ -56,9 +62,9 @@ namespace xsde const char* root) const { - return empty () - ? false - : check_ (member_ns, member_name, root, 0); + return !empty () || callback_ != 0 + ? check_ (member_ns, member_name, root, 0) + : false; } inline substitution_map& diff --git a/libxsde/xsde/cxx/parser/validating/inheritance-map.cxx b/libxsde/xsde/cxx/parser/validating/inheritance-map.cxx index f23e841..c46ac1f 100644 --- a/libxsde/xsde/cxx/parser/validating/inheritance-map.cxx +++ b/libxsde/xsde/cxx/parser/validating/inheritance-map.cxx @@ -33,6 +33,11 @@ namespace xsde bool inheritance_map:: check (const char* derived, const char* base) const { + // Check for the special match-anything marker. + // + if (derived[0] == '*') + return true; + if (strcmp (derived, base) == 0) return true; diff --git a/libxsde/xsde/cxx/parser/validating/inheritance-map.hxx b/libxsde/xsde/cxx/parser/validating/inheritance-map.hxx index ba8335e..1462fb2 100644 --- a/libxsde/xsde/cxx/parser/validating/inheritance-map.hxx +++ b/libxsde/xsde/cxx/parser/validating/inheritance-map.hxx @@ -60,4 +60,3 @@ namespace xsde #include #endif // XSDE_CXX_PARSER_VALIDATING_INHERITANCE_MAP_HXX - diff --git a/libxsde/xsde/cxx/ro-string.ixx b/libxsde/xsde/cxx/ro-string.ixx index cf37c81..6906923 100644 --- a/libxsde/xsde/cxx/ro-string.ixx +++ b/libxsde/xsde/cxx/ro-string.ixx @@ -43,7 +43,7 @@ namespace xsde inline ro_string:: operator std::string () const { - return std::string (data (), size ()); + return size () ? std::string (data (), size ()) : std::string (); } #endif diff --git a/libxsde/xsde/cxx/serializer/genx/document.cxx b/libxsde/xsde/cxx/serializer/genx/document.cxx index 4c634a4..e8cacc0 100644 --- a/libxsde/xsde/cxx/serializer/genx/document.cxx +++ b/libxsde/xsde/cxx/serializer/genx/document.cxx @@ -514,7 +514,7 @@ namespace xsde // Call to check sets ns and n if successful. // if (strcmp (dt, root_static_type_) == 0 || - substitution_map_instance ().check (ns, n, dt)) + substitution_map_instance ().check (ns, n, dt, 0)) dt = 0; } diff --git a/libxsde/xsde/cxx/serializer/substitution-map-callback.hxx b/libxsde/xsde/cxx/serializer/substitution-map-callback.hxx new file mode 100644 index 0000000..c29a00d --- /dev/null +++ b/libxsde/xsde/cxx/serializer/substitution-map-callback.hxx @@ -0,0 +1,26 @@ +// file : xsde/cxx/serializer/substitution-map-callback.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2005-2010 Code Synthesis Tools CC +// license : GNU GPL v2 + exceptions; see accompanying LICENSE file + +#ifndef XSDE_CXX_SERIALIZER_SUBSTITUTION_MAP_CALLBACK_HXX +#define XSDE_CXX_SERIALIZER_SUBSTITUTION_MAP_CALLBACK_HXX + +namespace xsde +{ + namespace cxx + { + namespace serializer + { + void + serializer_smap_callback ( + bool (*callback) ( + const char* type, + const void* obj, + const char*& ns, + const char*& name)); + } + } +} + +#endif // XSDE_CXX_SERIALIZER_SUBSTITUTION_MAP_CALLBACK_HXX diff --git a/libxsde/xsde/cxx/serializer/substitution-map.cxx b/libxsde/xsde/cxx/serializer/substitution-map.cxx index 28f01b5..dc73fd2 100644 --- a/libxsde/xsde/cxx/serializer/substitution-map.cxx +++ b/libxsde/xsde/cxx/serializer/substitution-map.cxx @@ -17,6 +17,7 @@ #endif #include +#include #include namespace xsde @@ -103,8 +104,19 @@ namespace xsde bool substitution_map:: check_ (const char*& ns, const char*& name, - const char* type) const + const char* type, + const void* obj, + bool top) const { + // Call the callback first to allow the user to provide a custom + // substitution group mapping. + // + if (top && callback_ != 0) + { + if (callback_ (type, obj, ns, name)) + return true; + } + size_t h = hash (name); if (ns) @@ -115,70 +127,70 @@ namespace xsde const bucket* p = find (h); - if (p == 0) - return false; - - // Search for the entry in the bucket. - // - const size_t el_size = sizeof (element) + sizeof (hashmap*); - const char* b = reinterpret_cast (p) + sizeof (bucket); - const char* e = b + p->size_ * el_size; - - size_t nl = ns ? strlen (name) : 0; - - for (; b < e; b += el_size) + if (p != 0) { - const element* e = reinterpret_cast (b); + // Search for the entry in the bucket. + // + const size_t el_size = sizeof (element) + sizeof (hashmap*); + const char* b = reinterpret_cast (p) + sizeof (bucket); + const char* e = b + p->size_ * el_size; + + size_t nl = ns ? strlen (name) : 0; - if (e->hash_ == h) + for (; b < e; b += el_size) { - if (ns == 0) - { - if (strcmp (e->key_, name) == 0) - break; - } - else + const element* e = reinterpret_cast (b); + + if (e->hash_ == h) { - if (strncmp (e->key_, name, nl) == 0 && - e->key_[nl] == ' ' && - strcmp (e->key_ + nl + 1, ns) == 0) - break; + if (ns == 0) + { + if (strcmp (e->key_, name) == 0) + break; + } + else + { + if (strncmp (e->key_, name, nl) == 0 && + e->key_[nl] == ' ' && + strcmp (e->key_ + nl + 1, ns) == 0) + break; + } } } - } - if (b == e) - return false; - - const hashmap* map = *reinterpret_cast ( - b + sizeof (element)); + if (b != e) + { + const hashmap* map = *reinterpret_cast ( + b + sizeof (element)); - // See if we have a direct substitution. - // - if (const value* v = static_cast (map->find (type))) - { - ns = v->ns_; - name = v->name_; - return true; - } + // See if we have a direct substitution. + // + if (const value* v = static_cast (map->find (type))) + { + ns = v->ns_; + name = v->name_; + return true; + } - // Otherwise we have to iterate over possible substitutions and - // see if any of them can in turn be substituted with something - // that we can use. - // - for (const_iterator i (map->begin ()), end (map->end ()); - i != end; ++i) - { - const value* v = static_cast (*i); + // Otherwise we have to iterate over possible substitutions and + // see if any of them can in turn be substituted with something + // that we can use. + // + for (const_iterator i (map->begin ()), end (map->end ()); + i != end; ++i) + { + const value* v = static_cast (*i); - const char* tns = v->ns_; - const char* tn = v->name_; + const char* tns = v->ns_; + const char* tn = v->name_; - if (check_ (tns, tn, type)) - { - ns = tns; - name = tn; - return true; + if (check_ (tns, tn, type, obj, false)) + { + ns = tns; + name = tn; + return true; + } + } } } @@ -264,7 +276,20 @@ namespace xsde #endif } + // Callback. // + void + serializer_smap_callback ( + bool (*callback) ( + const char* type, + const void* obj, + const char*& ns, + const char*& name)) + { + substitution_map_instance ().callback (callback); + } + + // Load. // size_t serializer_smap_elements () diff --git a/libxsde/xsde/cxx/serializer/substitution-map.hxx b/libxsde/xsde/cxx/serializer/substitution-map.hxx index ae16f4c..e36c481 100644 --- a/libxsde/xsde/cxx/serializer/substitution-map.hxx +++ b/libxsde/xsde/cxx/serializer/substitution-map.hxx @@ -30,21 +30,34 @@ namespace xsde const char* member_name, const char* member_type); + typedef bool (*callback_func) ( + const char* type, + const void* obj, + const char*& ns, + const char*& name); + + void + callback (callback_func); + // Check whether there is a substitution available for this // root element with the specified type. If so, return true // and override namespace and name (ns is 0 if there is no - // namespace). + // namespace). The obj argument is an opaque pointer to the + // instance being serialized or 0 if there is none. // bool check (const char*& ns, const char*& name, - const char* type) const; + const char* type, + const void* obj) const; private: bool check_ (const char*& ns, const char*& name, - const char* type) const; + const char* type, + const void* obj, + bool top) const; private: struct value @@ -52,6 +65,9 @@ namespace xsde const char* ns_; const char* name_; }; + + private: + callback_func callback_; }; @@ -85,4 +101,3 @@ namespace xsde #include #endif // XSDE_CXX_SERIALIZER_SUBSTITUTION_MAP_HXX - diff --git a/libxsde/xsde/cxx/serializer/substitution-map.ixx b/libxsde/xsde/cxx/serializer/substitution-map.ixx index 6622e33..6dbd951 100644 --- a/libxsde/xsde/cxx/serializer/substitution-map.ixx +++ b/libxsde/xsde/cxx/serializer/substitution-map.ixx @@ -15,12 +15,21 @@ namespace xsde { } + inline void substitution_map:: + callback (callback_func c) + { + callback_ = c; + } + inline bool substitution_map:: check (const char*& ns, const char*& name, - const char* type) const + const char* type, + const void* obj) const { - return empty () ? false : check_ (ns, name, type); + return !empty () || callback_ != 0 + ? check_ (ns, name, type, obj, true) + : false; } inline substitution_map& -- cgit v1.1