//********************************** Banshee Engine (www.banshee3d.com) **************************************************// //**************** Copyright (c) 2016 Marko Pintera (marko.pintera@gmail.com). All rights reserved. **********************// #include "Serialization/BsManagedSerializableDiff.h" #include "Serialization/BsManagedSerializableObject.h" #include "Serialization/BsManagedSerializableObjectInfo.h" #include "Serialization/BsManagedSerializableField.h" #include "Serialization/BsManagedSerializableArray.h" #include "Serialization/BsManagedSerializableList.h" #include "Serialization/BsManagedSerializableDictionary.h" #include "RTTI/BsManagedSerializableDiffRTTI.h" namespace bs { ManagedSerializableDiff::ModifiedField::ModifiedField(const SPtr& parentType, const SPtr& fieldType, const SPtr& modification) :parentType(parentType), fieldType(fieldType), modification(modification) { } RTTITypeBase* ManagedSerializableDiff::ModifiedField::getRTTIStatic() { return ModifiedFieldRTTI::instance(); } RTTITypeBase* ManagedSerializableDiff::ModifiedField::getRTTI() const { return getRTTIStatic(); } ManagedSerializableDiff::ModifiedArrayEntry::ModifiedArrayEntry(UINT32 idx, const SPtr& modification) :idx(idx), modification(modification) { } RTTITypeBase* ManagedSerializableDiff::ModifiedArrayEntry::getRTTIStatic() { return ModifiedArrayEntryRTTI::instance(); } RTTITypeBase* ManagedSerializableDiff::ModifiedArrayEntry::getRTTI() const { return getRTTIStatic(); } ManagedSerializableDiff::ModifiedDictionaryEntry::ModifiedDictionaryEntry( const SPtr& key, const SPtr& modification) :key(key), modification(modification) { } RTTITypeBase* ManagedSerializableDiff::ModifiedDictionaryEntry::getRTTIStatic() { return ModifiedDictionaryEntryRTTI::instance(); } RTTITypeBase* ManagedSerializableDiff::ModifiedDictionaryEntry::getRTTI() const { return getRTTIStatic(); } ManagedSerializableDiff::Modification::~Modification() { } RTTITypeBase* ManagedSerializableDiff::Modification::getRTTIStatic() { return ModificationRTTI::instance(); } RTTITypeBase* ManagedSerializableDiff::Modification::getRTTI() const { return getRTTIStatic(); } SPtr ManagedSerializableDiff::ModifiedObject::create() { return bs_shared_ptr_new(); } RTTITypeBase* ManagedSerializableDiff::ModifiedObject::getRTTIStatic() { return ModifiedObjectRTTI::instance(); } RTTITypeBase* ManagedSerializableDiff::ModifiedObject::getRTTI() const { return getRTTIStatic(); } SPtr ManagedSerializableDiff::ModifiedArray::create() { return bs_shared_ptr_new(); } RTTITypeBase* ManagedSerializableDiff::ModifiedArray::getRTTIStatic() { return ModifiedArrayRTTI::instance(); } RTTITypeBase* ManagedSerializableDiff::ModifiedArray::getRTTI() const { return getRTTIStatic(); } SPtr ManagedSerializableDiff::ModifiedDictionary::create() { return bs_shared_ptr_new(); } RTTITypeBase* ManagedSerializableDiff::ModifiedDictionary::getRTTIStatic() { return ModifiedDictionaryRTTI::instance(); } RTTITypeBase* ManagedSerializableDiff::ModifiedDictionary::getRTTI() const { return getRTTIStatic(); } ManagedSerializableDiff::ModifiedEntry::ModifiedEntry(const SPtr& value) :value(value) { } SPtr ManagedSerializableDiff::ModifiedEntry::create(const SPtr& value) { return bs_shared_ptr_new(value); } RTTITypeBase* ManagedSerializableDiff::ModifiedEntry::getRTTIStatic() { return ModifiedEntryRTTI::instance(); } RTTITypeBase* ManagedSerializableDiff::ModifiedEntry::getRTTI() const { return getRTTIStatic(); } ManagedSerializableDiff::ManagedSerializableDiff() : mModificationRoot(ModifiedObject::create()) { } ManagedSerializableDiff::~ManagedSerializableDiff() { } SPtr ManagedSerializableDiff::create(const SPtr& oldObj, const SPtr& newObj) { assert(oldObj != nullptr && newObj != nullptr); SPtr oldObjInfo = oldObj->getObjectInfo(); SPtr newObjInfo = newObj->getObjectInfo(); if (!oldObjInfo->mTypeInfo->matches(newObjInfo->mTypeInfo)) return nullptr; SPtr output = bs_shared_ptr_new(); SPtr modifications = output->generateDiff(oldObj, newObj); if (modifications != nullptr) { output->mModificationRoot->entries = modifications->entries; return output; } return nullptr; } SPtr ManagedSerializableDiff::generateDiff (const SPtr& oldObj, const SPtr& newObj) { SPtr output = nullptr; SPtr curObjInfo = newObj->getObjectInfo(); while (curObjInfo != nullptr) { for (auto& field : curObjInfo->mFields) { if (!field.second->isSerializable()) continue; UINT32 fieldTypeId = field.second->mTypeInfo->getTypeId(); SPtr oldData = oldObj->getFieldData(field.second); SPtr newData = newObj->getFieldData(field.second); SPtr newMod = generateDiff(oldData, newData, fieldTypeId); if (newMod != nullptr) { if (output == nullptr) output = ModifiedObject::create(); output->entries.push_back(ModifiedField(curObjInfo->mTypeInfo, field.second, newMod)); } } curObjInfo = curObjInfo->mBaseClass; } return output; } SPtr ManagedSerializableDiff::generateDiff( const SPtr& oldData, const SPtr& newData, UINT32 entryTypeId) { bool isPrimitive = entryTypeId == TID_SerializableTypeInfoPrimitive || entryTypeId == TID_SerializableTypeInfoRef; // It's possible the field data is null if the class structure changed (i.e. new field was added that is not present // in serialized data). Check for this case first to ensure field data is valid for the remainder of the method. if(oldData == nullptr) { if (newData == nullptr) return nullptr; else return ModifiedEntry::create(newData); } else { if (newData == nullptr) return nullptr; } SPtr newMod = nullptr; if (isPrimitive) { if (!oldData->equals(newData)) newMod = ModifiedEntry::create(newData); } else { switch (entryTypeId) { case TID_SerializableTypeInfoObject: { SPtr oldObjData = std::static_pointer_cast(oldData); SPtr newObjData = std::static_pointer_cast(newData); if (oldObjData->value != nullptr && newObjData->value != nullptr) { newMod = generateDiff(oldObjData->value, newObjData->value); } else if (oldObjData->value == nullptr && newObjData->value == nullptr) { // No change } else // We either record null if new value is null, or the entire object if old value is null { newMod = ModifiedEntry::create(newData); } } break; case TID_SerializableTypeInfoArray: { SPtr oldArrayData = std::static_pointer_cast(oldData); SPtr newArrayData = std::static_pointer_cast(newData); if (oldArrayData->value != nullptr && newArrayData->value != nullptr) { UINT32 oldLength = oldArrayData->value->getTotalLength(); UINT32 newLength = newArrayData->value->getTotalLength(); SPtr arrayMods = nullptr; for (UINT32 i = 0; i < newLength; i++) { SPtr arrayElemMod = nullptr; SPtr newArrayElem = newArrayData->value->getFieldData(i); if (i < oldLength) { SPtr oldArrayElem = oldArrayData->value->getFieldData(i); UINT32 arrayElemTypeId = newArrayData->value->getTypeInfo()->mElementType->getTypeId(); arrayElemMod = generateDiff(oldArrayElem, newArrayElem, arrayElemTypeId); } else { arrayElemMod = ModifiedEntry::create(newArrayElem); } if (arrayElemMod != nullptr) { if (arrayMods == nullptr) arrayMods = ModifiedArray::create(); arrayMods->entries.push_back(ModifiedArrayEntry(i, arrayElemMod)); } } if (oldLength != newLength) { if (arrayMods == nullptr) arrayMods = ModifiedArray::create(); } if (arrayMods != nullptr) { arrayMods->origSizes = oldArrayData->value->getLengths(); arrayMods->newSizes = newArrayData->value->getLengths(); } newMod = arrayMods; } else if (oldArrayData->value == nullptr && newArrayData->value == nullptr) { // No change } else // We either record null if new value is null, or the entire array if old value is null { newMod = ModifiedEntry::create(newData); } } break; case TID_SerializableTypeInfoList: { SPtr oldListData = std::static_pointer_cast(oldData); SPtr newListData = std::static_pointer_cast(newData); if (oldListData->value != nullptr && newListData->value != nullptr) { UINT32 oldLength = oldListData->value->getLength(); UINT32 newLength = newListData->value->getLength(); SPtr listMods = nullptr; for (UINT32 i = 0; i < newLength; i++) { SPtr listElemMod = nullptr; SPtr newListElem = newListData->value->getFieldData(i); if (i < oldLength) { SPtr oldListElem = oldListData->value->getFieldData(i); UINT32 arrayElemTypeId = newListData->value->getTypeInfo()->mElementType->getTypeId(); listElemMod = generateDiff(oldListElem, newListElem, arrayElemTypeId); } else { listElemMod = ModifiedEntry::create(newListElem); } if (listElemMod != nullptr) { if (listMods == nullptr) listMods = ModifiedArray::create(); listMods->entries.push_back(ModifiedArrayEntry(i, listElemMod)); } } if (oldLength != newLength) { if (listMods == nullptr) listMods = ModifiedArray::create(); } if (listMods != nullptr) { listMods->origSizes.push_back(oldLength); listMods->newSizes.push_back(newLength); } newMod = listMods; } else if (oldListData->value == nullptr && newListData->value == nullptr) { // No change } else // We either record null if new value is null, or the entire list if old value is null { newMod = ModifiedEntry::create(newData); } } break; case TID_SerializableTypeInfoDictionary: { SPtr oldDictData = std::static_pointer_cast(oldData); SPtr newDictData = std::static_pointer_cast(newData); if (oldDictData->value != nullptr && newDictData->value != nullptr) { SPtr dictMods = nullptr; auto newEnumerator = newDictData->value->getEnumerator(); while (newEnumerator.moveNext()) { SPtr dictElemMod = nullptr; SPtr key = newEnumerator.getKey(); if (oldDictData->value->contains(key)) { UINT32 dictElemTypeId = newDictData->value->getTypeInfo()->mValueType->getTypeId(); dictElemMod = generateDiff(oldDictData->value->getFieldData(key), newEnumerator.getValue(), dictElemTypeId); } else { dictElemMod = ModifiedEntry::create(newEnumerator.getValue()); } if (dictElemMod != nullptr) { if (dictMods == nullptr) dictMods = ModifiedDictionary::create(); dictMods->entries.push_back(ModifiedDictionaryEntry(key, dictElemMod)); } } auto oldEnumerator = oldDictData->value->getEnumerator(); while (oldEnumerator.moveNext()) { SPtr key = oldEnumerator.getKey(); if (!newDictData->value->contains(oldEnumerator.getKey())) { if (dictMods == nullptr) dictMods = ModifiedDictionary::create(); dictMods->removed.push_back(key); } } newMod = dictMods; } else if (oldDictData->value == nullptr && newDictData->value == nullptr) { // No change } else // We either record null if new value is null, or the entire dictionary if old value is null { newMod = ModifiedEntry::create(newData); } } break; default: assert(false); // Invalid type break; } } return newMod; } void ManagedSerializableDiff::apply(const SPtr& obj) { applyDiff(mModificationRoot, obj); } SPtr ManagedSerializableDiff::applyDiff(const SPtr& mod, const SPtr& obj) { SPtr objInfo = obj->getObjectInfo(); for (auto& modEntry : mod->entries) { SPtr fieldType = modEntry.fieldType; SPtr typeInfo = modEntry.parentType; SPtr matchingFieldInfo = objInfo->findMatchingField(fieldType, typeInfo); if (matchingFieldInfo == nullptr) continue; // Field no longer exists in the type SPtr origData = obj->getFieldData(matchingFieldInfo); SPtr newData = applyDiff(modEntry.modification, matchingFieldInfo->mTypeInfo, origData); if (newData != nullptr) obj->setFieldData(matchingFieldInfo, newData); } return nullptr; } SPtr ManagedSerializableDiff::applyDiff(const SPtr& mod, const SPtr& obj) { bool needsResize = false; for (UINT32 i = 0; i < (UINT32)mod->newSizes.size(); i++) { if (mod->newSizes[i] != obj->getLength(i)) { needsResize = true; break; } } SPtr newArray; if (needsResize) { obj->resize(mod->newSizes); newArray = ManagedSerializableFieldData::create(obj->getTypeInfo(), obj->getManagedInstance()); } for (auto& modEntry : mod->entries) { UINT32 arrayIdx = modEntry.idx; SPtr origData = obj->getFieldData(arrayIdx); SPtr newData = applyDiff(modEntry.modification, obj->getTypeInfo()->mElementType, origData); if (newData != nullptr) obj->setFieldData(arrayIdx, newData); } return newArray; } SPtr ManagedSerializableDiff::applyDiff(const SPtr& mod, const SPtr& obj) { bool needsResize = mod->newSizes[0] != obj->getLength(); SPtr newList; if (needsResize) { obj->resize(mod->newSizes[0]); newList = ManagedSerializableFieldData::create(obj->getTypeInfo(), obj->getManagedInstance()); } for (auto& modEntry : mod->entries) { UINT32 arrayIdx = modEntry.idx; SPtr origData = obj->getFieldData(arrayIdx); SPtr newData = applyDiff(modEntry.modification, obj->getTypeInfo()->mElementType, origData); if (newData != nullptr) obj->setFieldData(arrayIdx, newData); } return newList; } SPtr ManagedSerializableDiff::applyDiff(const SPtr& mod, const SPtr& obj) { for (auto& modEntry : mod->entries) { SPtr key = modEntry.key; SPtr origData = obj->getFieldData(key); SPtr newData = applyDiff(modEntry.modification, obj->getTypeInfo()->mValueType, origData); if (newData != nullptr) obj->setFieldData(key, newData); } for (auto& key : mod->removed) { obj->removeFieldData(key); } return nullptr; } SPtr ManagedSerializableDiff::applyDiff(const SPtr& mod, const SPtr& fieldType, const SPtr& origData) { SPtr newData; switch (mod->getTypeId()) { case TID_ScriptModifiedObject: { SPtr origObjData = std::static_pointer_cast(origData); SPtr childObj = origObjData->value; SPtr objTypeInfo = std::static_pointer_cast(fieldType); if (childObj == nullptr) // Object was deleted in original but we have modifications for it, so we create it { childObj = ManagedSerializableObject::createNew(objTypeInfo); newData = ManagedSerializableFieldData::create(objTypeInfo, childObj->getManagedInstance()); } SPtr childMod = std::static_pointer_cast(mod); applyDiff(childMod, childObj); } break; case TID_ScriptModifiedArray: { if (fieldType->getTypeId() == TID_SerializableTypeInfoArray) { SPtr origArrayData = std::static_pointer_cast(origData); SPtr childArray = origArrayData->value; SPtr arrayTypeInfo = std::static_pointer_cast(fieldType); SPtr childMod = std::static_pointer_cast(mod); if (childArray == nullptr) // Object was deleted in original but we have modifications for it, so we create it childArray = ManagedSerializableArray::createNew(arrayTypeInfo, childMod->origSizes); newData = applyDiff(childMod, childArray); } else if (fieldType->getTypeId() == TID_SerializableTypeInfoList) { SPtr origListData = std::static_pointer_cast(origData); SPtr childList = origListData->value; SPtr listTypeInfo = std::static_pointer_cast(fieldType); SPtr childMod = std::static_pointer_cast(mod); if (childList == nullptr) // Object was deleted in original but we have modifications for it, so we create it childList = ManagedSerializableList::createNew(listTypeInfo, childMod->origSizes[0]); newData = applyDiff(childMod, childList); } } break; case TID_ScriptModifiedDictionary: { SPtr origObjData = std::static_pointer_cast(origData); SPtr childDict = origObjData->value; SPtr dictTypeInfo = std::static_pointer_cast(fieldType); if (childDict == nullptr) // Object was deleted in original but we have modifications for it, so we create it { childDict = ManagedSerializableDictionary::createNew(dictTypeInfo); newData = ManagedSerializableFieldData::create(dictTypeInfo, childDict->getManagedInstance()); } SPtr childMod = std::static_pointer_cast(mod); applyDiff(childMod, childDict); } break; default: // Modified field { SPtr childMod = std::static_pointer_cast(mod); newData = childMod->value; } break; } return newData; } RTTITypeBase* ManagedSerializableDiff::getRTTIStatic() { return ManagedSerializableDiffRTTI::instance(); } RTTITypeBase* ManagedSerializableDiff::getRTTI() const { return ManagedSerializableDiff::getRTTIStatic(); } }