diff options
author | Henning Baldersheim <balder@yahoo-inc.com> | 2017-06-06 15:11:38 +0200 |
---|---|---|
committer | Henning Baldersheim <balder@yahoo-inc.com> | 2017-06-07 09:50:23 +0200 |
commit | ece112cf6aadbf135c92f1d5ddada739b290a118 (patch) | |
tree | 3fb49a56dcfd77e27ee335f5be1f172d3a7975c9 /staging_vespalib | |
parent | 8438c19df2999aed6fd89c470c52a9c04c77dcdc (diff) |
Forward declare XmlOutputStream and FieldValue::IteratorHandler.
Diffstat (limited to 'staging_vespalib')
-rw-r--r-- | staging_vespalib/src/tests/xmlserializable/xmlserializabletest.cpp | 2 | ||||
-rw-r--r-- | staging_vespalib/src/vespa/vespalib/util/CMakeLists.txt | 1 | ||||
-rw-r--r-- | staging_vespalib/src/vespa/vespalib/util/polymorphicarray.h | 82 | ||||
-rw-r--r-- | staging_vespalib/src/vespa/vespalib/util/polymorphicarraybase.h | 18 | ||||
-rw-r--r-- | staging_vespalib/src/vespa/vespalib/util/polymorphicarrays.h | 66 | ||||
-rw-r--r-- | staging_vespalib/src/vespa/vespalib/util/xmlserializable.cpp | 459 | ||||
-rw-r--r-- | staging_vespalib/src/vespa/vespalib/util/xmlserializable.h | 203 | ||||
-rw-r--r-- | staging_vespalib/src/vespa/vespalib/util/xmlstream.cpp | 463 | ||||
-rw-r--r-- | staging_vespalib/src/vespa/vespalib/util/xmlstream.h | 210 | ||||
-rw-r--r-- | staging_vespalib/src/vespa/vespalib/util/xmlstream.hpp (renamed from staging_vespalib/src/vespa/vespalib/util/xmlserializable.hpp) | 8 |
10 files changed, 785 insertions, 727 deletions
diff --git a/staging_vespalib/src/tests/xmlserializable/xmlserializabletest.cpp b/staging_vespalib/src/tests/xmlserializable/xmlserializabletest.cpp index 90fcdb8f94f..4af70f7e6fe 100644 --- a/staging_vespalib/src/tests/xmlserializable/xmlserializabletest.cpp +++ b/staging_vespalib/src/tests/xmlserializable/xmlserializabletest.cpp @@ -1,7 +1,7 @@ // Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/vespalib/testkit/testapp.h> -#include <vespa/vespalib/util/xmlserializable.h> +#include <vespa/vespalib/util/xmlstream.h> namespace vespalib { diff --git a/staging_vespalib/src/vespa/vespalib/util/CMakeLists.txt b/staging_vespalib/src/vespa/vespalib/util/CMakeLists.txt index ca440428e0a..3b36b863707 100644 --- a/staging_vespalib/src/vespa/vespalib/util/CMakeLists.txt +++ b/staging_vespalib/src/vespa/vespalib/util/CMakeLists.txt @@ -18,5 +18,6 @@ vespa_add_library(staging_vespalib_vespalib_util OBJECT shutdownguard.cpp timer.cpp xmlserializable.cpp + xmlstream.cpp DEPENDS ) diff --git a/staging_vespalib/src/vespa/vespalib/util/polymorphicarray.h b/staging_vespalib/src/vespa/vespalib/util/polymorphicarray.h new file mode 100644 index 00000000000..6c4d8e37311 --- /dev/null +++ b/staging_vespalib/src/vespa/vespalib/util/polymorphicarray.h @@ -0,0 +1,82 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// +#pragma once + +#include "polymorphicarraybase.h" + +namespace vespalib { + +/** + * Describes an interface an array of polymorphic types. + * The intention is to allow efficient implementations when that is possible + * while still enjoying the flexibility of the polymorph interface. + * It is not a full feldged Array implementation as std::vector. It contains just + * the minimum required to allow for efficient implementations for document::ArrayFieldValue. + * + * You specify the base type the interface shall provide. This base type must define + * virtual void assign(const B & rhs); + * For use with ComplexArrayT your type also need + * virtual T * clone() const; + */ +template<typename B> +class IArrayT : public IArrayBase { +public: + class iterator { + public: + iterator(IArrayT &a, size_t i) : _a(&a), _i(i) {} + iterator operator+(size_t diff) const { return iterator(*_a, _i + diff); } + iterator &operator++() { + ++_i; + return *this; + } + iterator operator++(int) { + iterator other(*this); + ++_i; + return other; + } + bool operator==(const iterator &other) const { return (_a == other._a) && (_i == other._i); } + bool operator!=(const iterator &other) const { return (_i != other._i) || (_a != other._a); } + B &operator*() { return (*_a)[_i]; } + B *operator->() { return &(*_a)[_i]; } + friend ssize_t operator-(const iterator &a, const iterator &b) { return a._i - b._i; } + private: + IArrayT *_a; + size_t _i; + }; + + class const_iterator { + public: + const_iterator(const IArrayT &a, size_t i) : _a(&a), _i(i) {} + const_iterator operator+(size_t diff) const { return const_iterator(*_a, _i + diff); } + const_iterator &operator++() { + ++_i; + return *this; + } + const_iterator operator++(int) { + const_iterator other(*this); + ++_i; + return other; + } + bool operator==(const const_iterator &other) const { return (_a == other._a) && (_i == other._i); } + bool operator!=(const const_iterator &other) const { return (_i != other._i) || (_a != other._a); } + const B &operator*() const { return (*_a)[_i]; } + const B *operator->() const { return &(*_a)[_i]; } + size_t operator-(const const_iterator &b) const { return _i - b._i; } + private: + const IArrayT *_a; + size_t _i; + }; + + typedef std::unique_ptr<IArrayT> UP; + virtual const B &operator[](size_t i) const = 0; + virtual B &operator[](size_t i) = 0; + virtual IArrayT *clone() const override = 0; + virtual iterator erase(iterator it) = 0; + virtual const_iterator begin() const { return const_iterator(*this, 0); } + virtual const_iterator end() const { return const_iterator(*this, size()); } + virtual iterator begin() { return iterator(*this, 0); } + virtual iterator end() { return iterator(*this, size()); } + virtual void push_back(const B &v) = 0; +}; + +} diff --git a/staging_vespalib/src/vespa/vespalib/util/polymorphicarraybase.h b/staging_vespalib/src/vespa/vespalib/util/polymorphicarraybase.h new file mode 100644 index 00000000000..0fccd6cabb7 --- /dev/null +++ b/staging_vespalib/src/vespa/vespalib/util/polymorphicarraybase.h @@ -0,0 +1,18 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// +#pragma once + +namespace vespalib { + +class IArrayBase { +public: + virtual ~IArrayBase() {} + virtual void resize(size_t sz) = 0; + virtual void reserve(size_t sz) = 0; + virtual void clear() = 0; + virtual IArrayBase *clone() const = 0; + virtual size_t size() const = 0; + bool empty() const { return size() == 0; } +}; + +} diff --git a/staging_vespalib/src/vespa/vespalib/util/polymorphicarrays.h b/staging_vespalib/src/vespa/vespalib/util/polymorphicarrays.h index 98c9d271ea2..646ca522ba3 100644 --- a/staging_vespalib/src/vespa/vespalib/util/polymorphicarrays.h +++ b/staging_vespalib/src/vespa/vespalib/util/polymorphicarrays.h @@ -2,76 +2,12 @@ // #pragma once +#include "polymorphicarray.h" #include <vespa/vespalib/util/memory.h> #include <vector> namespace vespalib { -/** - * Describes an interface an array of polymorphic types. - * The intention is to allow efficient implementations when that is possible - * while still enjoying the flexibility of the polymorph interface. - * It is not a full feldged Array implementation as std::vector. It contains just - * the minimum required to allow for efficient implementations for document::ArrayFieldValue. - * - * You specify the base type the interface shall provide. This base type must define - * virtual void assign(const B & rhs); - * For use with ComplexArrayT your type also need - * virtual T * clone() const; - */ -template<typename B> -class IArrayT -{ -public: - class iterator { - public: - iterator(IArrayT & a, size_t i) : _a(&a), _i(i) { } - iterator operator+(size_t diff) const { return iterator(*_a, _i + diff); } - iterator& operator++() { ++_i; return *this; } - iterator operator++(int) { iterator other(*this); ++_i; return other; } - bool operator==(const iterator & other) const { return (_a == other._a) && (_i == other._i); } - bool operator!=(const iterator & other) const { return (_i != other._i) || (_a != other._a); } - B & operator*() { return (*_a)[_i]; } - B * operator->() { return &(*_a)[_i]; } - friend ssize_t operator - (const iterator & a, const iterator & b) { return a._i - b._i; } - private: - IArrayT * _a; - size_t _i; - }; - class const_iterator { - public: - const_iterator(const IArrayT & a, size_t i) : _a(&a), _i(i) { } - const_iterator operator+(size_t diff) const { return const_iterator(*_a, _i + diff); } - const_iterator& operator++() { ++_i; return *this; } - const_iterator operator++(int) { const_iterator other(*this); ++_i; return other; } - bool operator==(const const_iterator & other) const { return (_a == other._a) && (_i == other._i); } - bool operator!=(const const_iterator & other) const { return (_i != other._i) || (_a != other._a); } - const B & operator*() const { return (*_a)[_i]; } - const B * operator->() const { return &(*_a)[_i]; } - size_t operator - (const const_iterator & b) const { return _i - b._i; } - private: - const IArrayT * _a; - size_t _i; - }; - typedef std::unique_ptr<IArrayT> UP; - - virtual ~IArrayT() { } - virtual const B & operator [] (size_t i) const = 0; - virtual B & operator [] (size_t i) = 0; - virtual void resize(size_t sz) = 0; - virtual void reserve(size_t sz) = 0; - virtual void clear() = 0; - virtual IArrayT * clone() const = 0; - virtual size_t size() const = 0; - virtual iterator erase(iterator it) = 0; - virtual const_iterator begin() const { return const_iterator(*this, 0); } - virtual const_iterator end() const { return const_iterator(*this, size()); } - virtual iterator begin() { return iterator(*this, 0); } - virtual iterator end() { return iterator(*this, size()); } - bool empty() const { return size() == 0; } - virtual void push_back(const B & v) = 0; -}; - template <typename T, typename B> class PrimitiveArrayT : public IArrayT<B> { diff --git a/staging_vespalib/src/vespa/vespalib/util/xmlserializable.cpp b/staging_vespalib/src/vespa/vespalib/util/xmlserializable.cpp index 043cddc3259..357b3d94992 100644 --- a/staging_vespalib/src/vespa/vespalib/util/xmlserializable.cpp +++ b/staging_vespalib/src/vespa/vespalib/util/xmlserializable.cpp @@ -1,449 +1,12 @@ // Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include "xmlserializable.hpp" -#include <vespa/vespalib/encoding/base64.h> -#include <vespa/vespalib/stllike/asciistream.h> -#include <vespa/vespalib/util/stringfmt.h> -#include <cassert> +#include "xmlserializable.h" +#include "xmlstream.h" +#include <sstream> namespace vespalib { namespace xml { -namespace { - - std::vector<bool> getLegalIdentifierFirstCharacters() { - std::vector<bool> vec(256, false); - for (uint32_t i='a'; i<='z'; ++i) vec[i] = true; - for (uint32_t i='A'; i<='Z'; ++i) vec[i] = true; - vec[':'] = true; - vec['_'] = true; - return vec; - } - - std::vector<bool> getLegalIdentifierCharacters() { - std::vector<bool> vec(getLegalIdentifierFirstCharacters()); - vec['-'] = true; - vec['.'] = true; - for (uint32_t i='0'; i<='9'; ++i) { - vec[i] = true; - } - return vec; - } - - std::vector<bool> getBinaryCharacters() { - std::vector<bool> vec(256, false); - for (uint32_t i=0; i<32; ++i) { - vec[i] = true; - } - vec['\t'] = false; - vec['\n'] = false; - vec['\r'] = false; - vec['\f'] = false; - return vec; - } - - std::vector<bool> getEscapedXmlCharacters() { - std::vector<bool> vec(256, false); - for (uint32_t i=0; i<32; ++i) { - vec[i] = true; - } - vec['\n'] = false; - vec['<'] = true; - vec['>'] = true; - vec['&'] = true; - return vec; - } - - std::vector<bool> legalIdentifierFirstChar( - getLegalIdentifierFirstCharacters()); - std::vector<bool> legalIdentifierChars = getLegalIdentifierCharacters(); - std::vector<bool> binaryChars = getBinaryCharacters(); - std::vector<bool> escapedXmlChars = getEscapedXmlCharacters(); - - bool containsBinaryCharacters(const std::string& s) { - for (int i=0, n=s.size(); i<n; ++i) { - if (binaryChars[static_cast<uint8_t>(s[i])]) return true; - } - return false; - } - - const std::string xmlAttributeEscape(const std::string& s) { - vespalib::asciistream ost; - for (uint32_t i=0, n=s.size(); i<n; ++i) { - if (s[i] == '"' || s[i] == '\n' - || escapedXmlChars[static_cast<uint8_t>(s[i])]) - { - if (s[i] == '<') ost << "<"; - else if (s[i] == '>') ost << ">"; - else if (s[i] == '&') ost << "&"; - else if (s[i] == '"') ost << """; - else { - ost << "&#" << (int) s[i] << ";"; - } - } else { - ost << s[i]; - } - } - return ost.str(); - } - - void writeEscaped(std::ostream& out, const std::string& s) { - for (uint32_t i=0, n=s.size(); i<n; ++i) { - if (escapedXmlChars[static_cast<uint8_t>(s[i])]) { - if (s[i] == '<') out << "<"; - else if (s[i] == '>') out << ">"; - else if (s[i] == '&') out << "&"; - else { - out << "&#" << (int) s[i] << ";"; - } - } else { - out << s[i]; - } - } - } - - void writeBase64Encoded(std::ostream& out, const std::string& s) { - out << vespalib::Base64::encode(&s[0], s.size()); - } -} - -bool isLegalName(const std::string& name) { - if (name.size() == 0) return false; - if (!legalIdentifierFirstChar[static_cast<uint8_t>(name[0])]) return false; - for (int i=1, n=name.size(); i<n; ++i) { - if (!legalIdentifierChars[static_cast<uint8_t>(name[i])]) return false; - } - return true; -} - -void convertToLegalName(std::string& name) { - if (name.size() == 0) { - name == "__no_name__"; - } else { - if (!legalIdentifierFirstChar[static_cast<uint8_t>(name[0])]) { - name[0] = '_'; - } - for (int i=1, n=name.size(); i<n; ++i) { - if (!legalIdentifierChars[static_cast<uint8_t>(name[i])]) { - name[i] = '_'; - } - } - } -} - -XmlOutputStream::XmlOutputStream(std::ostream& ostream, - const std::string& indent) - : _indent(indent), - _wrappedStream(ostream), - _tagStack(), - _cachedTag(), - _cachedAttributes(), - _cachedContent() -{ -} - -XmlAttribute::~XmlAttribute() -{ -} - -XmlContent::~XmlContent() -{ -} - -XmlOutputStream::~XmlOutputStream() -{ -} - -XmlOutputStream& -XmlOutputStream::operator<<(const XmlTag& tag) -{ - //std::cerr << "Trying to add tag " << tag.getName() << ". cached tag is " - // << (void*) _cachedTag.get() << "\n"; - if (_cachedTag.get() != 0) flush(false); - _cachedTag.reset(new XmlTag(tag)); - _cachedContentType = XmlContent::AUTO; - //std::cerr << "Added tag " << _cachedTag->getName() << "\n"; - return *this; -} - -XmlOutputStream& -XmlOutputStream::operator<<(const XmlAttribute& attribute) -{ - //std::cerr << "Adding attribute\n"; - if (_cachedTag.get() == 0) { - throw vespalib::IllegalStateException("Cannot add attribute " - + attribute.getName() + ", as no tag is open"); - } - _cachedAttributes.push_back(attribute); - return *this; -} - -XmlOutputStream& -XmlOutputStream::operator<<(const XmlEndTag&) -{ - //std::cerr << "Adding endtag\n"; - if (_cachedTag.get()) { - flush(true); - _cachedContentType = XmlContent::ESCAPED; - } else if (_tagStack.empty()) { - throw vespalib::IllegalStateException("No open tags left to end"); - } else { - for (uint32_t i=1; i<_tagStack.size(); ++i) { - _wrappedStream << _indent; - } - _wrappedStream << "</" << _tagStack.back() << ">"; - _tagStack.pop_back(); - if (!_tagStack.empty()) _wrappedStream << '\n'; - _cachedContentType = XmlContent::ESCAPED; - } - return *this; -} - -XmlOutputStream& -XmlOutputStream::operator<<(const XmlContent& content) -{ - //std::cerr << "Adding content\n"; - if (_cachedTag.get() == 0 && _tagStack.empty()) { - throw vespalib::IllegalStateException( - "No open tag to write content in"); - } - if (_cachedTag.get() != 0) { - //std::cerr << "Content is '" << content.getContent() << "'\n"; - if (content.getType() == XmlContent::AUTO) { // Do nothing.. Always ok - } else if (_cachedContentType == XmlContent::AUTO) { - _cachedContentType = content.getType(); - } else if (_cachedContentType != content.getType()) { - throw vespalib::IllegalStateException( - "Have already added content of different type"); - } - _cachedContent.push_back(content); - } else { - if (content.getType() == XmlContent::BASE64) { - throw vespalib::IllegalStateException( - "Cannot add Base64 encoded content after tag content"); - } - for (uint32_t i=0; i<_tagStack.size(); ++i) { - _wrappedStream << _indent; - } - _wrappedStream << content.getContent() << '\n'; - } - return *this; -} - -XmlOutputStream& -XmlOutputStream::operator<<(const XmlSerializable& serializable) -{ - //std::cerr << "Adding serializable\n"; - serializable.printXml(*this); - return *this; -} - -XmlOutputStream& -XmlOutputStream::operator<<(const std::string& content) -{ - //std::cerr << "Adding content string\n"; - return *this << XmlContent(content); -} - -XmlOutputStream& -XmlOutputStream::operator<<(char c) -{ - return *this << XmlContent(std::string(&c, 1)); -} - -XmlOutputStream& -XmlOutputStream::operator<<(int32_t i) -{ - return *this << XmlContent(vespalib::make_string("%d", i)); -} - -XmlOutputStream& -XmlOutputStream::operator<<(int64_t i) -{ - return *this << XmlContent(vespalib::make_string("%" PRId64, i)); -} - -XmlOutputStream& -XmlOutputStream::operator<<(float f) -{ - return *this << XmlContent(vespalib::make_string("%g", f)); -} - -XmlOutputStream& -XmlOutputStream::operator<<(double d) -{ - return *this << XmlContent(vespalib::make_string("%g", d)); -} - -void -XmlOutputStream::flush(bool endTag) -{ - //std::cerr << "Flushing\n"; - if (_cachedTag.get() == 0) { - throw vespalib::IllegalStateException("Cannot write non-existing tag"); - } - for (uint32_t i=0; i<_tagStack.size(); ++i) { - _wrappedStream << _indent; - } - _wrappedStream << '<' << _cachedTag->getName(); - for (std::list<XmlAttribute>::const_iterator it = _cachedAttributes.begin(); - it != _cachedAttributes.end(); ++it) - { - _wrappedStream << ' ' << it->getName() << "=\"" - << xmlAttributeEscape(it->getValue()) << '"'; - } - _cachedAttributes.clear(); - if (_cachedContent.empty() && endTag) { - _wrappedStream << "/>\n"; - } else if (_cachedContent.empty()) { - _wrappedStream << ">\n"; - _tagStack.push_back(_cachedTag->getName()); - } else { - if (_cachedContentType == XmlContent::AUTO) { - _cachedContentType = XmlContent::ESCAPED; - for (std::list<XmlContent>::const_iterator it - = _cachedContent.begin(); it != _cachedContent.end(); ++it) - { - if (containsBinaryCharacters(it->getContent())) { - _cachedContentType = XmlContent::BASE64; - break; - } - } - } - if (_cachedContentType == XmlContent::BASE64) { - _wrappedStream << " binaryencoding=\"base64\""; - } - _wrappedStream << '>'; - for (std::list<XmlContent>::const_iterator it = _cachedContent.begin(); - it != _cachedContent.end(); ++it) - { - if (!endTag) { - _wrappedStream << '\n'; - for (uint32_t i=0; i<=_tagStack.size(); ++i) { - _wrappedStream << _indent; - } - } - switch (_cachedContentType) { - case XmlContent::ESCAPED: { - writeEscaped(_wrappedStream, it->getContent()); - break; - } - case XmlContent::BASE64: { - writeBase64Encoded(_wrappedStream, it->getContent()); - break; - } - default: assert(false); - } - } - _cachedContent.clear(); - if (endTag) { - _wrappedStream << "</" << _cachedTag->getName() << ">\n"; - } else { - _wrappedStream << '\n'; - _tagStack.push_back(_cachedTag->getName()); - } - } - _cachedTag.reset(0); -} - -XmlTag::XmlTag(const XmlTag& tag) - : _name(tag._name), - _attributes(), - _content(), - _flags(tag._flags) -{ -} - -XmlTag::~XmlTag() {} - -XmlTag::XmlTag(const std::string& name, XmlTagFlags flags) - : _name(name), - _attributes(), - _content(), - _flags(flags) -{ - if (_flags == CONVERT_ILLEGAL_CHARACTERS) { - convertToLegalName(_name); - } - if (!isLegalName(_name)) { - throw vespalib::IllegalArgumentException("Name '" + _name + "' contains " - "illegal XML characters and cannot be used as tag name"); - } -} - -XmlAttribute::XmlAttribute(const XmlAttribute& attribute) - : _name(attribute._name), - _value(attribute._value), - _next() -{ -} - -XmlAttribute::XmlAttribute(const std::string& name, const char * value, uint32_t flags) - : _name(name), - _value(), - _next() -{ - vespalib::asciistream ost; - if (flags & HEX) ost << vespalib::hex << "0x"; - ost << value; - _value = ost.str(); - if (!isLegalName(name)) { - throw vespalib::IllegalArgumentException("Name '" + name + "' contains " - "illegal XML characters and cannot be used as attribute name"); - } -} - -XmlEndTag::XmlEndTag() -{ -} - -XmlContent::XmlContent(Type type) - : _type(type), - _content(), - _nextContent(), - _nextTag() -{ -} - -XmlContent::XmlContent() - : _type(AUTO), - _content(), - _nextContent(), - _nextTag() -{ -} - -XmlContent::XmlContent(const XmlContent& content) - : _type(content._type), - _content(content._content), - _nextContent(), - _nextTag() -{ -} - -XmlContent::XmlContent(const std::string& value) - : _type(AUTO), - _content(value), - _nextContent(), - _nextTag() -{ -} - -XmlContentWrapper::XmlContentWrapper(const XmlContentWrapper& wrapper) - : XmlContent(wrapper) -{ -} - -XmlContentWrapper::XmlContentWrapper(const char* value) - : XmlContent(std::string(value)) -{ -} - -XmlContentWrapper::XmlContentWrapper(const char* value, uint32_t size) - : XmlContent(std::string(value, size)) -{ -} - std::string XmlSerializable::toXml(const std::string& indent) const { @@ -452,22 +15,6 @@ XmlSerializable::toXml(const std::string& indent) const printXml(xos); return ost.str(); } -using CharP = char *; -using ConstCharP = const char *; - -template XmlAttribute::XmlAttribute(const std::string &, const std::string &, unsigned int); -template XmlAttribute::XmlAttribute(const std::string &, const vespalib::string &, unsigned int); -template XmlAttribute::XmlAttribute(const std::string &, const vespalib::stringref &, unsigned int); -template XmlAttribute::XmlAttribute(const std::string &, const CharP &, unsigned int); -template XmlAttribute::XmlAttribute(const std::string &, const ConstCharP &, unsigned int); -template XmlAttribute::XmlAttribute(const std::string &, const bool &, unsigned int); -template XmlAttribute::XmlAttribute(const std::string &, const int16_t &, unsigned int); -template XmlAttribute::XmlAttribute(const std::string &, const int32_t &, unsigned int); -template XmlAttribute::XmlAttribute(const std::string &, const int64_t &, unsigned int); -template XmlAttribute::XmlAttribute(const std::string &, const uint16_t &, unsigned int); -template XmlAttribute::XmlAttribute(const std::string &, const uint32_t &, unsigned int); -template XmlAttribute::XmlAttribute(const std::string &, const uint64_t &, unsigned int); -template XmlAttribute::XmlAttribute(const std::string &, const double &, unsigned int); } // xml } // vespalib diff --git a/staging_vespalib/src/vespa/vespalib/util/xmlserializable.h b/staging_vespalib/src/vespa/vespalib/util/xmlserializable.h index b688c699d76..a4ddb12ed8d 100644 --- a/staging_vespalib/src/vespa/vespalib/util/xmlserializable.h +++ b/staging_vespalib/src/vespa/vespalib/util/xmlserializable.h @@ -1,166 +1,14 @@ // Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -/** - * @file xmlserializable.h - * @ingroup util - * - * @brief Interfaces to be used for XML serialization. - * - * This file contains XML utility classes, to make XML serialization simple. - * Rather than users writing their own XML, these tools let you define a tree - * structure, and this library builds the XML for you. This ensures that you - * write legal XML and that stuff that needs to be escaped is. - * <p> - * It defines a superclass for XML serializable classes, called XmlSerializable. - * This is what classes that should be XML serializable will inherit. - * <p> - * When implementing the printXml() function in XmlSerializable, one will - * use the various XML helper classes defined here to build a tree structure - * creating the XML. These are: XmlTag, XmlEndTag, XmlAttribute, and XmlContent. - * Some subclasses exist of XmlContent to facilitate various types of content. - * <p> - * The XmlOutputStream wraps a regular std::ostream. You write XML objects to it - * and it is responsible for writing all the XML code. This way, the XML - * serialization is done without interfering with regular output operators. - * <p> - * For example usage, refer to the unit test: - * vespalib/tests/xmlserializable/xmlserializabletest.cpp - * - */ #pragma once -#include <iosfwd> -#include <list> -#include <memory> +#include <string> namespace vespalib { namespace xml { -class XmlAttribute; -class XmlContent; class XmlOutputStream; -bool isLegalName(const std::string& name); - -enum XmlTagFlags { NONE = 0, CONVERT_ILLEGAL_CHARACTERS = 1 }; - -/** - * @class document::XmlTag - * - * @brief Start a new tag with given name. - */ -class XmlTag { - std::string _name; - std::unique_ptr<XmlAttribute> _attributes; - std::unique_ptr<XmlContent> _content; - XmlTagFlags _flags; -public: - XmlTag(const XmlTag&); - XmlTag(const std::string& name, XmlTagFlags = NONE); - ~XmlTag(); - - const std::string& getName() const { return _name; } -}; - -/** - * @class document::XmlEndTag - * - * @brief Indicates that current tag is closed. - */ -class XmlEndTag { -public: - XmlEndTag(); -}; - -/** - * @class document::XmlAttribute - * - * @brief Defined a single attribute within an XML tag. - * - * When adding an XML to an XML stream, the attribute will be added to the last - * tag added. This can not be called after the last tag opened in the stream is - * closed, so add all attributes before starting to add new XML child tags. - */ -class XmlAttribute { - std::string _name; - std::string _value; - std::unique_ptr<XmlAttribute> _next; -public: - enum Flag { NONE = 0x0, HEX = 0x1 }; - XmlAttribute(const XmlAttribute&); - /** Add any value that can be written to an ostringstream. */ - template<typename T> - XmlAttribute(const std::string& name, const T& value, uint32_t flags = NONE); - XmlAttribute(const std::string& name, const char * value, uint32_t flags = NONE); - ~XmlAttribute(); - - const std::string& getName() const { return _name; } - const std::string& getValue() const { return _value; } -}; - - -/** - * @class document::XmlContent - * - * XML content to be written to stream. By default it will autodetect whether to - * escape or base64 encode content. XmlOutputStream functions taking primitives - * will generate XmlContent instances. - */ -class XmlContent { -public: - enum Type { AUTO, ESCAPED, BASE64 }; -protected: - XmlContent(Type type); -private: - Type _type; - std::string _content; - std::unique_ptr<XmlContent> _nextContent; - std::unique_ptr<XmlTag> _nextTag; - -public: - XmlContent(); - XmlContent(const XmlContent&); - XmlContent(const std::string& value); - ~XmlContent(); - - Type getType() const { return _type; } - const std::string& getContent() const { return _content; } -}; - -/** - * @class document::XmlEscapedContent - * - * Token used to tell that this content field should only be XML escaped. - */ -class XmlEscapedContent : public XmlContent { -public: - XmlEscapedContent() : XmlContent(ESCAPED) {} -}; - -/** - * @class document::XmlBase64Content - * - * Token used to tell that this content field should always be base64 encoded. - */ -class XmlBase64Content : public XmlContent { -public: - XmlBase64Content() : XmlContent(BASE64) {} -}; - -/** - * @class document::XmlContentWrapper - * - * A wrapper class for content that one doesn't want to copy or release - * ownership of. This wrapper merely takes pointer to data, and assumes it - * will stay alive as long as needed. - */ -class XmlContentWrapper : public XmlContent { -public: - XmlContentWrapper(const XmlContentWrapper&); - XmlContentWrapper(const char* value); - XmlContentWrapper(const char* value, uint32_t size); -}; - /** * @class document::XmlSerializable * @@ -178,51 +26,6 @@ public: virtual std::string toXml(const std::string& indent = "") const; }; -/** - * @class document::XmlOutputStream - * - * @brief std::ostream wrapper, only accepting data that will become XML. - * - * After XmlEndTag() has been sent to the stream, the tag is guarantueed to have - * been written. Call isFinalized() to ensure that you have closed all the tags - * that have been opened. Within a tag, the stream will cache some information, - * as more information might be required before knowing what to print. - */ -class XmlOutputStream { - const std::string _indent; - std::ostream& _wrappedStream; - std::list<std::string> _tagStack; - std::unique_ptr<XmlTag> _cachedTag; - std::list<XmlAttribute> _cachedAttributes; - std::list<XmlContent> _cachedContent; - XmlContent::Type _cachedContentType; - - void flush(bool endTag); - -public: - - XmlOutputStream(std::ostream& ostream, const std::string& indent = ""); - ~XmlOutputStream(); - - bool isFinalized() const - { return (_tagStack.empty() && _cachedTag.get() == 0); } - - std::ostream& getWrappedStream() { return _wrappedStream; } - - XmlOutputStream& operator<<(const XmlTag& tag); - XmlOutputStream& operator<<(const XmlAttribute& attribute); - XmlOutputStream& operator<<(const XmlEndTag& endtag); - XmlOutputStream& operator<<(const XmlContent& content); - XmlOutputStream& operator<<(const XmlSerializable& serializable); - - XmlOutputStream& operator<<(const std::string& content); - XmlOutputStream& operator<<(char c); - XmlOutputStream& operator<<(int32_t i); - XmlOutputStream& operator<<(int64_t i); - XmlOutputStream& operator<<(float f); - XmlOutputStream& operator<<(double d); -}; - } // xml // The XmlSerializable and XmlOutputStream is often used in header files @@ -230,8 +33,8 @@ public: // vespalib namespace with all the other classes, use // "using namespace vespalib::xml" within your printXml functions -typedef vespalib::xml::XmlOutputStream XmlOutputStream; -typedef vespalib::xml::XmlSerializable XmlSerializable; +using XmlSerializable = vespalib::xml::XmlSerializable; +using XmlOutputStream = vespalib::xml::XmlOutputStream; } // vespalib diff --git a/staging_vespalib/src/vespa/vespalib/util/xmlstream.cpp b/staging_vespalib/src/vespa/vespalib/util/xmlstream.cpp new file mode 100644 index 00000000000..16fce61ddd1 --- /dev/null +++ b/staging_vespalib/src/vespa/vespalib/util/xmlstream.cpp @@ -0,0 +1,463 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "xmlstream.hpp" +#include <vespa/vespalib/encoding/base64.h> +#include <vespa/vespalib/stllike/asciistream.h> +#include <vespa/vespalib/util/stringfmt.h> +#include <cassert> + +namespace vespalib::xml { + +namespace { + + std::vector<bool> getLegalIdentifierFirstCharacters() { + std::vector<bool> vec(256, false); + for (uint32_t i='a'; i<='z'; ++i) vec[i] = true; + for (uint32_t i='A'; i<='Z'; ++i) vec[i] = true; + vec[':'] = true; + vec['_'] = true; + return vec; + } + + std::vector<bool> getLegalIdentifierCharacters() { + std::vector<bool> vec(getLegalIdentifierFirstCharacters()); + vec['-'] = true; + vec['.'] = true; + for (uint32_t i='0'; i<='9'; ++i) { + vec[i] = true; + } + return vec; + } + + std::vector<bool> getBinaryCharacters() { + std::vector<bool> vec(256, false); + for (uint32_t i=0; i<32; ++i) { + vec[i] = true; + } + vec['\t'] = false; + vec['\n'] = false; + vec['\r'] = false; + vec['\f'] = false; + return vec; + } + + std::vector<bool> getEscapedXmlCharacters() { + std::vector<bool> vec(256, false); + for (uint32_t i=0; i<32; ++i) { + vec[i] = true; + } + vec['\n'] = false; + vec['<'] = true; + vec['>'] = true; + vec['&'] = true; + return vec; + } + + std::vector<bool> legalIdentifierFirstChar( + getLegalIdentifierFirstCharacters()); + std::vector<bool> legalIdentifierChars = getLegalIdentifierCharacters(); + std::vector<bool> binaryChars = getBinaryCharacters(); + std::vector<bool> escapedXmlChars = getEscapedXmlCharacters(); + + bool containsBinaryCharacters(const std::string& s) { + for (int i=0, n=s.size(); i<n; ++i) { + if (binaryChars[static_cast<uint8_t>(s[i])]) return true; + } + return false; + } + + const std::string xmlAttributeEscape(const std::string& s) { + vespalib::asciistream ost; + for (uint32_t i=0, n=s.size(); i<n; ++i) { + if (s[i] == '"' || s[i] == '\n' + || escapedXmlChars[static_cast<uint8_t>(s[i])]) + { + if (s[i] == '<') ost << "<"; + else if (s[i] == '>') ost << ">"; + else if (s[i] == '&') ost << "&"; + else if (s[i] == '"') ost << """; + else { + ost << "&#" << (int) s[i] << ";"; + } + } else { + ost << s[i]; + } + } + return ost.str(); + } + + void writeEscaped(std::ostream& out, const std::string& s) { + for (uint32_t i=0, n=s.size(); i<n; ++i) { + if (escapedXmlChars[static_cast<uint8_t>(s[i])]) { + if (s[i] == '<') out << "<"; + else if (s[i] == '>') out << ">"; + else if (s[i] == '&') out << "&"; + else { + out << "&#" << (int) s[i] << ";"; + } + } else { + out << s[i]; + } + } + } + + void writeBase64Encoded(std::ostream& out, const std::string& s) { + out << vespalib::Base64::encode(&s[0], s.size()); + } +} + +bool isLegalName(const std::string& name) { + if (name.size() == 0) return false; + if (!legalIdentifierFirstChar[static_cast<uint8_t>(name[0])]) return false; + for (int i=1, n=name.size(); i<n; ++i) { + if (!legalIdentifierChars[static_cast<uint8_t>(name[i])]) return false; + } + return true; +} + +void convertToLegalName(std::string& name) { + if (name.size() == 0) { + name == "__no_name__"; + } else { + if (!legalIdentifierFirstChar[static_cast<uint8_t>(name[0])]) { + name[0] = '_'; + } + for (int i=1, n=name.size(); i<n; ++i) { + if (!legalIdentifierChars[static_cast<uint8_t>(name[i])]) { + name[i] = '_'; + } + } + } +} + +XmlOutputStream::XmlOutputStream(std::ostream& ostream, + const std::string& indent) + : _indent(indent), + _wrappedStream(ostream), + _tagStack(), + _cachedTag(), + _cachedAttributes(), + _cachedContent() +{ +} + +XmlAttribute::~XmlAttribute() +{ +} + +XmlContent::~XmlContent() +{ +} + +XmlOutputStream::~XmlOutputStream() +{ +} + +XmlOutputStream& +XmlOutputStream::operator<<(const XmlTag& tag) +{ + //std::cerr << "Trying to add tag " << tag.getName() << ". cached tag is " + // << (void*) _cachedTag.get() << "\n"; + if (_cachedTag.get() != 0) flush(false); + _cachedTag.reset(new XmlTag(tag)); + _cachedContentType = XmlContent::AUTO; + //std::cerr << "Added tag " << _cachedTag->getName() << "\n"; + return *this; +} + +XmlOutputStream& +XmlOutputStream::operator<<(const XmlAttribute& attribute) +{ + //std::cerr << "Adding attribute\n"; + if (_cachedTag.get() == 0) { + throw vespalib::IllegalStateException("Cannot add attribute " + + attribute.getName() + ", as no tag is open"); + } + _cachedAttributes.push_back(attribute); + return *this; +} + +XmlOutputStream& +XmlOutputStream::operator<<(const XmlEndTag&) +{ + //std::cerr << "Adding endtag\n"; + if (_cachedTag.get()) { + flush(true); + _cachedContentType = XmlContent::ESCAPED; + } else if (_tagStack.empty()) { + throw vespalib::IllegalStateException("No open tags left to end"); + } else { + for (uint32_t i=1; i<_tagStack.size(); ++i) { + _wrappedStream << _indent; + } + _wrappedStream << "</" << _tagStack.back() << ">"; + _tagStack.pop_back(); + if (!_tagStack.empty()) _wrappedStream << '\n'; + _cachedContentType = XmlContent::ESCAPED; + } + return *this; +} + +XmlOutputStream& +XmlOutputStream::operator<<(const XmlContent& content) +{ + //std::cerr << "Adding content\n"; + if (_cachedTag.get() == 0 && _tagStack.empty()) { + throw vespalib::IllegalStateException( + "No open tag to write content in"); + } + if (_cachedTag.get() != 0) { + //std::cerr << "Content is '" << content.getContent() << "'\n"; + if (content.getType() == XmlContent::AUTO) { // Do nothing.. Always ok + } else if (_cachedContentType == XmlContent::AUTO) { + _cachedContentType = content.getType(); + } else if (_cachedContentType != content.getType()) { + throw vespalib::IllegalStateException( + "Have already added content of different type"); + } + _cachedContent.push_back(content); + } else { + if (content.getType() == XmlContent::BASE64) { + throw vespalib::IllegalStateException( + "Cannot add Base64 encoded content after tag content"); + } + for (uint32_t i=0; i<_tagStack.size(); ++i) { + _wrappedStream << _indent; + } + _wrappedStream << content.getContent() << '\n'; + } + return *this; +} + +XmlOutputStream& +XmlOutputStream::operator<<(const XmlSerializable& serializable) +{ + //std::cerr << "Adding serializable\n"; + serializable.printXml(*this); + return *this; +} + +XmlOutputStream& +XmlOutputStream::operator<<(const std::string& content) +{ + //std::cerr << "Adding content string\n"; + return *this << XmlContent(content); +} + +XmlOutputStream& +XmlOutputStream::operator<<(char c) +{ + return *this << XmlContent(std::string(&c, 1)); +} + +XmlOutputStream& +XmlOutputStream::operator<<(int32_t i) +{ + return *this << XmlContent(vespalib::make_string("%d", i)); +} + +XmlOutputStream& +XmlOutputStream::operator<<(int64_t i) +{ + return *this << XmlContent(vespalib::make_string("%" PRId64, i)); +} + +XmlOutputStream& +XmlOutputStream::operator<<(float f) +{ + return *this << XmlContent(vespalib::make_string("%g", f)); +} + +XmlOutputStream& +XmlOutputStream::operator<<(double d) +{ + return *this << XmlContent(vespalib::make_string("%g", d)); +} + +void +XmlOutputStream::flush(bool endTag) +{ + //std::cerr << "Flushing\n"; + if (_cachedTag.get() == 0) { + throw vespalib::IllegalStateException("Cannot write non-existing tag"); + } + for (uint32_t i=0; i<_tagStack.size(); ++i) { + _wrappedStream << _indent; + } + _wrappedStream << '<' << _cachedTag->getName(); + for (std::list<XmlAttribute>::const_iterator it = _cachedAttributes.begin(); + it != _cachedAttributes.end(); ++it) + { + _wrappedStream << ' ' << it->getName() << "=\"" + << xmlAttributeEscape(it->getValue()) << '"'; + } + _cachedAttributes.clear(); + if (_cachedContent.empty() && endTag) { + _wrappedStream << "/>\n"; + } else if (_cachedContent.empty()) { + _wrappedStream << ">\n"; + _tagStack.push_back(_cachedTag->getName()); + } else { + if (_cachedContentType == XmlContent::AUTO) { + _cachedContentType = XmlContent::ESCAPED; + for (std::list<XmlContent>::const_iterator it + = _cachedContent.begin(); it != _cachedContent.end(); ++it) + { + if (containsBinaryCharacters(it->getContent())) { + _cachedContentType = XmlContent::BASE64; + break; + } + } + } + if (_cachedContentType == XmlContent::BASE64) { + _wrappedStream << " binaryencoding=\"base64\""; + } + _wrappedStream << '>'; + for (std::list<XmlContent>::const_iterator it = _cachedContent.begin(); + it != _cachedContent.end(); ++it) + { + if (!endTag) { + _wrappedStream << '\n'; + for (uint32_t i=0; i<=_tagStack.size(); ++i) { + _wrappedStream << _indent; + } + } + switch (_cachedContentType) { + case XmlContent::ESCAPED: { + writeEscaped(_wrappedStream, it->getContent()); + break; + } + case XmlContent::BASE64: { + writeBase64Encoded(_wrappedStream, it->getContent()); + break; + } + default: assert(false); + } + } + _cachedContent.clear(); + if (endTag) { + _wrappedStream << "</" << _cachedTag->getName() << ">\n"; + } else { + _wrappedStream << '\n'; + _tagStack.push_back(_cachedTag->getName()); + } + } + _cachedTag.reset(0); +} + +XmlTag::XmlTag(const XmlTag& tag) + : _name(tag._name), + _attributes(), + _content(), + _flags(tag._flags) +{ +} + +XmlTag::~XmlTag() {} + +XmlTag::XmlTag(const std::string& name, XmlTagFlags flags) + : _name(name), + _attributes(), + _content(), + _flags(flags) +{ + if (_flags == CONVERT_ILLEGAL_CHARACTERS) { + convertToLegalName(_name); + } + if (!isLegalName(_name)) { + throw vespalib::IllegalArgumentException("Name '" + _name + "' contains " + "illegal XML characters and cannot be used as tag name"); + } +} + +XmlAttribute::XmlAttribute(const XmlAttribute& attribute) + : _name(attribute._name), + _value(attribute._value), + _next() +{ +} + +XmlAttribute::XmlAttribute(const std::string& name, const char * value, uint32_t flags) + : _name(name), + _value(), + _next() +{ + vespalib::asciistream ost; + if (flags & HEX) ost << vespalib::hex << "0x"; + ost << value; + _value = ost.str(); + if (!isLegalName(name)) { + throw vespalib::IllegalArgumentException("Name '" + name + "' contains " + "illegal XML characters and cannot be used as attribute name"); + } +} + +XmlEndTag::XmlEndTag() +{ +} + +XmlContent::XmlContent(Type type) + : _type(type), + _content(), + _nextContent(), + _nextTag() +{ +} + +XmlContent::XmlContent() + : _type(AUTO), + _content(), + _nextContent(), + _nextTag() +{ +} + +XmlContent::XmlContent(const XmlContent& content) + : _type(content._type), + _content(content._content), + _nextContent(), + _nextTag() +{ +} + +XmlContent::XmlContent(const std::string& value) + : _type(AUTO), + _content(value), + _nextContent(), + _nextTag() +{ +} + +XmlContentWrapper::XmlContentWrapper(const XmlContentWrapper& wrapper) + : XmlContent(wrapper) +{ +} + +XmlContentWrapper::XmlContentWrapper(const char* value) + : XmlContent(std::string(value)) +{ +} + +XmlContentWrapper::XmlContentWrapper(const char* value, uint32_t size) + : XmlContent(std::string(value, size)) +{ +} + +using CharP = char *; +using ConstCharP = const char *; + +template XmlAttribute::XmlAttribute(const std::string &, const std::string &, unsigned int); +template XmlAttribute::XmlAttribute(const std::string &, const vespalib::string &, unsigned int); +template XmlAttribute::XmlAttribute(const std::string &, const vespalib::stringref &, unsigned int); +template XmlAttribute::XmlAttribute(const std::string &, const CharP &, unsigned int); +template XmlAttribute::XmlAttribute(const std::string &, const ConstCharP &, unsigned int); +template XmlAttribute::XmlAttribute(const std::string &, const bool &, unsigned int); +template XmlAttribute::XmlAttribute(const std::string &, const int16_t &, unsigned int); +template XmlAttribute::XmlAttribute(const std::string &, const int32_t &, unsigned int); +template XmlAttribute::XmlAttribute(const std::string &, const int64_t &, unsigned int); +template XmlAttribute::XmlAttribute(const std::string &, const uint16_t &, unsigned int); +template XmlAttribute::XmlAttribute(const std::string &, const uint32_t &, unsigned int); +template XmlAttribute::XmlAttribute(const std::string &, const uint64_t &, unsigned int); +template XmlAttribute::XmlAttribute(const std::string &, const double &, unsigned int); + +} diff --git a/staging_vespalib/src/vespa/vespalib/util/xmlstream.h b/staging_vespalib/src/vespa/vespalib/util/xmlstream.h new file mode 100644 index 00000000000..8ed781e57c3 --- /dev/null +++ b/staging_vespalib/src/vespa/vespalib/util/xmlstream.h @@ -0,0 +1,210 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +/** + * @file xmlserializable.h + * @ingroup util + * + * @brief Interfaces to be used for XML serialization. + * + * This file contains XML utility classes, to make XML serialization simple. + * Rather than users writing their own XML, these tools let you define a tree + * structure, and this library builds the XML for you. This ensures that you + * write legal XML and that stuff that needs to be escaped is. + * <p> + * It defines a superclass for XML serializable classes, called XmlSerializable. + * This is what classes that should be XML serializable will inherit. + * <p> + * When implementing the printXml() function in XmlSerializable, one will + * use the various XML helper classes defined here to build a tree structure + * creating the XML. These are: XmlTag, XmlEndTag, XmlAttribute, and XmlContent. + * Some subclasses exist of XmlContent to facilitate various types of content. + * <p> + * The XmlOutputStream wraps a regular std::ostream. You write XML objects to it + * and it is responsible for writing all the XML code. This way, the XML + * serialization is done without interfering with regular output operators. + * <p> + * For example usage, refer to the unit test: + * vespalib/tests/xmlserializable/xmlserializabletest.cpp + * + */ + +#pragma once + +#include "xmlserializable.h" +#include <iosfwd> +#include <list> +#include <memory> + +namespace vespalib::xml { + +class XmlAttribute; +class XmlContent; +class XmlOutputStream; + +bool isLegalName(const std::string& name); + +enum XmlTagFlags { NONE = 0, CONVERT_ILLEGAL_CHARACTERS = 1 }; + +/** + * @class document::XmlTag + * + * @brief Start a new tag with given name. + */ +class XmlTag { + std::string _name; + std::unique_ptr<XmlAttribute> _attributes; + std::unique_ptr<XmlContent> _content; + XmlTagFlags _flags; +public: + XmlTag(const XmlTag&); + XmlTag(const std::string& name, XmlTagFlags = NONE); + ~XmlTag(); + + const std::string& getName() const { return _name; } +}; + +/** + * @class document::XmlEndTag + * + * @brief Indicates that current tag is closed. + */ +class XmlEndTag { +public: + XmlEndTag(); +}; + +/** + * @class document::XmlAttribute + * + * @brief Defined a single attribute within an XML tag. + * + * When adding an XML to an XML stream, the attribute will be added to the last + * tag added. This can not be called after the last tag opened in the stream is + * closed, so add all attributes before starting to add new XML child tags. + */ +class XmlAttribute { + std::string _name; + std::string _value; + std::unique_ptr<XmlAttribute> _next; +public: + enum Flag { NONE = 0x0, HEX = 0x1 }; + XmlAttribute(const XmlAttribute&); + /** Add any value that can be written to an ostringstream. */ + template<typename T> + XmlAttribute(const std::string& name, const T& value, uint32_t flags = NONE); + XmlAttribute(const std::string& name, const char * value, uint32_t flags = NONE); + ~XmlAttribute(); + + const std::string& getName() const { return _name; } + const std::string& getValue() const { return _value; } +}; + + +/** + * @class document::XmlContent + * + * XML content to be written to stream. By default it will autodetect whether to + * escape or base64 encode content. XmlOutputStream functions taking primitives + * will generate XmlContent instances. + */ +class XmlContent { +public: + enum Type { AUTO, ESCAPED, BASE64 }; +protected: + XmlContent(Type type); +private: + Type _type; + std::string _content; + std::unique_ptr<XmlContent> _nextContent; + std::unique_ptr<XmlTag> _nextTag; + +public: + XmlContent(); + XmlContent(const XmlContent&); + XmlContent(const std::string& value); + ~XmlContent(); + + Type getType() const { return _type; } + const std::string& getContent() const { return _content; } +}; + +/** + * @class document::XmlEscapedContent + * + * Token used to tell that this content field should only be XML escaped. + */ +class XmlEscapedContent : public XmlContent { +public: + XmlEscapedContent() : XmlContent(ESCAPED) {} +}; + +/** + * @class document::XmlBase64Content + * + * Token used to tell that this content field should always be base64 encoded. + */ +class XmlBase64Content : public XmlContent { +public: + XmlBase64Content() : XmlContent(BASE64) {} +}; + +/** + * @class document::XmlContentWrapper + * + * A wrapper class for content that one doesn't want to copy or release + * ownership of. This wrapper merely takes pointer to data, and assumes it + * will stay alive as long as needed. + */ +class XmlContentWrapper : public XmlContent { +public: + XmlContentWrapper(const XmlContentWrapper&); + XmlContentWrapper(const char* value); + XmlContentWrapper(const char* value, uint32_t size); +}; + +/** + * @class document::XmlOutputStream + * + * @brief std::ostream wrapper, only accepting data that will become XML. + * + * After XmlEndTag() has been sent to the stream, the tag is guarantueed to have + * been written. Call isFinalized() to ensure that you have closed all the tags + * that have been opened. Within a tag, the stream will cache some information, + * as more information might be required before knowing what to print. + */ +class XmlOutputStream { + const std::string _indent; + std::ostream& _wrappedStream; + std::list<std::string> _tagStack; + std::unique_ptr<XmlTag> _cachedTag; + std::list<XmlAttribute> _cachedAttributes; + std::list<XmlContent> _cachedContent; + XmlContent::Type _cachedContentType; + + void flush(bool endTag); + +public: + + XmlOutputStream(std::ostream& ostream, const std::string& indent = ""); + ~XmlOutputStream(); + + bool isFinalized() const + { return (_tagStack.empty() && _cachedTag.get() == 0); } + + std::ostream& getWrappedStream() { return _wrappedStream; } + + XmlOutputStream& operator<<(const XmlTag& tag); + XmlOutputStream& operator<<(const XmlAttribute& attribute); + XmlOutputStream& operator<<(const XmlEndTag& endtag); + XmlOutputStream& operator<<(const XmlContent& content); + XmlOutputStream& operator<<(const XmlSerializable& serializable); + + XmlOutputStream& operator<<(const std::string& content); + XmlOutputStream& operator<<(char c); + XmlOutputStream& operator<<(int32_t i); + XmlOutputStream& operator<<(int64_t i); + XmlOutputStream& operator<<(float f); + XmlOutputStream& operator<<(double d); +}; + +} + diff --git a/staging_vespalib/src/vespa/vespalib/util/xmlserializable.hpp b/staging_vespalib/src/vespa/vespalib/util/xmlstream.hpp index 0da684d6b28..243efe9e94b 100644 --- a/staging_vespalib/src/vespa/vespalib/util/xmlserializable.hpp +++ b/staging_vespalib/src/vespa/vespalib/util/xmlstream.hpp @@ -2,12 +2,11 @@ #pragma once -#include "xmlserializable.h" +#include "xmlstream.h" #include <vespa/vespalib/util/exceptions.h> #include <sstream> -namespace vespalib { -namespace xml { +namespace vespalib::xml { template<typename T> XmlAttribute::XmlAttribute(const std::string& name, const T& value, uint32_t flags) @@ -20,10 +19,9 @@ XmlAttribute::XmlAttribute(const std::string& name, const T& value, uint32_t fla ost << value; _value = ost.str(); if (!isLegalName(name)) { - throw vespalib::IllegalArgumentException("Name '" + name + "' contains " + throw IllegalArgumentException("Name '" + name + "' contains " "illegal XML characters and cannot be used as attribute name"); } } } -} |