From 6a1eee8c3f6615c266e42149648b262b3b91c80e Mon Sep 17 00:00:00 2001 From: Timm Fitschen <t.fitschen@indiscale.com> Date: Sun, 15 Aug 2021 01:24:41 +0200 Subject: [PATCH] WIP: consolidation --- include/caosdb/data_type.h | 9 ++- include/caosdb/entity.h | 12 +++- include/caosdb/transaction.h | 2 +- include/caosdb/value.h | 127 ++++++++++++++++++++++++++++++++- test/CMakeLists.txt | 1 + test/test_data_type.cpp | 48 +++++++++++++ test/test_value.cpp | 131 +++++++++++++++++++++++++++++++++++ 7 files changed, 324 insertions(+), 6 deletions(-) create mode 100644 test/test_value.cpp diff --git a/include/caosdb/data_type.h b/include/caosdb/data_type.h index c21fe0d..a7798c4 100644 --- a/include/caosdb/data_type.h +++ b/include/caosdb/data_type.h @@ -21,9 +21,10 @@ #ifndef CAOSDB_DATA_TYPE_H #define CAOSDB_DATA_TYPE_H -#include <string> // for string #include "caosdb/protobuf_helper.h" // for ProtoMessageWrapper #include "caosdb/entity/v1alpha1/main.pb.h" // for RepeatedPtrField, Message +#include <memory> // for unique_ptr +#include <string> // for string namespace caosdb::entity { using ProtoAtomicDataType = caosdb::entity::v1alpha1::AtomicDataType; using ProtoDataType = caosdb::entity::v1alpha1::DataType; @@ -59,12 +60,18 @@ class ListDataType; class ReferenceDataType : public ProtoMessageWrapper<ProtoDataType> { public: [[nodiscard]] inline auto GetName() const noexcept -> const std::string & { + // is list of refrence? + if (this->wrapped->data_type_case() == DataTypeCase::kListDataType) { + return this->wrapped->list_data_type().reference_data_type().name(); + } return this->wrapped->reference_data_type().name(); } friend class DataType; friend class ListDataType; + inline auto GetWrapped() const -> const ProtoDataType * { return wrapped; } + protected: static auto GetEmptyInstance() -> const ReferenceDataType & { static ReferenceDataType instance; diff --git a/include/caosdb/entity.h b/include/caosdb/entity.h index f6f2204..f417512 100644 --- a/include/caosdb/entity.h +++ b/include/caosdb/entity.h @@ -448,11 +448,19 @@ private: class Entity { public: Entity(); - inline Entity(const Entity &original) : Entity(CreateProtoEntity()) { + inline Entity(const Entity &original) + : wrapped(CreateProtoEntity()), data_type(nullptr) { this->wrapped->CopyFrom(*original.wrapped); + data_type.wrapped = this->wrapped->mutable_data_type(); + properties.wrapped = this->wrapped->mutable_properties(); + parents.wrapped = this->wrapped->mutable_parents(); + errors.wrapped = CreateMessagesField(); + warnings.wrapped = CreateMessagesField(); + infos.wrapped = CreateMessagesField(); }; explicit Entity(IdResponse *id_response); - explicit Entity(ProtoEntity *wrapped) : wrapped(wrapped) { + explicit Entity(ProtoEntity *wrapped) : wrapped(wrapped), data_type(nullptr) { + data_type.wrapped = this->wrapped->mutable_data_type(); properties.wrapped = this->wrapped->mutable_properties(); parents.wrapped = this->wrapped->mutable_parents(); errors.wrapped = CreateMessagesField(); diff --git a/include/caosdb/transaction.h b/include/caosdb/transaction.h index cb9e5d7..49a76ab 100644 --- a/include/caosdb/transaction.h +++ b/include/caosdb/transaction.h @@ -34,7 +34,7 @@ #include <boost/log/sources/record_ostream.hpp> // for basic_record_o... #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/generated_message_util.h" // for CreateMessage... +#include <google/protobuf/generated_message_util.h> // for CreateMessage... #include <google/protobuf/arena.h> // for Arena #include <google/protobuf/util/json_util.h> // for MessageToJsonS... #include <grpcpp/impl/codegen/completion_queue.h> // for CompletionQueue diff --git a/include/caosdb/value.h b/include/caosdb/value.h index 2c3e459..231fc84 100644 --- a/include/caosdb/value.h +++ b/include/caosdb/value.h @@ -21,13 +21,25 @@ #ifndef CAOSDB_VALUE_H #define CAOSDB_VALUE_H -#include <string> // for string #include "caosdb/protobuf_helper.h" // for ProtoMessageWrapper #include "caosdb/entity/v1alpha1/main.pb.h" // for RepeatedPtrField, Message +#include <memory> // for unique_ptr +#include <string> // for string +#include <vector> // for vector + +#define LIST_VALUE_CONSTRUCTOR(TYPE, SETTER) \ + explicit inline Value(const std::vector<TYPE> &values) \ + : ProtoMessageWrapper<ProtoValue>() { \ + for (const auto &value : values) { \ + this->wrapped->mutable_list_values()->add_values()->SETTER(value); \ + } \ + } + namespace caosdb::entity { using caosdb::utility::ProtoMessageWrapper; using ProtoSpecialValue = caosdb::entity::v1alpha1::SpecialValue; using ProtoValue = caosdb::entity::v1alpha1::Value; +using ProtoScalarValue = caosdb::entity::v1alpha1::ScalarValue; using ValueCase = caosdb::entity::v1alpha1::Value::ValueCase; using ScalarValueCase = caosdb::entity::v1alpha1::ScalarValue::ScalarValueCase; @@ -42,6 +54,50 @@ enum SpecialValue { EMPTY_STRING = ProtoSpecialValue::SPECIAL_VALUE_EMPTY_STRING, }; +class ScalarValue : public ProtoMessageWrapper<ProtoScalarValue> { +public: + inline ScalarValue(ProtoScalarValue *wrapped) + : ProtoMessageWrapper<ProtoScalarValue>(wrapped) {} + + [[nodiscard]] inline auto IsString() const noexcept -> bool { + return (this->wrapped->scalar_value_case() == + ScalarValueCase::kStringValue) || + (this->wrapped->scalar_value_case() == + ScalarValueCase::kSpecialValue && + this->wrapped->special_value() == + ProtoSpecialValue::SPECIAL_VALUE_EMPTY_STRING); + return false; + } + [[nodiscard]] inline auto AsString() const noexcept -> const std::string & { + return this->wrapped->string_value(); + ; + } + + [[nodiscard]] inline auto IsDouble() const noexcept -> bool { + return (this->wrapped->scalar_value_case() == + ScalarValueCase::kDoubleValue); + } + [[nodiscard]] inline auto AsDouble() const noexcept -> double { + return this->wrapped->double_value(); + } + + [[nodiscard]] inline auto IsInteger() const noexcept -> bool { + return (this->wrapped->scalar_value_case() == + ScalarValueCase::kIntegerValue); + } + [[nodiscard]] inline auto AsInteger() const noexcept -> int64_t { + return this->wrapped->integer_value(); + } + + [[nodiscard]] inline auto IsBool() const noexcept -> bool { + return (this->wrapped->scalar_value_case() == + ScalarValueCase::kBooleanValue); + } + [[nodiscard]] inline auto AsBool() const noexcept -> bool { + return this->wrapped->boolean_value(); + } +}; + class Value : public ProtoMessageWrapper<ProtoValue> { public: inline Value() : ProtoMessageWrapper<ProtoValue>() { @@ -51,10 +107,31 @@ public: : ProtoMessageWrapper<ProtoValue>() { this->wrapped->mutable_scalar_value()->set_string_value(value); } + explicit inline Value(const char value[]) : Value(std::string(value)) {} explicit inline Value(double value) : ProtoMessageWrapper<ProtoValue>() { this->wrapped->mutable_scalar_value()->set_double_value(value); } - explicit inline Value(const char value[]) : Value(std::string(value)) {} + explicit inline Value(int64_t value) : ProtoMessageWrapper<ProtoValue>() { + this->wrapped->mutable_scalar_value()->set_integer_value(value); + } + explicit inline Value(int value) : Value((int64_t)value) {} + explicit inline Value(bool value) : ProtoMessageWrapper<ProtoValue>() { + this->wrapped->mutable_scalar_value()->set_boolean_value(value); + } + + explicit inline Value(const std::vector<std::string> &values) + : ProtoMessageWrapper<ProtoValue>() { + for (const auto &value : values) { + this->wrapped->mutable_list_values()->add_values()->set_string_value( + value); + } + } + + LIST_VALUE_CONSTRUCTOR(int64_t, set_integer_value) + + [[nodiscard]] inline auto IsNull() -> bool { + return this->wrapped->value_case() == ValueCase::VALUE_NOT_SET; + } [[nodiscard]] inline auto IsString() const noexcept -> bool { if (this->wrapped->value_case() == ValueCase::kScalarValue) { @@ -85,6 +162,49 @@ public: return this->wrapped->scalar_value().double_value(); } + [[nodiscard]] inline auto IsInteger() const noexcept -> bool { + if (this->wrapped->value_case() == ValueCase::kScalarValue) { + + return (this->wrapped->scalar_value().scalar_value_case() == + ScalarValueCase::kIntegerValue); + } + return false; + } + [[nodiscard]] inline auto AsInteger() const noexcept -> int64_t { + return this->wrapped->scalar_value().integer_value(); + } + + [[nodiscard]] inline auto IsBool() const noexcept -> bool { + if (this->wrapped->value_case() == ValueCase::kScalarValue) { + + return (this->wrapped->scalar_value().scalar_value_case() == + ScalarValueCase::kBooleanValue); + } + return false; + } + [[nodiscard]] inline auto AsBool() const noexcept -> bool { + return this->wrapped->scalar_value().boolean_value(); + } + + [[nodiscard]] inline auto IsList() const noexcept -> bool { + return this->wrapped->value_case() == ValueCase::kListValues; + } + [[nodiscard]] inline auto AsList() const noexcept + -> const std::vector<ScalarValue> & { + if (!IsList()) { + // create empty list + this->list_values = std::make_unique<std::vector<ScalarValue>>(); + } + if (this->list_values == nullptr) { + this->list_values = std::make_unique<std::vector<ScalarValue>>(); + for (auto &scalar : + *(this->wrapped->mutable_list_values()->mutable_values())) { + this->list_values->push_back(ScalarValue(&scalar)); + } + } + return *(this->list_values); + } + inline auto operator==(const Value &other) const noexcept -> bool { return this->wrapped->SerializeAsString() == other.wrapped->SerializeAsString(); @@ -92,6 +212,9 @@ public: friend class Entity; friend class Property; + +private: + mutable std::unique_ptr<std::vector<ScalarValue>> list_values; }; } // namespace caosdb::entity diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e3600c2..53c2f48 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -29,6 +29,7 @@ set(test_cases test_protobuf test_transaction test_utility + test_value test_ccaosdb ) diff --git a/test/test_data_type.cpp b/test/test_data_type.cpp index 2675a63..b57c4b2 100644 --- a/test/test_data_type.cpp +++ b/test/test_data_type.cpp @@ -21,6 +21,7 @@ */ #include "caosdb/data_type.h" // for DataType, AtomicDataType +#include "caosdb/entity.h" // for Entity #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... @@ -37,8 +38,15 @@ TEST(test_data_type, test_atomic) { ProtoDataType proto_data_type; for (int i = 1; i < 6; i++) { + Entity entity; + entity.SetRole(Role::PROPERTY); + entity.SetDataType(static_cast<AtomicDataType>(i)); + EXPECT_TRUE(entity.GetDataType().IsAtomic()); + EXPECT_EQ(entity.GetDataType().AsAtomic(), static_cast<AtomicDataType>(i)); + proto_data_type.set_atomic_data_type(static_cast<ProtoAtomicDataType>(i)); DataType data_type(&proto_data_type); + entity.SetDataType(data_type); EXPECT_FALSE(data_type.IsReference()); EXPECT_FALSE(data_type.IsList()); @@ -47,6 +55,25 @@ TEST(test_data_type, test_atomic) { } } +TEST(test_data_type, test_reference) { + ProtoDataType proto_data_type; + + Entity entity; + entity.SetRole(Role::PROPERTY); + entity.SetDataType("Person"); + EXPECT_TRUE(entity.GetDataType().IsReference()); + EXPECT_EQ(entity.GetDataType().AsReference().GetName(), "Person"); + + proto_data_type.mutable_reference_data_type()->set_name("Person"); + DataType data_type(&proto_data_type); + entity.SetDataType(data_type); + + EXPECT_TRUE(data_type.IsReference()); + EXPECT_FALSE(data_type.IsList()); + EXPECT_FALSE(data_type.IsAtomic()); + EXPECT_EQ(data_type.AsReference().GetName(), "Person"); +} + TEST(test_data_type, test_list_of_atomic) { ProtoDataType proto_data_type; auto *proto_list_data_type = proto_data_type.mutable_list_data_type(); @@ -61,9 +88,30 @@ TEST(test_data_type, test_list_of_atomic) { EXPECT_TRUE(data_type.IsList()); const auto &list_data_type = data_type.AsList(); EXPECT_TRUE(list_data_type.IsListOfAtomic()); + EXPECT_FALSE(list_data_type.IsListOfReference()); EXPECT_EQ(list_data_type.GetAtomicDataType(), static_cast<AtomicDataType>(i)); } } +TEST(test_data_type, test_list_of_reference) { + ProtoDataType proto_data_type; + auto *proto_list_data_type = proto_data_type.mutable_list_data_type(); + + proto_list_data_type->mutable_reference_data_type()->set_name("person"); + 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.IsListOfReference()); + EXPECT_FALSE(list_data_type.IsListOfAtomic()); + const auto *wrapped = list_data_type.GetReferenceDataType().GetWrapped(); + CAOSDB_DEBUG_MESSAGE_STRING(*wrapped, out) + CAOSDB_LOG_DEBUG("caosdb::entity") << "wrapped " + out; + EXPECT_EQ(list_data_type.GetReferenceDataType().GetName(), "person"); +} + } // namespace caosdb::entity diff --git a/test/test_value.cpp b/test/test_value.cpp new file mode 100644 index 0000000..890922e --- /dev/null +++ b/test/test_value.cpp @@ -0,0 +1,131 @@ +/* + * + * 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/value.h" // for Value +#include "caosdb/entity.h" // for Entity +#include "caosdb/entity/v1alpha1/main.pb.h" // for AtomicDataType, DataType +#include <cmath> // for isnan +#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_value, test_null) { + Value value; + EXPECT_TRUE(value.IsNull()); + EXPECT_FALSE(value.IsDouble()); + EXPECT_FALSE(value.IsBool()); + EXPECT_FALSE(value.IsInteger()); + EXPECT_FALSE(value.IsString()); + EXPECT_FALSE(value.IsList()); +} + +TEST(test_value, test_string) { + Value value("test"); + EXPECT_FALSE(value.IsNull()); + EXPECT_TRUE(value.IsString()); + EXPECT_FALSE(value.IsDouble()); + EXPECT_FALSE(value.IsBool()); + EXPECT_FALSE(value.IsInteger()); + + EXPECT_EQ(value.AsString(), "test"); + + Value empty_string(""); + EXPECT_FALSE(empty_string.IsNull()); + EXPECT_TRUE(empty_string.IsString()); + EXPECT_FALSE(empty_string.IsDouble()); + EXPECT_FALSE(empty_string.IsBool()); + EXPECT_FALSE(empty_string.IsInteger()); + + EXPECT_EQ(empty_string.AsString(), ""); +} + +TEST(test_value, test_double) { + Value value(2.26); + EXPECT_FALSE(value.IsNull()); + EXPECT_FALSE(value.IsString()); + EXPECT_TRUE(value.IsDouble()); + EXPECT_FALSE(value.IsBool()); + EXPECT_FALSE(value.IsInteger()); + + EXPECT_EQ(value.AsDouble(), 2.26); + + Value nan(std::sqrt(-1.0)); + EXPECT_FALSE(nan.IsNull()); + EXPECT_FALSE(nan.IsString()); + EXPECT_TRUE(nan.IsDouble()); + EXPECT_FALSE(nan.IsBool()); + EXPECT_FALSE(nan.IsInteger()); + + EXPECT_TRUE(std::isnan(nan.AsDouble())); +} + +TEST(test_value, test_integer) { + Value value(1337); + EXPECT_FALSE(value.IsNull()); + EXPECT_FALSE(value.IsString()); + EXPECT_FALSE(value.IsDouble()); + EXPECT_FALSE(value.IsBool()); + EXPECT_TRUE(value.IsInteger()); + + EXPECT_EQ(value.AsInteger(), 1337); +} + +TEST(test_value, test_boolean) { + Value value(true); + EXPECT_FALSE(value.IsNull()); + EXPECT_FALSE(value.IsString()); + EXPECT_FALSE(value.IsDouble()); + EXPECT_TRUE(value.IsBool()); + EXPECT_FALSE(value.IsInteger()); + + EXPECT_EQ(value.AsBool(), true); +} + +TEST(test_value, test_list) { + std::vector<std::string> ids; + for (std::string id : {"id0", "id1", "id2"}) { + ids.push_back(id); + } + Value value(ids); + + EXPECT_FALSE(value.IsNull()); + EXPECT_TRUE(value.IsList()); + EXPECT_FALSE(value.IsString()); + EXPECT_FALSE(value.IsDouble()); + EXPECT_FALSE(value.IsBool()); + EXPECT_FALSE(value.IsInteger()); + + auto list_value = value.AsList(); + int counter = 0; + for (auto item : list_value) { + EXPECT_EQ(item.IsString(), true); + EXPECT_EQ(item.AsString(), "id" + std::to_string(counter++)); + } +} +} // namespace caosdb::entity -- GitLab