diff --git a/Builds/VisualStudio2012/beast.vcxproj b/Builds/VisualStudio2012/beast.vcxproj
index 51c503376..c63d3621d 100644
--- a/Builds/VisualStudio2012/beast.vcxproj
+++ b/Builds/VisualStudio2012/beast.vcxproj
@@ -184,6 +184,7 @@
+
@@ -569,6 +570,12 @@
true
true
+
+ true
+ true
+ true
+ true
+
true
true
diff --git a/Builds/VisualStudio2012/beast.vcxproj.filters b/Builds/VisualStudio2012/beast.vcxproj.filters
index e6f5ed48b..6b718eb5c 100644
--- a/Builds/VisualStudio2012/beast.vcxproj.filters
+++ b/Builds/VisualStudio2012/beast.vcxproj.filters
@@ -1245,6 +1245,9 @@
beast
+
+ beast\utility
+
@@ -1793,6 +1796,9 @@
beast\boost
+
+ beast\utility\impl
+
diff --git a/beast/Utility.h b/beast/Utility.h
index 403f78ee3..952d0c225 100644
--- a/beast/Utility.h
+++ b/beast/Utility.h
@@ -25,6 +25,7 @@
#include "utility/Error.h"
#include "utility/Journal.h"
#include "utility/LeakChecked.h"
+#include "utility/PropertyStream.h"
#include "utility/StaticObject.h"
#endif
diff --git a/beast/utility/PropertyStream.h b/beast/utility/PropertyStream.h
new file mode 100644
index 000000000..a8fb8956c
--- /dev/null
+++ b/beast/utility/PropertyStream.h
@@ -0,0 +1,263 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#ifndef BEAST_UTILITY_PROPERTYSTREAM_H_INCLUDED
+#define BEAST_UTILITY_PROPERTYSTREAM_H_INCLUDED
+
+#include "../CStdInt.h"
+#include "../Uncopyable.h"
+#include "../intrusive/List.h"
+#include "../threads/SharedData.h"
+
+#include
+
+namespace beast {
+
+/** An output stream to procedurally generate an abstract property tree. */
+class PropertyStream
+{
+private:
+ class Proxy;
+
+public:
+ class ScopedArray;
+ class ScopedObject;
+ class Source;
+
+private:
+ class Item : public List - ::Node
+ {
+ public:
+ explicit Item (Source* source);
+ Source& source() const;
+ Source* operator-> () const;
+ Source& operator* () const;
+ private:
+ Source* m_source;
+ };
+
+public:
+ //--------------------------------------------------------------------------
+
+ class Sink : Uncopyable
+ {
+ public:
+ // Object output
+ //
+ // Default implementations convert to string
+ // Json doesn't support 64 bit so we convert these to string
+ // if they are outside the range of the corresponding 32 bit int
+ virtual void begin_object (std::string const& key) = 0;
+ virtual void end_object () = 0;
+ template
+ void lexical_write (std::string const &key, Value value)
+ {
+ std::stringstream ss;
+ ss << value;
+ write (key, ss.str());
+ }
+ virtual void write (std::string const& key, int32 value);
+ virtual void write (std::string const& key, uint32 value);
+ virtual void write (std::string const& key, int64 value);
+ virtual void write (std::string const& key, uint64 value);
+ virtual void write (std::string const& key, std::string const& value) = 0;
+
+ // Array output
+ //
+ virtual void begin_array (std::string const& key) = 0;
+ virtual void end_array () = 0;
+ template
+ void lexical_write (Value value)
+ {
+ std::stringstream ss;
+ ss << value;
+ write (ss.str());
+ }
+ virtual void write ( int32 value);
+ virtual void write (uint32 value);
+ virtual void write ( int64 value);
+ virtual void write (uint64 value);
+ virtual void write (std::string const& value) = 0;
+ };
+
+ //--------------------------------------------------------------------------
+
+ PropertyStream ();
+ PropertyStream (Sink& sink);
+ PropertyStream (PropertyStream const& other);
+ PropertyStream& operator= (PropertyStream const& other);
+
+ /** Object output.
+ */
+ /** @{ */
+ void begin_object (std::string const& key) const;
+ void end_object () const;
+
+ template
+ void write (std::string const& key, Value value) const
+ {
+ m_sink->write (key, value);
+ }
+
+ template
+ void write (Key key, Value value) const
+ {
+ std::stringstream ss;
+ ss << key;
+ write (ss.str(), value);
+ }
+
+ Proxy operator[] (std::string const& key) const;
+
+ template
+ Proxy operator[] (Key key) const;
+
+ /** @} */
+
+ /** Array output.
+ */
+ /** @{ */
+ void begin_array (std::string const& key) const;
+ void end_array () const;
+
+ template
+ void append (Value value) const
+ { m_sink->write (value); }
+
+ template
+ PropertyStream const& operator<< (Value value) const
+ { append (value); return &this; }
+ /** @} */
+
+private:
+ static Sink& nullSink();
+
+ Sink* m_sink;
+};
+
+//------------------------------------------------------------------------------
+
+class PropertyStream::Proxy
+{
+private:
+ PropertyStream m_stream;
+ std::string m_key;
+
+public:
+ Proxy (PropertyStream stream, std::string const& key);
+
+ template
+ Proxy& operator= (Value value)
+ { m_stream.write (m_key, value); return *this; }
+};
+
+//------------------------------------------------------------------------------
+
+template
+PropertyStream::Proxy PropertyStream::operator[] (Key key) const
+{
+ std::stringstream ss;
+ ss << key;
+ return operator[] (ss.str());
+}
+
+//------------------------------------------------------------------------------
+
+class PropertyStream::ScopedObject
+{
+private:
+ PropertyStream m_stream;
+
+public:
+ ScopedObject (std::string const& key, PropertyStream stream);
+ ~ScopedObject ();
+};
+
+//------------------------------------------------------------------------------
+
+class PropertyStream::ScopedArray
+{
+private:
+ PropertyStream m_stream;
+
+public:
+ ScopedArray (std::string const& key, PropertyStream stream);
+ ~ScopedArray ();
+};
+
+//------------------------------------------------------------------------------
+
+/** Subclasses can be called to write to a stream and have children. */
+class PropertyStream::Source : public Uncopyable
+{
+private:
+ struct State
+ {
+ explicit State (Source* source)
+ : item (source)
+ , parent (nullptr)
+ { }
+ Item item;
+ Source* parent;
+ List
- children;
+ };
+
+ typedef SharedData SharedState;
+
+ std::string const m_name;
+ SharedState m_state;
+
+ void remove (SharedState::Access& state, SharedState::Access& childState);
+ void removeAll (SharedState::Access& state);
+
+public:
+ explicit Source (std::string const& name);
+ ~Source ();
+
+ /** Add a child source. */
+ void add (Source& source);
+
+ /** Add a child source by pointer.
+ This returns the passed source so it can be conveniently chained
+ in ctor-initializer lists.
+ */
+ template
+ Derived* add (Derived* child)
+ {
+ add (*static_cast (child));
+ return child;
+ }
+
+ /** Remove a child source. */
+ void remove (Source& child);
+
+ /** Remove all child sources. */
+ void removeAll ();
+
+ void write (PropertyStream stream, bool includeChildren);
+ void write (std::string const& path, PropertyStream stream);
+
+ virtual void onWrite (PropertyStream) { }
+};
+
+//------------------------------------------------------------------------------
+
+}
+
+#endif
diff --git a/beast/utility/Utility.cpp b/beast/utility/Utility.cpp
index f66ea5a69..e2b254bfd 100644
--- a/beast/utility/Utility.cpp
+++ b/beast/utility/Utility.cpp
@@ -28,3 +28,4 @@
#include "impl/Journal.cpp"
#include "impl/LeakChecked.cpp"
#include "impl/StaticObject.cpp"
+#include "impl/PropertyStream.cpp"
diff --git a/beast/utility/impl/PropertyStream.cpp b/beast/utility/impl/PropertyStream.cpp
new file mode 100644
index 000000000..64d1164a1
--- /dev/null
+++ b/beast/utility/impl/PropertyStream.cpp
@@ -0,0 +1,341 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#include "../PropertyStream.h"
+
+#include
+
+namespace beast {
+
+PropertyStream::Item::Item (Source* source)
+ : m_source (source)
+{
+}
+
+PropertyStream::Source& PropertyStream::Item::source() const
+{
+ return *m_source;
+}
+
+PropertyStream::Source* PropertyStream::Item::operator-> () const
+{
+ return &source();
+}
+
+PropertyStream::Source& PropertyStream::Item::operator* () const
+{
+ return source();
+}
+
+//------------------------------------------------------------------------------
+
+void PropertyStream::Sink::write (std::string const& key, int32 value)
+{
+ lexical_write (key, value);
+}
+
+void PropertyStream::Sink::write (std::string const& key, uint32 value)
+{
+ lexical_write (key, value);
+}
+
+void PropertyStream::Sink::write (std::string const& key, int64 value)
+{
+ if (value <= std::numeric_limits ::max() &&
+ value >= std::numeric_limits ::min())
+ {
+ write (key, int32(value));
+ }
+ else
+ {
+ lexical_write (key, value);
+ }
+}
+
+void PropertyStream::Sink::write (std::string const& key, uint64 value)
+{
+ if (value <= std::numeric_limits ::max() &&
+ value >= std::numeric_limits ::min())
+ {
+ write (key, uint32(value));
+ }
+ else
+ {
+ lexical_write (key, value);
+ }
+}
+
+void PropertyStream::Sink::write (int32 value)
+{
+ lexical_write (value);
+}
+
+void PropertyStream::Sink::write (uint32 value)
+{
+ lexical_write (value);
+}
+
+void PropertyStream::Sink::write (int64 value)
+{
+ if (value <= std::numeric_limits ::max() &&
+ value >= std::numeric_limits ::min())
+ {
+ write (int32(value));
+ }
+ else
+ {
+ lexical_write (value);
+ }
+}
+
+void PropertyStream::Sink::write (uint64 value)
+{
+ if (value <= std::numeric_limits ::max() &&
+ value >= std::numeric_limits ::min())
+ {
+ write (uint32(value));
+ }
+ else
+ {
+ lexical_write (value);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+PropertyStream::Proxy::Proxy (PropertyStream stream, std::string const& key)
+ : m_stream (stream)
+ , m_key (key)
+{
+}
+
+//------------------------------------------------------------------------------
+
+PropertyStream::ScopedObject::ScopedObject (std::string const& key, PropertyStream stream)
+ : m_stream (stream)
+{
+ m_stream.begin_object (key);
+}
+
+PropertyStream::ScopedObject::~ScopedObject ()
+{
+ m_stream.end_object ();
+}
+
+//------------------------------------------------------------------------------
+
+PropertyStream::Source::Source (std::string const& name)
+ : m_name (name)
+ , m_state (this)
+{
+}
+
+PropertyStream::Source::~Source ()
+{
+ SharedState::Access state (m_state);
+ if (state->parent != nullptr)
+ state->parent->remove (*this);
+ removeAll (state);
+}
+
+void PropertyStream::Source::remove (
+ SharedState::Access& state, SharedState::Access& childState)
+{
+ bassert (childState->parent == this);
+ state->children.erase (
+ state->children.iterator_to (
+ childState->item));
+ childState->parent = nullptr;
+}
+
+void PropertyStream::Source::removeAll (SharedState::Access& state)
+{
+ for (List
- ::iterator iter (state->children.begin());
+ iter != state->children.end();)
+ {
+ SharedState::Access childState ((*iter)->m_state);
+ remove (state, childState);
+ }
+}
+
+void PropertyStream::Source::add (Source& source)
+{
+ SharedState::Access state (m_state);
+ SharedState::Access childState (source.m_state);
+ bassert (childState->parent == nullptr);
+ state->children.push_back (childState->item);
+ childState->parent = this;
+}
+
+void PropertyStream::Source::remove (Source& child)
+{
+ SharedState::Access state (m_state);
+ SharedState::Access childState (child.m_state);
+ remove (state, childState);
+}
+
+void PropertyStream::Source::removeAll ()
+{
+ SharedState::Access state (m_state);
+ removeAll (state);
+}
+
+void PropertyStream::Source::write (PropertyStream stream, bool includeChildren)
+{
+ ScopedObject child (m_name, stream);
+ onWrite (stream);
+
+ if (includeChildren)
+ {
+ SharedState::Access state (m_state);
+ for (List
- ::iterator iter (state->children.begin());
+ iter != state->children.end(); ++iter)
+ {
+ (*iter)->write (stream, true);
+ }
+ }
+}
+
+void PropertyStream::Source::write (std::string const& path, PropertyStream stream)
+{
+ struct Parser
+ {
+ Parser (std::string const& path)
+ : m_first (path.begin())
+ , m_last (path.end())
+ {
+ }
+
+ std::string next ()
+ {
+ std::string::const_iterator pos (
+ std::find (m_first, m_last, '.'));
+ std::string const s (m_first, pos);
+ if (pos != m_last)
+ m_first = pos + 1;
+ else
+ m_first = pos;
+ return s;
+ }
+
+ std::string::const_iterator m_first;
+ std::string::const_iterator m_last;
+ };
+
+ //-----------------------------------------
+
+ if (path.empty ())
+ {
+ write (stream, true);
+ return;
+ }
+
+ Parser p (path);
+ Source* source (this);
+ if (p.next() != source->m_name)
+ return;
+
+ for (;;)
+ {
+ std::string const s (p.next());
+
+ if (s.empty())
+ {
+ source->write (stream, false);
+ break;
+ }
+ else if (s == "*")
+ {
+ source->write (stream, true);
+ break;
+ }
+ else
+ {
+ SharedState::Access state (source->m_state);
+ for (List
- ::iterator iter (state->children.begin());;)
+ {
+ if (iter->source().m_name == s)
+ {
+ source = &iter->source();
+ break;
+ }
+
+ if (++iter == state->children.end())
+ return;
+ }
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+
+PropertyStream::PropertyStream ()
+ : m_sink (&nullSink())
+{
+}
+
+PropertyStream::PropertyStream (Sink& sink)
+ : m_sink (&sink)
+{
+}
+
+PropertyStream::PropertyStream (PropertyStream const& other)
+ : m_sink (other.m_sink)
+{
+}
+
+PropertyStream& PropertyStream::operator= (PropertyStream const& other)
+{
+ m_sink = other.m_sink;
+ return *this;
+}
+
+PropertyStream::Proxy PropertyStream::operator[] (std::string const& key) const
+{
+ return Proxy (*this, key);
+}
+
+void PropertyStream::begin_object (std::string const& key) const
+{
+ m_sink->begin_object (key);
+}
+
+void PropertyStream::end_object () const
+{
+ m_sink->end_object ();
+}
+
+PropertyStream::Sink& PropertyStream::nullSink()
+{
+ struct NullSink : Sink
+ {
+ void begin_object (std::string const&) { }
+ void end_object () { }
+ void write (std::string const&, std::string const&) { }
+ void begin_array (std::string const&) { }
+ void end_array () { }
+ void write (std::string const&) { }
+ };
+
+ static NullSink sink;
+
+ return sink;
+}
+
+}