From bcc0f65926033f5dd410b5b57348c78739b47c2a Mon Sep 17 00:00:00 2001 From: Timm Fitschen <t.fitschen@indiscale.com> Date: Sat, 14 Aug 2021 16:16:54 +0200 Subject: [PATCH] WIP: consolidation --- include/caosdb/data_type.h | 116 +++++++++++++++++++++++++++---- include/caosdb/entity.h | 15 ++-- include/caosdb/protobuf_helper.h | 6 +- include/caosdb/value.h | 7 +- proto | 2 +- src/caosdb/entity.cpp | 11 ++- test/CMakeLists.txt | 1 + test/test_data_type.cpp | 69 ++++++++++++++++++ test/test_entity.cpp | 41 ++++++++--- 9 files changed, 235 insertions(+), 33 deletions(-) create mode 100644 test/test_data_type.cpp diff --git a/include/caosdb/data_type.h b/include/caosdb/data_type.h index 194a2a6..c21fe0d 100644 --- a/include/caosdb/data_type.h +++ b/include/caosdb/data_type.h @@ -27,10 +27,16 @@ namespace caosdb::entity { using ProtoAtomicDataType = caosdb::entity::v1alpha1::AtomicDataType; using ProtoDataType = caosdb::entity::v1alpha1::DataType; +using ProtoListDataType = caosdb::entity::v1alpha1::ListDataType; using ProtoReferenceDataType = caosdb::entity::v1alpha1::ReferenceDataType; using DataTypeCase = caosdb::entity::v1alpha1::DataType::DataTypeCase; +using ListDataTypeCase = + caosdb::entity::v1alpha1::ListDataType::ListDataTypeCase; using caosdb::utility::ProtoMessageWrapper; +class Entity; +class Property; + // Atomic data types. enum AtomicDataType { // The data type is unset/unknown. @@ -48,52 +54,136 @@ enum AtomicDataType { }; class DataType; +class ListDataType; -class ReferenceDataType : ProtoMessageWrapper<ProtoReferenceDataType> { +class ReferenceDataType : public ProtoMessageWrapper<ProtoDataType> { public: [[nodiscard]] inline auto GetName() const noexcept -> const std::string & { - return this->wrapped->name(); + return this->wrapped->reference_data_type().name(); + } + + friend class DataType; + friend class ListDataType; + +protected: + static auto GetEmptyInstance() -> const ReferenceDataType & { + static ReferenceDataType instance; + return instance; + } + inline static auto Create(ProtoDataType *wrapped) + -> std::unique_ptr<ReferenceDataType> { + return std::unique_ptr<ReferenceDataType>(new ReferenceDataType(wrapped)); + } + ReferenceDataType() : ProtoMessageWrapper<ProtoDataType>() {} + ReferenceDataType(ProtoDataType *wrapped) + : ProtoMessageWrapper<ProtoDataType>(wrapped) {} +}; + +class ListDataType : public ProtoMessageWrapper<ProtoDataType> { +public: + [[nodiscard]] inline auto IsListOfReference() const noexcept -> bool { + return this->wrapped->list_data_type().list_data_type_case() == + ListDataTypeCase::kReferenceDataType; + } + [[nodiscard]] inline auto GetReferenceDataType() const + -> const ReferenceDataType & { + if (!IsListOfReference()) { + return ReferenceDataType::GetEmptyInstance(); + } + if (reference_data_type == nullptr) { + this->reference_data_type = std::unique_ptr<ReferenceDataType>( + ReferenceDataType::Create(this->wrapped).release()); + } + return *this->reference_data_type; } - ReferenceDataType() : ProtoMessageWrapper<ProtoReferenceDataType>() {} + [[nodiscard]] inline auto IsListOfAtomic() const noexcept -> bool { + return this->wrapped->list_data_type().list_data_type_case() == + ListDataTypeCase::kAtomicDataType; + } + [[nodiscard]] inline auto GetAtomicDataType() const -> AtomicDataType { + return static_cast<AtomicDataType>( + this->wrapped->list_data_type().atomic_data_type()); + } friend class DataType; + +protected: + static auto GetEmptyInstance() -> const ListDataType & { + static auto empty_instance = ListDataType(); + return empty_instance; + } + inline static auto Create(ProtoDataType *wrapped) + -> std::unique_ptr<ListDataType> { + return std::unique_ptr<ListDataType>(new ListDataType(wrapped)); + } + ListDataType() : ProtoMessageWrapper<ProtoDataType>() {} + + ListDataType(ProtoDataType *wrapped) + : ProtoMessageWrapper<ProtoDataType>(wrapped) {} + + mutable std::unique_ptr<ReferenceDataType> reference_data_type; }; class DataType : public ProtoMessageWrapper<ProtoDataType> { public: + DataType(ProtoDataType *wrapped) + : ProtoMessageWrapper<ProtoDataType>(wrapped) {} DataType() : ProtoMessageWrapper<ProtoDataType>() {} + DataType(AtomicDataType data_type) : DataType() { + this->wrapped->set_atomic_data_type( + static_cast<ProtoAtomicDataType>(data_type)); + } DataType(const std::string &data_type) : DataType() { - this->wrapped->mutable_reference_type()->set_name(data_type); + this->wrapped->mutable_reference_data_type()->set_name(data_type); } [[nodiscard]] inline auto IsAtomic() const noexcept -> bool { - return this->wrapped->data_type_case() == DataTypeCase::kAtomicType; + return this->wrapped->data_type_case() == DataTypeCase::kAtomicDataType; } [[nodiscard]] inline auto AsAtomic() const noexcept -> AtomicDataType { - return static_cast<AtomicDataType>(this->wrapped->atomic_type()); + return static_cast<AtomicDataType>(this->wrapped->atomic_data_type()); } [[nodiscard]] inline auto IsReference() const noexcept -> bool { - return this->wrapped->data_type_case() == DataTypeCase::kReferenceType; + return this->wrapped->data_type_case() == DataTypeCase::kReferenceDataType; } - [[nodiscard]] inline auto AsReference() const noexcept -> ReferenceDataType { + [[nodiscard]] inline auto AsReference() const noexcept + -> const ReferenceDataType & { if (!IsReference()) { - return ReferenceDataType{}; + return ReferenceDataType::GetEmptyInstance(); } else if (reference_data_type == nullptr) { - reference_data_type = std::make_unique<ReferenceDataType>(); - this->reference_data_type->wrapped = - this->wrapped->mutable_reference_type(); + reference_data_type = std::unique_ptr<ReferenceDataType>( + ReferenceDataType::Create(this->wrapped).release()); } return *reference_data_type; } + [[nodiscard]] inline auto IsList() const noexcept -> bool { + return this->wrapped->data_type_case() == DataTypeCase::kListDataType; + } + + [[nodiscard]] inline auto AsList() const noexcept -> const ListDataType & { + if (!IsList()) { + return ListDataType::GetEmptyInstance(); + } else if (list_data_type == nullptr) { + list_data_type = std::unique_ptr<ListDataType>( + ListDataType::Create(this->wrapped).release()); + } + return *list_data_type; + } + inline auto operator==(const DataType &other) const noexcept -> bool { + // TODO(tf) Is this safe? return this->wrapped->SerializeAsString() == other.wrapped->SerializeAsString(); } -private: + friend class Entity; + friend class Property; + +protected: mutable std::unique_ptr<ReferenceDataType> reference_data_type; + mutable std::unique_ptr<ListDataType> list_data_type; }; } // namespace caosdb::entity diff --git a/include/caosdb/entity.h b/include/caosdb/entity.h index 0a66286..684872d 100644 --- a/include/caosdb/entity.h +++ b/include/caosdb/entity.h @@ -46,6 +46,8 @@ using ProtoMessage = caosdb::entity::v1alpha1::Message; using caosdb::entity::v1alpha1::EntityRole; using ProtoImportance = caosdb::entity::v1alpha1::Importance; +const static std::string logger_name = "caosdb::entity"; + /** * The property importance. */ @@ -268,7 +270,10 @@ private: */ class Property { public: - explicit inline Property(ProtoProperty *wrapped) : wrapped(wrapped){}; + explicit inline Property(ProtoProperty *wrapped) : wrapped(wrapped) { + data_type.wrapped = this->wrapped->mutable_data_type(); + value.wrapped = this->wrapped->mutable_value(); + }; Property(); /** @@ -337,6 +342,7 @@ public: * Set the datatype of this property. */ auto SetDataType(const DataType &new_data_type) -> StatusCode; + auto SetDataType(const AtomicDataType new_data_type) -> StatusCode; auto SetDataType(const std::string &new_data_type) -> StatusCode; /** @@ -488,8 +494,9 @@ public: // auto SetValue(const bool value) -> StatusCode; auto SetUnit(const std::string &unit) -> void; - // Currently no references or lists. + auto SetDataType(const DataType &new_data_type) -> StatusCode; + auto SetDataType(const AtomicDataType new_data_type) -> StatusCode; auto SetDataType(const std::string &new_data_type) -> StatusCode; auto AppendProperty(const Property &property) -> void; @@ -503,14 +510,14 @@ protected: static auto CreateProtoEntity() -> ProtoEntity *; auto SetId(const std::string &id) -> void; auto SetVersionId(const std::string &id) -> void; + +private: ProtoEntity *wrapped; Properties properties; Parents parents; Messages errors; Messages warnings; Messages infos; - -private: Value value; DataType data_type; }; diff --git a/include/caosdb/protobuf_helper.h b/include/caosdb/protobuf_helper.h index e4e89ae..051a896 100644 --- a/include/caosdb/protobuf_helper.h +++ b/include/caosdb/protobuf_helper.h @@ -40,9 +40,9 @@ public: } protected: - ProtoMessageWrapper() { - this->wrapped = Arena::CreateMessage<P>(get_arena()); - } + ProtoMessageWrapper() + : ProtoMessageWrapper(Arena::CreateMessage<P>(get_arena())) {} + ProtoMessageWrapper(P *wrapped) : wrapped(wrapped) {} P *wrapped; }; diff --git a/include/caosdb/value.h b/include/caosdb/value.h index 158a3e8..2c3e459 100644 --- a/include/caosdb/value.h +++ b/include/caosdb/value.h @@ -31,6 +31,9 @@ using ProtoValue = caosdb::entity::v1alpha1::Value; using ValueCase = caosdb::entity::v1alpha1::Value::ValueCase; using ScalarValueCase = caosdb::entity::v1alpha1::ScalarValue::ScalarValueCase; +class Entity; +class Property; + // Represents special values which are otherwise hard to tranfer via protobuf. enum SpecialValue { // Represent the NULL value. @@ -80,13 +83,15 @@ public: } [[nodiscard]] inline auto AsDouble() const noexcept -> double { return this->wrapped->scalar_value().double_value(); - ; } inline auto operator==(const Value &other) const noexcept -> bool { return this->wrapped->SerializeAsString() == other.wrapped->SerializeAsString(); } + + friend class Entity; + friend class Property; }; } // namespace caosdb::entity diff --git a/proto b/proto index 780c89e..f7388e1 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 780c89e86cb9c3b17171b88827aaedee3c3a9595 +Subproject commit f7388e18e0908d2b675159176f9c9a8a6f52637e diff --git a/src/caosdb/entity.cpp b/src/caosdb/entity.cpp index 5999196..846570e 100644 --- a/src/caosdb/entity.cpp +++ b/src/caosdb/entity.cpp @@ -76,7 +76,7 @@ auto Parents::Append(const Parent &parent) -> void { parent.wrapped = destination; } -Property::Property() : wrapped(Property::CreateProtoProperty()) {} +Property::Property() : Property(Property::CreateProtoProperty()) {} auto Property::CreateProtoProperty() -> ProtoProperty * { return google::protobuf::Arena::CreateMessage<ProtoProperty>(get_arena()); @@ -146,6 +146,10 @@ auto Property::SetDataType(const DataType &new_data_type) -> StatusCode { return this->data_type.CopyFrom(new_data_type); } +auto Property::SetDataType(const AtomicDataType new_data_type) -> StatusCode { + return SetDataType(DataType(new_data_type)); +} + auto Property::SetDataType(const std::string &new_data_type) -> StatusCode { return SetDataType(DataType(new_data_type)); } @@ -180,6 +184,7 @@ auto Entity::CreateProtoEntity() -> ProtoEntity * { } Entity::Entity() : wrapped(Entity::CreateProtoEntity()) { + data_type.wrapped = this->wrapped->mutable_data_type(); properties.wrapped = this->wrapped->mutable_properties(); parents.wrapped = this->wrapped->mutable_parents(); errors.wrapped = this->wrapped->mutable_errors(); @@ -244,6 +249,10 @@ auto Entity::SetDataType(const DataType &new_data_type) -> StatusCode { return this->data_type.CopyFrom(new_data_type); } +auto Entity::SetDataType(const AtomicDataType new_data_type) -> StatusCode { + return SetDataType(DataType(new_data_type)); +} + auto Entity::SetDataType(const std::string &new_data_type) -> StatusCode { return SetDataType(DataType(new_data_type)); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 389d5d9..4f92bbf 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -20,6 +20,7 @@ # append all the test cases here (file name without the ".cpp" suffix) set(test_cases + test_data_type test_configuration test_connection test_entity diff --git a/test/test_data_type.cpp b/test/test_data_type.cpp new file mode 100644 index 0000000..2675a63 --- /dev/null +++ b/test/test_data_type.cpp @@ -0,0 +1,69 @@ +/* + * + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ + +#include "caosdb/data_type.h" // for DataType, AtomicDataType +#include "caosdb/entity/v1alpha1/main.pb.h" // for AtomicDataType, DataType +#include <gtest/gtest-message.h> // for Message +#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApi... +#include <gtest/gtest_pred_impl.h> // for AssertionResult, Test +#include <memory> // for allocator + +namespace caosdb::entity { +using ProtoEntity = caosdb::entity::v1alpha1::Entity; +using ProtoParent = caosdb::entity::v1alpha1::Parent; +using ProtoDataType = caosdb::entity::v1alpha1::DataType; +using ProtoAtomicDataType = caosdb::entity::v1alpha1::AtomicDataType; + +TEST(test_data_type, test_atomic) { + ProtoDataType proto_data_type; + + for (int i = 1; i < 6; i++) { + proto_data_type.set_atomic_data_type(static_cast<ProtoAtomicDataType>(i)); + DataType data_type(&proto_data_type); + + EXPECT_FALSE(data_type.IsReference()); + EXPECT_FALSE(data_type.IsList()); + EXPECT_TRUE(data_type.IsAtomic()); + EXPECT_EQ(data_type.AsAtomic(), static_cast<AtomicDataType>(i)); + } +} + +TEST(test_data_type, test_list_of_atomic) { + ProtoDataType proto_data_type; + auto *proto_list_data_type = proto_data_type.mutable_list_data_type(); + + for (int i = 1; i < 6; i++) { + proto_list_data_type->set_atomic_data_type( + static_cast<ProtoAtomicDataType>(i)); + DataType data_type(&proto_data_type); + + EXPECT_FALSE(data_type.IsReference()); + EXPECT_FALSE(data_type.IsAtomic()); + EXPECT_TRUE(data_type.IsList()); + const auto &list_data_type = data_type.AsList(); + EXPECT_TRUE(list_data_type.IsListOfAtomic()); + EXPECT_EQ(list_data_type.GetAtomicDataType(), + static_cast<AtomicDataType>(i)); + } +} + +} // namespace caosdb::entity diff --git a/test/test_entity.cpp b/test/test_entity.cpp index 7e8ca9d..1c9e3a9 100644 --- a/test/test_entity.cpp +++ b/test/test_entity.cpp @@ -24,15 +24,20 @@ #include "caosdb/entity.h" // for Entity, Parent, Par... #include "caosdb/entity/v1alpha1/main.grpc.pb.h" // for EntityTransactionSe... #include "caosdb/entity/v1alpha1/main.pb.h" // for IdResponse, Message -#include "caosdb/message_code.h" // for MessageCode -#include "caosdb/protobuf_helper.h" // for get_arena -#include "caosdb/transaction.h" // for Transaction -#include "caosdb/value.h" // for Value -#include <google/protobuf/arena.h> // for Arena -#include <gtest/gtest-message.h> // for Message -#include <gtest/gtest-test-part.h> // for TestPartResult, Sui... -#include <gtest/gtest_pred_impl.h> // for Test, EXPECT_EQ -#include <memory> // for allocator, shared_ptr +#include "caosdb/logging.h" +#include "caosdb/message_code.h" // for MessageCode +#include "caosdb/protobuf_helper.h" // for get_arena +#include "caosdb/transaction.h" // for Transaction +#include "caosdb/value.h" // for Value +#include <boost/log/core/record.hpp> // for record +#include <boost/log/sources/record_ostream.hpp> // for record_pump<>:... +#include <boost/preprocessor/seq/limits/enum_256.hpp> // for BOOST_PP_SEQ_E... +#include <boost/preprocessor/seq/limits/size_256.hpp> // for BOOST_PP_SEQ_S... +#include <google/protobuf/arena.h> // for Arena +#include <gtest/gtest-message.h> // for Message +#include <gtest/gtest-test-part.h> // for TestPartResult, Sui... +#include <gtest/gtest_pred_impl.h> // for Test, EXPECT_EQ +#include <memory> // for allocator, shared_ptr namespace caosdb::entity { using caosdb::entity::v1alpha1::IdResponse; @@ -63,22 +68,38 @@ TEST(test_entity, test_append_parent) { } TEST(test_entity, test_property_setters) { + CAOSDB_LOG_DEBUG(logger_name) << "HERE 1"; auto prop = Property(); + CAOSDB_LOG_DEBUG(logger_name) << "HERE 2"; prop.SetName("prop_name"); + CAOSDB_LOG_DEBUG(logger_name) << "HERE 3"; prop.SetId("prop_id"); + CAOSDB_LOG_DEBUG(logger_name) << "HERE 4"; prop.SetImportance(Importance::OBLIGATORY); + CAOSDB_LOG_DEBUG(logger_name) << "HERE 5"; prop.SetValue("prop_value"); + CAOSDB_LOG_DEBUG(logger_name) << "HERE 6"; prop.SetUnit("prop_unit"); + CAOSDB_LOG_DEBUG(logger_name) << "HERE 7"; prop.SetDataType("prop_dtype"); + CAOSDB_LOG_DEBUG(logger_name) << "HERE 8"; EXPECT_EQ(prop.GetName(), "prop_name"); + CAOSDB_LOG_DEBUG(logger_name) << "HERE 1"; EXPECT_EQ(prop.GetId(), "prop_id"); + CAOSDB_LOG_DEBUG(logger_name) << "HERE 2"; EXPECT_EQ(prop.GetImportance(), Importance::OBLIGATORY); + CAOSDB_LOG_DEBUG(logger_name) << "HERE 3"; EXPECT_TRUE(prop.GetValue().IsString()); + CAOSDB_LOG_DEBUG(logger_name) << "HERE 4"; EXPECT_EQ(prop.GetValue().AsString(), "prop_value"); + CAOSDB_LOG_DEBUG(logger_name) << "HERE 5"; EXPECT_EQ(prop.GetUnit(), "prop_unit"); + CAOSDB_LOG_DEBUG(logger_name) << "HERE 6"; EXPECT_TRUE(prop.GetDataType().IsReference()); + CAOSDB_LOG_DEBUG(logger_name) << "HERE 7"; EXPECT_EQ(prop.GetDataType().AsReference().GetName(), "prop_dtype"); + CAOSDB_LOG_DEBUG(logger_name) << "HERE 8"; } TEST(test_entity, test_append_property) { @@ -159,7 +180,7 @@ TEST(test_entity, test_insert_with_role) { auto entity = Entity(); entity.SetRole(Role::PROPERTY); - entity.SetDataType("DOUBLE"); + entity.SetDataType(AtomicDataType::DOUBLE); entity.SetName("Length"); entity.SetUnit("m"); entity.SetValue(5.5); -- GitLab