diff --git a/CMakeLists.txt b/CMakeLists.txt index 8383529a38..21d572b1f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -439,6 +439,20 @@ set(SIMPLNX_HDRS ${SIMPLNX_SOURCE_DIR}/DataStructure/Geometry/INodeGeometry2D.hpp ${SIMPLNX_SOURCE_DIR}/DataStructure/Geometry/INodeGeometry3D.hpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/BaseMetadataValue.hpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/AbstractMetadataValue.hpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/AbstractVectorMetadataValue.hpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/BoolMetadataValue.hpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/BoolVectorMetadataValue.hpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/Float64MetadataValue.hpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/Float64VectorMetadataValue.hpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/Int32MetadataValue.hpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/Int32VectorMetadataValue.hpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/MetaDataList.hpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/StringMetadataValue.hpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/StringVectorMetadataValue.hpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/UnknownMetadataValue.hpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/DynamicListArray.hpp ${SIMPLNX_SOURCE_DIR}/DataStructure/EmptyDataStore.hpp @@ -684,6 +698,18 @@ set(SIMPLNX_SRCS ${SIMPLNX_SOURCE_DIR}/DataStructure/Geometry/TriangleGeom.cpp ${SIMPLNX_SOURCE_DIR}/DataStructure/Geometry/VertexGeom.cpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/BaseMetadataValue.cpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/BoolMetadataValue.cpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/BoolVectorMetadataValue.cpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/Float64MetadataValue.cpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/Float64VectorMetadataValue.cpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/Int32MetadataValue.cpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/Int32VectorMetadataValue.cpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/MetaDataList.cpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/StringMetadataValue.cpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/StringVectorMetadataValue.cpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Metadata/UnknownMetadataValue.cpp + ${SIMPLNX_SOURCE_DIR}/DataStructure/Montage/AbstractMontage.cpp ${SIMPLNX_SOURCE_DIR}/DataStructure/Montage/AbstractTileIndex.cpp ${SIMPLNX_SOURCE_DIR}/DataStructure/Montage/GridMontage.cpp diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/WriteDREAM3D.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/WriteDREAM3D.cpp index 20cd13fa3b..9e6e4c655b 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/WriteDREAM3D.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/WriteDREAM3D.cpp @@ -15,7 +15,8 @@ using namespace nx::core; namespace { -constexpr int32 k_FailedFindPipelineError = -15; +constexpr nx::core::int32 k_NoExportPathError = -1; +constexpr nx::core::int32 k_FailedFindPipelineError = -15; } // namespace // ----------------------------------------------------------------------------- @@ -30,9 +31,76 @@ WriteDREAM3D::WriteDREAM3D(DataStructure& dataStructure, const IFilter::MessageH // ----------------------------------------------------------------------------- WriteDREAM3D::~WriteDREAM3D() noexcept = default; +/** + * @brief Extracts the preceding Pipeline from the PipelineFilter. + * This method returns an empty Pipeline if the PipelineFilter is null. + * @param pipelineNode The PipelineFilter to extract the Pipeline from. + * @return Result The target Pipeline or an error message if the process failed to complete. + */ +Result ExtractPipeline(const PipelineFilter* pipelineNode) +{ + Pipeline pipeline; + if(pipelineNode != nullptr) + { + auto pipelinePtr = pipelineNode->getPrecedingPipeline(); + if(pipelinePtr == nullptr) + { + return MakeErrorResult(k_FailedFindPipelineError, "Failed to retrieve pipeline."); + } + + pipeline = *pipelinePtr; + } + return {pipeline}; +} + +/** + * @brief Writes the DREAM3D file to the temp file, commits changes, and then writes the XDMF file if requested. + * @param atomicFile Temp file for writing the DREAM3D file + * @param dataStructure DataStructure to be written to file. + * @param pipeline Pipeline to write to file. + * @param writeXdmfFile + * @return Result<> + */ +Result<> WriteDREAM3DFile(AtomicFile& atomicFile, const DataStructure& dataStructure, const Pipeline& pipeline, bool writeXdmfFile, + const nx::core::HDF5::DataStructureWriter::WriteOptions& writeOptions) +{ + auto exportFilePath = atomicFile.tempFilePath(); + + auto results = DREAM3D::WriteFile(exportFilePath, dataStructure, pipeline, writeXdmfFile, writeOptions); + if(results.invalid()) + { + return results; + } + + // Commit changes to the temp file. Return an invalid Result if errors occured. + if(auto commitResult = atomicFile.commit(); commitResult.invalid()) + { + return commitResult; + } + + // Write the XDMF file if specified + if(writeXdmfFile) + { + // TODO: Double check this + fs::path xdmfFilePath = exportFilePath.replace_extension(".xdmf"); + std::error_code errorCode; + fs::rename(xdmfFilePath, fs::path(exportFilePath).replace_extension(".xdmf"), errorCode); + + // If the XDMF file failed to be renamed, return an invalid Result + if(errorCode) + { + std::string ss = fmt::format("Failed to rename xdmf file with error: '{}'", errorCode.message()); + return MakeErrorResult(errorCode.value(), ss); + } + } + + return {}; +} + // ----------------------------------------------------------------------------- Result<> WriteDREAM3D::operator()() { + // Create AtomicFile to write. auto atomicFileResult = AtomicFile::Create(m_InputValues->ExportFilePath); if(atomicFileResult.invalid()) { @@ -40,43 +108,19 @@ Result<> WriteDREAM3D::operator()() } AtomicFile atomicFile = std::move(atomicFileResult.value()); - auto exportFilePath = atomicFile.tempFilePath(); - auto writeXdmf = m_InputValues->WriteXdmfFile; - + // Extract Preceding Pipeline Pipeline pipeline; - - if(m_InputValues->PipelineNode != nullptr) + if(auto result = ExtractPipeline(m_InputValues->PipelineNode); result.invalid()) { - auto pipelinePtr = m_InputValues->PipelineNode->getPrecedingPipeline(); - if(pipelinePtr == nullptr) - { - return MakeErrorResult(k_FailedFindPipelineError, "Failed to retrieve pipeline."); - } - - pipeline = *pipelinePtr; + return ConvertResult(std::move(result)); + } + else + { + pipeline = std::move(result.value()); } + // Write DREAM.3D file nx::core::HDF5::DataStructureWriter::WriteOptions writeOptions; writeOptions.compressionLevel = m_InputValues->UseCompression ? m_InputValues->CompressionLevel : 0; - auto results = DREAM3D::WriteFile(exportFilePath, m_DataStructure, pipeline, writeXdmf, writeOptions); - if(results.valid()) - { - Result<> commitResult = atomicFile.commit(); - if(commitResult.invalid()) - { - return commitResult; - } - if(writeXdmf) - { - fs::path xdmfFilePath = exportFilePath.replace_extension(".xdmf"); - std::error_code errorCode; - fs::rename(xdmfFilePath, m_InputValues->ExportFilePath.parent_path() / m_InputValues->ExportFilePath.stem().concat(".xdmf"), errorCode); - if(errorCode) - { - std::string ss = fmt::format("Failed to rename xdmf file with error: '{}'", errorCode.message()); - return MakeErrorResult(errorCode.value(), ss); - } - } - } - return results; + return WriteDREAM3DFile(atomicFile, m_DataStructure, pipeline, m_InputValues->WriteXdmfFile, writeOptions); } diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/WriteDREAM3DFilter.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/WriteDREAM3DFilter.cpp index 79c14387e5..16b4515fe4 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/WriteDREAM3DFilter.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/WriteDREAM3DFilter.cpp @@ -2,6 +2,7 @@ #include "SimplnxCore/Filters/Algorithms/WriteDREAM3D.hpp" +#include "SimplnxCore/Filters/Algorithms/WriteDREAM3D.hpp" #include "simplnx/Parameters/BoolParameter.hpp" #include "simplnx/Parameters/FileSystemPathParameter.hpp" #include "simplnx/Parameters/NumberParameter.hpp" diff --git a/src/Plugins/SimplnxCore/test/DREAM3DFileTest.cpp b/src/Plugins/SimplnxCore/test/DREAM3DFileTest.cpp index 1e51c94f05..0adbe85986 100644 --- a/src/Plugins/SimplnxCore/test/DREAM3DFileTest.cpp +++ b/src/Plugins/SimplnxCore/test/DREAM3DFileTest.cpp @@ -10,6 +10,14 @@ #include "simplnx/DataStructure/DataGroup.hpp" #include "simplnx/DataStructure/DataStore.hpp" #include "simplnx/DataStructure/DataStructure.hpp" +#include "simplnx/DataStructure/Metadata/BoolMetadataValue.hpp" +#include "simplnx/DataStructure/Metadata/BoolVectorMetadataValue.hpp" +#include "simplnx/DataStructure/Metadata/Float64MetadataValue.hpp" +#include "simplnx/DataStructure/Metadata/Float64VectorMetadataValue.hpp" +#include "simplnx/DataStructure/Metadata/Int32MetadataValue.hpp" +#include "simplnx/DataStructure/Metadata/Int32VectorMetadataValue.hpp" +#include "simplnx/DataStructure/Metadata/StringMetadataValue.hpp" +#include "simplnx/DataStructure/Metadata/StringVectorMetadataValue.hpp" #include "simplnx/Filter/Arguments.hpp" #include "simplnx/Filter/FilterHandle.hpp" #include "simplnx/Parameters/Dream3dImportParameter.hpp" @@ -44,12 +52,23 @@ const fs::path k_ExportFilename2 = "export2.dream3d"; const fs::path k_MultiExportFilename1 = "multi_export1.dream3d"; const fs::path k_MultiExportFilename2 = "multi_export2.dream3d"; const fs::path k_MultiExportFilename3 = "multi_export3.dream3d"; + +const ShapeType k_TestDataShape = {2, 3}; } // namespace Constants std::mutex m_DataMutex; namespace DataNames { +constexpr StringLiteral k_EdgeGeomName = "EdgeGeom"; +constexpr StringLiteral k_QuadGeomName = "QuadGeom"; +constexpr StringLiteral k_RectGridGeomName = "RectGridGeom"; +constexpr StringLiteral k_TetrahedralGeomName = "TetrahedralGeom"; +constexpr StringLiteral k_TriangleGeomName = "TriangleGeom"; +constexpr StringLiteral k_VertexGeomName = "VertexGeom"; + +constexpr StringLiteral k_VertexListName = "Vertex List"; + constexpr StringLiteral k_Group1Name = "Top-Level"; constexpr StringLiteral k_Group2Name = "Second-Level"; constexpr StringLiteral k_Group3Name = "Third-Level"; @@ -82,6 +101,17 @@ fs::path GetIODataPath() return GetDataDir(*app) / Constants::k_Dream3dFilename; } +fs::path GetMetaDataPath() +{ + auto app = Application::Instance(); + if(app == nullptr) + { + throw std::runtime_error("nx::core::Application instance not found"); + } + + return GetDataDir(*app) / "MetaDataTest.dream3d"; +} + fs::path GetExportDataPath() { auto app = Application::Instance(); @@ -137,9 +167,85 @@ fs::path GetReMultiExportDataPath() return GetDataDir(*app) / Constants::k_MultiExportFilename3; } +void SetupNodeGeom0D(DataStructure& dataStructure, INodeGeometry0D* geom, const ShapeType& tupleShape) +{ + auto vertexStore = DataStoreUtilities::CreateDataStore(tupleShape, {3}, IDataAction::Mode::Execute); + auto* vertexList = DataArray::Create(dataStructure, DataNames::k_VertexListName, vertexStore, geom->getId()); + geom->setVertices(*vertexList); +} + +void SetupNodeGeom1D(DataStructure& dataStructure, INodeGeometry1D* geom, const ShapeType& tupleShape) +{ + auto edgeStore = DataStoreUtilities::CreateDataStore(tupleShape, {3}, IDataAction::Mode::Execute); + auto* edgeList = DataArray::Create(dataStructure, DataNames::k_VertexListName, edgeStore, geom->getId()); + geom->setEdgeList(*edgeList); +} + +void SetupNodeGeom2D(DataStructure& dataStructure, INodeGeometry2D* geom, const ShapeType& tupleShape) +{ + auto faceStore = DataStoreUtilities::CreateDataStore(tupleShape, {3}, IDataAction::Mode::Execute); + auto* faceList = DataArray::Create(dataStructure, DataNames::k_VertexListName, faceStore, geom->getId()); + geom->setFaceList(*faceList); +} + +void SetupNodeGeom3D(DataStructure& dataStructure, INodeGeometry3D* geom, const ShapeType& tupleShape) +{ + auto polyhedraStore = DataStoreUtilities::CreateDataStore(tupleShape, {3}, IDataAction::Mode::Execute); + auto* polyhedraList = DataArray::Create(dataStructure, DataNames::k_VertexListName, polyhedraStore, geom->getId()); + geom->setPolyhedraList(*polyhedraList); +} + +void CreateEdgeGeom(DataStructure& dataStructure) +{ + auto* geom = EdgeGeom::Create(dataStructure, DataNames::k_EdgeGeomName); + SetupNodeGeom1D(dataStructure, geom, Constants::k_TestDataShape); +} + +void CreateQuadGeom(DataStructure& dataStructure) +{ + auto* geom = QuadGeom::Create(dataStructure, DataNames::k_QuadGeomName); + SetupNodeGeom2D(dataStructure, geom, Constants::k_TestDataShape); +} + +void CreateRectGridGeom(DataStructure& dataStructure) +{ + auto* geom = RectGridGeom::Create(dataStructure, DataNames::k_RectGridGeomName); +} + +void CreateTetrahedralGeom(DataStructure& dataStructure) +{ + auto* geom = TetrahedralGeom::Create(dataStructure, DataNames::k_TetrahedralGeomName); + SetupNodeGeom3D(dataStructure, geom, Constants::k_TestDataShape); +} + +void CreateTriangleGeom(DataStructure& dataStructure) +{ + auto* geom = TriangleGeom::Create(dataStructure, DataNames::k_TriangleGeomName); + SetupNodeGeom2D(dataStructure, geom, Constants::k_TestDataShape); +} + +void CreateVertexGeom(DataStructure& dataStructure) +{ + auto* geom = VertexGeom::Create(dataStructure, DataNames::k_VertexGeomName); + SetupNodeGeom0D(dataStructure, geom, Constants::k_TestDataShape); +} + +void CreateGeometries(DataStructure& dataStructure) +{ + CreateEdgeGeom(dataStructure); + CreateQuadGeom(dataStructure); + CreateRectGridGeom(dataStructure); + CreateTetrahedralGeom(dataStructure); + CreateTriangleGeom(dataStructure); + CreateVertexGeom(dataStructure); +} + DataStructure CreateTestDataStructure() { DataStructure dataStructure; + + CreateGeometries(dataStructure); + auto group1 = DataGroup::Create(dataStructure, DataNames::k_Group1Name); auto group2 = DataGroup::Create(dataStructure, DataNames::k_Group2Name, group1->getId()); auto group3 = DataGroup::Create(dataStructure, DataNames::k_Group3Name, group2->getId()); @@ -267,6 +373,348 @@ DREAM3D::FileData CreateFileData() return {CreateExportPipeline(), CreateTestDataStructure()}; } +void CompareBaseGroups(const BaseGroup* group1, const BaseGroup* group2) +{ + REQUIRE(group1 != nullptr); + REQUIRE(group2 != nullptr); + + REQUIRE(group1->getSize() == group2->getSize()); +} + +void CompareStringArrays(const DataObject* object1, const DataObject* object2) +{ + const auto* array1 = dynamic_cast(object1); + const auto* array2 = dynamic_cast(object2); + + REQUIRE(array1 != nullptr); + REQUIRE(array2 != nullptr); + + REQUIRE(array1->getSize() == array2->getSize()); + REQUIRE(array1->values() == array2->values()); +} + +template +void CompareNeighborLists(const DataObject* object1, const DataObject* object2) +{ + const auto* array1 = dynamic_cast*>(object1); + const auto* array2 = dynamic_cast*>(object2); + + const auto numLists = array1->getNumberOfLists(); + for(usize i = 0; i < numLists; i++) + { + REQUIRE(array1->getList(i) == array2->getList(i)); + } +} + +void CompareINeighborLists(const DataObject* object1, const DataObject* object2) +{ + const auto* array1 = dynamic_cast(object1); + const auto* array2 = dynamic_cast(object2); + + REQUIRE(array1 != nullptr); + REQUIRE(array2 != nullptr); + + REQUIRE(array1->getDataType() == array2->getDataType()); + switch(array1->getDataType()) + { + case DataType::int8: + CompareNeighborLists(object1, object2); + break; + case DataType::int16: + CompareNeighborLists(object1, object2); + break; + case DataType::int32: + CompareNeighborLists(object1, object2); + break; + case DataType::int64: + CompareNeighborLists(object1, object2); + break; + case DataType::uint8: + CompareNeighborLists(object1, object2); + break; + case DataType::uint16: + CompareNeighborLists(object1, object2); + break; + case DataType::uint32: + CompareNeighborLists(object1, object2); + break; + case DataType::uint64: + CompareNeighborLists(object1, object2); + break; + case DataType::float32: + CompareNeighborLists(object1, object2); + break; + case DataType::float64: + CompareNeighborLists(object1, object2); + break; + } +} + +template +void CompareDataArrays(const IDataArray* object1, const IDataArray* object2) +{ + const auto* array1 = dynamic_cast*>(object1); + const auto* array2 = dynamic_cast*>(object2); + + REQUIRE(array1->getTupleShape() == array2->getTupleShape()); + REQUIRE(array1->getComponentShape() == array2->getComponentShape()); + + auto& store1 = array1->getDataStoreRef(); + auto& store2 = array2->getDataStoreRef(); + + const auto size = store1.getSize(); + for(usize i = 0; i < size; i++) + { + REQUIRE(store1[i] == store2[i]); + } +} + +void CompareIDataArrays(const DataObject* object1, const DataObject* object2) +{ + const auto* array1 = dynamic_cast(object1); + const auto* array2 = dynamic_cast(object2); + + REQUIRE(array1 != nullptr); + REQUIRE(array2 != nullptr); + + REQUIRE(array1->getArrayType() == array2->getArrayType()); + REQUIRE(array1->getDataType() == array2->getDataType()); + + switch(array1->getDataType()) + { + case DataType::int8: + CompareDataArrays(array1, array2); + return; + case DataType::int16: + CompareDataArrays(array1, array2); + return; + case DataType::int32: + CompareDataArrays(array1, array2); + return; + case DataType::int64: + CompareDataArrays(array1, array2); + return; + case DataType::uint8: + CompareDataArrays(array1, array2); + return; + case DataType::uint16: + CompareDataArrays(array1, array2); + return; + case DataType::uint32: + CompareDataArrays(array1, array2); + return; + case DataType::uint64: + CompareDataArrays(array1, array2); + return; + case DataType::float32: + CompareDataArrays(array1, array2); + return; + case DataType::float64: + CompareDataArrays(array1, array2); + return; + case DataType::boolean: + CompareDataArrays(array1, array2); + return; + } +} + +void CompareNodeGeom0D(const INodeGeometry0D* geom1, const INodeGeometry0D* geom2) +{ + REQUIRE(geom1 != nullptr); + REQUIRE(geom2 != nullptr); + + CompareIDataArrays(geom1->getVertices(), geom2->getVertices()); +} + +void CompareNodeGeom1D(const INodeGeometry1D* geom1, const INodeGeometry1D* geom2) +{ + REQUIRE(geom1 != nullptr); + REQUIRE(geom2 != nullptr); + + CompareNodeGeom0D(geom1, geom2); + + CompareIDataArrays(geom1->getEdges(), geom2->getEdges()); +} + +void CompareNodeGeom2D(const INodeGeometry2D* geom1, const INodeGeometry2D* geom2) +{ + REQUIRE(geom1 != nullptr); + REQUIRE(geom2 != nullptr); + + CompareNodeGeom1D(geom1, geom2); + + CompareIDataArrays(geom1->getFaces(), geom2->getFaces()); +} + +void CompareNodeGeom3D(const INodeGeometry3D* geom1, const INodeGeometry3D* geom2) +{ + REQUIRE(geom1 != nullptr); + REQUIRE(geom2 != nullptr); + + CompareNodeGeom2D(geom1, geom2); + + CompareIDataArrays(geom1->getPolyhedra(), geom2->getPolyhedra()); +} + +void CompareEdgeGeom(const DataObject* object1, const DataObject* object2) +{ + const auto* geom1 = dynamic_cast(object1); + const auto* geom2 = dynamic_cast(object2); + + CompareBaseGroups(geom1, geom2); + CompareNodeGeom1D(geom1, geom2); +} + +void CompareHexahedralGeom(const DataObject* object1, const DataObject* object2) +{ + const auto* geom1 = dynamic_cast(object1); + const auto* geom2 = dynamic_cast(object2); + + CompareBaseGroups(geom1, geom2); + CompareNodeGeom3D(geom1, geom2); +} + +void CompareImageGeom(const DataObject* object1, const DataObject* object2) +{ + const auto* geom1 = dynamic_cast(object1); + const auto* geom2 = dynamic_cast(object2); + + CompareBaseGroups(geom1, geom2); + + REQUIRE(geom1->getSpacing() == geom2->getSpacing()); + REQUIRE(geom1->getOrigin() == geom2->getOrigin()); + REQUIRE(geom1->getDimensions() == geom2->getDimensions()); +} + +void CompareQuadGeom(const DataObject* object1, const DataObject* object2) +{ + const auto* geom1 = dynamic_cast(object1); + const auto* geom2 = dynamic_cast(object2); + + CompareBaseGroups(geom1, geom2); + CompareNodeGeom2D(geom1, geom2); +} + +void CompareRectGridGeom(const DataObject* object1, const DataObject* object2) +{ + const auto* geom1 = dynamic_cast(object1); + const auto* geom2 = dynamic_cast(object2); + + CompareBaseGroups(geom1, geom2); + CompareIDataArrays(geom1->getXBounds(), geom2->getXBounds()); + CompareIDataArrays(geom1->getYBounds(), geom2->getYBounds()); + CompareIDataArrays(geom1->getZBounds(), geom2->getZBounds()); +} + +void CompareTetrahedralGeom(const DataObject* object1, const DataObject* object2) +{ + const auto* geom1 = dynamic_cast(object1); + const auto* geom2 = dynamic_cast(object2); + + CompareBaseGroups(geom1, geom2); + CompareNodeGeom3D(geom1, geom2); +} + +void CompareTriangleGeom(const DataObject* object1, const DataObject* object2) +{ + const auto* geom1 = dynamic_cast(object1); + const auto* geom2 = dynamic_cast(object2); + + CompareBaseGroups(geom1, geom2); + CompareNodeGeom2D(geom1, geom2); +} + +void CompareVertexGeom(const DataObject* object1, const DataObject* object2) +{ + const auto* geom1 = dynamic_cast(object1); + const auto* geom2 = dynamic_cast(object2); + + CompareBaseGroups(geom1, geom2); + CompareNodeGeom0D(geom1, geom2); +} + +void CompareAttributeMatrices(const DataObject* object1, const DataObject* object2) +{ + const auto* group1 = dynamic_cast(object1); + const auto* group2 = dynamic_cast(object2); + + CompareBaseGroups(group1, group2); + + REQUIRE(group1->getNumberOfTuples() == group2->getNumberOfTuples()); + REQUIRE(group1->getShape() == group2->getShape()); +} + +void CompareDataGroups(const DataObject* object1, const DataObject* object2) +{ + const auto* group1 = dynamic_cast(object1); + const auto* group2 = dynamic_cast(object2); + + CompareBaseGroups(group1, group2); +} + +void CompareMetaData(const Metadata& metaData1, const Metadata& metaData2) +{ + for(const auto& [key, valuePtr] : metaData1) + { + auto& valueRef1 = *valuePtr.get(); + auto& valueRef2 = *metaData2.getDataValuePtr(key).get(); + + REQUIRE(valueRef1.toJson().dump() == valueRef2.toJson().dump()); + } +} + +void CompareDataObjects(const DataStructure& dataStruct1, const DataStructure& dataStruct2, const DataPath& dataPath) +{ + const auto* object1 = dataStruct1.getData(dataPath); + const auto* object2 = dataStruct2.getData(dataPath); + + if(object1 == nullptr) + { + if(object2 == nullptr) + { + return; + } + else + { + FAIL(); + } + } + + // Compare names + REQUIRE(object1->getName() == object2->getName()); + + // Compare MetaData + CompareMetaData(object1->getMetadata(), object2->getMetadata()); + + switch(object1->getDataObjectType()) + { + case DataObject::Type::AttributeMatrix: + CompareAttributeMatrices(object1, object2); + break; + case DataObject::Type::DataArray: + CompareIDataArrays(object1, object2); + break; + case DataObject::Type::DataGroup: + CompareDataGroups(object1, object2); + break; + } +} + +void CompareDataStructures(const DataStructure& dataStruct1, const DataStructure& dataStruct2) +{ + auto dataPaths1 = dataStruct1.getAllDataPaths(); + auto dataPaths2 = dataStruct2.getAllDataPaths(); + + REQUIRE(dataPaths1.size() == dataPaths2.size()); + for(const auto& dataPath : dataPaths1) + { + auto iter = std::find(dataPaths2.begin(), dataPaths2.end(), dataPath); + REQUIRE(iter != dataPaths2.end()); + + CompareDataObjects(dataStruct1, dataStruct2, dataPath); + } +} + } // End Namespace TEST_CASE("DREAM3DFileTest:DREAM3D File IO Test", "[WriteDREAM3DFilter]") @@ -381,6 +829,33 @@ TEST_CASE("DREAM3DFileTest:Import/Export DREAM3D Filter Test", "[ReadDREAM3DFilt } } +TEST_CASE("DREAM3DFileTest:Import/Export DREAM3D Filter Test: Expanded DataStructure", "[ReadDREAM3DFilter][WriteDREAM3DFilter]") +{ + auto app = Application::GetOrCreateInstance(); + + fs::path path = GetDataDir(*app) / "ExpandedDataStructureTestData.dream3d"; + + DataStructure exportDataStructure = CreateTestDataStructure(); + + WriteDREAM3DFilter writeDream3dFilter; + Arguments writeArgs; + writeArgs.insertOrAssign(WriteDREAM3DFilter::k_ExportFilePath, path); + writeArgs.insertOrAssign(WriteDREAM3DFilter::k_WriteXdmf, false); + Result<> writeResult = writeDream3dFilter.execute(exportDataStructure, writeArgs).result; + SIMPLNX_RESULT_REQUIRE_VALID(writeResult); + + DataStructure importDataStructure; + + ReadDREAM3DFilter readDream3dFilter; + Arguments readArgs; + Dream3dImportParameter::ImportData importData(path); + readArgs.insertOrAssign(ReadDREAM3DFilter::k_ImportFileData, importData); + Result<> readResult = readDream3dFilter.execute(importDataStructure, readArgs).result; + SIMPLNX_RESULT_REQUIRE_VALID(readResult); + + CompareDataStructures(exportDataStructure, importDataStructure); +} + TEST_CASE("DREAM3DFileTest:Import/Export Multi-DREAM3D Filter Test", "[ReadDREAM3DFilter][WriteDREAM3DFilter]") { UnitTest::LoadPlugins(); @@ -886,3 +1361,131 @@ TEST_CASE("WriteDREAM3DFilter: Compression_Preflight_RejectsOutOfRangeLevel", "[ REQUIRE(filter.preflight(ds, args).outputActions.valid()); } } + +TEST_CASE("SimplnxCore::WriteDREAM3DFilter: MetaData", "[SimplnxCore][WriteDREAM3DFilter][MetaData]") +{ + UnitTest::LoadPlugins(); + + const std::string groupName = "meta test"; + const std::string boolDataName = "bool"; + const std::string boolVecDataName = "bool-vec"; + const std::string intDataName = "int"; + const std::string intVecDataName = "int-vec"; + const std::string doubleDataName = "double"; + const std::string doubleVecDataName = "double-vec"; + const std::string stringDataName = "string"; + const std::string stringVecDataName = "string-vec"; + + constexpr bool boolValue = true; + constexpr int32 intValue = 5; + constexpr float64 doubleValue = 8.6; + const std::string stringValue = "test string"; + const std::vector boolVecValue = {true, false, true}; + const std::vector intVecValue = {5, 6, 7}; + const std::vector doubleVecValue = {8.6, 1.2}; + const std::vector stringVecValue = {"test string"}; + + DataStructure dataStructure; + auto* metaObject = DataGroup::Create(dataStructure, groupName); + auto& metadata = metaObject->getMetadata(); + + { + // Bool metadata + metadata.setData(boolDataName, boolValue); + metadata.setData(boolVecDataName, boolVecValue); + + // Int metadata + metadata.setData(intDataName, intValue); + metadata.setData(intVecDataName, intVecValue); + + // Double metadata + metadata.setData(doubleDataName, doubleValue); + metadata.setData(doubleVecDataName, doubleVecValue); + + // String metadata + metadata.setData(stringDataName, stringValue); + metadata.setData(stringVecDataName, stringVecValue); + } + + { + std::lock_guard lock(m_DataMutex); + std::filesystem::path metadataPath = GetMetaDataPath(); + // Write .dream3d file + { + auto writeResult = DREAM3D::WriteFile(metadataPath, dataStructure); + SIMPLNX_RESULT_REQUIRE_VALID(writeResult); + } + + // Read .dream3d file + { + auto fileReader = HDF5::FileIO::ReadFile(metadataPath); + auto fileResult = DREAM3D::ReadFile(fileReader); + SIMPLNX_RESULT_REQUIRE_VALID(fileResult); + + auto [pipeline, dataStructureRead] = fileResult.value(); + + // Get MetaData + const auto& metaObjectRead = dataStructureRead.getDataRefAs(DataPath({groupName})); + const auto& metaDataRead = metaObjectRead.getMetadata(); + + auto boolDataReadPtr = metaDataRead.getDataValuePtr(boolDataName); + auto boolVecDataReadPtr = metaDataRead.getDataValuePtr(boolVecDataName); + auto intDataReadPtr = metaDataRead.getDataValuePtr(intDataName); + auto intVecDataReadPtr = metaDataRead.getDataValuePtr(intVecDataName); + auto doubleDataReadPtr = metaDataRead.getDataValuePtr(doubleDataName); + auto doubleVecDataReadPtr = metaDataRead.getDataValuePtr(doubleVecDataName); + auto stringDataReadPtr = metaDataRead.getDataValuePtr(stringDataName); + auto stringVecDataReadPtr = metaDataRead.getDataValuePtr(stringVecDataName); + + // Require metadata exists + REQUIRE(boolDataReadPtr != nullptr); + REQUIRE(boolVecDataReadPtr != nullptr); + REQUIRE(intDataReadPtr != nullptr); + REQUIRE(intVecDataReadPtr != nullptr); + REQUIRE(doubleDataReadPtr != nullptr); + REQUIRE(doubleVecDataReadPtr != nullptr); + REQUIRE(stringDataReadPtr != nullptr); + REQUIRE(stringVecDataReadPtr != nullptr); + + // Require metadata preserves typename + REQUIRE(boolDataReadPtr->getTypeName() == BoolMetadataValue::k_TypeName); + REQUIRE(boolVecDataReadPtr->getTypeName() == BoolVectorMetadataValue::k_TypeName); + REQUIRE(intDataReadPtr->getTypeName() == Int32MetadataValue::k_TypeName); + REQUIRE(intVecDataReadPtr->getTypeName() == Int32VectorMetadataValue::k_TypeName); + REQUIRE(doubleDataReadPtr->getTypeName() == Float64MetadataValue::k_TypeName); + REQUIRE(doubleVecDataReadPtr->getTypeName() == Float64VectorMetadataValue::k_TypeName); + REQUIRE(stringDataReadPtr->getTypeName() == StringMetadataValue::k_TypeName); + REQUIRE(stringVecDataReadPtr->getTypeName() == StringVectorMetadataValue::k_TypeName); + + // Require metadata preserves values + auto& boolDataReadRef = *metaDataRead.getDataValuePtrAs(boolDataName).get(); + auto& boolVecDataReadRef = *metaDataRead.getDataValuePtrAs(boolVecDataName).get(); + auto& intDataReadRef = *metaDataRead.getDataValuePtrAs(intDataName).get(); + auto& intVecDataReadRef = *metaDataRead.getDataValuePtrAs(intVecDataName).get(); + auto& doubleDataReadRef = *metaDataRead.getDataValuePtrAs(doubleDataName).get(); + auto& doubleVecDataReadRef = *metaDataRead.getDataValuePtrAs(doubleVecDataName).get(); + auto& stringDataReadRef = *metaDataRead.getDataValuePtrAs(stringDataName).get(); + auto& stringVecDataReadRef = *metaDataRead.getDataValuePtrAs(stringVecDataName).get(); + + REQUIRE(boolDataReadRef == boolValue); + REQUIRE(boolVecDataReadRef == boolVecValue); + REQUIRE(intDataReadRef == intValue); + REQUIRE(intVecDataReadRef == intVecValue); + REQUIRE(doubleDataReadRef == doubleValue); + REQUIRE(doubleVecDataReadRef == doubleVecValue); + REQUIRE(stringDataReadRef == stringValue); + REQUIRE(stringVecDataReadRef == stringVecValue); + + // Simplified MetaData API + REQUIRE(metaDataRead.getDataAs(boolDataName) == boolValue); + REQUIRE(metaDataRead.getDataAs(intDataName) == intValue); + REQUIRE(metaDataRead.getDataAs(doubleDataName) == doubleValue); + REQUIRE(metaDataRead.getDataAs(stringDataName) == stringValue); + // vector data + REQUIRE(metaDataRead.getDataAs>(boolVecDataName) == boolVecValue); + REQUIRE(metaDataRead.getDataAs>(intVecDataName) == intVecValue); + REQUIRE(metaDataRead.getDataAs>(doubleVecDataName) == doubleVecValue); + REQUIRE(metaDataRead.getDataAs>(stringVecDataName) == stringVecValue); + } + } +} diff --git a/src/simplnx/Core/Application.cpp b/src/simplnx/Core/Application.cpp index ebd49eab85..b15a08cbd5 100644 --- a/src/simplnx/Core/Application.cpp +++ b/src/simplnx/Core/Application.cpp @@ -120,6 +120,7 @@ Result<> Application::initialize() m_CurrentPath = pathResult.value(); initDefaultDataTypes(); + m_MetaDataList = std::make_unique(); return combinedResult; } @@ -414,3 +415,8 @@ std::vector Application::getDataStoreFormats() const { return m_DataIOCollection->getFormatNames(); } + +nx::core::MetaDataList* Application::getMetaDataList() const +{ + return m_MetaDataList.get(); +} diff --git a/src/simplnx/Core/Application.hpp b/src/simplnx/Core/Application.hpp index 7f9d727b0f..6347f4eb6e 100644 --- a/src/simplnx/Core/Application.hpp +++ b/src/simplnx/Core/Application.hpp @@ -3,6 +3,7 @@ #include "simplnx/Common/Result.hpp" #include "simplnx/Core/Preferences.hpp" #include "simplnx/DataStructure/DataObject.hpp" +#include "simplnx/DataStructure/Metadata/MetaDataList.hpp" #include "simplnx/Filter/FilterList.hpp" #include "simplnx/Plugin/AbstractPlugin.hpp" #include "simplnx/simplnx_export.hpp" @@ -190,6 +191,12 @@ class SIMPLNX_EXPORT Application */ std::vector getDataStoreFormats() const; + /** + * @brief Returns a list of known MetaData formats for reading and writing DataObject metadata. + * @return Known metadata formats + */ + nx::core::MetaDataList* getMetaDataList() const; + protected: /** * @brief Constructs an Application using default values and replaces the @@ -234,6 +241,7 @@ class SIMPLNX_EXPORT Application //////////// // Variables std::unique_ptr m_FilterList; + std::unique_ptr m_MetaDataList; std::filesystem::path m_CurrentPath = ""; std::vector m_Simpl_Uuids; // no duplicates; index must match m_Simplnx_Uuids std::vector m_Simplnx_Uuids; // duplicate allowed conditionally; index must match m_Simpl_Uuids diff --git a/src/simplnx/DataStructure/DataStructure.hpp b/src/simplnx/DataStructure/DataStructure.hpp index 9f0fed78a8..3709f186fe 100644 --- a/src/simplnx/DataStructure/DataStructure.hpp +++ b/src/simplnx/DataStructure/DataStructure.hpp @@ -31,6 +31,7 @@ inline const std::string k_DataStructureTag = "DataStructure"; inline const std::string k_ObjectIdTag = "ObjectId"; inline const std::string k_NextIdTag = "NextObjectId"; inline const std::string k_ImportableTag = "Importable"; +inline const std::string k_ObjectMetaTag = "MetaData"; } // namespace Constants /** diff --git a/src/simplnx/DataStructure/IO/HDF5/DataStructureReader.cpp b/src/simplnx/DataStructure/IO/HDF5/DataStructureReader.cpp index 73b93b0990..34e20715bc 100644 --- a/src/simplnx/DataStructure/IO/HDF5/DataStructureReader.cpp +++ b/src/simplnx/DataStructure/IO/HDF5/DataStructureReader.cpp @@ -238,6 +238,12 @@ Result<> DataStructureReader::readObjectFromGroup(const nx::core::HDF5::GroupIO& { return errorCode; } + + auto metaDataResult = IDataIO::ReadMetaData(*this, parentGroup, objectName, objectId); + if(metaDataResult.invalid()) + { + return metaDataResult; + } } return {}; diff --git a/src/simplnx/DataStructure/IO/HDF5/IDataIO.cpp b/src/simplnx/DataStructure/IO/HDF5/IDataIO.cpp index 28f9347957..13b8548efe 100644 --- a/src/simplnx/DataStructure/IO/HDF5/IDataIO.cpp +++ b/src/simplnx/DataStructure/IO/HDF5/IDataIO.cpp @@ -1,6 +1,7 @@ #include "IDataIO.hpp" #include "simplnx/DataStructure/DataStructure.hpp" +#include "simplnx/DataStructure/IO/HDF5/DataStructureReader.hpp" #include "simplnx/DataStructure/IO/HDF5/DataStructureWriter.hpp" #include "simplnx/Utilities/Parsing/HDF5/IO/DatasetIO.hpp" #include "simplnx/Utilities/Parsing/HDF5/IO/GroupIO.hpp" @@ -49,12 +50,63 @@ Result<> IDataIO::WriteObjectAttributes(DataStructureWriter& dataStructureWriter int32 value = (importable ? 1 : 0); objectWriter.writeScalarAttribute(Constants::k_ImportableTag, value); + // Metadata + if(!dataObject.getMetadata().isEmpty()) + { + Result<> metaDataResult = objectWriter.writeStringAttribute(Constants::k_ObjectMetaTag, dataObject.getMetadata().toJson().dump()); + if(metaDataResult.invalid()) + { + return metaDataResult; + } + } + // Add to DataStructureWriter for use in linking dataStructureWriter.addWriter(objectWriter, dataObject.getId()); return {}; } +Result<> IDataIO::ReadMetaData(DataStructureReader& dataStructureReader, const DataObject::IdType& objectId, const object_reader_type& objectReader) +{ + if(!objectReader.hasAttribute(Constants::k_ObjectMetaTag)) + { + return {}; + } + + DataStructure& dataStructure = dataStructureReader.getDataStructure(); + if(!dataStructure.containsData(objectId)) + { + auto errorStr = fmt::format("DataStructure does not contain DataObject with ID '{}'", objectId); + return MakeErrorResult(-9700, errorStr); + } + + auto& dataObject = dataStructure.getDataRef(objectId); + + Result jsonResult = objectReader.readStringAttribute(Constants::k_ObjectMetaTag); + if(jsonResult.invalid()) + { + return ConvertResult(std::move(jsonResult)); + } + + dataObject.getMetadata().fromJson(jsonResult.value()); + + return {}; +} + +Result<> IDataIO::ReadMetaData(DataStructureReader& dataStructureReader, const group_reader_type& parentReader, const std::string& objectName, const DataObject::IdType& objectId) +{ + if(parentReader.isGroup(objectName)) + { + const auto& group = parentReader.openGroup(objectName); + return ReadMetaData(dataStructureReader, objectId, group); + } + else + { + const auto& dataset = parentReader.openDataset(objectName); + return ReadMetaData(dataStructureReader, objectId, dataset); + } +} + Result<> IDataIO::finishImportingData(DataStructure& dataStructure, const DataPath& dataPath, const group_reader_type& dataStructureGroup) const { return {}; diff --git a/src/simplnx/DataStructure/IO/HDF5/IDataIO.hpp b/src/simplnx/DataStructure/IO/HDF5/IDataIO.hpp index daf3abd1a0..ef7d07261b 100644 --- a/src/simplnx/DataStructure/IO/HDF5/IDataIO.hpp +++ b/src/simplnx/DataStructure/IO/HDF5/IDataIO.hpp @@ -26,6 +26,8 @@ class SIMPLNX_EXPORT IDataIO : public nx::core::IDataFactory ~IDataIO() noexcept override; + static Result<> ReadMetaData(DataStructureReader& dataStructureReader, const group_reader_type& parentReader, const std::string& objectName, const DataObject::IdType& objectId); + /** * @brief Attempts to read the DataObject from HDF5. * Returns a Result<> with any errors or warnings encountered during the process. @@ -93,6 +95,8 @@ class SIMPLNX_EXPORT IDataIO : public nx::core::IDataFactory */ static Result<> WriteObjectAttributes(DataStructureWriter& dataStructureWriter, const DataObject& dataObject, object_writer_type& objectWriter, bool importable); + static Result<> ReadMetaData(DataStructureReader& dataStructureReader, const DataObject::IdType& objectId, const object_reader_type& objectReader); + /** * @brief Protected constructor. */ diff --git a/src/simplnx/DataStructure/Metadata.cpp b/src/simplnx/DataStructure/Metadata.cpp index 78c5f80c5c..963dd27928 100644 --- a/src/simplnx/DataStructure/Metadata.cpp +++ b/src/simplnx/DataStructure/Metadata.cpp @@ -1,9 +1,12 @@ #include "Metadata.hpp" +#include "simplnx/Core/Application.hpp" +#include "simplnx/DataStructure/Metadata/MetaDataList.hpp" + +#include "nlohmann/json.hpp" + using namespace nx::core; -namespace nx::core -{ Metadata::Metadata() = default; Metadata::Metadata(const Metadata& rhs) = default; @@ -16,19 +19,30 @@ Metadata& Metadata::operator=(Metadata&& rhs) noexcept = default; Metadata::~Metadata() noexcept = default; +bool Metadata::isEmpty() const +{ + return m_Map.empty(); +} + bool Metadata::contains(const KeyType& key) const { return m_Map.find(key) != m_Map.end(); } -Metadata::ValueType Metadata::getData(const KeyType& key) const +const Metadata::ValuePtr& Metadata::getDataValuePtr(const KeyType& key) const { + if(!contains(key)) + { + return nullptr; + } + return m_Map.at(key); } -void Metadata::setData(const KeyType& key, const ValueType& value) +void Metadata::setDataValuePtr(const KeyType& key, const ValuePtr& value) { - m_Map[key] = value; + // m_Map.insert(key, value); + m_Map[key] = std::move(value); } void Metadata::remove(const KeyType& key) @@ -41,11 +55,6 @@ void Metadata::clear() m_Map.clear(); } -Metadata::ValueType& Metadata::operator[](const KeyType& key) -{ - return m_Map[key]; -} - Metadata::Iterator Metadata::begin() { return m_Map.begin(); @@ -65,4 +74,25 @@ Metadata::ConstIterator Metadata::end() const { return m_Map.end(); } -} // namespace nx::core + +nlohmann::json Metadata::toJson() const +{ + nlohmann::json json = nlohmann::json::object(); + for(const auto& [key, value] : m_Map) + { + json[key] = value->toJson(); + } + + return json; +} + +void Metadata::fromJson(const std::string& jsonStr) +{ + MetaDataList* metaDataList = Application::Instance()->getMetaDataList(); + + nlohmann::json json = nlohmann::json::parse(jsonStr); + for(auto& [key, value] : json.items()) + { + m_Map[key] = metaDataList->createValueFromJson(value); + } +} diff --git a/src/simplnx/DataStructure/Metadata.hpp b/src/simplnx/DataStructure/Metadata.hpp index 8d51fec279..5f3221b149 100644 --- a/src/simplnx/DataStructure/Metadata.hpp +++ b/src/simplnx/DataStructure/Metadata.hpp @@ -1,9 +1,15 @@ #pragma once +#include "simplnx/DataStructure/Metadata/AbstractMetadataValue.hpp" + #include "simplnx/simplnx_export.hpp" +#include +#include + #include #include +#include #include namespace nx::core @@ -21,9 +27,10 @@ class SIMPLNX_EXPORT Metadata { public: using KeyType = std::string; - using ValueType = std::any; - using Iterator = std::map::iterator; - using ConstIterator = std::map::const_iterator; + using ValueType = BaseMetadataValue; + using ValuePtr = std::shared_ptr; + using Iterator = typename std::map::iterator; + using ConstIterator = std::map::const_iterator; /** * @brief Default constructor. @@ -61,6 +68,13 @@ class SIMPLNX_EXPORT Metadata */ ~Metadata() noexcept; + /** + * @brief Returns true if there are no metadata values stored. + * Returns false otherwise + * @return bool is empty + */ + bool isEmpty() const; + /** * @brief Checks if metadata exists for the specified key. * @param key The key to check for @@ -69,19 +83,63 @@ class SIMPLNX_EXPORT Metadata bool contains(const KeyType& key) const; /** - * @brief Returns the ValueType for the target key. Returns an empty std::any + * @brief Returns the ValuePtr for the target key. Returns nullptr * if the key does not exist in the Metadata. * @param key The key to retrieve data for - * @return ValueType containing the metadata value, or empty std::any if key doesn't exist + * @return ValuePtr containing the metadata value, or nullptr if key doesn't exist */ - ValueType getData(const KeyType& key) const; + const ValuePtr& getDataValuePtr(const KeyType& key) const; + + /** + * @brief Returns the metadata value for the target key. + * Throws if the key does not exist in the Metadata. + * @param key The key to retrieve data for + * @return metadata value for key of type T + */ + template + std::shared_ptr getDataValuePtrAs(const KeyType& key) const + { + ValuePtr dataPtr = getDataValuePtr(key); + if(const auto typedDataPtr = std::static_pointer_cast(dataPtr); typedDataPtr != nullptr) + { + return typedDataPtr; + } + + std::string errorStr = fmt::format("Metadata '{}' does not exist or cannot be cast to type '{}'", key, typeid(T).name()); + throw std::runtime_error(errorStr); + } /** * @brief Adds or assigns the specified value for the target key. * @param key The key to set data for * @param value The value to associate with the key */ - void setData(const KeyType& key, const ValueType& value); + void setDataValuePtr(const KeyType& key, const ValuePtr& value); + + /** + * @brief Sets or creates a value with the specified key. + * @param key Name of the stored value. + * @param value Value to store + */ + template + void setData(const KeyType& key, const typename T::ValueType& value) + { + auto dataPtr = std::make_shared(); + *dataPtr.get() = value; + setDataValuePtr(key, dataPtr); + } + + /** + * @brief Returns the value stored in the metadata value specified by the given key. + * @param key Name of the stored value to lookup. + * @return T Stored value + */ + template + T getDataAs(const KeyType& key) const + { + const auto& dataValue = getDataValuePtrAs>(key); + return dataValue->getValue(); + } /** * @brief Clears the metadata with the specified key. Does nothing if the key @@ -95,14 +153,6 @@ class SIMPLNX_EXPORT Metadata */ void clear(); - /** - * @brief Returns a reference to the data with the target key. - * Returns and adds an empty std::any if no data exists with the key value. - * @param key The key to retrieve or create - * @return Reference to the ValueType at the specified key - */ - ValueType& operator[](const KeyType& key); - /** * @brief Returns an iterator to the beginning of the Metadata collection. * @return Iterator to the beginning @@ -127,7 +177,11 @@ class SIMPLNX_EXPORT Metadata */ ConstIterator end() const; + nlohmann::json toJson() const; + + void fromJson(const std::string& json); + private: - std::map m_Map; + std::map m_Map; }; } // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/AbstractMetadataValue.cpp b/src/simplnx/DataStructure/Metadata/AbstractMetadataValue.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/simplnx/DataStructure/Metadata/AbstractMetadataValue.hpp b/src/simplnx/DataStructure/Metadata/AbstractMetadataValue.hpp new file mode 100644 index 0000000000..7ab108808c --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/AbstractMetadataValue.hpp @@ -0,0 +1,99 @@ +#pragma once + +#include "BaseMetadataValue.hpp" + +#include "simplnx/Common/StringLiteral.hpp" + +#include + +#include + +namespace nx::core +{ +/** + * @brief Abstract typed meta data value class. Derived classes implement the + * specifics for reading and writing the target value, but the AbstractMetaData + * class allows reading and assigning the appropriate data type. + */ +template +class AbstractMetadataValue : public BaseMetadataValue +{ +public: + using ValueType = T; + + AbstractMetadataValue(const AbstractMetadataValue& other) = default; + AbstractMetadataValue(AbstractMetadataValue&& other) noexcept = default; + ~AbstractMetadataValue() noexcept = default; + + /** + * @brief Default cast to the type in question + * @return metadata value + */ + virtual operator ValueType() const = 0; + + /** + * @brief Returns the stored value. + * @return metadata value + */ + virtual ValueType getValue() const = 0; + + /** + * @brief Sets the stored value. + * @param value + */ + virtual void setValue(const ValueType& value) = 0; + + /** + * @brief Assignment operator + * @param rhs + */ + virtual AbstractMetadataValue& operator=(const ValueType& rhs) = 0; + + /** + * @brief Default equality operator + * @param rhs value to compare against + * @return is equal + */ + virtual bool operator==(const ValueType& rhs) const = 0; + + std::string getTypeName() const override + { + return getTypeNameImpl(); + } + + /** + * @brief Returns a json string representation for the meta data. + * @return json + */ + nlohmann::json toJson() const override + { + return toJsonImpl(); + } + + /** + * @brief Reads and updates the meta data value based on the provided json string + * @param json + */ + void fromJson(const nlohmann::json& json) override + { + return fromJsonImpl(json); + } + +protected: + AbstractMetadataValue() = default; + + virtual std::string getTypeNameImpl() const = 0; + + /** + * @brief Abstract method for derived classes to specify the appropriate json. + * @return json + */ + virtual nlohmann::json toJsonImpl() const = 0; + + /** + * @brief Abstract method for reading data from a json. + * @param jsonStr json + */ + virtual void fromJsonImpl(const nlohmann::json& json) = 0; +}; +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/AbstractVectorMetadataValue.hpp b/src/simplnx/DataStructure/Metadata/AbstractVectorMetadataValue.hpp new file mode 100644 index 0000000000..2f8bc0896e --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/AbstractVectorMetadataValue.hpp @@ -0,0 +1,108 @@ +#pragma once + +#include "AbstractMetadataValue.hpp" + +namespace nx::core +{ +template +class AbstractVectorMetadataValue : public AbstractMetadataValue> +{ +public: + using ParentType = typename AbstractMetadataValue>; + using ValueType = typename ParentType::ValueType; + using AssignmentReturnType = typename ParentType; + + AbstractVectorMetadataValue(const AbstractVectorMetadataValue& other) + : ParentType(other) + , m_Value(other.m_Value) + { + } + AbstractVectorMetadataValue(AbstractVectorMetadataValue&& other) noexcept + : ParentType(other) + , m_Value(std::move(other.m_Value)) + { + } + + ~AbstractVectorMetadataValue() noexcept = default; + + /** + * @brief Default cast to the type in question + * @return metadata value + */ + operator ValueType() const override + { + return m_Value; + } + + /** + * @brief Returns the stored value. + * @return metadata value + */ + ValueType getValue() const override + { + return m_Value; + } + + /** + * @brief Sets the stored value. + * @param value + */ + void setValue(const ValueType& value) override + { + m_Value = value; + } + + /** + * @brief Assignment operator + * @param rhs + */ + ParentType& operator=(const ValueType& rhs) override + { + m_Value = rhs; + return *this; + } + + /** + * @brief Default equality operator + * @param rhs value to compare against + * @return is equal + */ + bool operator==(const ValueType& rhs) const override + { + return m_Value == rhs; + } + +protected: + AbstractVectorMetadataValue() + : ParentType() + { + } + + AbstractVectorMetadataValue& operator=(const AbstractVectorMetadataValue& rhs) + { + m_Value = rhs.m_Value; + return *this; + } + AbstractVectorMetadataValue& operator=(AbstractVectorMetadataValue&& rhs) noexcept + { + m_Value = std::move(rhs.m_Value); + return *this; + } + + nlohmann::json toJsonImpl() const override + { + nlohmann::json json; + json[ParentType::k_ValueTypeKey] = ParentType::getTypeName(); + json[ParentType::k_ValueKey] = m_Value; + return json; + } + + void fromJsonImpl(const nlohmann::json& json) override + { + m_Value = json[ParentType::k_ValueKey].get(); + } + +private: + std::vector m_Value; +}; +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/BaseMetadataValue.cpp b/src/simplnx/DataStructure/Metadata/BaseMetadataValue.cpp new file mode 100644 index 0000000000..dc092d8fac --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/BaseMetadataValue.cpp @@ -0,0 +1,24 @@ +#include "BaseMetadataValue.hpp" + +#include "nlohmann/json.hpp" + +namespace nx::core +{ +std::string BaseMetadataValue::getTypeName() const +{ + return "[Not Implemented]"; +} + +nlohmann::json BaseMetadataValue::toJson() const +{ + nlohmann::json json; + json[k_ValueTypeKey] = "Error"; + json[k_ValueKey] = ""; + return json; +} + +void BaseMetadataValue::fromJson(const nlohmann::json& json) +{ + throw std::runtime_error("BaseMetadataValue::fromJson not implemented"); +} +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/BaseMetadataValue.hpp b/src/simplnx/DataStructure/Metadata/BaseMetadataValue.hpp new file mode 100644 index 0000000000..3d14e54a4d --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/BaseMetadataValue.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include "simplnx/Common/StringLiteral.hpp" + +#include "simplnx/simplnx_export.hpp" + +#include + +#include + +namespace nx::core +{ +/** + * @brief Base class for meta data values. + */ +class SIMPLNX_EXPORT BaseMetadataValue +{ +public: + static constexpr StringLiteral k_ValueTypeKey = "type"; + static constexpr StringLiteral k_ValueKey = "value"; + + BaseMetadataValue(const BaseMetadataValue& other) = default; + BaseMetadataValue(BaseMetadataValue&& other) noexcept = default; + ~BaseMetadataValue() noexcept = default; + + /** + * @brief Returns the metadata type name. + * @return std::string + */ + virtual std::string getTypeName() const; + + /** + * @brief Returns the metadata's json representation. + * @return json + */ + virtual nlohmann::json toJson() const; + + /** + * @brief Updates the metadata from the provided json value. + * @param json metadata json representation + */ + virtual void fromJson(const nlohmann::json& json); + +protected: + BaseMetadataValue() = default; +}; +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/BoolMetadataValue.cpp b/src/simplnx/DataStructure/Metadata/BoolMetadataValue.cpp new file mode 100644 index 0000000000..4bd0ce7106 --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/BoolMetadataValue.cpp @@ -0,0 +1,56 @@ +#include "BoolMetadataValue.hpp" + +#include "nlohmann/json.hpp" + +namespace nx::core +{ +BoolMetadataValue::BoolMetadataValue(bool value) +: ParentType() +, m_Value(value) +{ +} + +BoolMetadataValue::operator ValueType() const +{ + return m_Value; +} + +BoolMetadataValue::ValueType BoolMetadataValue::getValue() const +{ + return m_Value; +} + +void BoolMetadataValue::setValue(const ValueType& value) +{ + m_Value = value; +} + +bool BoolMetadataValue::operator==(const ValueType& rhs) const +{ + return m_Value == rhs; +} + +BoolMetadataValue::ParentType& BoolMetadataValue::operator=(const ValueType& rhs) +{ + m_Value = rhs; + return *this; +} + +std::string BoolMetadataValue::getTypeNameImpl() const +{ + return k_TypeName; +} + +nlohmann::json BoolMetadataValue::toJsonImpl() const +{ + nlohmann::json json; + json[k_ValueTypeKey.str()] = k_TypeName; + json[k_ValueKey.str()] = m_Value; + return json; +} + +void BoolMetadataValue::fromJsonImpl(const nlohmann::json& json) +{ + m_Value = json[k_ValueKey.str()].get(); +} +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/BoolMetadataValue.hpp b/src/simplnx/DataStructure/Metadata/BoolMetadataValue.hpp new file mode 100644 index 0000000000..9d5f482349 --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/BoolMetadataValue.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include "AbstractMetadataValue.hpp" + +namespace nx::core +{ +/** + * @brief Metadata class implementation for reading and writing boolean metadata. + */ +class SIMPLNX_EXPORT BoolMetadataValue : public AbstractMetadataValue +{ +public: + using ParentType = AbstractMetadataValue; + using ValueType = typename ParentType::ValueType; + + static constexpr StringLiteral k_TypeName = "bool"; + + BoolMetadataValue(ValueType value = false); + BoolMetadataValue(const BoolMetadataValue& other) = default; + BoolMetadataValue(BoolMetadataValue&& other) = default; + ~BoolMetadataValue() = default; + + /** + * @brief Default cast to the type in question + * @return metadata value + */ + operator ValueType() const override; + + /** + * @brief Returns the stored value. + * @return metadata value + */ + ValueType getValue() const override; + + /** + * @brief Sets the stored value. + * @param value + */ + void setValue(const ValueType& value) override; + + /** + * @brief Default equality operator + * @param rhs value to compare against + * @return is equal + */ + bool operator==(const ValueType& rhs) const override; + + /** + * @brief Assignment operator + * @param rhs + */ + ParentType& operator=(const ValueType& rhs) override; + +protected: + std::string getTypeNameImpl() const override; + + nlohmann::json toJsonImpl() const override; + + void fromJsonImpl(const nlohmann::json& json) override; + +private: + bool m_Value; +}; +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/BoolVectorMetadataValue.cpp b/src/simplnx/DataStructure/Metadata/BoolVectorMetadataValue.cpp new file mode 100644 index 0000000000..c294f1e523 --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/BoolVectorMetadataValue.cpp @@ -0,0 +1,44 @@ +#include "BoolVectorMetadataValue.hpp" + +namespace nx::core +{ +BoolVectorMetadataValue::BoolVectorMetadataValue() +: ParentType() +{ +} + +BoolVectorMetadataValue::BoolVectorMetadataValue(const BoolVectorMetadataValue& rhs) +: ParentType(rhs) +{ +} +BoolVectorMetadataValue::BoolVectorMetadataValue(BoolVectorMetadataValue&& rhs) noexcept +: ParentType(rhs) +{ +} + +BoolVectorMetadataValue& BoolVectorMetadataValue::operator=(const BoolVectorMetadataValue& rhs) +{ + setValue(rhs.getValue()); + return *this; +} +BoolVectorMetadataValue& BoolVectorMetadataValue::operator=(BoolVectorMetadataValue&& rhs) noexcept +{ + ParentType::operator=(rhs); + return *this; +} + +std::string BoolVectorMetadataValue::getTypeNameImpl() const +{ + return k_TypeName; +} + +BoolVectorMetadataValue::AssignmentReturnType& BoolVectorMetadataValue::operator=(const ValueType& rhs) +{ + return ParentType::operator=(rhs); +} + +bool BoolVectorMetadataValue::operator==(const ValueType& rhs) const +{ + return ParentType::operator==(rhs); +} +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/BoolVectorMetadataValue.hpp b/src/simplnx/DataStructure/Metadata/BoolVectorMetadataValue.hpp new file mode 100644 index 0000000000..55d4c85584 --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/BoolVectorMetadataValue.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include "AbstractVectorMetadataValue.hpp" + +namespace nx::core +{ +class SIMPLNX_EXPORT BoolVectorMetadataValue : public AbstractVectorMetadataValue +{ +public: + using ParentType = AbstractVectorMetadataValue; + using ValueType = typename ParentType::ValueType; + using AssignmentReturnType = typename ParentType::AssignmentReturnType; + + static constexpr StringLiteral k_TypeName = "vec"; + + BoolVectorMetadataValue(); + BoolVectorMetadataValue(const BoolVectorMetadataValue& rhs); + BoolVectorMetadataValue(BoolVectorMetadataValue&& rhs) noexcept; + ~BoolVectorMetadataValue() noexcept = default; + + BoolVectorMetadataValue& operator=(const BoolVectorMetadataValue& rhs); + BoolVectorMetadataValue& operator=(BoolVectorMetadataValue&& rhs) noexcept; + + /** + * @brief Assignment operator + * @param rhs + */ + AssignmentReturnType& operator=(const ValueType& rhs) override; + + /** + * @brief Default equality operator + * @param rhs value to compare against + * @return is equal + */ + bool operator==(const ValueType& rhs) const override; + +protected: + std::string getTypeNameImpl() const override; +}; +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/Float64MetadataValue.cpp b/src/simplnx/DataStructure/Metadata/Float64MetadataValue.cpp new file mode 100644 index 0000000000..3991be7f8b --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/Float64MetadataValue.cpp @@ -0,0 +1,56 @@ +#include "Float64MetadataValue.hpp" + +#include "nlohmann/json.hpp" + +namespace nx::core +{ +Float64MetadataValue::Float64MetadataValue(ValueType value) +: ParentType() +, m_Value(value) +{ +} + +Float64MetadataValue::operator ValueType() const +{ + return m_Value; +} + +Float64MetadataValue::ValueType Float64MetadataValue::getValue() const +{ + return m_Value; +} + +void Float64MetadataValue::setValue(const ValueType& value) +{ + m_Value = value; +} + +bool Float64MetadataValue::operator==(const ValueType& rhs) const +{ + return m_Value == rhs; +} + +Float64MetadataValue::ParentType& Float64MetadataValue::operator=(const ValueType& rhs) +{ + m_Value = rhs; + return *this; +} + +std::string Float64MetadataValue::getTypeNameImpl() const +{ + return k_TypeName; +} + +nlohmann::json Float64MetadataValue::toJsonImpl() const +{ + nlohmann::json json; + json[k_ValueTypeKey.str()] = k_TypeName; + json[k_ValueKey.str()] = m_Value; + return json; +} + +void Float64MetadataValue::fromJsonImpl(const nlohmann::json& json) +{ + m_Value = json[k_ValueKey.str()].get(); +} +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/Float64MetadataValue.hpp b/src/simplnx/DataStructure/Metadata/Float64MetadataValue.hpp new file mode 100644 index 0000000000..9acba1e49e --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/Float64MetadataValue.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include "AbstractMetadataValue.hpp" + +namespace nx::core +{ +/** + * @brief Metadata class implementation for reading and writing floating-point metadata. + */ +class SIMPLNX_EXPORT Float64MetadataValue : public AbstractMetadataValue +{ +public: + using ParentType = AbstractMetadataValue; + using ValueType = typename ParentType::ValueType; + + static constexpr StringLiteral k_TypeName = "float64"; + + Float64MetadataValue(ValueType value = 0.0); + Float64MetadataValue(const Float64MetadataValue& other) = default; + Float64MetadataValue(Float64MetadataValue&& other) = default; + ~Float64MetadataValue() = default; + + /** + * @brief Default cast to the type in question + * @return metadata value + */ + operator ValueType() const override; + + /** + * @brief Returns the stored value. + * @return metadata value + */ + ValueType getValue() const override; + + /** + * @brief Sets the stored value. + * @param value + */ + void setValue(const ValueType& value) override; + + /** + * @brief Default equality operator + * @param rhs value to compare against + * @return is equal + */ + bool operator==(const ValueType& rhs) const override; + + /** + * @brief Assignment operator + * @param rhs + */ + ParentType& operator=(const ValueType& rhs) override; + +protected: + std::string getTypeNameImpl() const override; + + nlohmann::json toJsonImpl() const override; + + void fromJsonImpl(const nlohmann::json& json) override; + +private: + ValueType m_Value; +}; +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/Float64VectorMetadataValue.cpp b/src/simplnx/DataStructure/Metadata/Float64VectorMetadataValue.cpp new file mode 100644 index 0000000000..fd1c05a04d --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/Float64VectorMetadataValue.cpp @@ -0,0 +1,33 @@ +#include "Float64VectorMetadataValue.hpp" + +namespace nx::core +{ +Float64VectorMetadataValue::Float64VectorMetadataValue() +: ParentType() +{ +} + +Float64VectorMetadataValue::Float64VectorMetadataValue(const Float64VectorMetadataValue& rhs) +: ParentType(rhs) +{ +} +Float64VectorMetadataValue::Float64VectorMetadataValue(Float64VectorMetadataValue&& rhs) noexcept +: ParentType(rhs) +{ +} + +std::string Float64VectorMetadataValue::getTypeNameImpl() const +{ + return k_TypeName; +} + +Float64VectorMetadataValue::AssignmentReturnType& Float64VectorMetadataValue::operator=(const ValueType& rhs) +{ + return ParentType::operator=(rhs); +} + +bool Float64VectorMetadataValue::operator==(const ValueType& rhs) const +{ + return ParentType::operator==(rhs); +} +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/Float64VectorMetadataValue.hpp b/src/simplnx/DataStructure/Metadata/Float64VectorMetadataValue.hpp new file mode 100644 index 0000000000..06a3abdb93 --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/Float64VectorMetadataValue.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include "AbstractVectorMetadataValue.hpp" + +namespace nx::core +{ +class SIMPLNX_EXPORT Float64VectorMetadataValue : public AbstractVectorMetadataValue +{ +public: + using ParentType = AbstractVectorMetadataValue; + using ValueType = typename ParentType::ValueType; + using AssignmentReturnType = typename ParentType::AssignmentReturnType; + + static constexpr StringLiteral k_TypeName = "vec"; + + Float64VectorMetadataValue(); + Float64VectorMetadataValue(const Float64VectorMetadataValue& rhs); + Float64VectorMetadataValue(Float64VectorMetadataValue&& rhs) noexcept; + ~Float64VectorMetadataValue() noexcept = default; + + Float64VectorMetadataValue& operator=(const Float64VectorMetadataValue& rhs) = default; + Float64VectorMetadataValue& operator=(Float64VectorMetadataValue&& rhs) noexcept = default; + + /** + * @brief Assignment operator + * @param rhs + */ + AssignmentReturnType& operator=(const ValueType& rhs) override; + + /** + * @brief Default equality operator + * @param rhs value to compare against + * @return is equal + */ + bool operator==(const ValueType& rhs) const override; + +protected: + std::string getTypeNameImpl() const override; +}; +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/Int32MetadataValue.cpp b/src/simplnx/DataStructure/Metadata/Int32MetadataValue.cpp new file mode 100644 index 0000000000..58bb5d4eb8 --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/Int32MetadataValue.cpp @@ -0,0 +1,56 @@ +#include "Int32MetadataValue.hpp" + +#include "nlohmann/json.hpp" + +namespace nx::core +{ +Int32MetadataValue::Int32MetadataValue(int32 value) +: AbstractMetadataValue() +, m_Value(value) +{ +} + +Int32MetadataValue::operator int32() const +{ + return m_Value; +} + +Int32MetadataValue::ValueType Int32MetadataValue::getValue() const +{ + return m_Value; +} + +void Int32MetadataValue::setValue(const ValueType& value) +{ + m_Value = value; +} + +bool Int32MetadataValue::operator==(const ValueType& rhs) const +{ + return m_Value == rhs; +} + +Int32MetadataValue::ParentType& Int32MetadataValue::operator=(const int32& rhs) +{ + m_Value = rhs; + return *this; +} + +std::string Int32MetadataValue::getTypeNameImpl() const +{ + return k_TypeName; +} + +nlohmann::json Int32MetadataValue::toJsonImpl() const +{ + nlohmann::json json; + json[k_ValueTypeKey.str()] = k_TypeName; + json[k_ValueKey.str()] = m_Value; + return json; +} + +void Int32MetadataValue::fromJsonImpl(const nlohmann::json& json) +{ + m_Value = json[k_ValueKey.str()].get(); +} +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/Int32MetadataValue.hpp b/src/simplnx/DataStructure/Metadata/Int32MetadataValue.hpp new file mode 100644 index 0000000000..7ef21e7555 --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/Int32MetadataValue.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include "AbstractMetadataValue.hpp" + +namespace nx::core +{ +/** + * @brief Metadata class implementation for reading and writing integer metadata. + */ +class SIMPLNX_EXPORT Int32MetadataValue : public AbstractMetadataValue +{ +public: + using ParentType = typename AbstractMetadataValue; + using ValueType = typename ParentType::ValueType; + + static constexpr StringLiteral k_TypeName = "int32"; + + Int32MetadataValue(ValueType value = 0); + Int32MetadataValue(const Int32MetadataValue& other) = default; + Int32MetadataValue(Int32MetadataValue&& other) = default; + ~Int32MetadataValue() = default; + + /** + * @brief Default cast to the type in question + * @return metadata value + */ + operator ValueType() const override; + + /** + * @brief Returns the stored value. + * @return metadata value + */ + ValueType getValue() const override; + + /** + * @brief Sets the stored value. + * @param value + */ + void setValue(const ValueType& value) override; + + /** + * @brief Default equality operator + * @param rhs value to compare against + * @return is equal + */ + bool operator==(const ValueType& rhs) const override; + + /** + * @brief Assignment operator + * @param rhs + */ + ParentType& operator=(const ValueType& rhs) override; + +protected: + std::string getTypeNameImpl() const override; + + nlohmann::json toJsonImpl() const override; + + void fromJsonImpl(const nlohmann::json& json) override; + +private: + int32 m_Value; +}; +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/Int32VectorMetadataValue.cpp b/src/simplnx/DataStructure/Metadata/Int32VectorMetadataValue.cpp new file mode 100644 index 0000000000..0fe259da5d --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/Int32VectorMetadataValue.cpp @@ -0,0 +1,33 @@ +#include "Int32VectorMetadataValue.hpp" + +namespace nx::core +{ +Int32VectorMetadataValue::Int32VectorMetadataValue() +: ParentType() +{ +} + +Int32VectorMetadataValue::Int32VectorMetadataValue(const Int32VectorMetadataValue& rhs) +: ParentType(rhs) +{ +} +Int32VectorMetadataValue::Int32VectorMetadataValue(Int32VectorMetadataValue&& rhs) noexcept +: ParentType(rhs) +{ +} + +std::string Int32VectorMetadataValue::getTypeNameImpl() const +{ + return k_TypeName; +} + +Int32VectorMetadataValue::AssignmentReturnType& Int32VectorMetadataValue::operator=(const ValueType& rhs) +{ + return ParentType::operator=(rhs); +} + +bool Int32VectorMetadataValue::operator==(const ValueType& rhs) const +{ + return ParentType::operator==(rhs); +} +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/Int32VectorMetadataValue.hpp b/src/simplnx/DataStructure/Metadata/Int32VectorMetadataValue.hpp new file mode 100644 index 0000000000..ed177874cb --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/Int32VectorMetadataValue.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include "AbstractVectorMetadataValue.hpp" + +namespace nx::core +{ +class SIMPLNX_EXPORT Int32VectorMetadataValue : public AbstractVectorMetadataValue +{ +public: + using ParentType = AbstractVectorMetadataValue; + using ValueType = typename ParentType::ValueType; + using AssignmentReturnType = typename ParentType::AssignmentReturnType; + + static constexpr StringLiteral k_TypeName = "vec"; + + Int32VectorMetadataValue(); + Int32VectorMetadataValue(const Int32VectorMetadataValue& rhs); + Int32VectorMetadataValue(Int32VectorMetadataValue&& rhs) noexcept; + ~Int32VectorMetadataValue() noexcept = default; + + Int32VectorMetadataValue& operator=(const Int32VectorMetadataValue& rhs) = default; + Int32VectorMetadataValue& operator=(Int32VectorMetadataValue&& rhs) noexcept = default; + + /** + * @brief Assignment operator + * @param rhs + */ + AssignmentReturnType& operator=(const ValueType& rhs) override; + + /** + * @brief Default equality operator + * @param rhs value to compare against + * @return is equal + */ + bool operator==(const ValueType& rhs) const override; + +protected: + std::string getTypeNameImpl() const override; +}; +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/MetaDataList.cpp b/src/simplnx/DataStructure/Metadata/MetaDataList.cpp new file mode 100644 index 0000000000..39e4549acc --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/MetaDataList.cpp @@ -0,0 +1,90 @@ +#include "MetaDataList.hpp" + +#include "simplnx/DataStructure/Metadata/BoolMetadataValue.hpp" +#include "simplnx/DataStructure/Metadata/BoolVectorMetadataValue.hpp" +#include "simplnx/DataStructure/Metadata/Float64MetadataValue.hpp" +#include "simplnx/DataStructure/Metadata/Float64VectorMetadataValue.hpp" +#include "simplnx/DataStructure/Metadata/Int32MetadataValue.hpp" +#include "simplnx/DataStructure/Metadata/Int32VectorMetadataValue.hpp" +#include "simplnx/DataStructure/Metadata/StringMetadataValue.hpp" +#include "simplnx/DataStructure/Metadata/StringVectorMetadataValue.hpp" +#include "simplnx/DataStructure/Metadata/UnknownMetadataValue.hpp" + +#include "nlohmann/json.hpp" + +namespace nx::core +{ +MetaDataList::MetaDataList() +{ + addDefaultTypes(); +} + +void MetaDataList::addDefaultTypes() +{ + // Boolean Metadata + addMetaDataType(BoolMetadataValue::k_TypeName, [](const nlohmann::json& json) { + auto metaData = std::make_unique(); + metaData->fromJson(json); + return metaData; + }); + // Boolean Vector Metadata + addMetaDataType(BoolVectorMetadataValue::k_TypeName, [](const nlohmann::json& json) { + auto metaData = std::make_unique(); + metaData->fromJson(json); + return metaData; + }); + // Double Metadata + addMetaDataType(Float64MetadataValue::k_TypeName, [](const nlohmann::json& json) { + auto metaData = std::make_unique(); + metaData->fromJson(json); + return metaData; + }); + // Double Vector Metadata + addMetaDataType(Float64VectorMetadataValue::k_TypeName, [](const nlohmann::json& json) { + auto metaData = std::make_unique(); + metaData->fromJson(json); + return metaData; + }); + // Integer Metadata + addMetaDataType(Int32MetadataValue::k_TypeName, [](const nlohmann::json& json) { + auto metaData = std::make_unique(); + metaData->fromJson(json); + return metaData; + }); + // Integer Vector Metadata + addMetaDataType(Int32VectorMetadataValue::k_TypeName, [](const nlohmann::json& json) { + auto metaData = std::make_unique(); + metaData->fromJson(json); + return metaData; + }); + // String Metadata + addMetaDataType(StringMetadataValue::k_TypeName, [](const nlohmann::json& json) { + auto metaData = std::make_unique(); + metaData->fromJson(json); + return metaData; + }); + // String Vector Metadata + addMetaDataType(StringVectorMetadataValue::k_TypeName, [](const nlohmann::json& json) { + auto metaData = std::make_unique(); + metaData->fromJson(json); + return metaData; + }); +} + +void MetaDataList::addMetaDataType(const KeyType& name, MetaDataCreationFnc constructorFnc) +{ + m_CreationMap[name] = constructorFnc; +} + +std::unique_ptr MetaDataList::createValueFromJson(const nlohmann::json& json) const +{ + std::string type = json[BaseMetadataValue::k_ValueTypeKey.str()].get(); + + if(!m_CreationMap.contains(type)) + { + return std::make_unique(json); + } + + return m_CreationMap.at(type)(json); +} +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/MetaDataList.hpp b/src/simplnx/DataStructure/Metadata/MetaDataList.hpp new file mode 100644 index 0000000000..40a52ed4db --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/MetaDataList.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include "simplnx/DataStructure/Metadata/BaseMetadataValue.hpp" + +#include "simplnx/simplnx_export.hpp" + +#include +#include +#include + +namespace nx::core +{ +class SIMPLNX_EXPORT MetaDataList +{ +public: + using KeyType = std::string; + using CreatedValueType = std::unique_ptr; + using MetaDataCreationFnc = std::function; + using ContainerType = std::map; + + MetaDataList(); + MetaDataList(const MetaDataList& other) = default; + MetaDataList(MetaDataList&& other) = default; + ~MetaDataList() = default; + + /** + * @brief Add a meta data value type for creation from json. + * @param name Metadata type name + * @param constructorFnc Function to create and return a metadata value pointer. + */ + void addMetaDataType(const KeyType& name, MetaDataCreationFnc constructorFnc); + + /** + * @brief Creates and returns a metadata value from the provided json. + * @param json + * @return std::unique_ptr + */ + std::unique_ptr createValueFromJson(const nlohmann::json& json) const; + +protected: + /** + * @brief Adds default simplnx metadata value types. + */ + void addDefaultTypes(); + +private: + ContainerType m_CreationMap; +}; +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/StringMetadataValue.cpp b/src/simplnx/DataStructure/Metadata/StringMetadataValue.cpp new file mode 100644 index 0000000000..6ca52e5921 --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/StringMetadataValue.cpp @@ -0,0 +1,61 @@ +#include "StringMetadataValue.hpp" + +#include "nlohmann/json.hpp" + +namespace nx::core +{ +StringMetadataValue::StringMetadataValue() +: AbstractMetadataValue() +{ +} + +StringMetadataValue::StringMetadataValue(const std::string& value) +: AbstractMetadataValue() +, m_Value(value) +{ +} + +StringMetadataValue::operator StringMetadataValue::ValueType() const +{ + return m_Value; +} + +StringMetadataValue::ValueType StringMetadataValue::getValue() const +{ + return m_Value; +} + +void StringMetadataValue::setValue(const ValueType& value) +{ + m_Value = value; +} + +bool StringMetadataValue::operator==(const ValueType& rhs) const +{ + return m_Value == rhs; +} + +StringMetadataValue::ParentType& StringMetadataValue::operator=(const std::string& rhs) +{ + m_Value = rhs; + return *this; +} + +std::string StringMetadataValue::getTypeNameImpl() const +{ + return k_TypeName; +} + +nlohmann::json StringMetadataValue::toJsonImpl() const +{ + nlohmann::json json; + json[k_ValueTypeKey.str()] = k_TypeName; + json[k_ValueKey.str()] = m_Value; + return json; +} + +void StringMetadataValue::fromJsonImpl(const nlohmann::json& json) +{ + m_Value = json[k_ValueKey.str()].get(); +} +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/StringMetadataValue.hpp b/src/simplnx/DataStructure/Metadata/StringMetadataValue.hpp new file mode 100644 index 0000000000..e89241db7a --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/StringMetadataValue.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include "AbstractMetadataValue.hpp" + +#include "simplnx/simplnx_export.hpp" + +#include + +namespace nx::core +{ +/** + * @brief String metadata class implementation for reading and writing string-based metadata. + */ +class SIMPLNX_EXPORT StringMetadataValue : public AbstractMetadataValue +{ +public: + using ParentType = AbstractMetadataValue; + using ValueType = typename ParentType::ValueType; + + static constexpr StringLiteral k_TypeName = "string"; + + StringMetadataValue(); + StringMetadataValue(const ValueType& value); + StringMetadataValue(const StringMetadataValue& other) = default; + StringMetadataValue(StringMetadataValue&& other) = default; + ~StringMetadataValue() = default; + + /** + * @brief Default cast to the type in question + * @return metadata value + */ + operator ValueType() const override; + + /** + * @brief Returns the stored value. + * @return metadata value + */ + ValueType getValue() const override; + + /** + * @brief Sets the stored value. + * @param value + */ + void setValue(const ValueType& value) override; + + /** + * @brief Default equality operator + * @param rhs value to compare against + * @return is equal + */ + bool operator==(const ValueType& rhs) const override; + + /** + * @brief Assignment operator + * @param rhs + */ + ParentType& operator=(const ValueType& rhs) override; + +protected: + /** + * @brief Returns the typename for the StringMetadata + * @return typename string + */ + std::string getTypeNameImpl() const override; + + nlohmann::json toJsonImpl() const override; + + void fromJsonImpl(const nlohmann::json& json) override; + +private: + std::string m_Value; +}; +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/StringVectorMetadataValue.cpp b/src/simplnx/DataStructure/Metadata/StringVectorMetadataValue.cpp new file mode 100644 index 0000000000..1c92dbbbe2 --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/StringVectorMetadataValue.cpp @@ -0,0 +1,33 @@ +#include "StringVectorMetadataValue.hpp" + +namespace nx::core +{ +StringVectorMetadataValue::StringVectorMetadataValue() +: ParentType() +{ +} + +StringVectorMetadataValue::StringVectorMetadataValue(const StringVectorMetadataValue& rhs) +: ParentType(rhs) +{ +} +StringVectorMetadataValue::StringVectorMetadataValue(StringVectorMetadataValue&& rhs) noexcept +: ParentType(rhs) +{ +} + +std::string StringVectorMetadataValue::getTypeNameImpl() const +{ + return k_TypeName; +} + +StringVectorMetadataValue::AssignmentReturnType& StringVectorMetadataValue::operator=(const ValueType& rhs) +{ + return ParentType::operator=(rhs); +} + +bool StringVectorMetadataValue::operator==(const ValueType& rhs) const +{ + return ParentType::operator==(rhs); +} +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/StringVectorMetadataValue.hpp b/src/simplnx/DataStructure/Metadata/StringVectorMetadataValue.hpp new file mode 100644 index 0000000000..0bd341c074 --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/StringVectorMetadataValue.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include "AbstractVectorMetadataValue.hpp" + +namespace nx::core +{ +class SIMPLNX_EXPORT StringVectorMetadataValue : public AbstractVectorMetadataValue +{ +public: + using ParentType = AbstractVectorMetadataValue; + using ValueType = typename ParentType::ValueType; + using AssignmentReturnType = typename ParentType::AssignmentReturnType; + + static constexpr StringLiteral k_TypeName = "vec"; + + StringVectorMetadataValue(); + StringVectorMetadataValue(const StringVectorMetadataValue& rhs); + StringVectorMetadataValue(StringVectorMetadataValue&& rhs) noexcept; + ~StringVectorMetadataValue() noexcept = default; + + StringVectorMetadataValue& operator=(const StringVectorMetadataValue& rhs) = default; + StringVectorMetadataValue& operator=(StringVectorMetadataValue&& rhs) noexcept = default; + + /** + * @brief Assignment operator + * @param rhs + */ + AssignmentReturnType& operator=(const ValueType& rhs) override; + + /** + * @brief Default equality operator + * @param rhs value to compare against + * @return is equal + */ + bool operator==(const ValueType& rhs) const override; + +protected: + std::string getTypeNameImpl() const override; +}; +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/UnknownMetadataValue.cpp b/src/simplnx/DataStructure/Metadata/UnknownMetadataValue.cpp new file mode 100644 index 0000000000..4cd44353d7 --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/UnknownMetadataValue.cpp @@ -0,0 +1,24 @@ +#include "UnknownMetadataValue.hpp" + +using namespace nx::core; + +UnknownMetadataValue::UnknownMetadataValue(const nlohmann::json& json) +: ParentType() +, m_Json(json) +{ +} + +std::string UnknownMetadataValue::getTypeName() const +{ + return k_TypeName; +} + +nlohmann::json UnknownMetadataValue::toJson() const +{ + return m_Json; +} + +void UnknownMetadataValue::fromJson(const nlohmann::json& json) +{ + m_Json = json; +} diff --git a/src/simplnx/DataStructure/Metadata/UnknownMetadataValue.hpp b/src/simplnx/DataStructure/Metadata/UnknownMetadataValue.hpp new file mode 100644 index 0000000000..3dd7f37109 --- /dev/null +++ b/src/simplnx/DataStructure/Metadata/UnknownMetadataValue.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include "BaseMetadataValue.hpp" + +namespace nx::core +{ +/** + * @brief Metadata class implementation for reading and writing unkown metadata. + * This is used when reading metadata of a type that is not known to the MetaDataList + * and preserves the typename of the desired type. + */ +class SIMPLNX_EXPORT UnknownMetadataValue : public BaseMetadataValue +{ +public: + using ParentType = BaseMetadataValue; + + static constexpr StringLiteral k_TypeName = "[unknown]"; + + /** + * @brief Constructs an UnknownMetadataValue object with the specified json. + * @param jsonStr json for unknown metadata value type + */ + UnknownMetadataValue(const nlohmann::json& json); + UnknownMetadataValue(const UnknownMetadataValue& other) = default; + UnknownMetadataValue(UnknownMetadataValue&& other) = default; + ~UnknownMetadataValue() = default; + + /** + * @brief Returns the typename for the UnknownMetadataValue + * @return typename string + */ + std::string getTypeName() const override; + + /** + * @brief Returns the unknown metadata json. + * @return json + */ + nlohmann::json toJson() const override; + + /** + * Saves the unknown metadata's json for later use. + * @param json + */ + void fromJson(const nlohmann::json& json) override; + +private: + nlohmann::json m_Json; +}; +} // namespace nx::core diff --git a/src/simplnx/DataStructure/Metadata/VectorMetadataValue.cpp b/src/simplnx/DataStructure/Metadata/VectorMetadataValue.cpp new file mode 100644 index 0000000000..e69de29bb2