diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index d583901b41..30108d973e 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -50,6 +50,7 @@ #include "OctreeLogging.h" #include "OctreeQueryNode.h" #include "OctreeUtils.h" +#include "OctreeEntitiesFileParser.h" QVector<QString> PERSIST_EXTENSIONS = {"json", "json.gz"}; @@ -827,12 +828,19 @@ bool Octree::readJSONFromStream(uint64_t streamLength, QDataStream& inputStream, jsonBuffer += QByteArray(rawData, got); } + OctreeEntitiesFileParser octreeParser; + octreeParser.setEntitiesString(jsonBuffer); + QVariantMap asMap; + bool parseSuccess = octreeParser.parseEntities(asMap); + + /* QJsonDocument asDocument = QJsonDocument::fromJson(jsonBuffer); if (!marketplaceID.isEmpty()) { asDocument = addMarketplaceIDToDocumentEntities(asDocument, marketplaceID); } QVariant asVariant = asDocument.toVariant(); QVariantMap asMap = asVariant.toMap(); + */ bool success = readFromMap(asMap); delete[] rawData; return success; diff --git a/libraries/octree/src/OctreeEntitiesFileParser.cpp b/libraries/octree/src/OctreeEntitiesFileParser.cpp new file mode 100644 index 0000000000..14380de835 --- /dev/null +++ b/libraries/octree/src/OctreeEntitiesFileParser.cpp @@ -0,0 +1,218 @@ +// +// OctreeEntititesFileParser.cpp +// libraries/octree/src +// +// Created by Simon Walton on Oct 15, 2018. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include <sstream> +#include <QUuid> +#include <QJsonDocument> +#include <QJsonObject> +#include <QVariantList> + +#include "OctreeEntitiesFileParser.h" + +using std::string; + +OctreeEntitiesFileParser::OctreeEntitiesFileParser() { +} + +std::string OctreeEntitiesFileParser::getErrorString() const { + std::ostringstream err; + if (_errorString.size() != 0) { + err << "Error: Line " << _line << ", byte position " << _position << ": " << _errorString; + }; + + return err.str(); +} + +void OctreeEntitiesFileParser::setEntitiesString(const QByteArray& entitiesContents) { + _entitiesContents = entitiesContents; + _entitiesLength = _entitiesContents.length(); + _position = 0; + _line = 1; +} + +bool OctreeEntitiesFileParser::parseEntities(QVariantMap& parsedEntities) { + if (nextToken() != '{') { + _errorString = "Text before start of object"; + return false; + } + + bool gotDataVersion = false; + bool gotEntities = false; + bool gotId = false; + bool gotVersion = false; + + while (!(gotDataVersion && gotEntities && gotId && gotVersion)) { + if (nextToken() != '"') { + _errorString = "Incorrect key string"; + return false; + } + + string key = readString(); + if (key.size() == 0) { + _errorString = "Missing object key"; + return false; + } + + if (nextToken() != ':') { + _errorString = "Ill-formed id/value entry"; + return false; + } + + if (key == "DataVersion") { + if (gotDataVersion) { + _errorString = "Duplicate DataVersion entries"; + return false; + } + + int dataVersionValue = readInteger(); + parsedEntities["DataVersion"] = dataVersionValue; + gotDataVersion = true; + } else if (key == "Entities") { + if (gotEntities) { + _errorString = "Duplicate Entities entries"; + return false; + } + + QVariantList entitiesValue; + if (!readEntitiesArray(entitiesValue)) { + return false; + } + + parsedEntities["Entities"] = entitiesValue; + gotEntities = true; + } else if (key == "Id") { + if (gotId) { + _errorString = "Duplicate Id entries"; + return false; + } + + if (nextToken() != '"') { + _errorString = "Invalid Id value"; + return false; + }; + string idString = readString(); + if (idString.size() == 0) { + _errorString = "Invalid Id string"; + return false; + } + QUuid idValue = QUuid::fromString(QLatin1String(idString.c_str()) ); + if (idValue.isNull()) { + _errorString = "Id value invalid UUID string: " + idString; + return false; + } + + parsedEntities["Id"] = idValue; + gotId = true; + } else if (key == "Version") { + if (gotVersion) { + _errorString = "Duplicate Version entries"; + return false; + } + + int versionValue = readInteger(); + parsedEntities["Version"] = versionValue; + gotVersion = true; + } else { + _errorString = "Unrecognized key name: " + key; + return false; + } + + if (gotDataVersion && gotEntities && gotId && gotVersion) { + break; + } else if (nextToken() != ',') { + _errorString = "Id/value incorrectly terminated"; + return false; + } + } + + if (nextToken() != '}' || nextToken() != -1) { + _errorString = "Ill-formed end of object"; + return false; + } + + return true; +} + +int OctreeEntitiesFileParser::nextToken() { + while (_position < _entitiesLength) { + char c = _entitiesContents[_position++]; + if (c != ' ' && c != '\t' && c != '\n' && c != '\r') { + return c; + } + if (c == '\n') { + ++_line; + } + } + + return -1; +} + +string OctreeEntitiesFileParser::readString() { + string returnString; + while (_position < _entitiesLength) { + char c = _entitiesContents[_position++]; + if (c == '"') { + break; + } else { + returnString.push_back(c); + } + } + + return returnString; +} + +int OctreeEntitiesFileParser::readInteger() { + const char* currentPosition = _entitiesContents.constData() + _position; + int i = atoi(currentPosition); + + int token; + do { + token = nextToken(); + } while (token == '-' || token == '+' || (token >= '0' && token <= '9')); + + --_position; + return i; +} + +bool OctreeEntitiesFileParser::readEntitiesArray(QVariantList& entitiesArray) { + if (nextToken() != '[') { + _errorString = "Entities entry is not an array"; + return false; + } + + while (true) { + QJsonParseError parseError; + QByteArray entitiesJson(_entitiesContents.right(_entitiesLength - _position)); + QJsonDocument entity = QJsonDocument::fromJson(entitiesJson, &parseError); + if (parseError.error != QJsonParseError::GarbageAtEnd) { + _errorString = "Ill-formed entity array"; + return false; + } + int entityLength = parseError.offset; + entitiesJson.truncate(entityLength); + _position += entityLength; + + entity = QJsonDocument::fromJson(entitiesJson, &parseError); + if (parseError.error != QJsonParseError::NoError) { + _errorString = "Entity item parse error"; + return false; + } + entitiesArray.append(entity.object()); + char c = nextToken(); + if (c == ']') { + return true; + } else if (c != ',') { + _errorString = "Entity array item incorrectly terminated"; + return false; + } + } + return true; +} diff --git a/libraries/octree/src/OctreeEntitiesFileParser.h b/libraries/octree/src/OctreeEntitiesFileParser.h new file mode 100644 index 0000000000..562a18e833 --- /dev/null +++ b/libraries/octree/src/OctreeEntitiesFileParser.h @@ -0,0 +1,38 @@ +// +// OctreeEntititesFileParser.h +// libraries/octree/src +// +// Created by Simon Walton on Oct 15, 2018. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_OctreeEntitiesFileParser_h +#define hifi_OctreeEntitiesFileParser_h + +#include <QVariantMap> +#include <QByteArray> + +class OctreeEntitiesFileParser { +public: + OctreeEntitiesFileParser(); + void setEntitiesString(const QByteArray& entitiesContents); + bool parseEntities(QVariantMap& parsedEntities); + std::string getErrorString() const; + +private: + int nextToken(); + std::string readString(); + int readInteger(); + bool readEntitiesArray(QVariantList& entitiesArray); + + QByteArray _entitiesContents; + int _position { 0 }; + int _line { 1 }; + int _entitiesLength { 0 }; + std::string _errorString; +}; + +#endif // hifi_OctreeEntitiesFileParser_h