--- /dev/null
+
+/*******************************************************************************
+
+ Copyright (c) 2009, Charles McGarvey
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*******************************************************************************/
+
+#include <fstream>
+#include <queue>
+
+#include <yajl/yajl_parse.h>
+
+#include "Deserializer.hh"
+#include "Serializable.hh"
+
+
+namespace Mf {
+
+
+class Deserializer::DeserializerImpl
+{
+public:
+ DeserializerImpl(const std::string& filePath, bool comments = false,
+ bool check = false)
+ {
+ std::ifstream* input = new std::ifstream(filePath.c_str());
+ init(*input, true, comments, check);
+ }
+
+ DeserializerImpl(std::istream& input, bool comments = false,
+ bool check = false)
+ {
+ init(input, false, comments, check);
+ }
+
+ ~DeserializerImpl()
+ {
+ while (!parsed.empty())
+ {
+ delete parsed.front();
+ parsed.pop();
+ }
+
+ if (deleteWhenDone)
+ {
+ delete in;
+ }
+ yajl_free(hand);
+ }
+
+ void throwError()
+ {
+ unsigned char* errorStr = yajl_get_error(hand, 0, 0, 0);
+ Deserializer::Exception exception((char*)errorStr);
+ yajl_free_error(hand, errorStr);
+ throw exception;
+ }
+
+
+ static int parsedNull(void* ctx)
+ {
+ ((DeserializerImpl*)ctx)->parsed.push(new SerializableNull);
+ return 1;
+ }
+
+ static int parsedBoolean(void* ctx, int value)
+ {
+ ((DeserializerImpl*)ctx)->parsed.push(new SerializableBoolean(value));
+ return 1;
+ }
+
+ static int parsedInteger(void* ctx, long value)
+ {
+ ((DeserializerImpl*)ctx)->parsed.push(new SerializableInteger(value));
+ return 1;
+ }
+
+ static int parsedFloat(void* ctx, double value)
+ {
+ ((DeserializerImpl*)ctx)->parsed.push(new SerializableReal(value));
+ return 1;
+ }
+
+ static int parsedString(void* ctx, const unsigned char* value,
+ unsigned length)
+ {
+ SerializableString* parsed =
+ new SerializableString(std::string((char*)value, length));
+ ((DeserializerImpl*)ctx)->parsed.push(parsed);
+ return 1;
+ }
+
+ static int parsedBeginMap(void* ctx)
+ {
+ ((DeserializerImpl*)ctx)->parsed.push(new SerializableMap);
+ return 1;
+ }
+
+ static int parsedMapKey(void* ctx, const unsigned char* value,
+ unsigned length)
+ {
+ // same thing as a string
+ return parsedString(ctx, value, length);
+ }
+
+ static int parsedEndMap(void* ctx)
+ {
+ ((DeserializerImpl*)ctx)->parsed.push(0);
+ return 1;
+ }
+
+ static int parsedBeginArray(void* ctx)
+ {
+ ((DeserializerImpl*)ctx)->parsed.push(new SerializableArray);
+ return 1;
+ }
+
+ static int parsedEndArray(void* ctx)
+ {
+ ((DeserializerImpl*)ctx)->parsed.push(0);
+ return 1;
+ }
+
+
+ void parse()
+ {
+ unsigned char buffer[4096];
+
+ yajl_status stat;
+
+ while (parsed.empty() && in->good())
+ {
+ in->read((char*)buffer, sizeof(buffer));
+ unsigned readIn = in->gcount();
+
+ if (readIn > 0)
+ {
+ stat = yajl_parse(hand, buffer, readIn);
+ }
+ else
+ {
+ stat = yajl_parse_complete(hand);
+ }
+
+ if (stat != yajl_status_ok &&
+ stat != yajl_status_insufficient_data)
+ {
+ throwError();
+ }
+ }
+ }
+
+
+ yajl_handle hand;
+
+ std::istream* in;
+ bool deleteWhenDone;
+
+ std::queue<Serializable*> parsed;
+
+private:
+ void init(std::istream& input, bool deleteIn, bool comments, bool check)
+ {
+ // this has to be static because yajl actually does not copy it into its
+ // internal data structures but rather keeps a pointer to this
+ static const yajl_callbacks callbacks =
+ {
+ DeserializerImpl::parsedNull,
+ DeserializerImpl::parsedBoolean,
+ DeserializerImpl::parsedInteger,
+ DeserializerImpl::parsedFloat,
+ 0,
+ DeserializerImpl::parsedString,
+ DeserializerImpl::parsedBeginMap,
+ DeserializerImpl::parsedMapKey,
+ DeserializerImpl::parsedEndMap,
+ DeserializerImpl::parsedBeginArray,
+ DeserializerImpl::parsedEndArray
+ };
+
+ in = &input;
+ deleteWhenDone = deleteIn;
+
+ yajl_parser_config config = {comments, check};
+ hand = yajl_alloc(&callbacks, &config, NULL, this);
+ }
+};
+
+
+Deserializer::Deserializer(const std::string& filePath, bool comments,
+ bool check) :
+ // pass through
+ impl_(new Deserializer::DeserializerImpl(filePath, comments, check)) {}
+
+Deserializer::Deserializer(std::istream& input, bool comments, bool check) :
+ // pass through
+ impl_(new Deserializer::DeserializerImpl(input, comments, check)) {}
+
+
+SerializablePtr Deserializer::deserialize()
+{
+ Serializable* ptr = pullNext();
+ if (ptr)
+ {
+ ptr->deserialize(*this);
+ }
+ return SerializablePtr(ptr);
+}
+
+
+Serializable* Deserializer::pullNext()
+{
+ impl_->parse();
+ if (!impl_->parsed.empty())
+ {
+ Serializable* ptr = impl_->parsed.front();
+ return ptr;
+ }
+ return 0;
+}
+
+void Deserializer::pop()
+{
+ // pass through
+ impl_->parsed.pop();
+}
+
+
+} // namespace Mf
+
+/** vim: set ts=4 sw=4 tw=80: *************************************************/
+