File indexing completed on 2024-05-19 05:42:09
0001 // ct_lvtldr_typenode.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_nodestorage.h> 0021 #include <ct_lvtldr_typenode.h> 0022 0023 namespace Codethink::lvtldr { 0024 0025 using namespace lvtshr; 0026 0027 // ========================== 0028 // class TypeNode 0029 // ========================== 0030 0031 TypeNode::TypeNode(NodeStorage& store): LakosianNode(store, std::nullopt) 0032 { 0033 // Only to be used on tests 0034 } 0035 0036 TypeNode::TypeNode(NodeStorage& store, 0037 std::optional<std::reference_wrapper<DatabaseHandler>> dbHandler, 0038 std::optional<TypeNodeFields> fields): 0039 LakosianNode(store, dbHandler), d_dbHandler(dbHandler), d_fields(*fields) 0040 { 0041 setName(d_fields.name); 0042 d_qualifiedNameParts = NamingUtils::buildQualifiedNamePrefixParts(d_fields.qualifiedName, "::"); 0043 } 0044 0045 TypeNode::~TypeNode() noexcept = default; 0046 0047 lvtshr::DiagramType TypeNode::type() const 0048 { 0049 return lvtshr::DiagramType::ClassType; 0050 } 0051 0052 lvtshr::UDTKind TypeNode::kind() const 0053 { 0054 return d_fields.kind; 0055 } 0056 0057 std::string TypeNode::qualifiedName() const 0058 { 0059 return NamingUtils::buildQualifiedName(d_qualifiedNameParts, name(), "::"); 0060 } 0061 0062 std::string TypeNode::parentName() 0063 { 0064 if (!d_fields.parentPackageId) { 0065 return ""; 0066 } 0067 // TODO 695: Should this be Package or Component? 0068 auto *node = d->store.findById({DiagramType::PackageType, *d_fields.parentPackageId}); 0069 return node->qualifiedName(); 0070 } 0071 0072 void TypeNode::setParentPackageId(Codethink::lvtshr::UniqueId::RecordNumberType id) 0073 { 0074 d_fields.parentPackageId = id; 0075 d_dbHandler->get().updateFields(d_fields); 0076 } 0077 0078 long long TypeNode::id() const 0079 { 0080 return d_fields.id; 0081 } 0082 0083 lvtshr::UniqueId TypeNode::uid() const 0084 { 0085 return {lvtshr::DiagramType::ClassType, id()}; 0086 } 0087 0088 LakosianNode::IsLakosianResult TypeNode::isLakosian() 0089 { 0090 return IsLakosianResult::IsLakosian; 0091 } 0092 0093 void TypeNode::loadParent() 0094 { 0095 if (d->parentLoaded) { 0096 return; 0097 } 0098 d->parentLoaded = true; 0099 0100 if (d_fields.classNamespaceId) { 0101 d->parent = d->store.findById({DiagramType::ClassType, *d_fields.classNamespaceId}); 0102 return; 0103 } 0104 0105 // If not found, use component for parent 0106 // unfortunately UDT <-> SourceComponent is many to many 0107 // although in the common case there should be only one component 0108 if (d_fields.componentIds.empty()) { 0109 d->parent = nullptr; 0110 return; 0111 } 0112 // If found, use the first component in the list 0113 // TODO: if necessary, figure out a good heuristic for this 0114 auto firstComponent = d_fields.componentIds[0]; 0115 d->parent = d->store.findById({DiagramType::ComponentType, firstComponent}); 0116 } 0117 0118 void TypeNode::loadChildren() 0119 { 0120 if (d->childrenLoaded) { 0121 return; 0122 } 0123 d_fields = d_dbHandler->get().getUdtFieldsById(d_fields.id); 0124 d->childrenLoaded = true; 0125 0126 d->children.clear(); 0127 d->children.reserve(d_fields.nestedTypeIds.size()); 0128 for (auto&& id : d_fields.nestedTypeIds) { 0129 LakosianNode *node = d->store.findById({DiagramType::ClassType, id}); 0130 d->children.push_back(node); 0131 } 0132 } 0133 0134 cpp::result<void, AddChildError> TypeNode::addChild(LakosianNode *child) 0135 { 0136 // don't add things twice. 0137 if (std::find(std::begin(d->children), std::end(d->children), child) != std::end(d->children)) { 0138 std::string errorString = "The entity " + child->name() + " is already a child of" + name(); 0139 return cpp::fail(AddChildError{errorString}); 0140 } 0141 0142 if (dynamic_cast<TypeNode *>(child) != nullptr) { 0143 d_fields.nestedTypeIds.emplace_back(child->id()); 0144 } 0145 0146 Q_EMIT onChildCountChanged(d->children.size()); 0147 d->children.push_back(child); 0148 return {}; 0149 } 0150 0151 bool TypeNode::isA(TypeNode *other) 0152 { 0153 // TODO: Lazy check instead of getting info from database 0154 d_fields = (d_dbHandler->get()).getUdtFieldsById(d_fields.id); 0155 0156 auto& v = d_fields.isAIds; 0157 return std::find(v.begin(), v.end(), other->id()) != v.end(); 0158 } 0159 0160 bool TypeNode::usesInTheImplementation(TypeNode *other) 0161 { 0162 // TODO: Lazy check instead of getting info from database 0163 d_fields = (d_dbHandler->get()).getUdtFieldsById(d_fields.id); 0164 0165 auto& v = d_fields.usesInImplementationIds; 0166 return std::find(v.begin(), v.end(), other->id()) != v.end(); 0167 } 0168 0169 bool TypeNode::usesInTheInterface(TypeNode *other) 0170 { 0171 // TODO: Lazy check instead of getting info from database 0172 d_fields = (d_dbHandler->get()).getUdtFieldsById(d_fields.id); 0173 0174 auto& v = d_fields.usesInInterfaceIds; 0175 return std::find(v.begin(), v.end(), other->id()) != v.end(); 0176 } 0177 0178 void TypeNode::loadProviders() 0179 { 0180 if (d->providersLoaded) { 0181 return; 0182 } 0183 d_fields = d_dbHandler->get().getUdtFieldsById(d_fields.id); 0184 d->providersLoaded = true; 0185 0186 d->providers.reserve(d_fields.isAIds.size() + d_fields.usesInInterfaceIds.size() 0187 + d_fields.usesInImplementationIds.size()); 0188 0189 for (auto&& id : d_fields.isAIds) { 0190 auto *node = d->store.findById({DiagramType::ClassType, id}); 0191 0192 // skip if one is a namepace of the other (or vice-versa) 0193 if (d_fields.classNamespaceId == node->id() 0194 || dynamic_cast<TypeNode *>(node)->classNamespaceId() == d_fields.id) { 0195 continue; 0196 } 0197 d->providers.emplace_back(lvtshr::LakosRelationType::IsA, node); 0198 } 0199 for (auto&& id : d_fields.usesInInterfaceIds) { 0200 auto *node = d->store.findById({DiagramType::ClassType, id}); 0201 0202 // skip if one is a namepace of the other (or vice-versa) 0203 if (d_fields.classNamespaceId == node->id() 0204 || dynamic_cast<TypeNode *>(node)->classNamespaceId() == d_fields.id) { 0205 continue; 0206 } 0207 d->providers.emplace_back(lvtshr::LakosRelationType::UsesInTheInterface, node); 0208 } 0209 for (auto&& id : d_fields.usesInImplementationIds) { 0210 auto *node = d->store.findById({DiagramType::ClassType, id}); 0211 0212 // skip if one is a namepace of the other (or vice-versa) 0213 if (d_fields.classNamespaceId == node->id() 0214 || dynamic_cast<TypeNode *>(node)->classNamespaceId() == d_fields.id) { 0215 continue; 0216 } 0217 d->providers.emplace_back(lvtshr::LakosRelationType::UsesInTheImplementation, node); 0218 } 0219 } 0220 0221 void TypeNode::loadClients() 0222 { 0223 if (d->clientsLoaded) { 0224 return; 0225 } 0226 d_fields = d_dbHandler->get().getUdtFieldsById(d_fields.id); 0227 d->clientsLoaded = true; 0228 0229 d->clients.reserve(d_fields.isBaseOfIds.size() + d_fields.usedByInterfaceIds.size() 0230 + d_fields.usedByImplementationIds.size()); 0231 0232 for (auto&& id : d_fields.isBaseOfIds) { 0233 auto *node = d->store.findById({DiagramType::ClassType, id}); 0234 0235 // skip if one is a namepace of the other (or vice-versa) 0236 if (d_fields.classNamespaceId == node->id() 0237 || dynamic_cast<TypeNode *>(node)->classNamespaceId() == d_fields.id) { 0238 continue; 0239 } 0240 d->clients.emplace_back(lvtshr::LakosRelationType::IsA, node); 0241 } 0242 for (auto&& id : d_fields.usedByInterfaceIds) { 0243 auto *node = d->store.findById({DiagramType::ClassType, id}); 0244 0245 // skip if one is a namepace of the other (or vice-versa) 0246 if (d_fields.classNamespaceId == node->id() 0247 || dynamic_cast<TypeNode *>(node)->classNamespaceId() == d_fields.id) { 0248 continue; 0249 } 0250 d->clients.emplace_back(lvtshr::LakosRelationType::UsesInTheInterface, node); 0251 } 0252 for (auto&& id : d_fields.usedByImplementationIds) { 0253 auto *node = d->store.findById({DiagramType::ClassType, id}); 0254 0255 // skip if one is a namepace of the other (or vice-versa) 0256 if (d_fields.classNamespaceId == node->id() 0257 || dynamic_cast<TypeNode *>(node)->classNamespaceId() == d_fields.id) { 0258 continue; 0259 } 0260 d->clients.emplace_back(lvtshr::LakosRelationType::UsesInTheImplementation, node); 0261 } 0262 } 0263 0264 void TypeNode::invalidateFieldNames() 0265 { 0266 d->fieldNames.clear(); 0267 d->fieldsLoaded = false; 0268 } 0269 0270 void TypeNode::addFieldName(std::string fieldName) 0271 { 0272 loadFields(); 0273 0274 // TODO: Really update the database 0275 d_fields.fieldNames.push_back(fieldName); 0276 d->fieldNames.push_back(fieldName); 0277 d->fieldsLoaded = true; 0278 } 0279 0280 void TypeNode::loadFields() 0281 { 0282 // TODO: Renaming needed as "field" refers to many things 0283 0284 if (d->fieldsLoaded) { 0285 return; 0286 } 0287 d_fields = d_dbHandler->get().getUdtFieldsById(d_fields.id); 0288 d->fieldsLoaded = true; 0289 0290 d->fieldNames.clear(); 0291 d->fieldNames.reserve(d_fields.fieldNames.size()); 0292 for (auto&& name : d_fields.fieldNames) { 0293 d->fieldNames.push_back(name); 0294 } 0295 } 0296 0297 bool TypeNode::hasClassNamespace() const 0298 { 0299 return d_fields.classNamespaceId.has_value(); 0300 } 0301 0302 lvtshr::UniqueId::RecordNumberType TypeNode::classNamespaceId() const 0303 { 0304 if (d_fields.classNamespaceId) { 0305 return *d_fields.classNamespaceId; 0306 } 0307 return -1; 0308 } 0309 0310 } // namespace Codethink::lvtldr