File indexing completed on 2024-05-19 05:42:07
0001 // ct_lvtldr_nodestorage.h -*-C++-*- 0002 0003 /* 0004 // Copyright 2023 Codethink Ltd <codethink@codethink.co.uk> 0005 // SPDX-License-Identifier: Apache-2.0 0006 // 0007 // Licensed under the Apache License, Version 2.0 (the "License"); 0008 // you may not use this file except in compliance with the License. 0009 // You may obtain a copy of the License at 0010 // 0011 // http://www.apache.org/licenses/LICENSE-2.0 0012 // 0013 // Unless required by applicable law or agreed to in writing, software 0014 // distributed under the License is distributed on an "AS IS" BASIS, 0015 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 0016 // See the License for the specific language governing permissions and 0017 // limitations under the License. 0018 */ 0019 0020 #include <ct_lvtldr_nodestorage.h> 0021 0022 #include <ct_lvtldr_componentnode.h> 0023 #include <ct_lvtldr_databasehandler.h> 0024 #include <ct_lvtldr_freefunctionnode.h> 0025 #include <ct_lvtldr_lakosiannode.h> 0026 #include <ct_lvtldr_packagenode.h> 0027 #include <ct_lvtldr_repositorynode.h> 0028 #include <ct_lvtldr_typenode.h> 0029 0030 #include <ct_lvtshr_stringhelpers.h> 0031 0032 #include <iostream> 0033 #include <optional> 0034 #include <unordered_map> 0035 0036 using namespace Codethink::lvtldr; 0037 using namespace Codethink::lvtshr; 0038 using RecordNumberType = Codethink::lvtshr::UniqueId::RecordNumberType; 0039 0040 struct NodeStorage::Private { 0041 std::unique_ptr<DatabaseHandler> dbHandler = nullptr; 0042 std::unordered_map<lvtshr::UniqueId, std::unique_ptr<LakosianNode>, lvtshr::UniqueId::Hash> nodes; 0043 }; 0044 0045 NodeStorage::NodeStorage(): d(std::make_unique<Private>()) 0046 { 0047 } 0048 0049 NodeStorage::NodeStorage(NodeStorage&& other) noexcept: d(std::move(other.d)) 0050 { 0051 } 0052 0053 NodeStorage::~NodeStorage() noexcept = default; 0054 0055 void NodeStorage::setDatabaseSourcePath(std::string const& path) 0056 { 0057 d->dbHandler = std::make_unique<DatabaseHandlerType>(path); 0058 clear(); 0059 preloadHighLevelComponents(); 0060 } 0061 0062 void NodeStorage::closeDatabase() 0063 { 0064 if (d->dbHandler) { 0065 d->dbHandler->close(); 0066 clear(); 0067 } 0068 } 0069 0070 void NodeStorage::preloadHighLevelComponents() 0071 { 0072 std::function<void(LakosianNode *)> loadChildrenRecursively = [&](LakosianNode *node) -> void { 0073 if (node->type() == DiagramType::ComponentType) { 0074 // Do not load component children (Will be loaded on demand) 0075 return; 0076 } 0077 0078 (void) node->children(); 0079 for (auto *c : node->children()) { 0080 loadChildrenRecursively(c); 0081 } 0082 }; 0083 0084 for (auto *pkg : getTopLevelPackages()) { 0085 loadChildrenRecursively(pkg); 0086 } 0087 } 0088 0089 // TODO: See #435 - This modifies the current database, instead of working on a copy of it. 0090 cpp::result<LakosianNode *, ErrorAddPackage> NodeStorage::addPackage(const std::string& name, 0091 const std::string& qualifiedName, 0092 Codethink::lvtldr::LakosianNode *parent, 0093 std::any userdata) 0094 { 0095 using Kind = ErrorAddPackage::Kind; 0096 0097 if (findByQualifiedName(lvtshr::DiagramType::PackageType, qualifiedName)) { 0098 return cpp::fail(ErrorAddPackage{Kind::QualifiedNameAlreadyRegistered, qualifiedName}); 0099 } 0100 0101 if (parent && !parent->children().empty()) { 0102 if (parent->children()[0]->type() == DiagramType::ComponentType) { 0103 return cpp::fail(ErrorAddPackage{Kind::CannotAddPackageToStandalonePackage, {}}); 0104 } 0105 } 0106 0107 // Create the package on database 0108 // A Created package does not have a directory on disk, so the path is empty. 0109 auto dao = PackageNodeFields{}; 0110 dao.name = name; 0111 dao.qualifiedName = qualifiedName; 0112 dao.diskPath = ""; 0113 if (parent) { 0114 dao.parentId = parent->id(); 0115 } 0116 d->dbHandler->addFields(dao); 0117 0118 // Update internal nodes cache 0119 auto uid = lvtshr::UniqueId{lvtshr::DiagramType::PackageType, dao.id}; 0120 auto [it, _] = d->nodes.emplace(uid, std::make_unique<PackageNode>(*this, *d->dbHandler, dao)); 0121 auto *lakosianNode = it->second.get(); 0122 0123 if (parent) { 0124 // This is a new element, so addChild should work on the parent. 0125 // there are no possibilities of the new entity already having the parent as child of 0126 // this node. so failures here are not expected. don't even return. 0127 0128 auto result = parent->addChild(lakosianNode); 0129 if (result.has_error()) { 0130 return cpp::fail(ErrorAddPackage{Kind::CantAddChildren, result.error().what}); 0131 } 0132 } 0133 0134 Q_EMIT nodeAdded(lakosianNode, std::move(userdata)); 0135 connect(lakosianNode, &LakosianNode::onNameChanged, this, [this](LakosianNode *node) { 0136 updateAndNotifyNodeRename<lvtldr::PackageNode>(node); 0137 }); 0138 Q_EMIT storageChanged(); 0139 return lakosianNode; 0140 } 0141 0142 cpp::result<void, ErrorRemoveEntity> NodeStorage::removePackage(LakosianNode *node) 0143 { 0144 using Kind = ErrorRemoveEntity::Kind; 0145 0146 if (!node->providers().empty()) { 0147 return cpp::fail(ErrorRemoveEntity{Kind::CannotRemoveWithProviders}); 0148 } 0149 if (!node->clients().empty()) { 0150 return cpp::fail(ErrorRemoveEntity{Kind::CannotRemoveWithClients}); 0151 } 0152 if (!node->children().empty()) { 0153 return cpp::fail(ErrorRemoveEntity{Kind::CannotRemoveWithChildren}); 0154 } 0155 0156 if (node->parent()) { 0157 dynamic_cast<PackageNode *>(node->parent())->removeChildPackage(dynamic_cast<PackageNode *>(node)); 0158 node->parent()->invalidateChildren(); 0159 } 0160 0161 // we know that this package has nothing, so it's safe to delete. 0162 // we may need to provide a better algorithm in the future. 0163 d->dbHandler->removePackageFieldsById(node->id()); 0164 0165 Q_EMIT nodeRemoved(node); 0166 d->nodes.erase(node->uid()); 0167 Q_EMIT storageChanged(); 0168 return {}; 0169 } 0170 0171 cpp::result<LakosianNode *, ErrorAddComponent> 0172 NodeStorage::addComponent(const std::string& name, const std::string& qualifiedName, LakosianNode *parentPackage) 0173 { 0174 using Kind = ErrorAddComponent::Kind; 0175 0176 if (parentPackage == nullptr) { 0177 return cpp::fail(ErrorAddComponent{Kind::MissingParent}); 0178 } 0179 if (findByQualifiedName(lvtshr::DiagramType::ComponentType, qualifiedName)) { 0180 return cpp::fail(ErrorAddComponent{Kind::QualifiedNameAlreadyRegistered}); 0181 } 0182 if (parentPackage->isPackageGroup()) { 0183 return cpp::fail(ErrorAddComponent{Kind::CannotAddComponentToPkgGroup}); 0184 } 0185 0186 // Create the component on database 0187 auto dao = ComponentNodeFields{}; 0188 dao.name = name; 0189 dao.qualifiedName = qualifiedName; 0190 dao.packageId = parentPackage->id(); 0191 d->dbHandler->addFields(dao); 0192 0193 auto uid = lvtshr::UniqueId{lvtshr::DiagramType::ComponentType, dao.id}; 0194 auto [it, _] = d->nodes.emplace(uid, std::make_unique<ComponentNode>(*this, *d->dbHandler, dao)); 0195 auto *lakosianNode = it->second.get(); 0196 0197 auto result = parentPackage->addChild(lakosianNode); 0198 if (result.has_error()) { 0199 std::cerr << "Unexpected failure adding a component to the NodeStorage." << std::endl; 0200 std::cerr << "This means the database in memory is in an inconsistent state" << std::endl; 0201 std::cerr << "Error: " << result.error().what; 0202 std::exit(EXIT_FAILURE); 0203 } 0204 0205 Q_EMIT nodeAdded(lakosianNode, std::any()); 0206 connect(lakosianNode, &LakosianNode::onNameChanged, this, [this](LakosianNode *node) { 0207 updateAndNotifyNodeRename<lvtldr::ComponentNode>(node); 0208 }); 0209 Q_EMIT storageChanged(); 0210 return lakosianNode; 0211 } 0212 0213 cpp::result<void, ErrorRemoveEntity> NodeStorage::removeComponent(LakosianNode *node) 0214 { 0215 using Kind = ErrorRemoveEntity::Kind; 0216 0217 if (!node->providers().empty()) { 0218 return cpp::fail(ErrorRemoveEntity{Kind::CannotRemoveWithProviders}); 0219 } 0220 0221 if (!node->clients().empty()) { 0222 return cpp::fail(ErrorRemoveEntity{Kind::CannotRemoveWithClients}); 0223 } 0224 if (!node->children().empty()) { 0225 return cpp::fail(ErrorRemoveEntity{Kind::CannotRemoveWithChildren}); 0226 } 0227 0228 // Must be done before actual removal 0229 Q_EMIT nodeRemoved(node); 0230 0231 // Update cache 0232 d->nodes.at(node->uid()); 0233 auto e = d->nodes.extract(node->uid()); 0234 assert(!e.empty()); 0235 auto *removedNode = e.mapped().get(); 0236 assert(removedNode == node); // Sanity check 0237 removedNode->parent()->invalidateChildren(); 0238 0239 // Update database 0240 d->dbHandler->removeComponentFieldsById(removedNode->id()); 0241 0242 Q_EMIT storageChanged(); 0243 return {}; 0244 } 0245 0246 cpp::result<LakosianNode *, ErrorAddUDT> NodeStorage::addLogicalEntity(const std::string& name, 0247 const std::string& qualifiedName, 0248 LakosianNode *parent, 0249 lvtshr::UDTKind kind) 0250 { 0251 using ErrorKind = ErrorAddUDT::Kind; 0252 0253 auto isValidParentType = [&parent]() { 0254 if (!parent) { 0255 return false; 0256 } 0257 0258 if (parent->type() == DiagramType::ComponentType) { 0259 return true; 0260 } 0261 0262 if (parent->type() == DiagramType::ClassType) { 0263 auto *parentAsClass = dynamic_cast<TypeNode *>(parent); 0264 if (parentAsClass->kind() == lvtshr::UDTKind::Class || parentAsClass->kind() == lvtshr::UDTKind::Struct) { 0265 return true; 0266 } 0267 } 0268 0269 return false; 0270 }(); 0271 0272 if (!isValidParentType) { 0273 return cpp::fail(ErrorAddUDT{ErrorKind::BadParentType}); 0274 } 0275 0276 // Update backend database 0277 auto dao = TypeNodeFields{}; 0278 dao.name = name; 0279 dao.qualifiedName = qualifiedName; 0280 dao.kind = kind; 0281 0282 if (parent->type() == DiagramType::ComponentType) { 0283 dao.componentIds.emplace_back(parent->id()); 0284 } else if (parent->type() == DiagramType::ClassType) { 0285 dao.classNamespaceId = parent->id(); 0286 dao.componentIds.emplace_back(parent->parent()->id()); 0287 } 0288 d->dbHandler->addFields(dao); 0289 0290 // Update cache 0291 auto id = lvtshr::UniqueId{lvtshr::DiagramType::ClassType, dao.id}; 0292 auto *addedNode = fetchFromDBById<lvtldr::TypeNode>(id); 0293 assert(addedNode); 0294 parent->addChild(addedNode).expect("Unexpected failure adding a logical entity to the NodeStorage."); 0295 0296 Q_EMIT nodeAdded(addedNode, std::any()); 0297 Q_EMIT storageChanged(); 0298 return addedNode; 0299 } 0300 0301 cpp::result<void, ErrorRemoveEntity> NodeStorage::removeLogicalEntity(LakosianNode *node) 0302 { 0303 using Kind = ErrorRemoveEntity::Kind; 0304 0305 if (!node->providers().empty()) { 0306 return cpp::fail(ErrorRemoveEntity{Kind::CannotRemoveWithProviders}); 0307 } 0308 if (!node->clients().empty()) { 0309 return cpp::fail(ErrorRemoveEntity{Kind::CannotRemoveWithClients}); 0310 } 0311 if (!node->children().empty()) { 0312 return cpp::fail(ErrorRemoveEntity{Kind::CannotRemoveWithChildren}); 0313 } 0314 0315 // Update cache 0316 d->nodes.at(node->uid()); 0317 auto e = d->nodes.extract(node->uid()); 0318 assert(!e.empty()); 0319 auto *removedNode = e.mapped().get(); 0320 assert(removedNode == node); // Sanity check 0321 auto *parent = removedNode->parent(); 0322 if (parent && dynamic_cast<ComponentNode *>(parent) != nullptr) { 0323 dynamic_cast<ComponentNode *>(parent)->removeChild(node); 0324 } 0325 0326 // Update database 0327 d->dbHandler->removeUdtFieldsById(removedNode->id()); 0328 0329 // Notify 0330 Q_EMIT nodeRemoved(removedNode); 0331 Q_EMIT storageChanged(); 0332 return {}; 0333 } 0334 0335 cpp::result<void, ErrorAddPhysicalDependency> NodeStorage::addPhysicalDependency(LakosianNode *source, 0336 LakosianNode *target) 0337 { 0338 using Kind = ErrorAddPhysicalDependency::Kind; 0339 0340 auto isValidType = [](auto const& e) { 0341 return e->type() == DiagramType::PackageType || e->type() == DiagramType::ComponentType; 0342 }; 0343 if (!isValidType(source) || !isValidType(target)) { 0344 return cpp::fail(ErrorAddPhysicalDependency{Kind::InvalidType}); 0345 } 0346 if (source == target) { 0347 return cpp::fail(ErrorAddPhysicalDependency{Kind::SelfRelation}); 0348 } 0349 if (source->hasProvider(target)) { 0350 return cpp::fail(ErrorAddPhysicalDependency{Kind::DependencyAlreadyExists}); 0351 } 0352 0353 auto isPkgGrp = [](auto *e) { 0354 return e && e->type() == DiagramType::PackageType && e->parent() == nullptr; 0355 }; 0356 auto isStandalonePackage = [](auto *e) { 0357 return e && e->type() == DiagramType::PackageType 0358 && (e->children().empty() || e->children()[0]->type() == DiagramType::ComponentType); 0359 }; 0360 auto isPkg = [](auto *e) { 0361 return e && e->type() == DiagramType::PackageType && e->parent() != nullptr; 0362 }; 0363 auto isComponent = [](auto *e) { 0364 return e && e->type() == DiagramType::ComponentType; 0365 }; 0366 0367 if (isPkgGrp(source)) { 0368 auto *targetPackage = dynamic_cast<PackageNode *>(target); 0369 if (!(isPkgGrp(target) || (targetPackage && targetPackage->isStandalone()))) { 0370 // A package group cannot depend on an entity from a different level 0371 return cpp::fail(ErrorAddPhysicalDependency{Kind::HierarchyLevelMismatch}); 0372 } 0373 0374 dynamic_cast<PackageNode *>(source)->addConcreteDependency(dynamic_cast<PackageNode *>(target)); 0375 } else if (isPkg(source)) { 0376 if (!isPkg(target)) { 0377 // A package cannot depend on an entity from a different level 0378 return cpp::fail(ErrorAddPhysicalDependency{Kind::HierarchyLevelMismatch}); 0379 } 0380 if (source->parent() != target->parent() && !source->parent()->hasProvider(target->parent())) { 0381 // A package can only depend on other if there's a dependency between their parents 0382 return cpp::fail(ErrorAddPhysicalDependency{Kind::MissingParentDependency}); 0383 } 0384 0385 dynamic_cast<PackageNode *>(source)->addConcreteDependency(dynamic_cast<PackageNode *>(target)); 0386 } else if (isComponent(source)) { 0387 if (!isComponent(target)) { 0388 // Components can only depend on other components 0389 return cpp::fail(ErrorAddPhysicalDependency{Kind::HierarchyLevelMismatch}); 0390 } 0391 0392 if (source->parent() != target->parent()) { 0393 // Rules for components with different parents 0394 0395 auto isAllowedDueToPkgGroupToStandalonePkgDependency = [&]() { 0396 // If this component is within a package group and the target component is within a standalone group ... 0397 if (isPkgGrp(source->parent()->parent()) && isStandalonePackage(target->parent())) { 0398 auto *pkggrp = source->parent()->parent(); 0399 auto *standalonegrp = target->parent(); 0400 // ... and there's a dependency between the source's group and target's standalone package ... 0401 if (pkggrp->hasProvider(standalonegrp)) { 0402 // ... then the component's dependency is allowed. 0403 return true; 0404 } 0405 } 0406 return false; 0407 }(); 0408 auto isAllowedDueToFwdDependency = source->parent()->hasProvider(target->parent()); 0409 0410 if (!isAllowedDueToPkgGroupToStandalonePkgDependency && !isAllowedDueToFwdDependency) { 0411 // A component can only depend on other if there's a dependency between their parents 0412 return cpp::fail(ErrorAddPhysicalDependency{Kind::MissingParentDependency}); 0413 } 0414 } 0415 0416 dynamic_cast<ComponentNode *>(source)->addConcreteDependency(dynamic_cast<ComponentNode *>(target)); 0417 } 0418 0419 // TODO [#455]: It is possible to update the data structures instead of invalidate them. 0420 source->invalidateProviders(); 0421 target->invalidateClients(); 0422 0423 Q_EMIT physicalDependencyAdded(source, target); 0424 Q_EMIT storageChanged(); 0425 return {}; 0426 } 0427 0428 cpp::result<void, ErrorRemovePhysicalDependency> NodeStorage::removePhysicalDependency(LakosianNode *source, 0429 LakosianNode *target) 0430 { 0431 using Kind = ErrorRemovePhysicalDependency::Kind; 0432 0433 if (!source->hasProvider(target)) { 0434 return cpp::fail(ErrorRemovePhysicalDependency{Kind::InexistentRelation}); 0435 } 0436 0437 // Package Group 0438 auto isPkgGrp = [](auto const& e) { 0439 return e->type() == DiagramType::PackageType && e->parent() == nullptr; 0440 }; 0441 if (isPkgGrp(source)) { 0442 dynamic_cast<PackageNode *>(source)->removeConcreteDependency(dynamic_cast<PackageNode *>(target)); 0443 } 0444 0445 // Package 0446 auto isPkg = [](auto const& e) { 0447 return e->type() == DiagramType::PackageType && e->parent() != nullptr; 0448 }; 0449 if (isPkg(source)) { 0450 dynamic_cast<PackageNode *>(source)->removeConcreteDependency(dynamic_cast<PackageNode *>(target)); 0451 } 0452 0453 // Component 0454 auto isComponent = [](auto const& e) { 0455 return e->type() == DiagramType::ComponentType; 0456 }; 0457 0458 if (isComponent(source) && isComponent(target)) { 0459 dynamic_cast<ComponentNode *>(source)->removeConcreteDependency(dynamic_cast<ComponentNode *>(target)); 0460 } 0461 0462 // TODO [#455]: It is possible to update the data structures instead of invalidate them. 0463 source->invalidateProviders(); 0464 target->invalidateClients(); 0465 0466 Q_EMIT physicalDependencyRemoved(source, target); 0467 Q_EMIT storageChanged(); 0468 return {}; 0469 } 0470 0471 cpp::result<void, ErrorAddLogicalRelation> 0472 NodeStorage::addLogicalRelation(TypeNode *source, TypeNode *target, LakosRelationType type) 0473 { 0474 using Kind = ErrorAddLogicalRelation::Kind; 0475 0476 if (source == nullptr || target == nullptr) { 0477 return cpp::fail(ErrorAddLogicalRelation{Kind::InvalidRelation}); 0478 } 0479 if (source == target) { 0480 return cpp::fail(ErrorAddLogicalRelation{Kind::SelfRelation}); 0481 } 0482 if (type != LakosRelationType::IsA && type != LakosRelationType::UsesInTheImplementation 0483 && type != LakosRelationType::UsesInTheInterface) { 0484 return cpp::fail(ErrorAddLogicalRelation{Kind::InvalidLakosRelationType}); 0485 } 0486 if (source->hasProvider(target)) { 0487 return cpp::fail(ErrorAddLogicalRelation{Kind::AlreadyHaveDependency}); 0488 } 0489 if (source->parent() != target->parent() && !source->parent()->hasProvider(target->parent())) { 0490 // different semantics here. if we are on the class level, the next parent relation can be an 0491 // isA, usesInImplementation, usesInInterface. 0492 // but if it's a component, there's only one type of relation possible, a Dependency. 0493 // so we need two errors to sinalize that. 0494 using FieldType = Codethink::lvtshr::DiagramType; 0495 if (source->parent()->type() == FieldType::ComponentType 0496 && target->parent()->type() == FieldType::ComponentType) { 0497 return cpp::fail(ErrorAddLogicalRelation{Kind::ComponentDependencyRequired}); 0498 } 0499 return cpp::fail(ErrorAddLogicalRelation{Kind::ParentDependencyRequired}); 0500 } 0501 0502 // Update database 0503 if (type == LakosRelationType::IsA) { 0504 auto& parent = target; 0505 auto& child = source; 0506 d->dbHandler->addClassHierarchy(parent->id(), child->id()); 0507 } 0508 if (type == LakosRelationType::UsesInTheImplementation) { 0509 d->dbHandler->addImplementationRelationship(source->id(), target->id()); 0510 } 0511 if (type == LakosRelationType::UsesInTheInterface) { 0512 d->dbHandler->addInterfaceRelationship(source->id(), target->id()); 0513 } 0514 0515 // TODO [#455]: It is possible to update the data structures instead of invalidate them. 0516 source->invalidateProviders(); 0517 target->invalidateClients(); 0518 0519 Q_EMIT logicalRelationAdded(source, target, type); 0520 Q_EMIT storageChanged(); 0521 return {}; 0522 } 0523 0524 cpp::result<void, ErrorRemoveLogicalRelation> 0525 NodeStorage::removeLogicalRelation(TypeNode *source, TypeNode *target, LakosRelationType type) 0526 { 0527 using Kind = ErrorRemoveLogicalRelation::Kind; 0528 0529 auto r = [&]() -> cpp::result<void, ErrorRemoveLogicalRelation> { 0530 if (type == LakosRelationType::IsA) { 0531 auto& parent = target; 0532 auto& child = source; 0533 if (!dynamic_cast<TypeNode *>(child)->isA(dynamic_cast<TypeNode *>(parent))) { 0534 return cpp::fail(ErrorRemoveLogicalRelation{Kind::InexistentRelation}); 0535 } 0536 d->dbHandler->removeClassHierarchy(parent->id(), child->id()); 0537 return {}; 0538 } 0539 if (type == LakosRelationType::UsesInTheImplementation) { 0540 if (!dynamic_cast<TypeNode *>(source)->usesInTheImplementation(dynamic_cast<TypeNode *>(target))) { 0541 return cpp::fail(ErrorRemoveLogicalRelation{Kind::InexistentRelation}); 0542 } 0543 d->dbHandler->removeImplementationRelationship(source->id(), target->id()); 0544 return {}; 0545 } 0546 if (type == LakosRelationType::UsesInTheInterface) { 0547 if (!dynamic_cast<TypeNode *>(source)->usesInTheInterface(dynamic_cast<TypeNode *>(target))) { 0548 return cpp::fail(ErrorRemoveLogicalRelation{Kind::InexistentRelation}); 0549 } 0550 d->dbHandler->removeInterfaceRelationship(source->id(), target->id()); 0551 return {}; 0552 } 0553 return cpp::fail(ErrorRemoveLogicalRelation{Kind::InvalidLakosRelationType}); 0554 }(); 0555 if (r.has_error()) { 0556 return r; 0557 } 0558 0559 // TODO [#455]: It is possible to update the data structures instead of invalidate them. 0560 source->invalidateProviders(); 0561 target->invalidateClients(); 0562 0563 Q_EMIT logicalRelationRemoved(source, target, type); 0564 Q_EMIT storageChanged(); 0565 return {}; 0566 } 0567 0568 cpp::result<void, ErrorReparentEntity> NodeStorage::reparentEntity(LakosianNode *entity, LakosianNode *newParent) 0569 { 0570 using Kind = ErrorReparentEntity::Kind; 0571 0572 if (entity == nullptr || entity->type() != DiagramType::ComponentType) { 0573 return cpp::fail(ErrorReparentEntity{Kind::InvalidEntity}); 0574 } 0575 if (newParent == nullptr || newParent->type() != DiagramType::PackageType) { 0576 return cpp::fail(ErrorReparentEntity{Kind::InvalidParent}); 0577 } 0578 0579 auto *oldParent = dynamic_cast<PackageNode *>(entity->parent()); 0580 for (auto *udt : entity->children()) { 0581 dynamic_cast<TypeNode *>(udt)->setParentPackageId(newParent->id()); 0582 } 0583 dynamic_cast<ComponentNode *>(entity)->setParentPackageId(newParent->id()); 0584 oldParent->removeChild(entity); 0585 (void) newParent->addChild(dynamic_cast<ComponentNode *>(entity)); 0586 0587 // Update (invalidate) caches 0588 oldParent->invalidateChildren(); 0589 newParent->invalidateChildren(); 0590 entity->invalidateParent(); 0591 0592 Q_EMIT entityReparent(entity, oldParent, newParent); 0593 Q_EMIT storageChanged(); 0594 return {}; 0595 } 0596 0597 LakosianNode *NodeStorage::findById(const lvtshr::UniqueId& id) 0598 { 0599 // Search for the node on cache 0600 if (d->nodes.find(id) != d->nodes.end()) { 0601 return d->nodes.at(id).get(); 0602 } 0603 0604 switch (id.diagramType()) { 0605 case lvtshr::DiagramType::ClassType: { 0606 return fetchFromDBById<lvtldr::TypeNode>(id); 0607 } 0608 case lvtshr::DiagramType::ComponentType: { 0609 return fetchFromDBById<lvtldr::ComponentNode>(id); 0610 } 0611 case lvtshr::DiagramType::PackageType: { 0612 return fetchFromDBById<lvtldr::PackageNode>(id); 0613 } 0614 case lvtshr::DiagramType::RepositoryType: { 0615 return fetchFromDBById<lvtldr::RepositoryNode>(id); 0616 } 0617 case lvtshr::DiagramType::FreeFunctionType: { 0618 return fetchFromDBById<lvtldr::FreeFunctionNode>(id); 0619 } 0620 case DiagramType::NoneType: 0621 break; 0622 } 0623 return nullptr; 0624 } 0625 0626 LakosianNode *NodeStorage::findByQualifiedName(lvtshr::DiagramType type, const std::string& qualifiedName) 0627 { 0628 // Search for the node on cache 0629 auto it = std::find_if(d->nodes.begin(), d->nodes.end(), [&](auto const& it) { 0630 auto const& node = it.second; 0631 return node->type() == type && node->qualifiedName() == qualifiedName; 0632 }); 0633 if (it != d->nodes.end()) { 0634 return it->second.get(); 0635 } 0636 0637 // Search for the node in the backend 0638 switch (type) { 0639 case lvtshr::DiagramType::ClassType: { 0640 return fetchFromDBByQualifiedName<lvtldr::TypeNode>(qualifiedName); 0641 } 0642 case lvtshr::DiagramType::ComponentType: { 0643 return fetchFromDBByQualifiedName<lvtldr::ComponentNode>(qualifiedName); 0644 } 0645 case lvtshr::DiagramType::PackageType: { 0646 return fetchFromDBByQualifiedName<lvtldr::PackageNode>(qualifiedName); 0647 } 0648 case lvtshr::DiagramType::RepositoryType: { 0649 return fetchFromDBByQualifiedName<lvtldr::RepositoryNode>(qualifiedName); 0650 } 0651 case lvtshr::DiagramType::FreeFunctionType: { 0652 return fetchFromDBByQualifiedName<lvtldr::FreeFunctionNode>(qualifiedName); 0653 } 0654 case lvtshr::DiagramType::NoneType: { 0655 break; 0656 } 0657 } 0658 0659 return nullptr; 0660 } 0661 0662 LakosianNode *NodeStorage::findByQualifiedName(const std::string& qualifiedName) 0663 { 0664 using lvtshr::DiagramType; 0665 for (auto const& type : {DiagramType::RepositoryType, 0666 DiagramType::PackageType, 0667 DiagramType::ComponentType, 0668 DiagramType::ClassType, 0669 DiagramType::RepositoryType, 0670 DiagramType::FreeFunctionType}) { 0671 auto *lakosianNode = findByQualifiedName(type, qualifiedName); 0672 if (lakosianNode) { 0673 return lakosianNode; 0674 } 0675 } 0676 return nullptr; 0677 } 0678 0679 std::vector<LakosianNode *> NodeStorage::getTopLevelPackages() 0680 { 0681 auto topLvlEntityIds = d->dbHandler->getTopLevelEntityIds(); 0682 auto out = std::vector<LakosianNode *>{}; 0683 for (auto&& uid : topLvlEntityIds) { 0684 auto *maybePackage = findById(uid); 0685 if (maybePackage) { 0686 out.emplace_back(maybePackage); 0687 } 0688 } 0689 return out; 0690 } 0691 0692 void NodeStorage::clear() 0693 { 0694 Q_EMIT storageCleared(); 0695 d->nodes.clear(); 0696 } 0697 0698 template<typename LDR_TYPE> 0699 auto getFieldsByQualifiedName(DatabaseHandler& dbHandler, const std::string& qualifiedName) 0700 { 0701 } 0702 template<> 0703 auto getFieldsByQualifiedName<ComponentNode>(DatabaseHandler& dbHandler, const std::string& qualifiedName) 0704 { 0705 return dbHandler.getComponentFieldsByQualifiedName(qualifiedName); 0706 } 0707 template<> 0708 auto getFieldsByQualifiedName<PackageNode>(DatabaseHandler& dbHandler, const std::string& qualifiedName) 0709 { 0710 return dbHandler.getPackageFieldsByQualifiedName(qualifiedName); 0711 } 0712 template<> 0713 auto getFieldsByQualifiedName<TypeNode>(DatabaseHandler& dbHandler, const std::string& qualifiedName) 0714 { 0715 return dbHandler.getUdtFieldsByQualifiedName(qualifiedName); 0716 } 0717 template<> 0718 auto getFieldsByQualifiedName<RepositoryNode>(DatabaseHandler& dbHandler, const std::string& qualifiedName) 0719 { 0720 return dbHandler.getRepositoryFieldsByQualifiedName(qualifiedName); 0721 } 0722 template<> 0723 auto getFieldsByQualifiedName<FreeFunctionNode>(DatabaseHandler& dbHandler, const std::string& qualifiedName) 0724 { 0725 return dbHandler.getFreeFunctionFieldsByQualifiedName(qualifiedName); 0726 } 0727 0728 template<typename LDR_TYPE> 0729 LakosianNode *NodeStorage::fetchFromDBByQualifiedName(const std::string& qualifiedName) 0730 { 0731 auto dao = getFieldsByQualifiedName<LDR_TYPE>(*d->dbHandler, qualifiedName); 0732 if (dao.id == -1) { 0733 return nullptr; 0734 } 0735 0736 auto uid = lvtshr::UniqueId{LakosianNodeType<LDR_TYPE>::diagramType, dao.id}; 0737 auto [it, _] = d->nodes.emplace(uid, std::make_unique<LDR_TYPE>(*this, *d->dbHandler, dao)); 0738 auto *lakosianNode = it->second.get(); 0739 connect(lakosianNode, &LakosianNode::onNameChanged, this, [this](LakosianNode *node) { 0740 updateAndNotifyNodeRename<LDR_TYPE>(node); 0741 }); 0742 return lakosianNode; 0743 } 0744 0745 template<typename LDR_TYPE> 0746 auto getFieldsById(DatabaseHandler& dbHandler, RecordNumberType id) 0747 { 0748 } 0749 template<> 0750 auto getFieldsById<ComponentNode>(DatabaseHandler& dbHandler, RecordNumberType id) 0751 { 0752 return dbHandler.getComponentFieldsById(id); 0753 } 0754 template<> 0755 auto getFieldsById<PackageNode>(DatabaseHandler& dbHandler, RecordNumberType id) 0756 { 0757 return dbHandler.getPackageFieldsById(id); 0758 } 0759 template<> 0760 auto getFieldsById<TypeNode>(DatabaseHandler& dbHandler, RecordNumberType id) 0761 { 0762 return dbHandler.getUdtFieldsById(id); 0763 } 0764 template<> 0765 auto getFieldsById<RepositoryNode>(DatabaseHandler& dbHandler, RecordNumberType id) 0766 { 0767 return dbHandler.getRepositoryFieldsById(id); 0768 } 0769 template<> 0770 auto getFieldsById<FreeFunctionNode>(DatabaseHandler& dbHandler, RecordNumberType id) 0771 { 0772 return dbHandler.getFreeFunctionFieldsById(id); 0773 } 0774 0775 template<typename LDR_TYPE> 0776 LakosianNode *NodeStorage::fetchFromDBById(const Codethink::lvtshr::UniqueId& uid) 0777 { 0778 auto dao = getFieldsById<LDR_TYPE>(*d->dbHandler, uid.recordNumber()); 0779 if (dao.id == -1) { 0780 return nullptr; 0781 } 0782 0783 auto [it, _] = d->nodes.emplace(uid, std::make_unique<LDR_TYPE>(*this, *d->dbHandler, dao)); 0784 auto *lakosianNode = it->second.get(); 0785 connect(lakosianNode, &LakosianNode::onNameChanged, this, [this](LakosianNode *node) { 0786 updateAndNotifyNodeRename<LDR_TYPE>(node); 0787 }); 0788 return lakosianNode; 0789 } 0790 0791 template<typename LDR_TYPE> 0792 void NodeStorage::updateAndNotifyNodeRename(LakosianNode *node) 0793 { 0794 d->dbHandler->updateFields(LDR_TYPE::from(node)->getFields()); 0795 Q_EMIT nodeNameChanged(node); 0796 Q_EMIT storageChanged(); 0797 } 0798 0799 std::invoke_result_t<decltype(&NodeStorage::getSession), NodeStorage> NodeStorage::getSession() 0800 { 0801 return dynamic_cast<DatabaseHandlerType *>(d->dbHandler.get())->getSession(); 0802 }