#include #include #include "jsonElement.hh" #include "streamConsumer.hh" StreamConsumer::StreamConsumer(std::istream &s): stream(s), root(nullptr) { } StreamConsumer::~StreamConsumer() { if (root) delete root; for (Warning i : warnings) { delete i.getPtrUnsafe(); } } StreamConsumer *StreamConsumer::withConfig(const AParams *p) { params = p; return this; } StreamConsumer *StreamConsumer::read() { if (root) return this; root = readNext(nullptr); return this; } JSonElement *StreamConsumer::readNext(JSonContainer *parent) { std::string buf; JSonElement *root = consumeToken(parent, buf); if (root == nullptr) { if (buf == "{") { return readObject(parent); } else if (buf == "[") { return readArray(parent); } else return nullptr; } return root; } JSonElement * const StreamConsumer::getRoot() const { return root; } JSonObject *StreamConsumer::readObject(JSonContainer *parent) { JSonElement *keyObj; JSonObject *result = nullptr; std::string buf; do { keyObj = consumeToken(result, buf); if (result == nullptr && keyObj == nullptr && buf == "}") return new JSonObject(parent); JSonPrimitive *key = dynamic_cast *>(keyObj); if (key == nullptr) throw JSonObject::NotAKeyException(stream.tellg(), history); if (consumeToken(parent, buf) != nullptr || buf != ":") throw JsonUnexpectedException(':', stream.tellg(), history); if (result == nullptr) result = new JSonObject(parent); else if (result->contains(key->getValue())) { if (params->isStrict()) throw JSonObject::DoubleKeyException(stream.tellg(), key->getValue(), history); else // add Warning, new key erase previous one { result->erase(key->getValue()); warnings.push_back(Warning(new JSonObject::DoubleKeyException(stream.tellg(), key->getValue(), history))); } } JSonElement *child = readNext(result); result->push(key->getValue(), child); delete keyObj; keyObj = consumeToken(result, buf); } while (!keyObj && buf != "}"); return result; } JSonArray *StreamConsumer::readArray(JSonContainer *parent) { JSonArray *result = nullptr; std::string buf; JSonElement *child = consumeToken(parent, buf); if (child == nullptr && buf == "]") return new JSonArray(parent); //Empty object do { if (child == nullptr && buf == "[") child = readArray(nullptr); if (child == nullptr && buf == "{") child = readObject(nullptr); if (child == nullptr) throw JsonNotJsonException(stream.tellg(), history); if (result == nullptr) result = new JSonArray(parent); child->setParent(result); result->push_back(child); child = consumeToken(result, buf); if (child != nullptr) throw JsonUnexpectedException(']', stream.tellg(), history); else if (buf == "]") break; else if (buf != ",") throw JsonUnexpectedException(']', stream.tellg(), history); child = consumeToken(result, buf); } while (true); return result; } JSonElement *StreamConsumer::consumeToken(JSonContainer *parent, std::string &buf) { bool escaped = false; bool inString = false; bool inBool = false; bool inNumber = false; bool numberIsDouble = false; while (stream.good()) { char c = stream.get(); history.put(c); if (inString) { if (!escaped) { if (c == '"') return new JSonPrimitive(parent, buf); else if (c == '\\') escaped = true; else buf += c; } else { if (c == '\\' || c == '"') { buf += c; escaped = false; } else if (c == 'u') { if (params && params->isIgnoringUnicode()) { buf += "\\u"; escaped = false; } else { char unicodeBuf[4]; stream.read(unicodeBuf, 4); std::streamsize gcount = stream.gcount(); history.put(unicodeBuf, gcount); if (gcount != 4) break; try { appendUnicode(unicodeBuf, buf); } catch (std::invalid_argument &e) { throw JsonHexvalueException(e.what(), stream.tellg(), history); } escaped = false; } } else throw JsonEscapedException(c, stream.tellg(), history); } } else if (inBool) { if (c == 'a' || c == 'e' || c == 'l' || c == 'r' || c == 's' || c == 'u') buf += c; else if (buf == "true") { history.pop_back(); stream.unget(); return new JSonPrimitive(parent, true); } else if (buf == "false") { history.pop_back(); stream.unget(); return new JSonPrimitive(parent, false); } else if (ignoreChar(c)) ; else throw JsonFormatException(stream.tellg(), history); } else if (inNumber) { if (c >= '0' && c <= '9') buf += c; else if (c == '.' && !numberIsDouble) { numberIsDouble = true; buf += c; } else { history.pop_back(); stream.unget(); if (numberIsDouble) { try { return new JSonPrimitive(parent, atof(buf.c_str())); } catch (std::runtime_error &e) { throw JsonFormatException(stream.tellg(), history); } } try { return new JSonPrimitive(parent, std::stoi(buf)); } catch(std::out_of_range e) { return new JSonPrimitive(parent, std::stol(buf)); } } } else { //!InString, !inbool if (c == '"') { buf = ""; inString = true; } else if (c == 't' || c == 'f') { buf = c; inBool = true; } else if (c == '{' || c == '[' || c == '}' || c == ']' || c == ':' || c == ',') { buf = c; return nullptr; } else if ((c >= '0' && c <= '9') || c == '.' || c == '-') { buf = c; inNumber = true; } else if (!ignoreChar(c)) throw JsonFormatException(stream.tellg(), history); } } buf = ""; return nullptr; } static unsigned char hexbyte(const char c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'A' && c <= 'F') return c - 'A' + 10; if (c >= 'a' && c <= 'f') return c - 'a' + 10; throw std::invalid_argument(JsonHexvalueException::msg(c)); } static unsigned char hexbyte(const char str[2]) { unsigned char result = 0; result = (hexbyte(*str) <<4) + hexbyte(str[1]); return result; } void StreamConsumer::appendUnicode(const char unicode[4], std::string &buf) { unsigned short uni = (hexbyte(unicode) <<8) + hexbyte(unicode+2); char test[5]; bzero(test, sizeof(*test) *5); snprintf(test, 4, "%lc", uni); buf += test; } bool StreamConsumer::ignoreChar(char c) const noexcept { return (c <= 32 || c >= 127 || c == '\n'); } const std::list &StreamConsumer::getMessages() const { return warnings; }