227 lines
9.6 KiB
C++
227 lines
9.6 KiB
C++
#ifndef FOREIGN_NODE_COMPLETER_HPP
|
|
#define FOREIGN_NODE_COMPLETER_HPP
|
|
|
|
#include "matador/object/internal/shadow_repository.hpp"
|
|
#include "matador/object/many_to_many_relation.hpp"
|
|
#include "matador/object/repository_node.hpp"
|
|
#include "matador/object/join_columns_collector.hpp"
|
|
#include "matador/object/object_ptr.hpp"
|
|
|
|
#include "matador/utils/primary_key_attribute.hpp"
|
|
|
|
#include "matador/logger/log_manager.hpp"
|
|
|
|
#include <stack>
|
|
|
|
#include "repository.hpp"
|
|
|
|
namespace matador::object {
|
|
/**
|
|
* Processes the given node and ensures
|
|
* that all foreign nodes needed by the given node
|
|
* relations are attached in schema.
|
|
*/
|
|
template<typename NodeType>
|
|
class foreign_node_completer final {
|
|
private:
|
|
using node_ptr = std::shared_ptr<repository_node>;
|
|
|
|
public:
|
|
template<class Type>
|
|
static void complete(const std::shared_ptr<repository_node> &node) {
|
|
internal::shadow_repository shadow(node->repo_);
|
|
foreign_node_completer<Type> completer(shadow);
|
|
|
|
completer.complete_node(node);
|
|
}
|
|
|
|
template<class PrimaryKeyType>
|
|
static void on_primary_key(const char * /*id*/, PrimaryKeyType &/*pk*/, const utils::primary_key_attribute & /*attr*/ = utils::default_pk_attributes) {}
|
|
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
|
template<typename AttributeType>
|
|
static void on_attribute(const char * /*id*/, AttributeType &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
|
template<typename AttributeType>
|
|
static void on_attribute(const char * /*id*/, std::optional<AttributeType> &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
|
template<class ForeignPointerType>
|
|
void on_belongs_to(const char *id, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/);
|
|
template<class ForeignPointerType>
|
|
void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/);
|
|
template<class CollectionType>
|
|
void on_has_many(const char *id, CollectionType &,
|
|
const char *join_column,
|
|
const utils::foreign_attributes &attr,
|
|
std::enable_if_t<is_object_ptr<typename CollectionType::value_type>::value> * = nullptr);
|
|
|
|
template<class CollectionType>
|
|
void on_has_many(const char * /*id*/,
|
|
CollectionType &,
|
|
const char * /*join_column*/,
|
|
const utils::foreign_attributes & /*attr*/,
|
|
std::enable_if_t<!is_object_ptr<typename CollectionType::value_type>::value> * = nullptr) {
|
|
}
|
|
|
|
template<class CollectionType>
|
|
void on_has_many_to_many(const char *id,
|
|
CollectionType &collection,
|
|
const char *join_column,
|
|
const char *inverse_join_column,
|
|
const utils::foreign_attributes &attr);
|
|
template<class CollectionType>
|
|
void on_has_many_to_many(const char *id, CollectionType &collection, const utils::foreign_attributes &attr);
|
|
|
|
private:
|
|
explicit foreign_node_completer(internal::shadow_repository &shadow)
|
|
: repo_(shadow)
|
|
, log_(logger::create_logger("node_completer")) {}
|
|
|
|
void complete_node(const std::shared_ptr<repository_node> &node) {
|
|
nodes_.push(node);
|
|
|
|
NodeType obj;
|
|
access::process(*this, obj);
|
|
nodes_.pop();
|
|
}
|
|
|
|
template<typename Type>
|
|
void attach_node(const std::string &name = "") {
|
|
if (repo_.contains(typeid(Type))) {
|
|
return;
|
|
}
|
|
const auto node = repository_node::make_node<Type>(repo_.repo(), name);
|
|
if (auto result = repo_.attach_node(node)) {
|
|
complete<Type>(result.value());
|
|
}
|
|
}
|
|
|
|
template<typename Type>
|
|
void attach_relation_node(const std::string &name, const std::string &join_column, const std::string &inverse_join_column) {
|
|
using relation_value_type = many_to_many_relation<Type, NodeType>;
|
|
using value_type = Type;
|
|
|
|
// Check if the object_ptr type is already inserted in the schema (by id)
|
|
auto result = repo_.find_node(typeid(value_type));
|
|
if (!result) {
|
|
// Todo: throw internal error or attach node
|
|
return;
|
|
}
|
|
|
|
const auto foreign_node = result.value();
|
|
result = repo_.find_node(name);
|
|
if (result) {
|
|
return;
|
|
}
|
|
// Relation does not exist. Create it.
|
|
auto creator = [join_column, inverse_join_column] {
|
|
return std::make_unique<relation_value_type>(join_column, inverse_join_column);
|
|
};
|
|
|
|
result = repository_node::make_relation_node<relation_value_type>(repo_.repo(), name, std::move(creator));
|
|
if (!result) {
|
|
// Todo: throw internal error
|
|
return;
|
|
}
|
|
|
|
auto& node = result.value();
|
|
complete<relation_value_type>(result.value());
|
|
|
|
// auto& node = result.value();
|
|
const auto local_endpoint = std::make_shared<relation_endpoint>(name, relation_type::HasMany, node);
|
|
const auto join_endpoint = std::make_shared<relation_endpoint>(join_column, relation_type::BelongsTo, nodes_.top());
|
|
const auto inverse_join_endpoint = std::make_shared<relation_endpoint>(inverse_join_column, relation_type::BelongsTo, foreign_node);
|
|
const auto foreign_endpoint = std::make_shared<relation_endpoint>(name, relation_type::HasMany, node);
|
|
// register relation endpoint in local node
|
|
nodes_.top()->info_->register_relation_endpoint(typeid(value_type), local_endpoint);
|
|
// register relation endpoint in foreign node
|
|
foreign_node->info_->register_relation_endpoint(nodes_.top()->type_index(), foreign_endpoint);
|
|
// register endpoints in relation node
|
|
node->info_->register_relation_endpoint(nodes_.top()->type_index(), join_endpoint);
|
|
node->info_->register_relation_endpoint(typeid(value_type), inverse_join_endpoint);
|
|
// link endpoints
|
|
link_relation_endpoints(local_endpoint, join_endpoint);
|
|
link_relation_endpoints(foreign_endpoint, inverse_join_endpoint);
|
|
}
|
|
|
|
private:
|
|
using endpoint_ptr = std::shared_ptr<relation_endpoint>;
|
|
static void register_relation_endpoints(const endpoint_ptr &endpoint,
|
|
const endpoint_ptr &other_endpoint);
|
|
static void link_relation_endpoints(const endpoint_ptr &endpoint,
|
|
const endpoint_ptr &other_endpoint);
|
|
|
|
private:
|
|
template<typename Type>
|
|
friend class foreign_node_completer;
|
|
|
|
private:
|
|
std::stack<node_ptr> nodes_;
|
|
internal::shadow_repository &repo_;
|
|
logger::logger log_;
|
|
join_columns_collector join_columns_collector_{};
|
|
};
|
|
|
|
|
|
template<typename NodeType>
|
|
template<class ForeignPointerType>
|
|
void foreign_node_completer<NodeType>::on_belongs_to(const char * /*id*/, ForeignPointerType &, const utils::foreign_attributes &) {
|
|
attach_node<typename ForeignPointerType::value_type>();
|
|
}
|
|
|
|
template<typename NodeType>
|
|
template<class ForeignPointerType>
|
|
void foreign_node_completer<NodeType>::on_has_one(const char *, ForeignPointerType &, const utils::foreign_attributes &) {
|
|
attach_node<typename ForeignPointerType::value_type>();
|
|
}
|
|
|
|
template<typename NodeType>
|
|
template<class CollectionType>
|
|
void foreign_node_completer<NodeType>::on_has_many(const char * /*id*/,
|
|
CollectionType &, const char * /*join_column*/,
|
|
const utils::foreign_attributes & /*attr*/,
|
|
std::enable_if_t<is_object_ptr<typename CollectionType::value_type>::value> *) {
|
|
attach_node<typename CollectionType::value_type::value_type>();
|
|
}
|
|
|
|
template<typename NodeType>
|
|
template<class CollectionType>
|
|
void foreign_node_completer<NodeType>::on_has_many_to_many(const char *id, CollectionType & /*collection*/,
|
|
const char *join_column,
|
|
const char *inverse_join_column,
|
|
const utils::foreign_attributes & /*attr*/) {
|
|
if (!repo_.expecting_relation_node(id)) {
|
|
repo_.expect_relation_node(id, typeid(typename CollectionType::value_type::value_type));
|
|
} else {
|
|
attach_relation_node<typename CollectionType::value_type::value_type>(id, join_column, inverse_join_column);
|
|
repo_.remove_expected_relation_node(id);
|
|
}
|
|
}
|
|
|
|
template<typename NodeType>
|
|
template<class CollectionType>
|
|
void foreign_node_completer<NodeType>::on_has_many_to_many(const char *id,
|
|
CollectionType & /*collection*/,
|
|
const utils::foreign_attributes & /*attr*/) {
|
|
if (!repo_.expecting_relation_node(id)) {
|
|
repo_.expect_relation_node(id, typeid(typename CollectionType::value_type::value_type));
|
|
} else {
|
|
const auto join_columns = join_columns_collector_.collect<typename CollectionType::value_type::value_type>();
|
|
attach_relation_node<typename CollectionType::value_type::value_type>(id, join_columns.join_column, join_columns.inverse_join_column);
|
|
repo_.remove_expected_relation_node(id);
|
|
}
|
|
}
|
|
|
|
template<typename NodeType>
|
|
void foreign_node_completer<NodeType>::register_relation_endpoints(const endpoint_ptr &endpoint,
|
|
const endpoint_ptr &other_endpoint) {
|
|
endpoint->node_->info_->register_relation_endpoint(other_endpoint->node_->type_index(), endpoint);
|
|
other_endpoint->node_->info_->register_relation_endpoint(endpoint->node_->type_index(), other_endpoint);
|
|
link_relation_endpoints(endpoint, other_endpoint);
|
|
}
|
|
|
|
template<typename NodeType>
|
|
void foreign_node_completer<NodeType>::link_relation_endpoints(const endpoint_ptr &endpoint, const endpoint_ptr &other_endpoint) {
|
|
endpoint->link_foreign_endpoint(other_endpoint);
|
|
other_endpoint->link_foreign_endpoint(endpoint);
|
|
}
|
|
}
|
|
#endif //FOREIGN_NODE_COMPLETER_HPP
|