From 37c977af024966ea2cde01d843f8ec6f60c54308 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 16 Jun 2014 19:12:21 -0700 Subject: [PATCH] Tests working for reading JSON. --- libraries/metavoxels/src/Bitstream.cpp | 208 +++++++++++++++++++++--- libraries/metavoxels/src/Bitstream.h | 6 +- tests/metavoxels/src/MetavoxelTests.cpp | 60 ++++++- tools/mtc/src/main.cpp | 27 ++- 4 files changed, 260 insertions(+), 41 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 8f880a1803..86f508ca11 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -1178,7 +1178,9 @@ Bitstream& Bitstream::operator>(ObjectStreamerPointer& streamer) { } for (int i = 0; i < localProperties.size(); i++) { const StreamerPropertyPair& property = properties.at(i); - if (localProperties.at(i).first != property.first || property.second.propertyIndex() != i) { + const StreamerPropertyPair& localProperty = localProperties.at(i); + if (property.first != localProperty.first || + property.second.propertyIndex() != localProperty.second.propertyIndex()) { streamer = ObjectStreamerPointer(new MappedObjectStreamer(metaObject, properties)); return *this; } @@ -1866,7 +1868,6 @@ void JSONWriter::addSharedObject(const SharedObjectPointer& object) { QJsonObject sharedObject; sharedObject.insert("id", object->getID()); - sharedObject.insert("originID", object->getOriginID()); sharedObject.insert("data", getData(static_cast(object.data()))); _sharedObjects.replace(index, sharedObject); } @@ -1887,17 +1888,121 @@ JSONReader::JSONReader(const QJsonDocument& document, Bitstream::GenericsMode ge for (int i = types.size() - 1; i >= 0; i--) { QJsonObject type = types.at(i).toObject(); QString name = type.value("name").toString(); + QByteArray latinName = name.toLatin1(); + const TypeStreamer* baseStreamer = Bitstream::getTypeStreamers().value(QMetaType::type(latinName)); + if (!baseStreamer) { + baseStreamer = Bitstream::getEnumStreamersByName().value(latinName); + } + if (!baseStreamer && genericsMode == Bitstream::NO_GENERICS) { + continue; + } QString category = type.value("category").toString(); + if (!baseStreamer || genericsMode == Bitstream::ALL_GENERICS) { + TypeStreamerPointer streamer; + if (category == "ENUM") { + QVector values; + int highestValue = 0; + foreach (const QJsonValue& value, type.value("values").toArray()) { + QJsonObject object = value.toObject(); + int value = object.value("value").toInt(); + highestValue = qMax(value, highestValue); + values.append(NameIntPair(object.value("key").toString().toLatin1(), value)); + } + streamer = TypeStreamerPointer(new GenericEnumTypeStreamer(latinName, + values, getBitsForHighestValue(highestValue), QByteArray())); + + } else if (category == "STREAMABLE") { + QVector fields; + foreach (const QJsonValue& field, type.value("fields").toArray()) { + QJsonObject object = field.toObject(); + fields.append(StreamerNamePair(getTypeStreamer(object.value("type").toString()), + object.value("name").toString().toLatin1())); + } + streamer = TypeStreamerPointer(new GenericStreamableTypeStreamer(latinName, + fields, QByteArray())); + + } else if (category == "LIST") { + streamer = TypeStreamerPointer(new GenericListTypeStreamer(latinName, + getTypeStreamer(type.value("valueType").toString()))); + + } else if (category == "SET") { + streamer = TypeStreamerPointer(new GenericSetTypeStreamer(latinName, + getTypeStreamer(type.value("valueType").toString()))); + + } else if (category == "MAP") { + streamer = TypeStreamerPointer(new GenericMapTypeStreamer(latinName, + getTypeStreamer(type.value("keyType").toString()), + getTypeStreamer(type.value("valueType").toString()))); + } + _typeStreamers.insert(name, streamer); + static_cast(streamer.data())->_weakSelf = streamer; + continue; + } if (category == "ENUM") { - + QHash mappings; + int highestValue = 0; + QMetaEnum metaEnum = baseStreamer->getMetaEnum(); + QJsonArray array = type.value("values").toArray(); + bool matches = (array.size() == metaEnum.keyCount()); + foreach (const QJsonValue& value, array) { + QJsonObject object = value.toObject(); + int value = object.value("value").toInt(); + highestValue = qMax(value, highestValue); + int mapping = metaEnum.keyToValue(object.value("key").toString().toLatin1()); + if (mapping != -1) { + mappings.insert(value, mapping); + } + matches &= (value == mapping); + } + if (matches) { + _typeStreamers.insert(name, baseStreamer->getSelf()); + } else { + _typeStreamers.insert(name, TypeStreamerPointer(new MappedEnumTypeStreamer(baseStreamer, + getBitsForHighestValue(highestValue), mappings))); + } } else if (category == "STREAMABLE") { - + QVector fields; + QJsonArray array = type.value("fields").toArray(); + const QVector& metaFields = baseStreamer->getMetaFields(); + bool matches = (array.size() == metaFields.size()); + for (int j = 0; j < array.size(); j++) { + QJsonObject object = array.at(j).toObject(); + TypeStreamerPointer streamer = getTypeStreamer(object.value("type").toString()); + int index = baseStreamer->getFieldIndex(object.value("name").toString().toLatin1()); + fields.append(StreamerIndexPair(streamer, index)); + matches &= (index == j && streamer == metaFields.at(j).getStreamer()); + } + if (matches) { + _typeStreamers.insert(name, baseStreamer->getSelf()); + } else { + _typeStreamers.insert(name, TypeStreamerPointer(new MappedStreamableTypeStreamer(baseStreamer, fields))); + } } else if (category == "LIST") { - + TypeStreamerPointer valueStreamer = getTypeStreamer(type.value("valueType").toString()); + if (valueStreamer == baseStreamer->getValueStreamer()) { + _typeStreamers.insert(name, baseStreamer->getSelf()); + + } else { + _typeStreamers.insert(name, TypeStreamerPointer(new MappedListTypeStreamer(baseStreamer, valueStreamer))); + } } else if (category == "SET") { - + TypeStreamerPointer valueStreamer = getTypeStreamer(type.value("valueType").toString()); + if (valueStreamer == baseStreamer->getValueStreamer()) { + _typeStreamers.insert(name, baseStreamer->getSelf()); + + } else { + _typeStreamers.insert(name, TypeStreamerPointer(new MappedSetTypeStreamer(baseStreamer, valueStreamer))); + } } else if (category == "MAP") { - + TypeStreamerPointer keyStreamer = getTypeStreamer(type.value("keyType").toString()); + TypeStreamerPointer valueStreamer = getTypeStreamer(type.value("valueType").toString()); + if (keyStreamer == baseStreamer->getKeyStreamer() && valueStreamer == baseStreamer->getValueStreamer()) { + _typeStreamers.insert(name, baseStreamer->getSelf()); + + } else { + _typeStreamers.insert(name, TypeStreamerPointer(new MappedMapTypeStreamer( + baseStreamer, keyStreamer, valueStreamer))); + } } } @@ -1905,11 +2010,45 @@ JSONReader::JSONReader(const QJsonDocument& document, Bitstream::GenericsMode ge for (int i = classes.size() - 1; i >= 0; i--) { QJsonObject clazz = classes.at(i).toObject(); QString name = clazz.value("name").toString(); - QJsonArray properties = clazz.value("properties").toArray(); - foreach (const QJsonValue& property, properties) { - QJsonObject object = property.toObject(); - object.value("type"); - object.value("name"); + QByteArray latinName = name.toLatin1(); + const ObjectStreamer* baseStreamer = Bitstream::getObjectStreamers().value( + Bitstream::getMetaObjects().value(latinName)); + if (!baseStreamer && genericsMode == Bitstream::NO_GENERICS) { + continue; + } + if (!baseStreamer || genericsMode == Bitstream::ALL_GENERICS) { + QVector properties; + foreach (const QJsonValue& property, clazz.value("properties").toArray()) { + QJsonObject object = property.toObject(); + properties.append(StreamerNamePair(getTypeStreamer(object.value("type").toString()), + object.value("name").toString().toLatin1())); + } + ObjectStreamerPointer streamer = ObjectStreamerPointer(new GenericObjectStreamer( + latinName, properties, QByteArray())); + _objectStreamers.insert(name, streamer); + static_cast(streamer.data())->_weakSelf = streamer; + continue; + } + const QMetaObject* metaObject = baseStreamer->getMetaObject(); + const QVector& baseProperties = baseStreamer->getProperties(); + QVector properties; + QJsonArray propertyArray = clazz.value("properties").toArray(); + bool matches = (baseProperties.size() == propertyArray.size()); + for (int j = 0; j < propertyArray.size(); j++) { + QJsonObject object = propertyArray.at(j).toObject(); + TypeStreamerPointer typeStreamer = getTypeStreamer(object.value("type").toString()); + QMetaProperty metaProperty = metaObject->property(metaObject->indexOfProperty( + object.value("name").toString().toLatin1())); + properties.append(StreamerPropertyPair(typeStreamer, metaProperty)); + + const StreamerPropertyPair& baseProperty = baseProperties.at(i); + matches &= (typeStreamer == baseProperty.first && + metaProperty.propertyIndex() == baseProperty.second.propertyIndex()); + } + if (matches) { + _objectStreamers.insert(name, baseStreamer->getSelf()); + } else { + _objectStreamers.insert(name, ObjectStreamerPointer(new MappedObjectStreamer(metaObject, properties))); } } @@ -1917,9 +2056,11 @@ JSONReader::JSONReader(const QJsonDocument& document, Bitstream::GenericsMode ge for (int i = objects.size() - 1; i >= 0; i--) { QJsonObject object = objects.at(i).toObject(); int id = object.value("id").toInt(); - int originID = object.value("originID").toInt(); - QJsonObject data = object.value("data").toObject(); - + QObject* qObject; + putData(object.value("data"), qObject); + if (qObject) { + _sharedObjects.insert(id, static_cast(qObject)); + } } _contents = top.value("contents").toArray(); @@ -2051,16 +2192,12 @@ void JSONReader::putData(const QJsonValue& data, const QMetaObject*& value) { void JSONReader::putData(const QJsonValue& data, QVariant& value) { QJsonObject object = data.toObject(); QString type = object.value("type").toString(); - const TypeStreamer* streamer = _typeStreamers.value(type).data(); - if (!streamer) { - streamer = Bitstream::getTypeStreamers().value(QMetaType::type(type.toLatin1())); - if (!streamer) { - qWarning() << "Unknown type:" << type; - value = QVariant(); - return; - } + TypeStreamerPointer streamer = getTypeStreamer(type); + if (streamer) { + streamer->putJSONVariantData(*this, object.value("value"), value); + } else { + value = QVariant(); } - streamer->putJSONData(*this, object.value("value"), value); } void JSONReader::putData(const QJsonValue& data, SharedObjectPointer& value) { @@ -2073,6 +2210,19 @@ void JSONReader::putData(const QJsonValue& data, QObject*& value) { value = streamer ? streamer->putJSONData(*this, object) : NULL; } +TypeStreamerPointer JSONReader::getTypeStreamer(const QString& name) const { + TypeStreamerPointer streamer = _typeStreamers.value(name); + if (!streamer) { + const TypeStreamer* defaultStreamer = Bitstream::getTypeStreamers().value(QMetaType::type(name.toLatin1())); + if (defaultStreamer) { + streamer = defaultStreamer->getSelf(); + } else { + qWarning() << "Unknown type:" << name; + } + } + return streamer; +} + ObjectStreamer::ObjectStreamer(const QMetaObject* metaObject) : _metaObject(metaObject) { } @@ -2438,6 +2588,10 @@ void TypeStreamer::putJSONData(JSONReader& reader, const QJsonValue& data, QVari value = QVariant(); } +void TypeStreamer::putJSONVariantData(JSONReader& reader, const QJsonValue& data, QVariant& value) const { + putJSONData(reader, data, value); +} + bool TypeStreamer::equal(const QVariant& first, const QVariant& second) const { return first == second; } @@ -2750,6 +2904,12 @@ const char* GenericTypeStreamer::getName() const { return _name.constData(); } +void GenericTypeStreamer::putJSONVariantData(JSONReader& reader, const QJsonValue& data, QVariant& value) const { + QVariant containedValue; + putJSONData(reader, data, containedValue); + value = QVariant::fromValue(GenericValue(_weakSelf, containedValue)); +} + QVariant GenericTypeStreamer::readVariant(Bitstream& in) const { return QVariant::fromValue(GenericValue(_weakSelf, read(in))); } diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 0cad33acea..776a362717 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -893,7 +893,7 @@ public: template JSONReader& operator>>(T& value) { putData(*_contentsIterator++, value); return *this; } - TypeStreamerPointer getTypeStreamer(const QString& name) const { return _typeStreamers.value(name); } + TypeStreamerPointer getTypeStreamer(const QString& name) const; ObjectStreamerPointer getObjectStreamer(const QString& name) const { return _objectStreamers.value(name); } SharedObjectPointer getSharedObject(int id) const { return _sharedObjects.value(id); } @@ -1020,6 +1020,7 @@ public: private: friend class Bitstream; + friend class JSONReader; QByteArray _name; WeakObjectStreamerPointer _weakSelf; @@ -1107,6 +1108,7 @@ public: virtual QJsonValue getJSONData(JSONWriter& writer, const QVariant& value) const; virtual QJsonValue getJSONVariantData(JSONWriter& writer, const QVariant& value) const; virtual void putJSONData(JSONReader& reader, const QJsonValue& data, QVariant& value) const; + virtual void putJSONVariantData(JSONReader& reader, const QJsonValue& data, QVariant& value) const; virtual bool equal(const QVariant& first, const QVariant& second) const; @@ -1237,11 +1239,13 @@ public: GenericTypeStreamer(const QByteArray& name); virtual const char* getName() const; + virtual void putJSONVariantData(JSONReader& reader, const QJsonValue& data, QVariant& value) const; virtual QVariant readVariant(Bitstream& in) const; protected: friend class Bitstream; + friend class JSONReader; QByteArray _name; WeakTypeStreamerPointer _weakSelf; diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 618178383f..4a3010caf4 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -252,8 +252,64 @@ static bool testSerialization(Bitstream::MetadataType metadataType) { jsonWriter << testObjectReadA; jsonWriter << testObjectReadB; jsonWriter << messageRead; - qDebug() << jsonWriter.getDocument().toJson(); - qDebug(); + jsonWriter << endRead; + QByteArray encodedJson = jsonWriter.getDocument().toJson(); + + // and read from JSON + JSONReader jsonReader(QJsonDocument::fromJson(encodedJson), Bitstream::ALL_GENERICS); + jsonReader >> testObjectReadA; + jsonReader >> testObjectReadB; + jsonReader >> messageRead; + jsonReader >> endRead; + + // reassign the ids + testObjectReadA->setID(testObjectWrittenA->getID()); + testObjectReadA->setOriginID(testObjectWrittenA->getOriginID()); + testObjectReadB->setID(testObjectWrittenB->getID()); + testObjectReadB->setOriginID(testObjectWrittenB->getOriginID()); + + // and back to binary + QByteArray secondCompareArray; + QDataStream secondCompareOutStream(&secondCompareArray, QIODevice::WriteOnly); + Bitstream secondCompareOut(secondCompareOutStream, Bitstream::FULL_METADATA); + secondCompareOut << testObjectReadA; + secondCompareOut << testObjectReadB; + secondCompareOut << messageRead; + secondCompareOut << endRead; + secondCompareOut.flush(); + + if (compareArray != secondCompareArray) { + qDebug() << "Mismatch between written/JSON streams (generics)."; + return true; + } + + // once more, with mapping! + JSONReader secondJSONReader(QJsonDocument::fromJson(encodedJson)); + secondJSONReader >> testObjectReadA; + secondJSONReader >> testObjectReadB; + secondJSONReader >> messageRead; + secondJSONReader >> endRead; + + // reassign the ids + testObjectReadA->setID(testObjectWrittenA->getID()); + testObjectReadA->setOriginID(testObjectWrittenA->getOriginID()); + testObjectReadB->setID(testObjectWrittenB->getID()); + testObjectReadB->setOriginID(testObjectWrittenB->getOriginID()); + + // and back to binary + QByteArray thirdCompareArray; + QDataStream thirdCompareOutStream(&thirdCompareArray, QIODevice::WriteOnly); + Bitstream thirdCompareOut(thirdCompareOutStream, Bitstream::FULL_METADATA); + thirdCompareOut << testObjectReadA; + thirdCompareOut << testObjectReadB; + thirdCompareOut << messageRead; + thirdCompareOut << endRead; + thirdCompareOut.flush(); + + if (compareArray != thirdCompareArray) { + qDebug() << "Mismatch between written/JSON streams (mapped)."; + return true; + } return false; } diff --git a/tools/mtc/src/main.cpp b/tools/mtc/src/main.cpp index 267e45a5a5..dd0ab837a5 100644 --- a/tools/mtc/src/main.cpp +++ b/tools/mtc/src/main.cpp @@ -123,11 +123,6 @@ void generateOutput (QTextStream& out, const QList& streamables) { out << "QHash " << name << "::createFieldIndices() {\n"; out << " QHash indices;\n"; out << " int index = 1;\n"; - foreach (const QString& base, str.clazz.bases) { - out << " foreach (const MetaField& field, " << base << "::getMetaFields()) {\n"; - out << " indices.insert(field.getName(), index++);\n"; - out << " }\n"; - } out << " foreach (const MetaField& field, getMetaFields()) {\n"; out << " indices.insert(field.getName(), index++);\n"; out << " }\n"; @@ -231,15 +226,19 @@ void generateOutput (QTextStream& out, const QList& streamables) { out << "}\n"; out << "template<> void JSONReader::putData(const QJsonValue& data, " << name << "& value) {\n"; - out << " QJsonArray array = data.toArray();\n"; - out << " QJsonArray::const_iterator it = array.constBegin();\n"; - /* foreach (const QString& base, str.clazz.bases) { - out << " foreach (const QJsonValue& element, getData(static_cast(value)).toArray()) {\n"; - out << " array.append(element);\n"; - out << " }\n"; - } */ - foreach (const Field& field, str.fields) { - out << " putData(*it++, value." << field.name << ");\n"; + if (!(str.clazz.bases.isEmpty() && str.fields.isEmpty())) { + out << " QJsonArray array = data.toArray(), subarray;\n"; + out << " QJsonArray::const_iterator it = array.constBegin();\n"; + foreach (const QString& base, str.clazz.bases) { + out << " subarray = QJsonArray();\n"; + out << " for (int i = 0; i < " << base << "::getMetaFields().size(); i++) {\n"; + out << " subarray.append(*it++);\n"; + out << " }\n"; + out << " putData(subarray, static_cast<" << base << "&>(value));\n"; + } + foreach (const Field& field, str.fields) { + out << " putData(*it++, value." << field.name << ");\n"; + } } out << "}\n";