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; +} + +}