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

0001 // ct_lvtqtwc_tool_add_logical_relation.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_lvtqtc_tool_add_logical_relation.h>
0021 
0022 #include <ct_lvtqtc_graphicsscene.h>
0023 #include <ct_lvtqtc_graphicsview.h>
0024 #include <ct_lvtqtc_iconhelpers.h>
0025 #include <ct_lvtqtc_lakosentity.h>
0026 #include <ct_lvtqtc_undo_add_edge.h>
0027 #include <ct_lvtqtc_util.h>
0028 
0029 #include <ct_lvtldr_lakosiannode.h>
0030 #include <ct_lvtldr_nodestorage.h>
0031 #include <ct_lvtldr_typenode.h>
0032 
0033 #include <ct_lvtshr_functional.h>
0034 
0035 #include <QDebug>
0036 #include <QMessageBox>
0037 
0038 using namespace Codethink::lvtldr;
0039 
0040 namespace Codethink::lvtqtc {
0041 
0042 struct ToolAddLogicalRelation::Private {
0043     NodeStorage& nodeStorage;
0044 
0045     explicit Private(NodeStorage& nodeStorage): nodeStorage(nodeStorage)
0046     {
0047     }
0048 };
0049 
0050 ToolAddLogicalRelation::ToolAddLogicalRelation(const QString& name,
0051                                                const QString& tooltip,
0052                                                const QIcon& icon,
0053                                                GraphicsView *gv,
0054                                                Codethink::lvtldr::NodeStorage& nodeStorage):
0055     EdgeBasedTool(name, tooltip, icon, gv), d(std::make_unique<Private>(nodeStorage))
0056 {
0057 }
0058 
0059 ToolAddLogicalRelation::~ToolAddLogicalRelation() = default;
0060 
0061 bool ToolAddLogicalRelation::doRun(LakosEntity *fromItem, LakosEntity *toItem, lvtshr::LakosRelationType type)
0062 {
0063     auto *fromTypeNode = dynamic_cast<TypeNode *>(fromItem->internalNode());
0064     auto *toTypeNode = dynamic_cast<TypeNode *>(toItem->internalNode());
0065 
0066     auto result = d->nodeStorage.addLogicalRelation(fromTypeNode, toTypeNode, type);
0067     bool needsParentDependencies = false;
0068     if (result.has_error()) {
0069         using Kind = ErrorAddLogicalRelation::Kind;
0070 
0071         switch (result.error().kind) {
0072         case Kind::InvalidRelation: {
0073             Q_EMIT sendMessage(tr("Cannot create logical relation between those entities"), KMessageWidget::Error);
0074             return false;
0075         }
0076         case Kind::InvalidLakosRelationType: {
0077             assert(false && "Unexpected bad LogicalRelation Tool implementation");
0078             return false;
0079         }
0080         case Kind::SelfRelation: {
0081             Q_EMIT sendMessage(tr("Cannot create self-dependency"), KMessageWidget::Error);
0082             return false;
0083         }
0084         case Kind::AlreadyHaveDependency: {
0085             Q_EMIT sendMessage(tr("Those elements already have a dependency"), KMessageWidget::Error);
0086             return false;
0087         }
0088         case Kind::ParentDependencyRequired: {
0089             Q_EMIT sendMessage(
0090                 tr("Cannot create logical entity dependency without the parent entities having a dependency"),
0091                 KMessageWidget::Error);
0092             return false;
0093         }
0094         case Kind::ComponentDependencyRequired: {
0095             // do not show message box in debug mode. assume "yes".
0096             if (property("debug").isValid()) {
0097                 needsParentDependencies = true;
0098             } else {
0099                 auto ret = QMessageBox::question(
0100                     graphicsView(),
0101                     tr("Automatically add Dependencies?"),
0102                     tr("Parents are not connected, should we add the dependencies automatically?"));
0103                 if (ret == QMessageBox::No) {
0104                     Q_EMIT sendMessage(tr("We can only add a dependency if the parents are conected"),
0105                                        KMessageWidget::Error);
0106                     return false;
0107                 }
0108                 needsParentDependencies = true;
0109                 break;
0110             }
0111         }
0112         }
0113     }
0114 
0115     if (needsParentDependencies) {
0116         auto hierarchy = calculateHierarchy(fromItem, toItem);
0117         auto *macro = new QUndoCommand("Multiple Add Edges");
0118         for (const auto& pair : hierarchy) {
0119             const auto& iFrom = pair.first->internalNode();
0120             const auto& iTo = pair.second->internalNode();
0121             if (iFrom == iTo) {
0122                 // Ignore if we find a common ancestor. For instance, if two different components are inside a common
0123                 // package, do not try to connect the package to itself.
0124                 continue;
0125             }
0126 
0127             auto ret = d->nodeStorage.addPhysicalDependency(iFrom, iTo);
0128             if (ret.has_error()) {
0129                 Q_EMIT sendMessage(tr("Error connecting toplevel parents"), KMessageWidget::Error);
0130                 return false;
0131             }
0132 
0133             // See the `macro`  on the end the call, adding this on the child
0134             // list of the QUndoCommand parent. This is not leaking, Qt is just
0135             // weird sometimes.
0136             new UndoAddEdge(pair.first->qualifiedName(),
0137                             pair.second->qualifiedName(),
0138                             lvtshr::LakosRelationType::PackageDependency,
0139                             QtcUtil::UndoActionType::e_Add,
0140                             d->nodeStorage,
0141                             macro);
0142         }
0143 
0144         // try to add the edge again, now that the parents are ok.
0145         auto *fromNode = dynamic_cast<lvtldr::TypeNode *>(fromItem->internalNode());
0146         auto *toNode = dynamic_cast<lvtldr::TypeNode *>(toItem->internalNode());
0147         auto result = d->nodeStorage.addLogicalRelation(fromNode, toNode, type);
0148 
0149         // should not have an error, since we tested before, but we still need to test.
0150         if (result.has_error()) {
0151             Q_EMIT sendMessage(tr("Error connecting toplevel parents"), KMessageWidget::Error);
0152             return false;
0153         }
0154 
0155         new UndoAddEdge(fromNode->qualifiedName(),
0156                         toNode->qualifiedName(),
0157                         type,
0158                         QtcUtil::UndoActionType::e_Add,
0159                         d->nodeStorage,
0160                         macro);
0161 
0162         Q_EMIT undoCommandCreated(macro);
0163     } else {
0164         Q_EMIT undoCommandCreated(new UndoAddEdge(fromItem->qualifiedName(),
0165                                                   toItem->qualifiedName(),
0166                                                   type,
0167                                                   QtcUtil::UndoActionType::e_Add,
0168                                                   d->nodeStorage));
0169     }
0170     return true;
0171 }
0172 
0173 ToolAddIsARelation::ToolAddIsARelation(GraphicsView *gv, Codethink::lvtldr::NodeStorage& nodeStorage):
0174     ToolAddLogicalRelation(tr("Is A"),
0175                            tr("Creates an 'isA' relation between two elements"),
0176                            IconHelpers::iconFrom(":/icons/add_is_a"),
0177                            gv,
0178                            nodeStorage)
0179 {
0180 }
0181 
0182 bool ToolAddIsARelation::run(LakosEntity *source, LakosEntity *target)
0183 {
0184     return doRun(source, target, lvtshr::IsA);
0185 }
0186 
0187 ToolAddUsesInTheImplementation::ToolAddUsesInTheImplementation(GraphicsView *gv,
0188                                                                Codethink::lvtldr::NodeStorage& nodeStorage):
0189     ToolAddLogicalRelation(tr("Uses In The Impl"),
0190                            tr("Creates an 'Uses In The Implementation' relation between two elements"),
0191                            IconHelpers::iconFrom(":/icons/add_uses_in_the_implementation"),
0192                            gv,
0193                            nodeStorage)
0194 {
0195 }
0196 
0197 bool ToolAddUsesInTheImplementation::run(LakosEntity *source, LakosEntity *target)
0198 {
0199     return doRun(source, target, lvtshr::UsesInTheImplementation);
0200 }
0201 
0202 ToolAddUsesInTheInterface::ToolAddUsesInTheInterface(GraphicsView *gv, Codethink::lvtldr::NodeStorage& nodeStorage):
0203     ToolAddLogicalRelation(tr("Uses In The Interface"),
0204                            tr("Creates an 'Uses In The Interface' relation between two elements"),
0205                            IconHelpers::iconFrom(":/icons/add_uses_in_the_interface"),
0206                            gv,
0207                            nodeStorage)
0208 {
0209 }
0210 
0211 bool ToolAddUsesInTheInterface::run(LakosEntity *source, LakosEntity *target)
0212 {
0213     return doRun(source, target, lvtshr::UsesInTheInterface);
0214 }
0215 
0216 } // namespace Codethink::lvtqtc