File indexing completed on 2024-05-19 05:42:06

0001 // ct_lvtldr_componentnode.cpp                                       -*-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_componentnode.h>
0021 
0022 #include <ct_lvtldr_nodestorage.h>
0023 #include <ct_lvtldr_packagenode.h>
0024 #include <ct_lvtshr_stringhelpers.h>
0025 #include <regex>
0026 
0027 namespace {
0028 using namespace Codethink;
0029 
0030 std::string fixQName(const std::string& qname)
0031 {
0032     return std::regex_replace(qname, std::regex("\\\\"), "/");
0033 }
0034 
0035 } // namespace
0036 
0037 // ==========================
0038 // class ComponentNode
0039 // ==========================
0040 
0041 namespace Codethink::lvtldr {
0042 
0043 using namespace lvtshr;
0044 
0045 ComponentNode::ComponentNode(NodeStorage& store): LakosianNode(store, std::nullopt)
0046 {
0047     // Only to be used on tests
0048     // TODO: Let d_dbo be a provider interface instead of a dbo pointer directly
0049 }
0050 
0051 ComponentNode::ComponentNode(NodeStorage& store,
0052                              std::optional<std::reference_wrapper<DatabaseHandler>> dbHandler,
0053                              std::optional<ComponentNodeFields> fields):
0054     LakosianNode(store, dbHandler), d_dbHandler(dbHandler), d_fields(*fields)
0055 {
0056     setName(d_fields.name);
0057     d_qualifiedNameParts = NamingUtils::buildQualifiedNamePrefixParts(fixQName(d_fields.qualifiedName), "/");
0058 }
0059 
0060 ComponentNode::~ComponentNode() noexcept = default;
0061 
0062 void ComponentNode::setParentPackageId(Codethink::lvtshr::UniqueId::RecordNumberType id)
0063 {
0064     d_fields.packageId = id;
0065     d_dbHandler->get().updateFields(d_fields);
0066 }
0067 
0068 void ComponentNode::addConcreteDependency(ComponentNode *other)
0069 {
0070     d_fields.providerIds.emplace_back(other->id());
0071     other->d_fields.clientIds.emplace_back(d_fields.id);
0072     d_dbHandler->get().addComponentDependency(d_fields.id, other->id());
0073 }
0074 
0075 void ComponentNode::removeConcreteDependency(ComponentNode *other)
0076 {
0077     {
0078         auto& v = d_fields.providerIds;
0079         v.erase(std::remove(v.begin(), v.end(), other->id()), v.end());
0080     }
0081     {
0082         auto& v = other->d_fields.clientIds;
0083         v.erase(std::remove(v.begin(), v.end(), other->id()), v.end());
0084     }
0085     d_dbHandler->get().removeComponentDependency(d_fields.id, other->id());
0086 }
0087 
0088 lvtshr::DiagramType ComponentNode::type() const
0089 {
0090     return lvtshr::DiagramType::ComponentType;
0091 }
0092 
0093 std::string ComponentNode::qualifiedName() const
0094 {
0095     return NamingUtils::buildQualifiedName(d_qualifiedNameParts, name(), "/");
0096 }
0097 
0098 std::string ComponentNode::parentName()
0099 {
0100     auto *parentPkg = dynamic_cast<PackageNode *>(parent());
0101     if (!parentPkg) {
0102         return "";
0103     }
0104     return parentPkg->name();
0105 }
0106 
0107 long long ComponentNode::id() const
0108 {
0109     return d_fields.id;
0110 }
0111 
0112 lvtshr::UniqueId ComponentNode::uid() const
0113 {
0114     return {lvtshr::DiagramType::ComponentType, id()};
0115 }
0116 
0117 LakosianNode::IsLakosianResult ComponentNode::isLakosian()
0118 {
0119     auto *parentPkg = dynamic_cast<PackageNode *>(parent());
0120     if (!parentPkg) {
0121         return IsLakosianResult::ComponentHasNoPackage;
0122     }
0123     if (!lvtshr::StrUtil::beginsWith(name(), parentPkg->canonicalName() + "_")) {
0124         return IsLakosianResult::ComponentDoesntStartWithParentName;
0125     }
0126     return IsLakosianResult::IsLakosian;
0127 }
0128 
0129 void ComponentNode::loadParent()
0130 {
0131     if (d->parentLoaded) {
0132         return;
0133     }
0134     d->parentLoaded = true;
0135 
0136     if (!d_fields.packageId) {
0137         d->parent = nullptr;
0138         return;
0139     }
0140 
0141     d->parent = d->store.findById({DiagramType::PackageType, *d_fields.packageId});
0142 }
0143 
0144 void ComponentNode::loadChildren()
0145 {
0146     if (d->childrenLoaded) {
0147         return;
0148     }
0149     d_fields = d_dbHandler->get().getComponentFieldsById(d_fields.id);
0150     d->childrenLoaded = true;
0151 
0152     d->children.clear();
0153     d->children.reserve(d_fields.childUdtIds.size() + d_fields.childGlobalFunctionIds.size());
0154     for (auto const& id : d_fields.childUdtIds) {
0155         LakosianNode *node = d->store.findById({DiagramType::ClassType, id});
0156         if (dynamic_cast<TypeNode *>(node)->hasClassNamespace()) {
0157             continue;
0158         }
0159         d->children.push_back(node);
0160     }
0161 
0162     for (auto const& id : d_fields.childGlobalFunctionIds) {
0163         LakosianNode *node = d->store.findById({DiagramType::FreeFunctionType, id});
0164         if (!node) {
0165             continue;
0166         }
0167         d->children.push_back(node);
0168     }
0169 }
0170 
0171 void ComponentNode::loadProviders()
0172 {
0173     if (d->providersLoaded) {
0174         return;
0175     }
0176     d_fields = d_dbHandler->get().getComponentFieldsById(d_fields.id);
0177     d->providersLoaded = true;
0178 
0179     for (auto&& id : d_fields.providerIds) {
0180         LakosianNode *node = d->store.findById({DiagramType::ComponentType, id});
0181         d->providers.emplace_back(LakosianEdge{lvtshr::PackageDependency, node});
0182     }
0183 }
0184 
0185 void ComponentNode::loadClients()
0186 {
0187     if (d->clientsLoaded) {
0188         return;
0189     }
0190     d->clientsLoaded = true;
0191 
0192     for (auto&& id : d_fields.clientIds) {
0193         LakosianNode *node = d->store.findById({DiagramType::ComponentType, id});
0194         d->clients.emplace_back(LakosianEdge{lvtshr::PackageDependency, node});
0195     }
0196 }
0197 
0198 cpp::result<void, AddChildError> ComponentNode::addChild(LakosianNode *child)
0199 {
0200     // don't add things twice.
0201     if (std::find(std::begin(d->children), std::end(d->children), child) != std::end(d->children)) {
0202         return cpp::fail(AddChildError{"The entity is already a child of this node"});
0203     }
0204     Q_EMIT onChildCountChanged(d->children.size());
0205     d->children.push_back(child);
0206     return {};
0207 }
0208 
0209 void ComponentNode::removeChild(LakosianNode *child)
0210 {
0211     Q_EMIT onChildCountChanged(d->children.size());
0212     {
0213         auto& v = d_fields.childUdtIds;
0214         v.erase(std::remove(v.begin(), v.end(), child->id()), v.end());
0215     }
0216     {
0217         auto& v = d_fields.childGlobalFunctionIds;
0218         v.erase(std::remove(v.begin(), v.end(), child->id()), v.end());
0219     }
0220     {
0221         auto& v = d->children;
0222         v.erase(std::remove(v.begin(), v.end(), child), v.end());
0223     }
0224 }
0225 
0226 } // namespace Codethink::lvtldr