#include "datamodel.h" #include "base/service.h" #include "objects/base/instance.h" #include "objects/base/service.h" #include "objects/meta.h" #include "workspace.h" #include "logger.h" #include "panic.h" #include #include #include const InstanceType DataModel::TYPE = { .super = &Instance::TYPE, .className = "DataModel", .constructor = nullptr, }; const InstanceType* DataModel::GetClass() { return &TYPE; } DataModel::DataModel() : Instance(&TYPE) { this->name = "Place"; } void DataModel::Init() { // Create the workspace if it doesn't exist if (this->services.count("Workspace") == 0) { this->services["Workspace"] = std::make_shared(); AddChild(this->services["Workspace"]); } // Init all services for (auto [_, service] : this->services) { service->InitService(); } } void DataModel::SaveToFile(std::optional path) { if (!path.has_value() && !this->currentFile.has_value()) { Logger::fatalError("Cannot save DataModel because no path was provided."); panic(); } std::string target = path.has_value() ? path.value() : this->currentFile.value(); std::ofstream outStream(target); pugi::xml_document doc; pugi::xml_node root = doc.append_child("openblocks"); for (InstanceRef child : this->GetChildren()) { child->Serialize(root); } doc.save(outStream); currentFile = target; name = target; Logger::info("Place saved successfully"); } void DataModel::DeserializeService(pugi::xml_node node) { std::string className = node.attribute("class").value(); if (INSTANCE_MAP.count(className) == 0) { Logger::fatalErrorf("Unknown service: '%s'", className.c_str()); return; } if (services.count(className) != 0) { Logger::fatalErrorf("Service %s defined multiple times in file", className.c_str()); return; } // This will error if an abstract instance is used in the file. Oh well, not my prob rn. InstanceRef object = INSTANCE_MAP[className]->constructor(); AddChild(object); // Read properties pugi::xml_node propertiesNode = node.child("Properties"); for (pugi::xml_node propertyNode : propertiesNode) { std::string propertyName = propertyNode.attribute("name").value(); auto meta_ = object->GetPropertyMeta(propertyName); if (!meta_.has_value()) { Logger::fatalErrorf("Attempt to set unknown property '%s' of %s", propertyName.c_str(), object->GetClass()->className.c_str()); continue; } Data::Variant value = Data::Variant::Deserialize(propertyNode); object->SetPropertyValue(propertyName, value); } // Add children for (pugi::xml_node childNode : node.children("Item")) { result child = Instance::Deserialize(childNode); if (child.is_error()) { std::get(child.error().value()).logMessage(); continue; } object->AddChild(child.expect()); } // We add the service to the list // All services get init'd at once in InitServices this->services[className] = std::dynamic_pointer_cast(object); } std::shared_ptr DataModel::LoadFromFile(std::string path) { std::ifstream inStream(path); pugi::xml_document doc; doc.load(inStream); pugi::xml_node rootNode = doc.child("openblocks"); std::shared_ptr newModel = std::make_shared(); for (pugi::xml_node childNode : rootNode.children("Item")) { newModel->DeserializeService(childNode); } newModel->Init(); return newModel; }