summaryrefslogtreecommitdiffstats
path: root/staging_vespalib
diff options
context:
space:
mode:
authorHenning Baldersheim <balder@yahoo-inc.com>2017-06-06 15:11:38 +0200
committerHenning Baldersheim <balder@yahoo-inc.com>2017-06-07 09:50:23 +0200
commitece112cf6aadbf135c92f1d5ddada739b290a118 (patch)
tree3fb49a56dcfd77e27ee335f5be1f172d3a7975c9 /staging_vespalib
parent8438c19df2999aed6fd89c470c52a9c04c77dcdc (diff)
Forward declare XmlOutputStream and FieldValue::IteratorHandler.
Diffstat (limited to 'staging_vespalib')
-rw-r--r--staging_vespalib/src/tests/xmlserializable/xmlserializabletest.cpp2
-rw-r--r--staging_vespalib/src/vespa/vespalib/util/CMakeLists.txt1
-rw-r--r--staging_vespalib/src/vespa/vespalib/util/polymorphicarray.h82
-rw-r--r--staging_vespalib/src/vespa/vespalib/util/polymorphicarraybase.h18
-rw-r--r--staging_vespalib/src/vespa/vespalib/util/polymorphicarrays.h66
-rw-r--r--staging_vespalib/src/vespa/vespalib/util/xmlserializable.cpp459
-rw-r--r--staging_vespalib/src/vespa/vespalib/util/xmlserializable.h203
-rw-r--r--staging_vespalib/src/vespa/vespalib/util/xmlstream.cpp463
-rw-r--r--staging_vespalib/src/vespa/vespalib/util/xmlstream.h210
-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 << "&lt;";
- else if (s[i] == '>') ost << "&gt;";
- else if (s[i] == '&') ost << "&amp;";
- else if (s[i] == '"') ost << "&quot;";
- 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 << "&lt;";
- else if (s[i] == '>') out << "&gt;";
- else if (s[i] == '&') out << "&amp;";
- 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 << "&lt;";
+ else if (s[i] == '>') ost << "&gt;";
+ else if (s[i] == '&') ost << "&amp;";
+ else if (s[i] == '"') ost << "&quot;";
+ 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 << "&lt;";
+ else if (s[i] == '>') out << "&gt;";
+ else if (s[i] == '&') out << "&amp;";
+ 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");
}
}
}
-}