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