From 927b97baaaf69e318ff7a0ce76d096375ec09da2 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 15 Mar 2013 08:22:58 +0200 Subject: Add support for returning XML attributes as map --- cutl/xml/parser.cxx | 112 +++++++++++++++++++++++++++++++++++++--------- cutl/xml/parser.hxx | 68 +++++++++++++++++++++++++--- cutl/xml/parser.ixx | 35 +++++++++++++++ cutl/xml/parser.txx | 30 +++++++++++++ cutl/xml/qname.cxx | 17 ++++++- cutl/xml/qname.hxx | 5 +++ cutl/xml/value-traits.cxx | 25 +++++++++++ cutl/xml/value-traits.hxx | 10 +++++ cutl/xml/value-traits.txx | 13 +++++- 9 files changed, 283 insertions(+), 32 deletions(-) create mode 100644 cutl/xml/parser.txx create mode 100644 cutl/xml/value-traits.cxx (limited to 'cutl/xml') diff --git a/cutl/xml/parser.cxx b/cutl/xml/parser.cxx index 08f08ff..ee62a24 100644 --- a/cutl/xml/parser.cxx +++ b/cutl/xml/parser.cxx @@ -92,8 +92,12 @@ namespace cutl : is_ (is), iname_ (iname), feature_ (f), depth_ (0), state_ (state_next), event_ (eof), queue_ (eof), pqname_ (&qname_), pvalue_ (&value_), - attr_i_ (0), start_ns_i_ (0), end_ns_i_ (0) + attr_unhandled_ (0), attr_i_ (0), start_ns_i_ (0), end_ns_i_ (0) { + if ((feature_ & receive_attributes_map) != 0 && + (feature_ & receive_attributes_event) != 0) + feature_ &= ~receive_attributes_map; + // Allocate the parser. Make sure nothing else can throw after // this call since otherwise we will leak it. // @@ -191,32 +195,56 @@ namespace cutl istream::iostate old_state_; }; + const string& parser:: + attribute (const qname_type& qn) const + { + attribute_map::const_iterator i (attr_map_.find (qn)); + + if (i != attr_map_.end ()) + { + if (!i->second.handled) + { + i->second.handled = true; + attr_unhandled_--; + } + return i->second.value; + } + else + throw parsing (*this, "attribute '" + qn.string () + "' expected"); + } + + string parser:: + attribute (const qname_type& qn, const string& dv) const + { + attribute_map::const_iterator i (attr_map_.find (qn)); + + if (i != attr_map_.end ()) + { + if (!i->second.handled) + { + i->second.handled = true; + attr_unhandled_--; + } + return i->second.value; + } + else + return dv; + } + void parser:: next_expect (event_type e) { if (next () != e) - throw parsing (*this, parser_event_str[e] + string (" expected")); + throw parsing (*this, string (parser_event_str[e]) + " expected"); } void parser:: next_expect (event_type e, const string& ns, const string& n) { if (next () != e || namespace_ () != ns || name () != n) - { - string m (parser_event_str[e]); - m += " '"; - - if (!ns.empty ()) - { - m += ns; - m += '#'; - } - - m += n; - m += "' expected"; - - throw parsing (*this, m); - } + throw parsing (*this, + string (parser_event_str[e]) + " '" + + qname_type (ns, n).string () + "' expected"); } parser::event_type parser:: @@ -266,6 +294,29 @@ namespace cutl parser::event_type parser:: next_body () { + // If the previous event is start_element and we return attributes + // as a map, make sure there are no unhandled attributes left. Also + // clear the map. + // + if (event_ == start_element && (feature_ & receive_attributes_map) != 0) + { + if (attr_unhandled_ != 0) + { + // Find the first unhandled attribute and report it. + // + for (attribute_map::const_iterator i (attr_map_.begin ()); + i != attr_map_.end (); ++i) + { + if (!i->second.handled) + throw parsing ( + *this, "unexpected attribute '" + i->first.string () + "'"); + } + assert (false); + } + + attr_map_.clear (); + } + // See if we have any start namespace declarations we need to return. // if (start_ns_i_ < start_ns_.size ()) @@ -299,7 +350,7 @@ namespace cutl } } - // See if we have any attributes we need to return. + // See if we have any attributes we need to return as events. // if (attr_i_ < attr_.size ()) { @@ -525,14 +576,31 @@ namespace cutl // Handle attributes. // - if ((p.feature_ & receive_attributes) != 0) + bool am ((p.feature_ & receive_attributes_map) != 0); + bool ae ((p.feature_ & receive_attributes_event) != 0); + if (am || ae) { for (; *atts != 0; atts += 2) { - p.attr_.push_back (attribute ()); - split_name (*atts, p.attr_.back ().qname); - p.attr_.back ().value = *(atts + 1); + if (am) + { + qname_type qn; + split_name (*atts, qn); + attribute_map::value_type v (qn, attribute_value ()); + v.second.value = *(atts + 1); + v.second.handled = false; + p.attr_map_.insert (v); + } + else + { + p.attr_.push_back (attribute_type ()); + split_name (*atts, p.attr_.back ().qname); + p.attr_.back ().value = *(atts + 1); + } } + + if (am) + p.attr_unhandled_ = p.attr_map_.size (); } XML_StopParser (p.p_, true); diff --git a/cutl/xml/parser.hxx b/cutl/xml/parser.hxx index 5d1e9e5..c84268a 100644 --- a/cutl/xml/parser.hxx +++ b/cutl/xml/parser.hxx @@ -5,8 +5,9 @@ #ifndef CUTL_XML_PARSER_HXX #define CUTL_XML_PARSER_HXX -#include +#include #include +#include #include #include // std::size_t @@ -82,14 +83,18 @@ namespace cutl typedef xml::qname qname_type; typedef unsigned short feature_type; + // If both receive_attributes_event and receive_attributes_map are + // specified, then receive_attributes_event is assumed. + // static const feature_type receive_elements = 0x0001; static const feature_type receive_characters = 0x0002; - static const feature_type receive_attributes = 0x0004; - static const feature_type receive_namespace_decls = 0x0008; + static const feature_type receive_attributes_map = 0x0004; + static const feature_type receive_attributes_event = 0x0008; + static const feature_type receive_namespace_decls = 0x0010; static const feature_type receive_default = receive_elements | receive_characters | - receive_attributes; + receive_attributes_map; // Parse std::istream. Input name is used in diagnostics to identify // the document being parsed. std::ios_base::failure exception is @@ -178,6 +183,42 @@ namespace cutl unsigned long long line () const {return line_;} unsigned long long column () const {return column_;} + // Attribute map lookup. If attribute is not found, then the version + // without the default value thows an appropriate parsing exception + // while the version with the default value returns that value. + // + // Note also that there is no attribute(ns,name) version since it + // would conflict with attribute(name,dv) (qualified attributes + // are not very common). + // + const std::string& + attribute (const std::string& name) const; + + template + T + attribute (const std::string& name) const; + + std::string + attribute (const std::string& name, const std::string& dv) const; + + template + T + attribute (const std::string& name, const T& dv) const; + + const std::string& + attribute (const qname_type& qname) const; + + template + T + attribute (const qname_type& qname) const; + + std::string + attribute (const qname_type& qname, const std::string& dv) const; + + template + T + attribute (const qname_type& qname, const T& dv) const; + // Optional content processing. // public: @@ -255,15 +296,27 @@ namespace cutl unsigned long long line_; unsigned long long column_; - // Attributes. + // Attributes as a map. + // + struct attribute_value + { + std::string value; + mutable bool handled; + }; + + typedef std::map attribute_map; + attribute_map attr_map_; + mutable attribute_map::size_type attr_unhandled_; + + // Attributes as events. // - struct attribute + struct attribute_type { qname_type qname; std::string value; }; - typedef std::vector attributes; + typedef std::vector attributes; attributes attr_; attributes::size_type attr_i_; // Index of the current attribute. @@ -299,5 +352,6 @@ namespace cutl } #include +#include #endif // CUTL_XML_PARSER_HXX diff --git a/cutl/xml/parser.ixx b/cutl/xml/parser.ixx index 61bb05b..fa5a7b6 100644 --- a/cutl/xml/parser.ixx +++ b/cutl/xml/parser.ixx @@ -2,10 +2,45 @@ // copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC // license : MIT; see accompanying LICENSE file +#include + namespace cutl { namespace xml { + inline const std::string& parser:: + attribute (const std::string& n) const + { + return attribute (qname_type (n)); + } + + template + inline T parser:: + attribute (const std::string& n) const + { + return attribute (qname_type (n)); + } + + inline std::string parser:: + attribute (const std::string& n, const std::string& dv) const + { + return attribute (qname_type (n), dv); + } + + template + inline T parser:: + attribute (const std::string& n, const T& dv) const + { + return attribute (qname_type (n), dv); + } + + template + inline T parser:: + attribute (const qname_type& qn) const + { + return value_traits::parse (attribute (qn), *this); + } + inline void parser:: next_expect (event_type e, const qname_type& qn) { diff --git a/cutl/xml/parser.txx b/cutl/xml/parser.txx new file mode 100644 index 0000000..cf27f2c --- /dev/null +++ b/cutl/xml/parser.txx @@ -0,0 +1,30 @@ +// file : cutl/xml/parser.txx +// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#include + +namespace cutl +{ + namespace xml + { + template + T parser:: + attribute (const qname_type& qn, const T& dv) const + { + attribute_map::const_iterator i (attr_map_.find (qn)); + + if (i != attr_map_.end ()) + { + if (!i->second.handled) + { + i->second.handled = true; + attr_unhandled_--; + } + return value_traits::parse (i->second.value, *this); + } + else + return dv; + } + } +} diff --git a/cutl/xml/qname.cxx b/cutl/xml/qname.cxx index ce7cca1..a32add2 100644 --- a/cutl/xml/qname.cxx +++ b/cutl/xml/qname.cxx @@ -12,11 +12,24 @@ namespace cutl { namespace xml { + string qname:: + string () const + { + std::string r; + if (!ns_.empty ()) + { + r += ns_; + r += '#'; + } + + r += name_; + return r; + } + ostream& operator<< (ostream& os, const qname& qn) { - const string& ns (qn.namespace_ ()); - return os << ns << (ns.empty () ? "" : "#") << qn.name (); + return os << qn.string (); } } } diff --git a/cutl/xml/qname.hxx b/cutl/xml/qname.hxx index ab1c8ea..0964705 100644 --- a/cutl/xml/qname.hxx +++ b/cutl/xml/qname.hxx @@ -38,6 +38,11 @@ namespace cutl std::string& name () {return name_;} std::string& prefix () {return prefix_;} + // Printable representation in the [#] form. + // + std::string + string () const; + // Note that comparison operators // public: diff --git a/cutl/xml/value-traits.cxx b/cutl/xml/value-traits.cxx new file mode 100644 index 0000000..7598645 --- /dev/null +++ b/cutl/xml/value-traits.cxx @@ -0,0 +1,25 @@ +// file : cutl/xml/value-traits.cxx +// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#include +#include + +using namespace std; + +namespace cutl +{ + namespace xml + { + bool default_value_traits:: + parse (string s, const parser& p) + { + if (s == "true" || s == "1" || s == "True" || s == "TRUE") + return true; + else if (s == "false" || s == "0" || s == "False" || s == "FALSE") + return false; + else + throw parsing (p, "invalid bool value '" + s + "'"); + } + } +} diff --git a/cutl/xml/value-traits.hxx b/cutl/xml/value-traits.hxx index f79f67f..0b95205 100644 --- a/cutl/xml/value-traits.hxx +++ b/cutl/xml/value-traits.hxx @@ -6,6 +6,7 @@ #define CUTL_XML_VALUE_TRAITS_HXX #include +#include // std::size_t #include @@ -19,6 +20,9 @@ namespace cutl template struct default_value_traits { + static T + parse (std::string, const parser&); + static std::string serialize (const T&, const serializer&); }; @@ -26,6 +30,9 @@ namespace cutl template <> struct LIBCUTL_EXPORT default_value_traits { + static bool + parse (std::string, const parser&); + static std::string serialize (bool v, const serializer&) { @@ -35,6 +42,9 @@ namespace cutl template struct value_traits: default_value_traits {}; + + template + struct value_traits: default_value_traits {}; } } diff --git a/cutl/xml/value-traits.txx b/cutl/xml/value-traits.txx index 000d6be..4868dba 100644 --- a/cutl/xml/value-traits.txx +++ b/cutl/xml/value-traits.txx @@ -12,12 +12,23 @@ namespace cutl namespace xml { template + T default_value_traits:: + parse (std::string s, const parser& p) + { + T r; + std::istringstream is (s); + if (!(is >> r && is.eof ()) ) + throw parsing (p, "invalid value '" + s + "'"); + return r; + } + + template std::string default_value_traits:: serialize (const T& v, const serializer& s) { std::ostringstream os; if (!(os << v)) - throw serialization (s.output_name (), "invalid value"); + throw serialization (s, "invalid value"); return os.str (); } } -- cgit v1.1