Files
xahaud/src/ripple/rpc/impl/JsonObject.h
2015-02-11 20:42:38 -05:00

390 lines
11 KiB
C++

//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
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 RIPPLE_RPC_JSONOBJECT_H_INCLUDED
#define RIPPLE_RPC_JSONOBJECT_H_INCLUDED
#include <ripple/rpc/impl/JsonWriter.h>
namespace ripple {
namespace RPC {
/**
Collection is a base class for Array and Object, classes which provide the
facade of JSON collections for the O(1) JSON writer, while still using no
heap memory and only a very small amount of stack.
From http://json.org, JSON has two types of collection: array, and object.
Everything else is a *scalar* - a number, a string, a boolean, the special
value null, or a legacy Json::Value.
Collections must write JSON "as-it-goes" in order to get the strong
performance guarantees. This puts restrictions upon API users:
1. Only one collection can be open for change at any one time.
This condition is enforced automatically and a JsonException thrown if it
is violated.
2. A tag may only be used once in an Object.
Some objects have many tags, so this condition might be a little
expensive. Enforcement of this condition is turned on in debug builds and
a JsonException is thrown when the tag is added for a second time.
Code samples:
Writer writer;
// An empty object.
{
Object::Root (writer);
}
// Outputs {}
// An object with one scalar value.
{
Object::Root root (writer);
write["hello"] = "world";
}
// Outputs {"hello":"world"}
// Same, using chaining.
{
Object::Root (writer)["hello"] = "world";
}
// Output is the same.
// Add several scalars, with chaining.
{
Object::Root (writer)
.set ("hello", "world")
.set ("flag", false)
.set ("x", 42);
}
// Outputs {"hello":"world","flag":false,"x":42}
// Add an array.
{
Object::Root root (writer);
{
auto array = root.makeArray ("hands");
array.append ("left");
array.append ("right");
}
}
// Outputs {"hands":["left", "right"]}
// Same, using chaining.
{
Object::Root (writer)
.makeArray ("hands")
.append ("left")
.append ("right");
}
// Output is the same.
// Add an object.
{
Object::Root root (writer);
{
auto object = root.makeObject ("hands");
object["left"] = false;
object["right"] = true;
}
}
// Outputs {"hands":{"left":false,"right":true}}
// Same, using chaining.
{
Object::Root (writer)
.makeObject ("hands")
.set ("left", false)
.set ("right", true);
}
}
// Outputs {"hands":{"left":false,"right":true}}
Typical ways to make mistakes and get a JsonException:
Writer writer;
Object::Root root (writer);
// Repeat a tag.
{
root ["hello"] = "world";
root ["hello"] = "there"; // THROWS! in a debug build.
}
// Open a subcollection, then set something else.
{
auto object = root.makeObject ("foo");
root ["hello"] = "world"; // THROWS!
}
// Open two subcollections at a time.
{
auto object = root.makeObject ("foo");
auto array = root.makeArray ("bar"); // THROWS!!
}
For more examples, check the unit tests.
*/
class Collection
{
public:
Collection (Collection&& c) noexcept;
Collection& operator= (Collection&& c) noexcept;
Collection() = delete;
~Collection();
protected:
// A null parent means "no parent at all".
// Writers cannot be null.
Collection (Collection* parent, Writer*);
void checkWritable (std::string const& label);
Collection* parent_;
Writer* writer_;
bool enabled_;
};
class Array;
//------------------------------------------------------------------------------
/** Represents a JSON object being written to a Writer. */
class Object : protected Collection
{
public:
/** Object::Root is the only Collection that has a public constructor. */
class Root;
/** Set a scalar value in the Object for a key.
A JSON scalar is a single value - a number, string, boolean, nullptr or
a Json::Value.
`set()` throws an exception if this object is disabled (which means that
one of its children is enabled).
In a debug build, `set()` also throws an exception if the key has
already been set() before.
An operator[] is provided to allow writing `object["key"] = scalar;`.
*/
template <typename Scalar>
Object& set (std::string const& key, Scalar const&);
// Detail class and method used to implement operator[].
class Proxy;
Proxy operator[] (std::string const& key);
Proxy operator[] (Json::StaticString const& key);
/** Make a new Object at a key and return it.
This Object is disabled until that sub-object is destroyed.
Throws an exception if this Object was already disabled.
*/
Object makeObject (std::string const& key);
/** Make a new Array at a key and return it.
This Object is disabled until that sub-array is destroyed.
Throws an exception if this Object was already disabled.
*/
Array makeArray (std::string const& key);
protected:
friend class Array;
Object (Collection* parent, Writer* w) : Collection (parent, w) {}
};
class Object::Root : public Object
{
public:
/** Each Object::Root must be constructed with its own unique Writer. */
Root (Writer&);
};
//------------------------------------------------------------------------------
/** Represents a JSON array being written to a Writer. */
class Array : private Collection
{
public:
/** Append a scalar to the Arrary.
Throws an exception if this array is disabled (which means that one of
its sub-collections is enabled).
*/
template <typename Scalar>
Array& append (const Scalar&);
/** Append a new Object and return it.
This Array is disabled until that sub-object is destroyed.
Throws an exception if this Array was already disabled.
*/
Object makeObject ();
/** Append a new Array and return it.
This Array is disabled until that sub-array is destroyed.
Throws an exception if this Array was already disabled.
*/
Array makeArray ();
protected:
friend class Object;
Array (Collection* parent, Writer* w) : Collection (parent, w) {}
};
//------------------------------------------------------------------------------
// Generic accessor functions to allow Json::Value and Collection to
// interoperate.
/** Add a new subarray at a named key in a Json object. */
Json::Value& addArray (Json::Value&, Json::StaticString const& key);
/** Add a new subarray at a named key in a Json object. */
Array addArray (Object&, Json::StaticString const& key);
/** Add a new subobject at a named key in a Json object. */
Json::Value& addObject (Json::Value&, Json::StaticString const& key);
/** Add a new subobject at a named key in a Json object. */
Object addObject (Object&, Json::StaticString const& key);
/** Copy all the keys and values from one object into another. */
void copyFrom (Json::Value& to, Json::Value const& from);
/** Copy all the keys and values from one object into another. */
void copyFrom (Object& to, Json::Value const& from);
/** An Object that contains its own Writer. */
class WriterObject
{
public:
WriterObject (Output const& output)
: writer_ (std::make_unique<Writer> (output)),
object_ (std::make_unique<Object::Root> (*writer_))
{
}
#ifdef _MSC_VER
WriterObject (WriterObject&& other) noexcept
: writer_ (std::move (other.writer_)),
object_ (std::move (other.object_))
{
}
#endif
Object* operator->()
{
return object_.get();
}
Object& operator*()
{
return *object_;
}
private:
std::unique_ptr <Writer> writer_;
std::unique_ptr<Object::Root> object_;
};
WriterObject stringWriterObject (std::string&);
//------------------------------------------------------------------------------
// Implementation details.
// Detail class for Object::operator[].
class Object::Proxy
{
private:
Object& object_;
std::string const key_;
public:
Proxy (Object& object, std::string const& key);
template <class T>
Object& operator= (T const& t)
{
object_.set (key_, t);
return object_;
}
};
//------------------------------------------------------------------------------
template <typename Scalar>
Array& Array::append (Scalar const& value)
{
checkWritable ("append");
if (writer_)
writer_->append (value);
return *this;
}
template <typename Scalar>
Object& Object::set (std::string const& key, Scalar const& value)
{
checkWritable ("set");
if (writer_)
writer_->set (key, value);
return *this;
}
inline
Json::Value& addArray (Json::Value& json, Json::StaticString const& key)
{
return (json[key] = Json::arrayValue);
}
inline
Array addArray (Object& json, Json::StaticString const& key)
{
return json.makeArray (std::string (key));
}
inline
Json::Value& addObject (Json::Value& json, Json::StaticString const& key)
{
return (json[key] = Json::objectValue);
}
inline
Object addObject (Object& object, Json::StaticString const& key)
{
return object.makeObject (std::string (key));
}
} // RPC
} // ripple
#endif