#include "matador/object/basic_repository.hpp" #include "matador/object/repository_node.hpp" #include "matador/object/repository_node_iterator.hpp" #include "matador/logger/log_manager.hpp" #include namespace matador::object { utils::error make_error(const error_code ec, const std::string &msg) { return utils::error(ec, msg); } basic_repository::basic_repository(std::string name) : name_(std::move(name)) , root_(new repository_node(*this)) , log_(logger::create_logger("schema")) { root_->first_child_ = std::make_unique(*this); root_->last_child_ = std::make_unique(*this); root_->first_child_->next_sibling_ = root_->last_child_.get(); root_->last_child_->previous_sibling_ = root_->first_child_.get(); root_->info_ = std::make_unique(*root_); } basic_repository::~basic_repository() { if (!root_) { return; } while (root_->first_child_->next_sibling_ != root_->last_child_.get()) { remove_node(root_->first_child_->next_sibling_); } delete root_; } utils::result basic_repository::detach(repository_node *node) { log_.debug("detach node '%s' (type: %s)", node->name().c_str(), node->type_index().name()); remove_node(node); node->on_detach(); return utils::ok(); } basic_repository::const_iterator basic_repository::begin() const { return const_iterator(root_->first_child_->next_sibling_); } basic_repository::const_iterator basic_repository::end() const { return const_iterator(root_->last_child_.get()); } bool basic_repository::empty() const { return root_->first_child_.get() == root_->last_child_->previous_sibling_; } size_t basic_repository::size() const { return static_cast(std::distance(begin(), end())); } std::string basic_repository::name() const { return name_; } bool basic_repository::contains( const std::string& name ) const { return nodes_by_name_.count(name) > 0; } bool basic_repository::contains( const std::type_index& index ) const { return nodes_by_type_.count(index) > 0; } utils::result basic_repository::basic_info(const std::type_index &ti) const { const auto it = find_node(ti); if (it == end()) { return utils::failure(make_error(error_code::NodeNotFound, "Node '" + std::string(ti.name()) + "' not found.")); } return utils::ok(basic_object_info_ref{it->info()}); } utils::result basic_repository::basic_info(const std::string& name) const { const auto it = find_node(name); if (it == end()) { return utils::failure(make_error(error_code::NodeNotFound, "Node '" + name + "' not found.")); } return utils::ok(basic_object_info_ref{it->info()}); } utils::result basic_repository::primary_key_attribute(const std::type_index &ti) const { const auto it = find_node(ti); if (it == end()) { return utils::failure(make_error(error_code::NodeNotFound, "Node '" + std::string(ti.name()) + "' not found.")); } if (!it->info().has_primary_key()) { return utils::failure(make_error(error_code::NodeAlreadyExists, "Object '" + it->name() + "' does not have a primary key.")); } return utils::ok(it->info().primary_key_attribute()); } void basic_repository::dump(std::ostream &os) const { for (const auto &node : *this) { dump(os, node); } os << "\n"; } void basic_repository::dump( std::ostream& os, const repository_node& node ) { os << "node [" << node.name() << "] (" << node.type_index().name() << ")\n"; for (auto it = node.info().endpoint_begin(); it != node.info().endpoint_end(); ++it) { os << " " << node.name() << "::" << it->second->field_name() << " (" << it->second->type_name() << ")"; if (it->second->foreign_endpoint()) { os << " <---> " << it->second->node().name() << "::" << it->second->foreign_endpoint()->field_name() << " (" << it->second->foreign_endpoint()->type_name() << ")\n"; } else { os << " -> " << it->second->node().name() << " (type: " << it->second->node().type_index().name() << ")\n"; } } } utils::result basic_repository::attach_node(repository_node *node, const std::string &parent) { if (has_node(node)) { return utils::failure(make_error(error_code::NodeAlreadyExists, "Node '" + node->name() + "' already exists.")); } log_.info("attach: insert node '%s' (type: %s)", node->name().c_str(), node->type_index().name()); // set node to root node auto parent_node = root_; if (!parent.empty()) { const auto result = find_node(parent); if (result == end()) { return utils::failure(make_error(error_code::NodeNotFound, "Parent node '" + parent + "' not found.")); } parent_node = result.get(); } insert_node(parent_node, node); // Todo: check return value nodes_by_name_.insert({node->name(), node}); nodes_by_type_.insert({node->type_index(), node}); node->on_attach(); return utils::ok(node); } basic_repository::const_iterator basic_repository::find_node(const std::string &name) const { // first search in the prototype map const auto i = nodes_by_name_.find(name); if (i == nodes_by_name_.end()) { return end(); } return const_iterator(i->second); } basic_repository::const_iterator basic_repository::find_node(const std::type_index &type_index) const { const auto i = nodes_by_type_.find(type_index); if (i == nodes_by_type_.end()) { return end(); } return const_iterator(i->second); } void basic_repository::insert_node(repository_node *parent, repository_node *child) { child->parent_ = parent; child->previous_sibling_ = parent->last_child_->previous_sibling_; child->next_sibling_ = parent->last_child_.get(); /* * +-----------------------------<- (first) parent (last) -> ----------------------------+ * | | * first (next) -> <- (prev) child_1 (next) -> <- (prev) new_child (next) -> <- (prev) last * ^^^^^^^ inserted ^^^^^^ */ if (const auto prev_sib = parent->last_child_->previous_sibling_) { prev_sib->next_sibling_ = child; } parent->last_child_->previous_sibling_ = child; // set depth // child->depth = depth + 1; } void basic_repository::remove_node(repository_node *node) { // auto next = node->next(); std::stack nodes_to_remove; nodes_to_remove.push(node); while (!nodes_to_remove.empty()) { const auto current = nodes_to_remove.top(); if (current->has_children()) { // Push all children to the stack (from right to left to maintain order) auto child = current->last_child_->previous_sibling_; while (child && child != current->first_child_.get()) { nodes_to_remove.push(child); child = child->previous_sibling_; } continue; } // No children left, safe to remove this node nodes_to_remove.pop(); current->unlink(); nodes_by_name_.erase(current->name()); nodes_by_type_.erase(current->type_index()); delete current; } } bool basic_repository::has_node(const std::string &name) const { return nodes_by_name_.count(name) > 0; } bool basic_repository::has_node(const std::type_index &index) const { return nodes_by_type_.count(index) > 0; } bool basic_repository::has_node(const repository_node* node) const { return nodes_by_name_.count(node->name()) > 0 || nodes_by_type_.count(node->type_index()) > 0; } bool basic_repository::expecting_relation_node( const std::string& name ) const { return expected_relation_nodes_.count(name) > 0; } void basic_repository::expect_relation_node(const std::string &name, const std::type_index &ti) { expected_relation_nodes_.insert({name, ti}); } void basic_repository::remove_expected_relation_node( const std::string& name ) { expected_relation_nodes_.erase(name); } std::shared_ptr basic_repository::provide_object_in_advance(const std::type_index &ti, const std::shared_ptr& obj) { return object_by_type_.insert({ti, obj}).first->second; } bool basic_repository::has_object_for_type(const std::type_index &ti) const { return object_by_type_.count(ti) > 0; } std::shared_ptr basic_repository::object_for_type(const std::type_index &ti) const { return object_by_type_.at(ti); } void basic_repository::remove_object_for_type(const std::type_index &ti) { object_by_type_.erase(ti); } bool basic_repository::is_node_announced(const std::type_index &ti) const { return announced_node_.count(ti) > 0; } void basic_repository::push_announce_node(const std::type_index &ti, std::unique_ptr &&node) { announced_node_.insert({ti, std::move(node)}); } repository_node* basic_repository::announce_node(const std::type_index &ti) const { return announced_node_.find(ti)->second.get(); } std::unique_ptr basic_repository::pop_announce_node(const std::type_index &ti) { const auto it = announced_node_.find(ti); if (it == announced_node_.end()) { return nullptr; } auto node = std::move(it->second); announced_node_.erase(it); return node; } }