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 }