Add archiver example

A simple (de)serialization framework using DOM and SAX API
diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt
index e00f77a..ff54199 100644
--- a/example/CMakeLists.txt
+++ b/example/CMakeLists.txt
@@ -32,6 +32,8 @@
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
 endif()
 
+add_executable(archivertest archiver/archiver.cpp archiver/archivertest.cpp)
+
 foreach (example ${EXAMPLES})
     add_executable(${example} ${example}/${example}.cpp)
 endforeach()
diff --git a/example/archiver/archiver.cpp b/example/archiver/archiver.cpp
new file mode 100644
index 0000000..59ae4c4
--- /dev/null
+++ b/example/archiver/archiver.cpp
@@ -0,0 +1,292 @@
+#include "archiver.h"
+#include <cassert>
+#include <stack>
+#include "rapidjson/document.h"
+#include "rapidjson/prettywriter.h"
+#include "rapidjson/stringbuffer.h"
+
+using namespace rapidjson;
+
+struct JsonReaderStackItem {
+    enum State {
+        BeforeStart,    //!< An object/array is in the stack but it is not yet called by StartObject()/StartArray().
+        Started,        //!< An object/array is called by StartObject()/StartArray().
+        Closed          //!< An array is closed after read all element, but before EndArray().
+    };
+
+    JsonReaderStackItem(const Value* value, State state) : value(value), state(state), index() {}
+
+    const Value* value;
+    State state;
+    SizeType index;   // For array iteration
+};
+
+typedef std::stack<JsonReaderStackItem> JsonReaderStack;
+
+#define DOCUMENT reinterpret_cast<Document*>(mDocument)
+#define STACK (reinterpret_cast<JsonReaderStack*>(mStack))
+#define TOP (STACK->top())
+#define CURRENT (*TOP.value)
+
+JsonReader::JsonReader(const char* json) : mDocument(), mStack(), mError(false) {
+    mDocument = new Document;
+    DOCUMENT->Parse(json);
+    if (DOCUMENT->HasParseError())
+        mError = true;
+    else {
+        mStack = new JsonReaderStack;
+        STACK->push(JsonReaderStackItem(DOCUMENT, JsonReaderStackItem::BeforeStart));
+    }
+}
+
+JsonReader::~JsonReader() {
+    delete DOCUMENT;
+    delete STACK;
+}
+
+// Archive concept
+JsonReader& JsonReader::StartObject() {
+    if (!mError) {
+        if (CURRENT.IsObject() && TOP.state == JsonReaderStackItem::BeforeStart)
+            TOP.state = JsonReaderStackItem::Started;
+        else
+            mError = true;
+    }
+    return *this;
+}
+
+JsonReader& JsonReader::EndObject() {
+    if (!mError) {
+        if (CURRENT.IsObject() && TOP.state == JsonReaderStackItem::Started)
+            Next();
+        else
+            mError = true;
+    }
+    return *this;
+}
+
+JsonReader& JsonReader::Member(const char* name) {
+    if (!mError) {
+        if (CURRENT.IsObject() && TOP.state == JsonReaderStackItem::Started) {
+            Value::ConstMemberIterator memberItr = CURRENT.FindMember(name);
+            if (memberItr != CURRENT.MemberEnd()) 
+                STACK->push(JsonReaderStackItem(&memberItr->value, JsonReaderStackItem::BeforeStart));
+            else
+                mError = true;
+        }
+        else
+            mError = true;
+    }
+    return *this;
+}
+
+bool JsonReader::HasMember(const char* name) const {
+    if (!mError && CURRENT.IsObject() && TOP.state == JsonReaderStackItem::Started)
+        return CURRENT.HasMember(name);
+    return false;
+}
+
+JsonReader& JsonReader::StartArray(size_t* size) {
+    if (!mError) {
+        if (CURRENT.IsArray() && TOP.state == JsonReaderStackItem::BeforeStart) {
+            TOP.state = JsonReaderStackItem::Started;
+            if (size)
+                *size = CURRENT.Size();
+
+            if (!CURRENT.Empty()) {
+                const Value* value = &CURRENT[TOP.index];
+                STACK->push(JsonReaderStackItem(value, JsonReaderStackItem::BeforeStart));
+            }
+            else
+                TOP.state = JsonReaderStackItem::Closed;
+        }
+        else
+            mError = true;
+    }
+    return *this;
+}
+
+JsonReader& JsonReader::EndArray() {
+    if (!mError) {
+        if (CURRENT.IsArray() && TOP.state == JsonReaderStackItem::Closed)
+            Next();
+        else
+            mError = true;
+    }
+    return *this;
+}
+
+JsonReader& JsonReader::operator&(bool& b) {
+    if (!mError) {
+        if (CURRENT.IsBool()) {
+            b = CURRENT.GetBool();
+            Next();
+        }
+        else
+            mError = true;
+    }
+    return *this;
+}
+
+JsonReader& JsonReader::operator&(unsigned& u) {
+    if (!mError) {
+        if (CURRENT.IsUint()) {
+            u = CURRENT.GetUint();
+            Next();
+        }
+        else
+            mError = true;
+    }
+    return *this;
+}
+
+JsonReader& JsonReader::operator&(int& i) {
+    if (!mError) {
+        if (CURRENT.IsInt()) {
+            i = CURRENT.GetInt();
+            Next();
+        }
+        else
+            mError = true;
+    }
+    return *this;
+}
+
+JsonReader& JsonReader::operator&(double& d) {
+    if (!mError) {
+        if (CURRENT.IsNumber()) {
+            d = CURRENT.GetDouble();
+            Next();
+        }
+        else
+            mError = true;
+    }
+    return *this;
+}
+
+JsonReader& JsonReader::operator&(std::string& s) {
+    if (!mError) {
+        if (CURRENT.IsString()) {
+            s = CURRENT.GetString();
+            Next();
+        }
+        else
+            mError = true;
+    }
+    return *this;
+}
+
+JsonReader& JsonReader::SetNull() {
+    // This function is for JsonWriter only.
+    mError = true;
+    return *this;
+}
+
+void JsonReader::Next() {
+    if (!mError) {
+        assert(!STACK->empty());
+        STACK->pop();
+
+        if (!STACK->empty() && CURRENT.IsArray()) {
+            if (TOP.state == JsonReaderStackItem::Started) { // Otherwise means reading array item pass end
+                if (TOP.index < CURRENT.Size() - 1) {
+                    const Value* value = &CURRENT[++TOP.index];
+                    STACK->push(JsonReaderStackItem(value, JsonReaderStackItem::BeforeStart));
+                }
+                else
+                    TOP.state = JsonReaderStackItem::Closed;
+            }
+            else
+                mError = true;
+        }
+    }
+}
+
+#undef DOCUMENT
+#undef STACK
+#undef TOP
+#undef CURRENT
+
+////////////////////////////////////////////////////////////////////////////////
+// JsonWriter
+
+#define WRITER reinterpret_cast<PrettyWriter<StringBuffer>*>(mWriter)
+#define STREAM reinterpret_cast<StringBuffer*>(mStream)
+
+JsonWriter::JsonWriter() : mWriter(), mStream() {
+    mStream = new StringBuffer;
+    mWriter = new PrettyWriter<StringBuffer>(*STREAM);
+}
+
+JsonWriter::~JsonWriter() { 
+    delete WRITER;
+    delete STREAM;
+}
+
+const char* JsonWriter::GetString() const {
+    return STREAM->GetString();
+}
+
+JsonWriter& JsonWriter::StartObject() {
+    WRITER->StartObject();
+    return *this;
+}
+
+JsonWriter& JsonWriter::EndObject() {
+    WRITER->EndObject();
+    return *this;
+}
+
+JsonWriter& JsonWriter::Member(const char* name) {
+    WRITER->String(name, static_cast<SizeType>(strlen(name)));
+    return *this;
+}
+
+bool JsonWriter::HasMember(const char*) const {
+    // This function is for JsonReader only.
+    assert(false);
+    return false;
+}
+
+JsonWriter& JsonWriter::StartArray(size_t*) {
+    WRITER->StartArray();   
+    return *this;
+}
+
+JsonWriter& JsonWriter::EndArray() {
+    WRITER->EndArray();
+    return *this;
+}
+
+JsonWriter& JsonWriter::operator&(bool& b) {
+    WRITER->Bool(b);
+    return *this;
+}
+
+JsonWriter& JsonWriter::operator&(unsigned& u) {
+    WRITER->Uint(u);
+    return *this;
+}
+
+JsonWriter& JsonWriter::operator&(int& i) {
+    WRITER->Int(i);
+    return *this;
+}
+
+JsonWriter& JsonWriter::operator&(double& d) {
+    WRITER->Double(d);
+    return *this;
+}
+
+JsonWriter& JsonWriter::operator&(std::string& s) {
+    WRITER->String(s.c_str(), static_cast<SizeType>(s.size()));
+    return *this;
+}
+
+JsonWriter& JsonWriter::SetNull() {
+    WRITER->Null();
+    return *this;
+}
+
+#undef STREAM
+#undef WRITER
diff --git a/example/archiver/archiver.h b/example/archiver/archiver.h
new file mode 100644
index 0000000..c7e74f0
--- /dev/null
+++ b/example/archiver/archiver.h
@@ -0,0 +1,139 @@
+#ifndef ARCHIVER_H_
+#define ARCHIVER_H_
+
+#include <cstddef>
+#include <string>
+
+/**
+\class Archiver
+\brief Archiver concept
+
+Archiver can be a reader or writer for serialization or deserialization respectively.
+
+class Archiver {
+public:
+    /// \returns true if the archiver is in normal state. false if it has errors.
+    operator bool() const;
+
+    /// Starts an object
+    Archiver& StartObject();
+    
+    /// After calling StartObject(), assign a member with a name
+    Archiver& Member(const char* name);
+
+    /// After calling StartObject(), check if a member presents
+    bool HasMember(const char* name) const;
+
+    /// Ends an object
+    Archiver& EndObject();
+
+    /// Starts an array
+    /// \param size If Archiver::IsReader is true, the size of array is written.
+    Archiver& StartArray(size_t* size = 0);
+
+    /// Ends an array
+    Archiver& EndArray();
+
+    /// Read/Write primitive types.
+    Archiver& operator&(bool& b);
+    Archiver& operator&(unsigned& u);
+    Archiver& operator&(int& i);
+    Archiver& operator&(double& d);
+    Archiver& operator&(std::string& s);
+
+    /// Write primitive types.
+    Archiver& SetNull();
+
+    //! Whether it is a reader.
+    static const bool IsReader;
+
+    //! Whether it is a writer.
+    static const bool IsWriter;
+};
+*/
+
+/// Represents a JSON reader which implements Archiver concept.
+class JsonReader {
+public:
+    /// Constructor.
+    /**
+        \param json A non-const source json string for in-situ parsing.
+        \note in-situ means the source JSON string will be modified after parsing.
+    */
+    JsonReader(const char* json);
+
+    /// Destructor.
+    ~JsonReader();
+
+    // Archive concept
+
+    operator bool() const { return !mError; }
+
+    JsonReader& StartObject();
+    JsonReader& Member(const char* name);
+    bool HasMember(const char* name) const;
+    JsonReader& EndObject();
+
+    JsonReader& StartArray(size_t* size = nullptr);
+    JsonReader& EndArray();
+
+    JsonReader& operator&(bool& b);
+    JsonReader& operator&(unsigned& u);
+    JsonReader& operator&(int& i);
+    JsonReader& operator&(double& d);
+    JsonReader& operator&(std::string& s);
+
+    JsonReader& SetNull();
+
+    static const bool IsReader = true;
+    static const bool IsWriter = !IsReader;
+
+private:
+    void Next();
+
+    // PIMPL
+    void* mDocument;              ///< DOM result of parsing.
+    void* mStack;                 ///< Stack for iterating the DOM
+    bool mError;                  ///< Whether an error is occured.
+};
+
+class JsonWriter {
+public:
+    /// Constructor.
+    JsonWriter();
+
+    /// Destructor.
+    ~JsonWriter();
+
+    /// Obtains the serialized JSON string.
+    const char* GetString() const;
+
+    // Archive concept
+
+    operator bool() const { return true; }
+
+    JsonWriter& StartObject();
+    JsonWriter& Member(const char* name);
+    bool HasMember(const char* name) const;
+    JsonWriter& EndObject();
+
+    JsonWriter& StartArray(size_t* size = 0);
+    JsonWriter& EndArray();
+
+    JsonWriter& operator&(bool& b);
+    JsonWriter& operator&(unsigned& u);
+    JsonWriter& operator&(int& i);
+    JsonWriter& operator&(double& d);
+    JsonWriter& operator&(std::string& s);
+    JsonWriter& SetNull();
+
+    static const bool IsReader = false;
+    static const bool IsWriter = !IsReader;
+
+private:
+    // PIMPL idiom
+    void* mWriter;      ///< JSON writer.
+    void* mStream;      ///< Stream buffer.
+};
+
+#endif // ARCHIVER_H__
diff --git a/example/archiver/archivertest.cpp b/example/archiver/archivertest.cpp
new file mode 100644
index 0000000..788db36
--- /dev/null
+++ b/example/archiver/archivertest.cpp
@@ -0,0 +1,281 @@
+#include "archiver.h"
+#include <iostream>
+#include <vector>
+
+//////////////////////////////////////////////////////////////////////////////
+// Test1: simple object
+
+struct Student {
+    std::string name;
+    unsigned age;
+    double height;
+    bool canSwim;
+};
+
+template <typename Archiver>
+Archiver& operator&(Archiver& ar, Student& s) {
+    ar.StartObject();
+    ar.Member("name") & s.name;
+    ar.Member("age") & s.age;
+    ar.Member("height") & s.height;
+    ar.Member("canSwim") & s.canSwim;
+    return ar.EndObject();
+}
+
+std::ostream& operator<<(std::ostream& os, const Student& s) {
+    return os << s.name << " " << s.age << " " << s.height << " " << s.canSwim;
+}
+
+void test1() {
+    std::string json;
+
+    // Serialize
+    {
+        Student s = { "Lua", 9, 150.5, true };
+
+        JsonWriter writer;
+        writer & s;
+        json = writer.GetString();
+        std::cout << json << std::endl;
+    }
+
+    // Deserialize
+    {
+        Student s;
+        JsonReader reader(json.c_str());
+        reader & s;
+        std::cout << s << std::endl;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Test2: std::vector <=> JSON array
+// 
+// You can map a JSON array to other data structures as well
+
+struct Group {
+    std::string groupName;
+    std::vector<Student> students;
+};
+
+template <typename Archiver>
+Archiver& operator&(Archiver& ar, Group& g) {
+    ar.StartObject();
+    
+    ar.Member("groupName");
+    ar & g.groupName;
+
+    ar.Member("students");
+    size_t studentCount = g.students.size();
+    ar.StartArray(&studentCount);
+    if (ar.IsReader)
+        g.students.resize(studentCount);
+    for (size_t i = 0; i < studentCount; i++)
+        ar & g.students[i];
+    ar.EndArray();
+
+    return ar.EndObject();
+}
+
+std::ostream& operator<<(std::ostream& os, const Group& g) {
+    os << g.groupName << std::endl;
+    for (std::vector<Student>::const_iterator itr = g.students.begin(); itr != g.students.end(); ++itr)
+        os << *itr << std::endl;
+    return os;
+}
+
+void test2() {
+    std::string json;
+
+    // Serialize
+    {
+        Group g;
+        g.groupName = "Rainbow";
+
+        Student s1 = { "Lua", 9, 150.5, true };
+        Student s2 = { "Mio", 7, 120.0, false };
+        g.students.push_back(s1);
+        g.students.push_back(s2);
+
+        JsonWriter writer;
+        writer & g;
+        json = writer.GetString();
+        std::cout << json << std::endl;
+    }
+
+    // Deserialize
+    {
+        Group g;
+        JsonReader reader(json.c_str());
+        reader & g;
+        std::cout << g << std::endl;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Test3: polymorphism & friend
+//
+// Note that friendship is not necessary but make things simpler.
+
+class Shape {
+public:
+    virtual ~Shape() {}
+    virtual const char* GetType() const = 0;
+    virtual void Print(std::ostream& os) const = 0;
+
+protected:
+    Shape() {}
+    Shape(double x, double y) : x_(x), y_(y) {}
+
+    template <typename Archiver>
+    friend Archiver& operator&(Archiver& ar, Shape& s);
+
+    double x_, y_;
+};
+
+template <typename Archiver>
+Archiver& operator&(Archiver& ar, Shape& s) {
+    ar.Member("x") & s.x_;
+    ar.Member("y") & s.y_;
+    return ar;
+}
+
+class Circle : public Shape {
+public:
+    Circle() {}
+    Circle(double x, double y, double radius) : Shape(x, y), radius_(radius) {}
+    ~Circle() {}
+
+    const char* GetType() const { return "Circle"; }
+
+    void Print(std::ostream& os) const {
+        os << "Circle (" << x_ << ", " << y_ << ")" << " radius = " << radius_;
+    }
+
+private:
+    template <typename Archiver>
+    friend Archiver& operator&(Archiver& ar, Circle& c);
+
+    double radius_;
+};
+
+template <typename Archiver>
+Archiver& operator&(Archiver& ar, Circle& c) {
+    ar & static_cast<Shape&>(c);
+    ar.Member("radius") & c.radius_;
+    return ar;
+}
+
+class Box : public Shape {
+public:
+    Box() {}
+    Box(double x, double y, double width, double height) : Shape(x, y), width_(width), height_(height) {}
+    ~Box() {}
+
+    const char* GetType() const { return "Box"; }
+
+    void Print(std::ostream& os) const {
+        os << "Box (" << x_ << ", " << y_ << ")" << " width = " << width_ << " height = " << height_;
+    }
+
+private:
+    template <typename Archiver>
+    friend Archiver& operator&(Archiver& ar, Box& b);
+
+    double width_, height_;
+};
+
+template <typename Archiver>
+Archiver& operator&(Archiver& ar, Box& b) {
+    ar & static_cast<Shape&>(b);
+    ar.Member("width") & b.width_;
+    ar.Member("height") & b.height_;
+    return ar;
+}
+
+class Canvas {
+public:
+    Canvas() {}
+    ~Canvas() { Clear(); }
+    
+    void Clear() {
+        for (std::vector<Shape*>::iterator itr = shapes_.begin(); itr != shapes_.end(); ++itr)
+            delete *itr;
+    }
+
+    void AddShape(Shape* shape) { shapes_.push_back(shape); }
+    
+    void Print(std::ostream& os) {
+        for (std::vector<Shape*>::iterator itr = shapes_.begin(); itr != shapes_.end(); ++itr) {
+            (*itr)->Print(os);
+            std::cout << std::endl;
+        }
+    }
+
+private:
+    template <typename Archiver>
+    friend Archiver& operator&(Archiver& ar, Canvas& c);
+
+    std::vector<Shape*> shapes_;
+};
+
+template <typename Archiver>
+Archiver& operator&(Archiver& ar, Shape*& shape) {
+    std::string type = ar.IsReader ? "" : shape->GetType();
+    ar.StartObject();
+    ar.Member("type") & type;
+    if (type == "Circle") {
+        if (ar.IsReader) shape = new Circle;
+        ar & static_cast<Circle&>(*shape);
+    }
+    else if (type == "Box") {
+        if (ar.IsReader) shape = new Box;
+        ar & static_cast<Box&>(*shape);
+    }
+    return ar.EndObject();
+}
+
+template <typename Archiver>
+Archiver& operator&(Archiver& ar, Canvas& c) {
+    size_t shapeCount = c.shapes_.size();
+    ar.StartArray(&shapeCount);
+    if (ar.IsReader) {
+        c.Clear();
+        c.shapes_.resize(shapeCount);
+    }
+    for (size_t i = 0; i < shapeCount; i++)
+        ar & c.shapes_[i];
+    return ar.EndArray();
+}
+
+void test3() {
+    std::string json;
+
+    // Serialize
+    {
+        Canvas c;
+        c.AddShape(new Circle(1.0, 2.0, 3.0));
+        c.AddShape(new Box(4.0, 5.0, 6.0, 7.0));
+
+        JsonWriter writer;
+        writer & c;
+        json = writer.GetString();
+        std::cout << json << std::endl;
+    }
+
+    // Deserialize
+    {
+        Canvas c;
+        JsonReader reader(json.c_str());
+        reader & c;
+        c.Print(std::cout);
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+int main() {
+    test1();
+    test2();
+    test3();
+}