diff --git a/dltmessageanalyzerplugin/src/CMakeLists.txt b/dltmessageanalyzerplugin/src/CMakeLists.txt index e68e8176..ed5e5392 100644 --- a/dltmessageanalyzerplugin/src/CMakeLists.txt +++ b/dltmessageanalyzerplugin/src/CMakeLists.txt @@ -46,6 +46,8 @@ add_library(antlr4_PCRE_static STATIC ${ANTLR_PCRE_CXX_OUTPUTS} ) +add_definitions(-DPUML_ENABLED) + set_property(TARGET antlr4_PCRE_static PROPERTY POSITION_INDEPENDENT_CODE ON) TARGET_LINK_LIBRARIES(antlr4_PCRE_static antlr4_static) @@ -103,7 +105,8 @@ target_link_libraries(DLT-Message-Analyzer DMA_filtersView_no_clang_tidy DMA_groupedView DMA_patternsView - DMA_PCRE) + DMA_PCRE + DMA_Plantuml) if(TURN_OFF_RCC) set(CMAKE_AUTORCC OFF) diff --git a/dltmessageanalyzerplugin/src/analyzer/CMTAnalyzer.hpp b/dltmessageanalyzerplugin/src/analyzer/CMTAnalyzer.hpp index 448d478c..e17ebf4f 100644 --- a/dltmessageanalyzerplugin/src/analyzer/CMTAnalyzer.hpp +++ b/dltmessageanalyzerplugin/src/analyzer/CMTAnalyzer.hpp @@ -108,6 +108,4 @@ private slots: tRequestId mRequestIdCounter; }; - - #endif // CDLTMESSAGEANALYZERCONTROLLER_HPP diff --git a/dltmessageanalyzerplugin/src/common/CMakeLists.txt b/dltmessageanalyzerplugin/src/common/CMakeLists.txt index 80ef9d0b..0e8a43e5 100644 --- a/dltmessageanalyzerplugin/src/common/CMakeLists.txt +++ b/dltmessageanalyzerplugin/src/common/CMakeLists.txt @@ -8,6 +8,7 @@ add_library(DMA_common STATIC CQtHelper.cpp) add_subdirectory(PCRE) +add_subdirectory(DMA_Plantuml) ################### QT #################################### target_link_libraries(DMA_common qdlt Qt5::Widgets ) diff --git a/dltmessageanalyzerplugin/src/common/DMA_Plantuml/CMakeLists.txt b/dltmessageanalyzerplugin/src/common/DMA_Plantuml/CMakeLists.txt new file mode 100644 index 00000000..4ff68bc1 --- /dev/null +++ b/dltmessageanalyzerplugin/src/common/DMA_Plantuml/CMakeLists.txt @@ -0,0 +1,2 @@ +add_library(DMA_Plantuml STATIC + DMA_Plantuml.cpp) diff --git a/dltmessageanalyzerplugin/src/common/DMA_Plantuml/DMA_Plantuml.cpp b/dltmessageanalyzerplugin/src/common/DMA_Plantuml/DMA_Plantuml.cpp new file mode 100644 index 00000000..bf9f42a4 --- /dev/null +++ b/dltmessageanalyzerplugin/src/common/DMA_Plantuml/DMA_Plantuml.cpp @@ -0,0 +1,901 @@ +#include + +#include "DMA_Plantuml.hpp" + +namespace DMA +{ + namespace PlantUML + { + static const std::string sNewLine("\n"); + static const std::string sPackageColor("#DDDDDD"); + static const std::string sIndentation(" "); + + /////////////////////////////tStringPtrWrapper/////////////////////////// + tStringPtrWrapper::tStringPtrWrapper(): pString(nullptr) + {} + + tStringPtrWrapper::tStringPtrWrapper(const tStringPtr& pString_): pString(pString_) + {} + + bool tStringPtrWrapper::operator< ( const tStringPtrWrapper& rVal ) const + { + bool bResult = false; + + if(pString == nullptr && rVal.pString != nullptr) + bResult = true; + else if(pString != nullptr && rVal.pString == nullptr) + bResult = false; + else if(pString == nullptr && rVal.pString == nullptr) + bResult = true; + else + { + if( *pString < *rVal.pString ) + { + bResult = true; + } + } + + return bResult; + } + + bool tStringPtrWrapper::operator== ( const tStringPtrWrapper& rVal ) const + { + if(pString == nullptr && rVal.pString != nullptr) + return false; + else if(pString != nullptr && rVal.pString == nullptr) + return false; + else if(pString == nullptr && rVal.pString == nullptr) + return true; + + return ( *pString == *rVal.pString ); + } + + bool tStringPtrWrapper::operator!= ( const tStringPtrWrapper& rVal ) const + { + return !( *this == rVal ); + } + ////////////////////////////////////////////////////////////////////////// + + /////////////////////////////CCreator/////////////////////////// + Creator::Creator(): + mPackageMap(), + mItemRegistry(), + mbIsinitialized(false), + mDataProtector() + {} + + Creator& Creator::getInstance() + { + static Creator result; + return result; + } + + void Creator::initialize() + { + std::lock_guard guard(mDataProtector); + + if(false == mbIsinitialized) + { + // at this point the model is formed, as this method is called somewhere in non-global area. + // The only thing we need to do is to assign dependency & inheritance pointers + + for(auto& packagePair : mPackageMap) + { + if(nullptr != packagePair.second) + { + for(auto& itemPair : packagePair.second->itemMap) + { + if(nullptr != itemPair.second) + { + auto& pItem = itemPair.second; + + auto& dependencyMap = pItem->getDependencyMap(); + + for(auto& dependencyPair : dependencyMap) + { + auto pDependency = dependencyPair.second; + + if(nullptr != pDependency) + { + auto foundItem = mItemRegistry.find(pDependency->itemName); + + if(foundItem != mItemRegistry.end() && nullptr != foundItem->second) + { + auto& pFoundItem = foundItem->second; + + pDependency->pToItem = pFoundItem; + + auto& dependentMap = pFoundItem->getDependentMap(); + tDependencyDataPtr pDependent = std::make_shared(); + pDependent->comment = pDependency->comment; + pDependent->pToItem = pItem; + pDependent->itemName = pItem->getItemName(); + pDependent->toNumber = pDependency->fromNumber; + pDependent->fromNumber = pDependency->toNumber; + pDependent->dependencyType = pDependency->dependencyType; + dependentMap[pItem->getItemName()] = pDependent; + } + } + } + + if(eItemType::eClass == pItem->getType()) + { + auto& inheritanceMap = pItem->getInheritanceMap(); + + for(auto& inheritancePair : inheritanceMap) + { + auto pInheritance = inheritancePair.second; + + if(nullptr != pInheritance) + { + auto foundItem = mItemRegistry.find(pInheritance->baseClass); + + if(foundItem != mItemRegistry.end() && nullptr != foundItem->second) + { + auto& pFoundItem = foundItem->second; + + pInheritance->pFromItem = pFoundItem; + } + } + } + } + } + } + } + } + + mbIsinitialized = true; + } + } + + bool Creator::isInitislized() const + { + bool bResult = false; + + { + std::lock_guard guard(*const_cast(&mDataProtector)); + bResult = mbIsinitialized; + } + + return bResult; + } + + static std::string getClassDiagramInternal(const tPackageMap& packageMap) + { + std::string diagram; + + if(false == packageMap.empty()) + { + int packageCounter = 0; + int packageSize = static_cast(packageMap.size()); + + diagram.append(sNewLine); + + diagram.append("@startuml").append(sNewLine); + diagram.append(sNewLine); + + for(const auto& packagePair : packageMap) + { + if(nullptr != packagePair.second) + { + const auto& pPackage = packagePair.second; + + diagram.append("package \"").append(*pPackage->packageName.pString).append("\" ").append(sPackageColor).append(sNewLine); + diagram.append("{").append(sNewLine); + diagram.append(sNewLine); + + auto appendMethods = [&diagram](const tMethodMap& methodMap) + { + for(const auto& methodPair : methodMap) + { + const auto& method = methodPair.second; + + diagram.append(sIndentation).append(method.accessModifier).append(" "); + + switch(method.methodType) + { + case eMethodType::eUsual: + { + diagram.append(*method.method.pString).append(sNewLine); + } + break; + case eMethodType::eStatic: + { + diagram.append("{static} ").append(*method.method.pString).append(sNewLine); + } + break; + case eMethodType::eVirtual: + { + diagram.append("virtual ").append(*method.method.pString).append(sNewLine); + } + break; + case eMethodType::eOverride: + { + diagram.append("virtual ").append(*method.method.pString).append(" override").append(sNewLine); + } + break; + case eMethodType::ePureVirtual: + { + diagram.append("{abstract} virtual ").append(*method.method.pString).append(" = 0").append(sNewLine); + } + break; + } + } + }; + + for(const auto& itemPair : pPackage->itemMap) + { + if(nullptr != itemPair.second) + { + auto pItem = itemPair.second; + + switch(pItem->getType()) + { + case eItemType::eClass: + { + diagram.append("class \""); + } + break; + case eItemType::eInterface: + { + diagram.append("interface \""); + } + break; + } + + diagram.append(*pItem->getItemName().pString).append("\"").append(sNewLine); + diagram.append("{").append(sNewLine); + + appendMethods(pItem->getMethodMap()); + + diagram.append("}").append(sNewLine); + diagram.append(sNewLine); + } + } + + diagram.append("}").append(sNewLine); + } + + if(packageCounter != packageSize - 1) + { + diagram.append(sNewLine); + } + + ++packageCounter; + } + + bool bShouldPlaceHeader = true; + + for(const auto& packagePair : packageMap) + { + if(nullptr != packagePair.second) + { + const auto& pPackage = packagePair.second; + + auto appendInheritance = [&diagram](const tInheritanceMap& inheritanceMap, const std::string& className) + { + for(const auto& inheritancePair : inheritanceMap) + { + auto& pInheritance = inheritancePair.second; + + if(nullptr != pInheritance) + { + diagram.append(*pInheritance->baseClass.pString).append(" <|-- ").append(className); + + if(false == pInheritance->comment.empty()) + { + diagram.append(" : ").append(pInheritance->comment); + } + + diagram.append(sNewLine); + } + } + }; + + for(const auto& itemPair : pPackage->itemMap) + { + if(nullptr != itemPair.second) + { + auto pItem = itemPair.second; + if(eItemType::eClass == pItem->getType()) + { + if(false == pItem->getInheritanceMap().empty() && true == bShouldPlaceHeader) + { + diagram.append(sNewLine); + diagram.append("\'====================Inheritance section===================="); + diagram.append(sNewLine); + bShouldPlaceHeader = false; + } + + appendInheritance(pItem->getInheritanceMap(), *pItem->getItemName().pString); + } + } + } + } + } + + bShouldPlaceHeader = true; + + for(const auto& packagePair : packageMap) + { + if(nullptr != packagePair.second) + { + const auto& pPackage = packagePair.second; + + auto appendDependencies = [&diagram](const tDependencyMap& dependencyMap, const std::string& className) + { + for(const auto& dependencyPair : dependencyMap) + { + auto pDependency = dependencyPair.second; + + diagram.append(className); + + if(false == pDependency->fromNumber.empty()) + { + diagram.append(" \"").append( pDependency->fromNumber ).append("\""); + } + + switch(pDependency->dependencyType) + { + case eDependencyType::eComposition: + { + diagram.append(" *-- "); + } + break; + case eDependencyType::eAggregation: + { + diagram.append(" o-- "); + } + break; + } + + if(false == pDependency->toNumber.empty()) + { + diagram.append("\"").append( pDependency->toNumber ).append("\" "); + } + + diagram.append(*pDependency->itemName.pString); + + if(false == pDependency->comment.empty()) + { + diagram.append(" : ").append(pDependency->comment); + } + + diagram.append(sNewLine); + } + }; + + for(const auto& itemPair : pPackage->itemMap) + { + if(nullptr != itemPair.second) + { + auto pItem = itemPair.second; + + if(false == pItem->getDependencyMap().empty() && true == bShouldPlaceHeader) + { + diagram.append(sNewLine); + diagram.append("\'====================Dependencies section===================="); + diagram.append(sNewLine); + bShouldPlaceHeader = false; + } + appendDependencies(pItem->getDependencyMap(), *pItem->getItemName().pString); + } + } + } + } + + diagram.append(sNewLine).append("@enduml"); + } + + return diagram; + } + + Creator::tClassDiagramResult Creator::getClassDiagram() const + { + tClassDiagramResult result; + + std::lock_guard guard(*const_cast(&mDataProtector)); + + result.diagramContent = getClassDiagramInternal(mPackageMap); + result.bIsSuccessful = true; + + return result; + } + + Creator::tClassDiagramResult Creator::getPackageClassDiagram(const std::string& packageName, + bool excludeDependencies) const + { + tClassDiagramResult result; + auto packageItemName = tStringPtrWrapper( std::make_shared(packageName) ); + + std::lock_guard guard(*const_cast(&mDataProtector)); + + auto foundPackage = mPackageMap.find(packageItemName); + + if(foundPackage != mPackageMap.end()) // package found + { + const auto& pRequestedPackage = foundPackage->second; + + // we should simply trace requested package, but in addition, add dependency + // and dependent items of all of its elements from the other packages. + + tPackageMap dumpMap; + + tPackageDataPtr pPackageFiltered = std::make_shared(); + pPackageFiltered->packageName = pRequestedPackage->packageName; + + for(const auto& itemPair : pRequestedPackage->itemMap) + { + const auto& pItem = itemPair.second; + + if(nullptr != pItem) + { + tIItemPtr pItemCopied = nullptr; + + switch(pItem->getType()) + { + case eItemType::eClass: + pItemCopied = std::make_shared(); + break; + case eItemType::eInterface: + pItemCopied = std::make_shared(); + break; + } + + pItemCopied->getParent() = pPackageFiltered; + pItemCopied->getItemName() = pItem->getItemName(); + pItemCopied->getMethodMap() = pItem->getMethodMap(); + pItemCopied->getDependentMap() = pItem->getDependentMap(); + pItemCopied->getDependencyMap() = pItem->getDependencyMap(); + + if(eItemType::eClass == pItem->getType()) + { + pItemCopied->getInheritanceMap() = pItem->getInheritanceMap(); + } + + pPackageFiltered->itemMap[pItemCopied->getItemName()] = pItemCopied; + } + } + + // we add add it to map at the beginning, as this value in map might + // be used within the loop. + dumpMap[pPackageFiltered->packageName] = pPackageFiltered; + + if(true == excludeDependencies) + { + // in this case let's exclude external dependencies from the package + for(const auto& itemPair : pPackageFiltered->itemMap) + { + const auto& pItem = itemPair.second; + + if(nullptr != pItem) + { + auto& dependencyMap = pItem->getDependencyMap(); + + for(auto it = dependencyMap.begin(); it != dependencyMap.end(); ) + { + auto& pDependency = it->second; + + if(nullptr != pDependency) + { + if(false == pDependency->pToItem.expired() + && false == pDependency->pToItem.lock()->getParent().expired()) + { + if(pDependency->pToItem.lock()->getParent().lock()->packageName != + pPackageFiltered->packageName) + { + it = dependencyMap.erase(it); + } + else + { + ++it; + } + } + else + { + ++it; + } + } + else + { + ++it; + } + } + + auto& dependentMap = pItem->getDependentMap(); + + for(auto it = dependentMap.begin(); it != dependentMap.end(); ) + { + auto& pDependent = it->second; + + if(false == pDependent.expired()) + { + if(false == pDependent.lock()->pToItem.expired() + && false == pDependent.lock()->pToItem.lock()->getParent().expired()) + { + if(pDependent.lock()->pToItem.lock()->getParent().lock()->packageName != + pPackageFiltered->packageName) + { + it = dependentMap.erase(it); + } + else + { + ++it; + } + } + else + { + ++it; + } + } + else + { + ++it; + } + } + } + } + } + + for(const auto& itemPair : pPackageFiltered->itemMap) + { + const auto& pItem = itemPair.second; + + if(nullptr != pItem) + { + if(false == excludeDependencies) + { + auto fillInDependency = [&dumpMap](const tDependencyMap& dependencyMap) + { + for(const auto& dependencyPair : dependencyMap) + { + auto& pDependency = dependencyPair.second; + + if(nullptr != pDependency) + { + if(false == pDependency->pToItem.expired()) + { + auto pToItem = pDependency->pToItem.lock(); + + if(nullptr != pToItem && false == pToItem->getParent().expired()) + { + auto pParentPackage = pToItem->getParent().lock(); + + if(nullptr != pParentPackage) + { + auto addFilteredItem = [&pToItem](const tPackageDataPtr& pDependencyPackage) + { + if(nullptr != pDependencyPackage) + { + auto foundItem = pDependencyPackage->itemMap.find(pToItem->getItemName()); + + if(foundItem == pDependencyPackage->itemMap.end()) + { + tIItemPtr pDependencyItemFiltered_ = nullptr; + + switch(pToItem->getType()) + { + case eItemType::eClass: + pDependencyItemFiltered_ = std::make_shared(); + break; + case eItemType::eInterface: + pDependencyItemFiltered_ = std::make_shared(); + break; + } + + pDependencyItemFiltered_->getParent() = pDependencyPackage; + pDependencyItemFiltered_->getItemName() = pToItem->getItemName(); + pDependencyItemFiltered_->getMethodMap() = pToItem->getMethodMap(); + + pDependencyPackage->itemMap[pDependencyItemFiltered_->getItemName()] = pDependencyItemFiltered_; + } + } + }; + + auto foundPackage_ = dumpMap.find(pParentPackage->packageName); + + if(foundPackage_ != dumpMap.end()) + { + auto pFoundPackage_ = foundPackage_->second; + auto& pDependencyPackage = foundPackage_->second; + addFilteredItem(pDependencyPackage); + } + else + { + tPackageDataPtr pDependencyPackage = std::make_shared(); + pDependencyPackage->packageName = pParentPackage->packageName; + + addFilteredItem(pDependencyPackage); + + dumpMap[pDependencyPackage->packageName] = pDependencyPackage; + } + } + } + } + } + } + }; + + fillInDependency(pItem->getDependencyMap()); + + tDependencyMap dependentLockedMap; + + for(const auto& dependentPair : pItem->getDependentMap()) + { + auto& pDependentWeak = dependentPair.second; + + if(false == pDependentWeak.expired()) + { + auto pDependent = pDependentWeak.lock(); + dependentLockedMap[pDependent->itemName] = pDependent; + } + } + + fillInDependency(dependentLockedMap); + } + + auto fillInInheritance = [&dumpMap](const tInheritanceMap& inheritanceMap) + { + for(const auto& inharitancePair : inheritanceMap) + { + auto& pInheritance = inharitancePair.second; + + if(nullptr != pInheritance) + { + if(false == pInheritance->pFromItem.expired()) + { + auto pFromItem = pInheritance->pFromItem.lock(); + + if(nullptr != pFromItem && false == pFromItem->getParent().expired()) + { + auto pParentPackage = pFromItem->getParent().lock(); + + if(nullptr != pParentPackage) + { + auto addFilteredItem = [&pFromItem](const tPackageDataPtr& pInheritancePackage) + { + if(nullptr != pInheritancePackage) + { + auto foundItem = pInheritancePackage->itemMap.find(pFromItem->getItemName()); + + if(foundItem == pInheritancePackage->itemMap.end()) + { + tIItemPtr pInheritanceItemFiltered_ = nullptr; + + switch(pFromItem->getType()) + { + case eItemType::eClass: + pInheritanceItemFiltered_ = std::make_shared(); + break; + case eItemType::eInterface: + pInheritanceItemFiltered_ = std::make_shared(); + break; + } + + pInheritanceItemFiltered_->getParent() = pInheritancePackage; + pInheritanceItemFiltered_->getItemName() = pFromItem->getItemName(); + pInheritanceItemFiltered_->getMethodMap() = pFromItem->getMethodMap(); + + pInheritancePackage->itemMap[pInheritanceItemFiltered_->getItemName()] = pInheritanceItemFiltered_; + } + } + }; + + auto foundPackage_ = dumpMap.find(pParentPackage->packageName); + + if(foundPackage_ != dumpMap.end()) + { + auto pFoundPackage_ = foundPackage_->second; + auto& pInheritancePackage = foundPackage_->second; + addFilteredItem(pInheritancePackage); + } + else + { + tPackageDataPtr pInheritancePackage = std::make_shared(); + pInheritancePackage->packageName = pParentPackage->packageName; + + addFilteredItem(pInheritancePackage); + + dumpMap[pInheritancePackage->packageName] = pInheritancePackage; + } + } + } + } + } + } + }; + + fillInInheritance(pItem->getInheritanceMap()); + } + } + + result.diagramContent = getClassDiagramInternal(dumpMap); + result.bIsSuccessful = true; + } + else + { + result.error.append("Error: package with name \"").append(packageName).append("\" was not found!"); + } + + return result; + } + + tStringPtrSet Creator::findPackagesByName( const std::string& packageName ) const + { + tStringPtrSet result; + + { + auto toLowerCandidate = packageName; + std::transform(toLowerCandidate.begin(), toLowerCandidate.end(), toLowerCandidate.begin(), + [](unsigned char c){ return std::tolower(c); }); + + std::lock_guard guard(*const_cast(&mDataProtector)); + + for(const auto& packagePair : mPackageMap) + { + if(nullptr != packagePair.first.pString) + { + auto toLowerPackageName = *packagePair.second->packageName.pString; + std::transform(toLowerPackageName.begin(), toLowerPackageName.end(), toLowerPackageName.begin(), + [](unsigned char c){ return std::tolower(c); }); + + if(toLowerPackageName.rfind(toLowerCandidate, 0) == 0) + { + result.insert(packagePair.first.pString); + } + } + } + } + + return result; + } + + void Creator::addItem( const tItemName& packageName, const tIItemPtr& pItemData ) + { + if(nullptr != pItemData) + { + std::lock_guard guard(mDataProtector); + + auto foundPackage = mPackageMap.find(packageName); + + if(foundPackage != mPackageMap.end()) + { + pItemData->getParent() = foundPackage->second; + foundPackage->second->itemMap[pItemData->getItemName()] = pItemData; + } + else + { + tPackageDataPtr pPackageData = std::make_shared(); + pItemData->getParent() = pPackageData; + pPackageData->itemMap[pItemData->getItemName()] = pItemData; + pPackageData->packageName = packageName; + mPackageMap[pPackageData->packageName] = pPackageData; + } + + mItemRegistry[pItemData->getItemName()] = pItemData; + } + } + //////////////////////////////////////////////////////////////// + + /////////////////////////////Item/////////////////////////////// + IItem::~IItem(){} + /////////////////////////////Item ( end )/////////////////////// + + /////////////////////////////tBaseData////////////////////////// + tItemName& tBaseData::getItemName() + { + return itemName; + } + + tDependencyMap& tBaseData::getDependencyMap() + { + return dependencyMap; + } + + tInheritanceMap& tBaseData::getInheritanceMap() + { + static tInheritanceMap inheritanceMap; + return inheritanceMap; + } + + tDependencyWeakMap& tBaseData::getDependentMap() + { + return dependentMap; + } + + tPackageDataWeakPtr& tBaseData::getParent() + { + return pParent; + } + + tMethodMap& tBaseData::getMethodMap() + { + return methodMap; + } + /////////////////////////////tBaseData ( end )////////////////// + + /////////////////////////////tClassData///////////////////////// + const eItemType& tClassData::getType() const + { + static const eItemType res = eItemType::eClass; + return res; + } + + tInheritanceMap& tClassData::getInheritanceMap() + { + return inheritanceMap; + } + /////////////////////////////tClassData ( end )///////////////// + + /////////////////////////////tInterfaceData///////////////////// + const eItemType& tInterfaceData::getType() const + { + static const eItemType res = eItemType::eInterface; + return res; + } + /////////////////////////////tInterfaceData ( end )///////////// + + /////////////////////////////Other////////////////////////////// + tCallOnCreate::tCallOnCreate( const std::function& callable ) + { + if(callable) + { + callable(); + } + } + + static std::string getAccessModifier( const std::string& candidate ) + { + std::string result; + + if(candidate != "+" && candidate != "#" && candidate != "~" && candidate != "-") // if user has provided garbage + { + result = "+"; // let's fallback too public method + } + else // otherwise + { + result = candidate; // let's return user's input + } + + return result; + } + + tMethodData::tMethodData(): + accessModifier("+"), + method(), + methodType(eMethodType::eUsual) + {} + + tMethodData::tMethodData(const tAccessModifier& accessModifier_, + const std::string& method_, + const eMethodType& methodType_): + accessModifier(accessModifier_), + method(tStringPtrWrapper(std::make_shared(method_))), + methodType(methodType_) + {} + + tDependencyData::tDependencyData(): + dependencyType(eDependencyType::eComposition), + itemName(), + pToItem(), + fromNumber(), + toNumber(), + comment() + {} + + tDependencyData::tDependencyData(const eDependencyType& dependencyType_, + const std::string& itemName_, + const tIItemPtr& pToItem_, + const std::string& fromNumber_, + const std::string& toNumber_, + const std::string& comment_): + dependencyType(dependencyType_), + itemName(tStringPtrWrapper(std::make_shared(itemName_))), + pToItem(pToItem_), + fromNumber(fromNumber_), + toNumber(toNumber_), + comment(comment_) + {} + /////////////////////////////Other ( end )////////////////////// + } +} diff --git a/dltmessageanalyzerplugin/src/common/DMA_Plantuml/DMA_Plantuml.hpp b/dltmessageanalyzerplugin/src/common/DMA_Plantuml/DMA_Plantuml.hpp new file mode 100644 index 00000000..eb88a0aa --- /dev/null +++ b/dltmessageanalyzerplugin/src/common/DMA_Plantuml/DMA_Plantuml.hpp @@ -0,0 +1,702 @@ +/** + * @file DMA_Plantuml.hpp + * @author vgoncharuk + * @brief Declaration of the DMA_Plantuml concept + */ +#ifndef DMA_COMPONENTS_IBASICITEM_HPP +#define DMA_COMPONENTS_IBASICITEM_HPP + +/** + * + * Module: DMA_Plantuml + * Version: 1.0.0 + * Author: Vladyslav Goncharuk ( svlad1990@gmail.com ) + * + * //////////////////////////////////////////////////////////////////////////// + * + * Brief + * + * Where am I? + * + * You are inside the header of the DMA_Plantuml concept - the poor man's C++ + * to platuml converter + * + * What it does? It allows you to declare a metadata for your classes, which is + * then allows your application to produce plantuml class diagrams regarding + * its own entities. + * + * Motivation: + * + * Firstly, as a developer I want documentation of my source code to be located + * as near as possible to the code itself. + * + * Secondly, I do not want to use existing tools, which are generating the + * diagrams during the build. Integration of such tools usually needs a lot of + * efforts to configure them properly, and, at final point, the result diagram + * is representing either the whole application ( which is overloaded with + * classes ), or only a single class ( which is not informative ). + * + * Thirdly, when user states, that there is an issue in some version of the SW, + * I want to be able to get class diagrams from the SW itself, without + * referring to a specific base-line in git. + * + * Fourthly, from time to time I want to be able to review and adjust the + * design of my application. When I'm doing this, I want to be able to + * instantly see the result of code modification in a diagram, without the + * need to refer to a thirdparty tools or files. + * + * Fifthly, currently existing plugins for IDE-s, which allow to visulize plantuml + * comments in the source code are not sufficient, as I want to review not only + * the diagram of a specific single class, but also connections between + * multiple classes, interfaces, packages, etc. Current concept supports + * filtering out the diagram based on certain package. You are able to request + * creation of diagram for the specified package and its nearest connections. + * + * As of now, this concept is manual, but, for sure, later on it can be used as + * a core module for a generator, which will produce the metadata macro- + * definitions on the fly. + * + * General idea of this concept is to allow you to have UML data ( mainly class + * diagrams ) of your app at your fingertips. From my previous experience, the + * more overhead you have to create the documentation, the poorer it would be, + * mainly due to lack of capacity to support it. With this small lib I try to + * have a proper tool, which I will use at least for my own projects ☺ + * + * ////////////////////////////////////////////////////// + * + * How to use it? + * + * Step 1 ( optional ). Declare some class hierarchy, for which you will + * declare the plantuml metadata afterwards. + * + * ////////////////////////////////////////////////////// + * + * Step 2 ( mandatory ). In your cpp(s) add declarations of your classes, using + * provided macro-based API. + * + * Example: + * + * > PUML_PACKAGE_BEGIN(test_main) + * > PUML_INTERFACE_BEGIN(IInterface) + * > PUML_VIRTUAL_METHOD(+, ~IInterface()) + * > PUML_VIRTUAL_METHOD(+, void virtFunc() ) + * > PUML_PURE_VIRTUAL_METHOD(+, void pureVirtFunc(const int& val1, const int& val2) ) + * > PUML_INTERFACE_END() + * > + * > PUML_CLASS_BEGIN(CImpl_Test) + * > PUML_INHERITANCE(IInterface, implements) + * > PUML_INHERITANCE(IInterfaceHelper, implements) + * > PUML_INHERITANCE(UndeclaredClass, extends) + * > PUML_METHOD(+, CImpl_Test()) + * > PUML_OVERRIDE_METHOD(+, void virtFunc() ) + * > PUML_OVERRIDE_METHOD(+, void pureVirtFunc(const int& val1, const int& val2) ) + * > PUML_STATIC_METHOD(+, void myStaticMethod() ) + * > PUML_COMPOSITION_DEPENDENCY(CompositionDependency, 1, 1, contains) + * > PUML_AGGREGATION_DEPENDENCY(AggregationDependency, 1, 1, uses) + * > PUML_CLASS_END() + * > PUML_PACKAGE_END() + * > + * > PUML_PACKAGE_BEGIN(test_helper) + * > + * > PUML_INTERFACE_BEGIN(IInterfaceHelper) + * > PUML_INTERFACE_END() + * > + * > PUML_INTERFACE_BEGIN(IDependency) + * > PUML_INTERFACE_END() + * > + * > PUML_CLASS_BEGIN(CompositionDependency) + * > PUML_INHERITANCE(IDependency, implements) + * > PUML_AGGREGATION_DEPENDENCY(ExternalDependency, 1, 1, uses) + * > PUML_CLASS_END() + * > + * > PUML_CLASS_BEGIN(AggregationDependency) + * > PUML_INHERITANCE(IDependency, implements) + * > PUML_AGGREGATION_DEPENDENCY(ExternalDependency, 1, 1, uses) + * > PUML_CLASS_END() + * > + * > PUML_CLASS_BEGIN(CImpl_Helper) + * > PUML_INHERITANCE(IInterface, implements) + * > PUML_METHOD(+, CImpl_Helper()) + * > PUML_OVERRIDE_METHOD(+, void virtFunc() ) + * > PUML_OVERRIDE_METHOD(+, void pureVirtFunc(const int& val1, const int& val2) ) + * > PUML_STATIC_METHOD(+, void myStaticMethod() ) + * > PUML_CLASS_END() + * > + * > PUML_CLASS_BEGIN(CImpl_HelperExtended) + * > PUML_INHERITANCE(CImpl_Helper, extends) + * > PUML_METHOD(+, CImpl_HelperExtended()) + * > PUML_OVERRIDE_METHOD(+, void virtFunc() ) + * > PUML_CLASS_END() + * > + * > PUML_PACKAGE_END() + * > + * > PUML_PACKAGE_BEGIN(test_external) + * > PUML_CLASS_BEGIN(ExternalDependency) + * > PUML_CLASS_END() + * > PUML_PACKAGE_END() + * + * Some rules and tips regarding the macro definitions: + * - Package is mandatory. You can not declare anything outside the package. + * You can try but you will get a compilcaiton error, as macro definitions are + * created to work only in that way. Otherwise produced C++ code won't be valid. + * That is my shoutout to the component architecture. Declare packages! + * - You can declare the same package in different files. E.g. if you have a + * package with dozens of files there is no need to have a seperate cpp in + * order to store the whole metadata of the package there. No! Idea is to + * declare metadata of the specific entities in their own cpp files. Even if + * multiple classes are related to the same package. + * - From the other side you can't split the definition of metadata for the + * classes and interfaces. There should be only one definition of entity with + * its unique name within a package. In case of multiple definitions of an + * element the "last win" strategy will be applied. So you can get partial + * data ☺ + * - Class has: methods, inheritance, dependencies + * - Interface has: methods, dependencies. No inheritance. + * - All parameters of the macro definitions should be placed without the + * quotes. Stringification will be done inside the macro definitions + * - Concept does NOT contain a type check mechanism. From one side that allows + * you to register some garbage. But from the other - it allows to create + * design for non-existing classes, which is useful. + * - It is better to specify class names with namespaces in order to avoid + * collisions between the entities. E.g. dependencies and inheritance are + * searched by their names without considering name of the package. Thus, if + * you will have the package_1::class_1 and package_2::class_1, then dependency + * to class_1 might lead to ambiguation, when random data will be chosen. + * - All internal search functionality is CASE SENSITIVE. Thus, metadata + * declarations should also consider this. Only Creator::findPackagesByName does + * a case-insensitive search. + * - Be aware, that you will need a graphwiz installed in order to get plantuml + * class diagrams. It is better to install version 2.38 of it. + * - There is NO support of the nested packages, interfaces or classes. Only + * package->class and package->interface folding. + * - keywords virtual, override and "=0" should NOT be added together with + * the method's definition. They will be added implicitly. + * - Dependencies of class or interface to itself will be ignored. + * + * ////////////////////////////////////////////////////// + * + * Step 3 ( mandatory ). Somewhere in your code ( in non-global section ) call: + * + * DMA::PlantUML::Creator::getInstance().initialize(); + * + * That will finish initialization of the model. + * + * ////////////////////////////////////////////////////// + * + * Step 4. To obtain the full class diagram of your application, do the + * following thing in your source code: + * + * > auto diagramResult = DMA::PlantUML::Creator::getInstance().getClassDiagram(); + * > + * > if(true == diagramResult.bIsSuccessful) + * > { + * > // do something with diagramResult.diagramContent + * > } + * > else + * > { + * > // dump diagramResult.error + * > } + * ////////////////////////////////////////////////////// + * + * Step 5. To obtain partial class diagram of the specific package, do the + * following thing in your source code: + * + * > auto diagramResult = DMA::PlantUML::Creator::getInstance().getPackageClassDiagram("test"); + * > + * > if(true == diagramResult.bIsSuccessful) + * > { + * > SEND_MSG(QString::fromStdString(diagramResult.diagramContent)); + * > } + * > else + * > { + * > SEND_ERR(QString::fromStdString(diagramResult.error)); + * > } + * + * ////////////////////////////////////////////////////// + * + * Step 6 ( mandatory ). Add the following define to your build: + * + * > #define PUML_ENABLED + * + * Without it, all macro definitions which you've used will produce nothing. + * Sometimes ( in release builds ) this define can be turned off by purpose in + * order to avoid runtime overheads. + * + * Example of resulting diagram, which is based on the model from step 2: + * + * > @startuml + * > + * > package "test_external" #DDDDDD + * > { + * > + * > class "ExternalDependency" + * > { + * > } + * > + * > } + * > + * > package "test_helper" #DDDDDD + * > { + * > + * > class "AggregationDependency" + * > { + * > } + * > + * > class "CImpl_Helper" + * > { + * > + CImpl_Helper() + * > + {static} void myStaticMethod() + * > + virtual void pureVirtFunc(const int& val1, const int& val2) override + * > + virtual void virtFunc() override + * > } + * > + * > class "CImpl_HelperExtended" + * > { + * > + CImpl_HelperExtended() + * > + virtual void virtFunc() override + * > } + * > + * > class "CompositionDependency" + * > { + * > } + * > + * > interface "IDependency" + * > { + * > } + * > + * > interface "IInterfaceHelper" + * > { + * > } + * > + * > } + * > + * > package "test_main" #DDDDDD + * > { + * > + * > class "CImpl_Test" + * > { + * > + CImpl_Test() + * > + {static} void myStaticMethod() + * > + virtual void pureVirtFunc(const int& val1, const int& val2) override + * > + virtual void virtFunc() override + * > } + * > + * > interface "IInterface" + * > { + * > + {abstract} virtual void pureVirtFunc(const int& val1, const int& val2) = 0 + * > + virtual void virtFunc() + * > + virtual ~IInterface() + * > } + * > + * > } + * > + * > '====================Inheritance section==================== + * > IDependency <|-- AggregationDependency : implements + * > IInterface <|-- CImpl_Helper : implements + * > CImpl_Helper <|-- CImpl_HelperExtended : extends + * > IDependency <|-- CompositionDependency : implements + * > IInterface <|-- CImpl_Test : implements + * > IInterfaceHelper <|-- CImpl_Test : implements + * > UndeclaredClass <|-- CImpl_Test : extends + * > + * > '====================Dependencies section==================== + * > AggregationDependency "1" o-- "1" ExternalDependency : uses + * > CompositionDependency "1" o-- "1" ExternalDependency : uses + * > CImpl_Test "1" o-- "1" AggregationDependency : uses + * > CImpl_Test "1" *-- "1" CompositionDependency : contains + * > + * > @enduml + * + */ + +//////////// dependencies to the standard library //////////// +#include +#include +#include +#include +#include +#include +#include +//////////// dependencies to the standard library ( end ) //// + +#define COMBINE1(X,Y) X##Y // helper macro +#define COMBINE(X,Y) COMBINE1(X,Y) + +///////////////////////////////////////////////////////////////////// +//////////////////////////////MACRO API////////////////////////////// +///////////////////////////////////////////////////////////////////// + +/* + * In case if PUML_ENABLED is not defined, all macro definitions will produce + * nothing. That can be used to turn off plantuml functionality in the release + * builds. + */ +#ifndef PUML_ENABLED + +#define PUML_PACKAGE_BEGIN( PACKAGE_NAME ) +#define PUML_PACKAGE_END() +#define PUML_INTERFACE_BEGIN( INTERFACE_NAME ) +#define PUML_INTERFACE_END() +#define PUML_CLASS_BEGIN( CLASS_NAME ) +#define PUML_CLASS_END() +#define PUML_VIRTUAL_METHOD( ACCESS_MODIFIER, METHOD ) +#define PUML_PURE_VIRTUAL_METHOD( ACCESS_MODIFIER, METHOD ) +#define PUML_METHOD( ACCESS_MODIFIER, METHOD ) +#define PUML_OVERRIDE_METHOD( ACCESS_MODIFIER, METHOD ) +#define PUML_STATIC_METHOD( ACCESS_MODIFIER, METHOD ) +#define PUML_INHERITANCE( BASE_CLASS, COMMENT ) +#define PUML_COMPOSITION_DEPENDENCY( ITEM, USING_NUMBER, USED_NUMBER, COMMENT ) +#define PUML_AGGREGATION_DEPENDENCY( ITEM, USING_NUMBER, USED_NUMBER, COMMENT ) + +#else + +#define PUML_PACKAGE_BEGIN( PACKAGE_NAME )\ +static DMA::PlantUML::tCallOnCreate COMBINE(PACKAGE_NAME##_##PackageRegistration_, __LINE__)\ +([](){\ +\ +DMA::PlantUML::tStringPtrWrapper pPackageName = std::make_shared(#PACKAGE_NAME);\ + +#define PUML_PACKAGE_END()\ +}); + +#define PUML_INTERFACE_BEGIN( INTERFACE_NAME )\ +{\ + DMA::PlantUML::tInterfaceDataPtr pItem = std::make_shared();\ + pItem->itemName = DMA::PlantUML::tStringPtrWrapper( std::make_shared(#INTERFACE_NAME) ); + +#define PUML_INTERFACE_END()\ + DMA::PlantUML::Creator::getInstance().addItem(pPackageName, pItem);\ +} + +#define PUML_CLASS_BEGIN( CLASS_NAME )\ +{\ + DMA::PlantUML::tClassDataPtr pItem = std::make_shared();\ + pItem->itemName = DMA::PlantUML::tStringPtrWrapper( std::make_shared(#CLASS_NAME) ); + +#define PUML_CLASS_END()\ + DMA::PlantUML::Creator::getInstance().addItem(pPackageName, pItem);\ +} + +#define PUML_VIRTUAL_METHOD( ACCESS_MODIFIER, METHOD )\ +{\ + DMA::PlantUML::tMethodData methodData( #ACCESS_MODIFIER, #METHOD, DMA::PlantUML::eMethodType::eVirtual );\ + pItem->methodMap[methodData.method] = methodData;\ +} + +#define PUML_PURE_VIRTUAL_METHOD( ACCESS_MODIFIER, METHOD )\ +{\ + DMA::PlantUML::tMethodData methodData( #ACCESS_MODIFIER, #METHOD, DMA::PlantUML::eMethodType::ePureVirtual );\ + pItem->methodMap[methodData.method] = methodData;\ +} + +#define PUML_METHOD( ACCESS_MODIFIER, METHOD )\ +{\ + DMA::PlantUML::tMethodData methodData( #ACCESS_MODIFIER, #METHOD, DMA::PlantUML::eMethodType::eUsual );\ + pItem->methodMap[methodData.method] = methodData;\ +} + +#define PUML_OVERRIDE_METHOD( ACCESS_MODIFIER, METHOD )\ +{\ + DMA::PlantUML::tMethodData methodData( #ACCESS_MODIFIER, #METHOD, DMA::PlantUML::eMethodType::eOverride );\ + pItem->methodMap[methodData.method] = methodData;\ +} + +#define PUML_STATIC_METHOD( ACCESS_MODIFIER, METHOD )\ +{\ + DMA::PlantUML::tMethodData methodData( #ACCESS_MODIFIER, #METHOD, DMA::PlantUML::eMethodType::eStatic );\ + pItem->methodMap[methodData.method] = methodData;\ +} + +#define PUML_INHERITANCE( BASE_CLASS, COMMENT )\ +{\ + DMA::PlantUML::tInheritanceDataPtr pInheritanceData = std::make_shared();\ + pInheritanceData->comment = #COMMENT;\ + pInheritanceData->baseClass = DMA::PlantUML::tStringPtrWrapper( std::make_shared(#BASE_CLASS) );\ + pItem->inheritanceMap[pInheritanceData->baseClass] = pInheritanceData;\ +} + +#define PUML_COMPOSITION_DEPENDENCY( ITEM, USING_NUMBER, USED_NUMBER, COMMENT )\ +{\ + DMA::PlantUML::tDependencyDataPtr pDependencyData =\ + std::make_shared(DMA::PlantUML::eDependencyType::eComposition,\ + #ITEM,\ + nullptr,\ + #USING_NUMBER,\ + #USED_NUMBER,\ + #COMMENT);\ +\ + pItem->dependencyMap[pDependencyData->itemName] = pDependencyData;\ +} + +#define PUML_AGGREGATION_DEPENDENCY( ITEM, USING_NUMBER, USED_NUMBER, COMMENT )\ +{\ + DMA::PlantUML::tDependencyDataPtr pDependencyData =\ + std::make_shared(DMA::PlantUML::eDependencyType::eAggregation,\ + #ITEM,\ + nullptr,\ + #USING_NUMBER,\ + #USED_NUMBER,\ + #COMMENT);\ +\ + pItem->dependencyMap[pDependencyData->itemName] = pDependencyData;\ +} + +#endif + +namespace DMA +{ + namespace PlantUML + { + typedef std::shared_ptr tStringPtr; + typedef std::set tStringPtrSet; + + struct tStringPtrWrapper + { + tStringPtrWrapper(); + tStringPtrWrapper(const tStringPtr& pString_); + bool operator== ( const tStringPtrWrapper& rVal ) const; + bool operator!= ( const tStringPtrWrapper& rVal ) const; + bool operator< ( const tStringPtrWrapper& rVal ) const; + tStringPtr pString = nullptr; + }; + + typedef tStringPtrWrapper tItemName; + typedef std::string tComment; + + // forward declarations + class IItem; + typedef std::shared_ptr tIItemPtr; + typedef std::weak_ptr tIItemWeakPtr; + typedef std::map tItemMap; + struct tPackageData; + typedef std::shared_ptr tPackageDataPtr; + typedef std::weak_ptr tPackageDataWeakPtr; + // forward declarations ( end ) + + enum class eDependencyType + { + eComposition = 0, + eAggregation + }; + + struct tDependencyData + { + tDependencyData(); + tDependencyData(const eDependencyType& dependencyType_, + const std::string& itemName_, + const tIItemPtr& pToItem_, + const std::string& fromNumber_, + const std::string& toNumber_, + const std::string& comment_); + eDependencyType dependencyType; + tItemName itemName; + tIItemWeakPtr pToItem; + std::string fromNumber; + std::string toNumber; + std::string comment; + }; + + typedef std::shared_ptr tDependencyDataPtr; + typedef std::weak_ptr tDependencyDataWeakPtr; + + typedef std::map tDependencyMap; + typedef std::map tDependencyWeakMap; + + struct tInheritanceData + { + tItemName baseClass; + tIItemWeakPtr pFromItem; + tComment comment; + }; + + typedef std::shared_ptr tInheritanceDataPtr; + + typedef std::map tInheritanceMap; + + typedef tStringPtrWrapper tMethod; + + typedef std::string tAccessModifier; + + enum class eMethodType + { + eUsual = 0, + eVirtual, + ePureVirtual, + eOverride, + eStatic + }; + + struct tMethodData + { + tMethodData(); + tMethodData(const tAccessModifier& accessModifier_, + const std::string& method_, + const eMethodType& methodType_); + tAccessModifier accessModifier; + tMethod method; + eMethodType methodType; + }; + + typedef std::map tMethodMap; + + enum class eItemType + { + eInterface = 0, + eClass = 1 + }; + + class IItem + { + public: + virtual ~IItem(); + virtual const eItemType& getType() const = 0; + virtual tItemName& getItemName() = 0; + virtual tDependencyMap& getDependencyMap() = 0; + virtual tDependencyWeakMap& getDependentMap() = 0; + virtual tInheritanceMap& getInheritanceMap() = 0; + virtual tPackageDataWeakPtr& getParent() = 0; + virtual tMethodMap & getMethodMap() = 0; + }; + + struct tBaseData : public IItem + { + virtual tItemName& getItemName() override; + virtual tDependencyMap& getDependencyMap() override; + virtual tInheritanceMap& getInheritanceMap() override; + virtual tDependencyWeakMap& getDependentMap() override; + virtual tPackageDataWeakPtr& getParent() override; + virtual tMethodMap& getMethodMap() override; + + tItemName itemName; + tMethodMap methodMap; + tDependencyMap dependencyMap; + tDependencyWeakMap dependentMap; + tPackageDataWeakPtr pParent; + }; + + struct tClassData : public tBaseData + { + virtual const eItemType& getType() const override; + virtual tInheritanceMap& getInheritanceMap() override; + + tInheritanceMap inheritanceMap; + }; + + typedef std::shared_ptr tClassDataPtr; + typedef std::map tClassMap; + + struct tInterfaceData : public tBaseData + { + virtual const eItemType& getType() const override; + }; + + typedef std::shared_ptr tInterfaceDataPtr; + typedef std::map tInterfaceMap; + + struct tPackageData + { + tItemName packageName; + tItemMap itemMap; + }; + + typedef std::map tPackageMap; + + /** + * @brief The Creator class - the class, which is used to store all UML-related data and + * dump the plantuml class diagram as a string. + * Note! This class IS thread-safe. + */ + class Creator + { + public: + + /** + * @brief getInstance - gets instance of single-tone + * @return - instance of single-tone + */ + static Creator& getInstance(); + + /** + * @brief initialize - should be called once AFTER all global variables were + * already initialized. In other words - call it from context of any user thread. + * E.g. inside main function, or anywhere else, but outside the global section. + * Call from global section will lead to undefined behavior of this concept! + */ + void initialize(); + + /** + * @brief isInitislized - tells whether UML data is initialized + * @return + */ + bool isInitislized() const; + + struct tClassDiagramResult + { + bool bIsSuccessful = false; + std::string error; + std::string diagramContent; + }; + + /** + * @brief getClassDiagram - gets full class diagram of all registered elements + * @return - instance of tClassDiagramResult struct, which contains content of + * diagram, in case if tClassDiagramResult::bIsSuccessful == true. + * Otherwise returns an error message. + */ + tClassDiagramResult getClassDiagram() const; + + /** + * @brief getPackageClassDiagram - gets filtered class diagram of sub-set of the + * registered elements + * @param packageName - name of target package, starting from which diagram should + * be created. Diagram will show target package and first level of its dependencies. + * @param excludeDependencies - whether we should represent package WITHOUT its + * first level external dependencies + * @return - instance of tClassDiagramResult struct, which contains content of + * diagram, in case if tClassDiagramResult::bIsSuccessful == true. + * Otherwise returns an error message. + */ + tClassDiagramResult getPackageClassDiagram(const std::string& packageName, bool excludeDependencies = false) const; + + /** + * @brief findPackagesByName - tries to find package by name. + * @param packageName - keyword to search for. + * @return - found elements, for which creation of diagram can be requested. + * Note! Search logic is using the "start with" policy. + * Search is case-insensitive. + */ + tStringPtrSet findPackagesByName( const std::string& packageName ) const; + + /** + * @brief addItem - adds item ( class or interface ) to the model + * @param packageName - name of the package. Can be empty, then item will be added + * to non-packaged elements. + * @param pItemData - pointer to item data + * Note! This method should be called before initialization of the model. + * After initialization it will do nothing. + */ + void addItem( const tItemName& packageName, const tIItemPtr& pItemData ); + + private: + + /** + * @brief Creator - private constructor of single-tone + */ + Creator(); + + private: + + tPackageMap mPackageMap; + tItemMap mItemRegistry; + bool mbIsinitialized; + std::mutex mDataProtector; + }; + + struct tCallOnCreate + { + tCallOnCreate( const std::function& callable ); + }; + } +} + +#endif // DMA_COMPONENTS_IBASICITEM_HPP diff --git a/dltmessageanalyzerplugin/src/common/Definitions.cpp b/dltmessageanalyzerplugin/src/common/Definitions.cpp index d1703c3f..11024416 100644 --- a/dltmessageanalyzerplugin/src/common/Definitions.cpp +++ b/dltmessageanalyzerplugin/src/common/Definitions.cpp @@ -507,7 +507,7 @@ bool tQStringPtrWrapper::operator== ( const tQStringPtrWrapper& rVal ) const else if(pString == nullptr && rVal.pString == nullptr) return true; - return ( *pString != *rVal.pString ); + return ( *pString == *rVal.pString ); } ////////////////////////////////////////////////////////////////////////// diff --git a/dltmessageanalyzerplugin/src/dltWrappers/CDLTFileWrapper.cpp b/dltmessageanalyzerplugin/src/dltWrappers/CDLTFileWrapper.cpp index 2fc7da2c..d7b813f0 100644 --- a/dltmessageanalyzerplugin/src/dltWrappers/CDLTFileWrapper.cpp +++ b/dltmessageanalyzerplugin/src/dltWrappers/CDLTFileWrapper.cpp @@ -366,7 +366,7 @@ bool CDLTFileWrapper::cacheMsgByIndexes( const QSet msgIdSet ) { bool bResult = true; - for( const auto msgId : msgIdSet ) + for( const auto& msgId : msgIdSet ) { bResult = cacheMsgByIndex(msgId); diff --git a/dltmessageanalyzerplugin/src/dltmessageanalyzerplugin.cpp b/dltmessageanalyzerplugin/src/dltmessageanalyzerplugin.cpp index 7d58c0b0..959fe88e 100644 --- a/dltmessageanalyzerplugin/src/dltmessageanalyzerplugin.cpp +++ b/dltmessageanalyzerplugin/src/dltmessageanalyzerplugin.cpp @@ -19,6 +19,9 @@ #include "patternsView/CPatternsView.hpp" #include "filtersView/CFiltersView.hpp" +#include "common/DMA_Plantuml/DMA_Plantuml.hpp" +#include "log/CLog.hpp" + Q_DECLARE_METATYPE(tDltMsgWrapperPtr) DLTMessageAnalyzerPlugin::DLTMessageAnalyzerPlugin(): @@ -36,6 +39,47 @@ mbAnalysisRunning(false) { //qDebug() << "DLTMessageAnalyzerPlugin lives in thread - " << QThread::currentThreadId(); qRegisterMetaType("tDLTMsgWrapperPtr"); + + DMA::PlantUML::Creator::getInstance().initialize(); + + { + auto diagramResult = DMA::PlantUML::Creator::getInstance().getClassDiagram(); + + if(true == diagramResult.bIsSuccessful) + { + SEND_MSG(QString::fromStdString(diagramResult.diagramContent)); + } + else + { + SEND_ERR(QString::fromStdString(diagramResult.error)); + } + } + + { + auto diagramResult = DMA::PlantUML::Creator::getInstance().getPackageClassDiagram("test_main"); + + if(true == diagramResult.bIsSuccessful) + { + SEND_MSG(QString::fromStdString(diagramResult.diagramContent)); + } + else + { + SEND_ERR(QString::fromStdString(diagramResult.error)); + } + } + + { + auto diagramResult = DMA::PlantUML::Creator::getInstance().getPackageClassDiagram("test_main", true); + + if(true == diagramResult.bIsSuccessful) + { + SEND_MSG(QString::fromStdString(diagramResult.diagramContent)); + } + else + { + SEND_ERR(QString::fromStdString(diagramResult.error)); + } + } } DLTMessageAnalyzerPlugin::~DLTMessageAnalyzerPlugin() diff --git a/dltmessageanalyzerplugin/src/filtersView/CFiltersModel.cpp b/dltmessageanalyzerplugin/src/filtersView/CFiltersModel.cpp index 6df2182d..a0c89fc6 100644 --- a/dltmessageanalyzerplugin/src/filtersView/CFiltersModel.cpp +++ b/dltmessageanalyzerplugin/src/filtersView/CFiltersModel.cpp @@ -253,7 +253,7 @@ bool CFiltersModel::setData(const QModelIndex &index, const QVariant &value, int if(role == Qt::EditRole) { auto pItem = static_cast(index.internalPointer()); - const auto currentValue = pItem->data(index.column()); + const auto& currentValue = pItem->data(index.column()); auto newValue = toRegexDataItem(value, static_cast(index.column())); if(currentValue != newValue) diff --git a/dltmessageanalyzerplugin/src/form.cpp b/dltmessageanalyzerplugin/src/form.cpp index f849f558..d5f0d4b0 100644 --- a/dltmessageanalyzerplugin/src/form.cpp +++ b/dltmessageanalyzerplugin/src/form.cpp @@ -39,6 +39,8 @@ Form::Form(DLTMessageAnalyzerPlugin* pDLTMessageAnalyzerPlugin, QWidget *parent) mpUI->setupUi(this); NDLTMessageAnalyzer::NConsole::tConsoleConfig consoleConfig; + consoleConfig.maxMsgSize = 10240; + consoleConfig.logSize = 1000; consoleConfig.pTabWidget = getMainTabWidget(); consoleConfig.pConsoleTab = getConsoleViewTab(); consoleConfig.pConsoleTextEdit = getConsoleView(); diff --git a/dltmessageanalyzerplugin/src/groupedView/CGroupedView.cpp b/dltmessageanalyzerplugin/src/groupedView/CGroupedView.cpp index ee79bd44..5d1bdc85 100644 --- a/dltmessageanalyzerplugin/src/groupedView/CGroupedView.cpp +++ b/dltmessageanalyzerplugin/src/groupedView/CGroupedView.cpp @@ -101,8 +101,8 @@ class CIndexWrapper return bResult_; }; - const auto pMyElement = static_cast(mModelIndex.internalPointer()); - const auto pValElement = static_cast(val.mModelIndex.internalPointer()); + auto* const pMyElement = static_cast(mModelIndex.internalPointer()); + auto* const pValElement = static_cast(val.mModelIndex.internalPointer()); if(myLevel < valLevel) { diff --git a/dltmessageanalyzerplugin/src/log/CConsoleCtrl.cpp b/dltmessageanalyzerplugin/src/log/CConsoleCtrl.cpp index 91c9a572..eb135d44 100644 --- a/dltmessageanalyzerplugin/src/log/CConsoleCtrl.cpp +++ b/dltmessageanalyzerplugin/src/log/CConsoleCtrl.cpp @@ -204,7 +204,10 @@ void CConsoleCtrl::addMessage( const QString& message, const tMessageSettings& m } else { - QString messageEscaped = message.toHtmlEscaped(); + + QString messageEscaped = message. toHtmlEscaped(); + messageEscaped.replace("\n", "
"); + messageEscaped.replace(" ", " "); QString HTMLMessage; if(true == messageSettings.bCustomColor) diff --git a/dltmessageanalyzerplugin/src/patternsView/doc/class.puml b/dltmessageanalyzerplugin/src/patternsView/doc/class.puml new file mode 100644 index 00000000..d91aefb7 --- /dev/null +++ b/dltmessageanalyzerplugin/src/patternsView/doc/class.puml @@ -0,0 +1,78 @@ +@startuml + +left to right direction + +'QT's classes +package "Qt" #DDDDDD +{ + class QWidget + class QTableView + class QTreeView + class QAbstractItemModel +} + +'Plugin's classes +package "DMA base" #DDDDDD +{ + class Form + class CDLTMessageAnalyzer{} +} + +package "DMA patterns view" #DDDDDD +{ + class CPatternsModel{} + class CPatternsView {} + class CPatternsFileView{} + class Ui_PatternsFileForm {} +} + +package "DMA authentication" #DDDDDD +{ + class CAuthenticationModel{} + class CAuthenticationView {} + class CAuthenticationItemView {} + class Ui_AuthenticationItemForm {} +} + +package "DMA network" #DDDDDD +{ + interface INetworkCtrl{} + class CNetworkCtrl{} +} + +package "DMA settings" +{ + class CSettingsManager + { + shouldMigrateUserSettingsToSQLiteDB() + } +} + +'Inheritance section +QTreeView <|-- CPatternsView : extends +QAbstractItemModel <|-- CPatternsModel : implements +QTableView <|-- CAuthenticationView : extends +QAbstractItemModel <|-- CAuthenticationModel : implements +QWidget <|-- CAuthenticationItemView : extends +QWidget <|-- CPatternsFileView : extends +INetworkCtrl <|-- CNetworkCtrl : implements + +'Composition section +CDLTMessageAnalyzer "1" *-- "1" CPatternsModel : creates and provides +Form "1" *-- "1" CPatternsView : creates and provides +CPatternsFileView "1" *-- "1" Ui_PatternsFileForm : contains +CAuthenticationItemView "1" *-- "1" Ui_AuthenticationItemForm : contains +CPatternsView "1" *-- "1" CPatternsFileView : contains +CDLTMessageAnalyzer "1" *-- "1" CAuthenticationModel : creates and provides +CDLTMessageAnalyzer "1" *-- "1" CAuthenticationView : creates and provides +CDLTMessageAnalyzer "1" *-- "1" INetworkCtrl : contains + +'Agregation section +CDLTMessageAnalyzer "1" o-- "1" CPatternsView : gets and uses +CPatternsFileView "1" o-- "1" CAuthenticationView : gets and uses +CAuthenticationView "1" o-- "1" CAuthenticationModel : gets and uses +CPatternsView "1" o-- "1" CPatternsModel : gets and uses +CPatternsFileView "1" o-- "1" INetworkCtrl : gets and uses +CPatternsView "1" o-- "1" INetworkCtrl : gets and uses + +@enduml \ No newline at end of file diff --git a/dltmessageanalyzerplugin/src/patternsView/doc/doc.md b/dltmessageanalyzerplugin/src/patternsView/doc/doc.md index c813dbd2..6434c6d7 100644 --- a/dltmessageanalyzerplugin/src/patternsView/doc/doc.md +++ b/dltmessageanalyzerplugin/src/patternsView/doc/doc.md @@ -2,7 +2,7 @@ ---- -![test plant-uml diagram](../../../../md/dev_docs/TO_DO.svg) +![Class diagram](./class.svg) ---- diff --git a/dltmessageanalyzerplugin/src/settings/TSettingItem.hpp b/dltmessageanalyzerplugin/src/settings/TSettingItem.hpp index a50d4fc1..42c799af 100644 --- a/dltmessageanalyzerplugin/src/settings/TSettingItem.hpp +++ b/dltmessageanalyzerplugin/src/settings/TSettingItem.hpp @@ -109,7 +109,7 @@ class TSettingItem : public CSettingItem } } - const auto updateFileFunc = getUpdateFileFunc(); + const auto& updateFileFunc = getUpdateFileFunc(); if(updateFileFunc) { @@ -230,7 +230,7 @@ class TRangedSettingItem: public TSettingItem } } - const auto updateFileFunc = tParent::getUpdateFileFunc(); + const auto& updateFileFunc = tParent::getUpdateFileFunc(); if(updateFileFunc) {