#include #include "matador/object/repository.hpp" namespace matador::object { utils::error make_error(const error_code ec, const std::string &msg) { return utils::error(ec, msg); } repository::repository(std::string name) : name_(std::move(name)) , root_(repository_node::make_null_node(*this)) , log_(logger::create_logger("schema")) { root_->first_child_ = std::shared_ptr(new repository_node(*this)); root_->last_child_ = std::shared_ptr(new repository_node(*this)); root_->first_child_->next_sibling_ = root_->last_child_; root_->last_child_->previous_sibling_ = root_->first_child_; } utils::result repository::detach(const node_ptr &node) { log_.debug("detach node '%s' (type: %s)", node->name().c_str(), node->type_index().name()); remove_node(node); return utils::ok(); } repository::const_iterator repository::begin() const { return const_iterator(root_->first_child_->next_sibling_); } repository::const_iterator repository::end() const { return const_iterator(root_->last_child_); } bool repository::empty() const { return root_->first_child_ == root_->last_child_->previous_sibling_; } size_t repository::size() const { return static_cast(std::distance(begin(), end())); } std::string repository::name() const { return name_; } bool repository::contains( const std::string& name ) const { return nodes_by_name_.count(name) > 0; } bool repository::contains( const std::type_index& index ) const { return nodes_by_type_.count(index) > 0; } utils::result repository::basic_info( const std::string& name ) const { auto result = find_node(name); if (!result) { return utils::failure(result.err()); } return utils::ok(basic_object_info_ref{result.value()->info()}); } utils::result repository::primary_key_attribute(const std::type_index &type_index) const { const auto result = find_node(type_index); if (!result) { return utils::failure(result.err()); } if (!result.value()->info().has_primary_key()) { return utils::failure(make_error(error_code::NodeAlreadyExists, "Object '" + result.value()->name() + "' does not have a primary key.")); } return utils::ok((*result)->info().primary_key_attribute()); } void repository::dump(std::ostream &os) const { for (const auto &node : *this) { dump(os, node); } os << "\n"; } void repository::dump( std::ostream& os, const node_ptr& 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 repository::attach_node(const std::shared_ptr &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()) { auto result = find_node(parent); if (!result.is_ok() && result.err().ec() != error_code::NodeNotFound) { return result; } parent_node = *result; } insert_node(parent_node, node); // Todo: check return value nodes_by_name_.insert({node->name(), node}); nodes_by_type_.insert({node->type_index(), node}); return utils::ok(node); } utils::result 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 utils::failure(make_error(error_code::NodeNotFound, "Couldn't find node by name '" + name + "'")); } return utils::ok(i->second); } utils::result 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 utils::failure(make_error(error_code::NodeNotFound, "Couldn't find node by type '" + std::string(type_index.name()) + "'")); } return utils::ok(i->second); } void repository::insert_node(const node_ptr &parent, const node_ptr &child) { child->parent_ = parent; child->previous_sibling_ = parent->last_child_->previous_sibling_; child->next_sibling_ = parent->last_child_; /* * +-----------------------------<- (first) parent (last) -> ----------------------------+ * | | * first (next) -> <- (prev) child_1 (next) -> <- (prev) new_child (next) -> <- (prev) last * ^^^^^^^ inserted ^^^^^^ */ parent->last_child_->previous_sibling_->next_sibling_ = child; parent->last_child_->previous_sibling_ = child; // set depth // child->depth = depth + 1; } void repository::remove_node(const node_ptr &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 != current->first_child_) { 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()); } } bool repository::has_node(const std::string &name) const { return nodes_by_name_.count(name) > 0; } bool repository::has_node(const std::type_index &index) const { return nodes_by_type_.count(index) > 0; } bool repository::has_node(const node_ptr& node) const { return nodes_by_name_.count(node->name()) > 0 || nodes_by_type_.count(node->type_index()) > 0; } } // namespace matador::object