Program Listing for File Node.cpp

Return to documentation for file (src/rdf4cpp/Node.cpp)

#include "Node.hpp"

#include <cassert>
#include <rdf4cpp/BlankNode.hpp>
#include <rdf4cpp/IRI.hpp>
#include <rdf4cpp/Literal.hpp>
#include <rdf4cpp/query/Variable.hpp>
#include <rdf4cpp/Assert.hpp>

namespace rdf4cpp {

Node::Node(storage::identifier::NodeBackendHandle id) noexcept : handle_(id) {}

Node Node::make_null() noexcept {
    return Node{};
}

Node Node::to_node_storage(storage::DynNodeStoragePtr node_storage) const {
    switch (handle_.type()) {
        case storage::identifier::RDFNodeType::Variable: {
            return query::Variable{handle_}.to_node_storage(node_storage);
        }
        case storage::identifier::RDFNodeType::BNode: {
            return BlankNode{handle_}.to_node_storage(node_storage);
        }
        case storage::identifier::RDFNodeType::IRI: {
            return IRI{handle_}.to_node_storage(node_storage);
        }
        case storage::identifier::RDFNodeType::Literal: {
            return Literal{handle_}.to_node_storage(node_storage);
        }
        default: {
            RDF4CPP_UNREACHABLE;
        }
    }
}

Node Node::try_get_in_node_storage(storage::DynNodeStoragePtr node_storage) const noexcept {
    switch (handle_.type()) {
        case storage::identifier::RDFNodeType::Variable: {
            return query::Variable{handle_}.try_get_in_node_storage(node_storage);
        }
        case storage::identifier::RDFNodeType::BNode: {
            return BlankNode{handle_}.try_get_in_node_storage(node_storage);
        }
        case storage::identifier::RDFNodeType::IRI: {
            return IRI{handle_}.try_get_in_node_storage(node_storage);
        }
        case storage::identifier::RDFNodeType::Literal: {
            return Literal{handle_}.try_get_in_node_storage(node_storage);
        }
        default: {
            RDF4CPP_UNREACHABLE;
        }
    }
}

bool Node::serialize(writer::BufWriterParts const writer, NodeSerializationOpts opts) const noexcept {
    if (null()) {
        return rdf4cpp::writer::write_str("null", writer);
    }

    switch (handle_.type()) {
        [[likely]] case storage::identifier::RDFNodeType::IRI: {
            return IRI{handle_}.serialize(writer);
        }
        case storage::identifier::RDFNodeType::Variable: {
            return query::Variable{handle_}.serialize(writer);
        }
        case storage::identifier::RDFNodeType::BNode: {
            return BlankNode{handle_}.serialize(writer);
        }
        case storage::identifier::RDFNodeType::Literal: {
            return Literal{handle_}.serialize(writer, opts);
        }
        default: {
            RDF4CPP_UNREACHABLE;
        }
    }
}

Node::operator std::string() const noexcept {
    switch (handle_.type()) {
        [[likely]] case storage::identifier::RDFNodeType::IRI: {
            return std::string{IRI{handle_}};
        }
        case storage::identifier::RDFNodeType::Variable: {
            return std::string{query::Variable{handle_}};
        }
        case storage::identifier::RDFNodeType::BNode: {
            return std::string{BlankNode{handle_}};
        }
        case storage::identifier::RDFNodeType::Literal: {
            return std::string{Literal{handle_}};
        }
        default: {
            RDF4CPP_UNREACHABLE;
        }
    }
}

TriBool Node::is_literal() const noexcept {
    return handle_.is_literal();
}
TriBool Node::is_variable() const noexcept {
    return handle_.is_variable();
}
TriBool Node::is_blank_node() const noexcept {
    return handle_.is_blank_node();
}
TriBool Node::is_iri() const noexcept {
    return handle_.is_iri();
}

bool Node::is_inlined() const noexcept {
    return handle_.is_inlined();
}

TriBool Node::eq_impl(Node const &other) const noexcept {
    if (null() || other.null()) {
        // "Apart from BOUND, COALESCE, NOT EXISTS and EXISTS, all functions and operators operate on RDF Terms and will produce a type error if any arguments are unbound."
        // - https://www.w3.org/TR/sparql11-query/#evaluation
        return TriBool::Err;
    }

    if (handle_ == other.handle_) {
        return TriBool::True;
    }

    if (handle_.type() != other.handle_.type()) {
        return TriBool::Err;
    }

    using storage::identifier::RDFNodeType;
    switch (handle_.type()) {
        case RDFNodeType::Literal: {
            return Literal{handle_}.eq(Literal{other.handle_});
        }
        case RDFNodeType::IRI: {
            return handle_.iri_backend() == other.handle_.iri_backend();
        }
        case RDFNodeType::BNode: {
            return BlankNode{handle_}.eq(BlankNode{other.handle_});
        }
        case RDFNodeType::Variable: {
            return query::Variable{handle_}.eq(query::Variable{other.handle_});
        }
        default: {
            RDF4CPP_UNREACHABLE;
        }
    }
}

std::partial_ordering Node::compare_impl(Node const &other) const noexcept {
    if (null() || other.null()) {
        // "Apart from BOUND, COALESCE, NOT EXISTS and EXISTS, all functions and operators operate on RDF Terms and will produce a type error if any arguments are unbound."
        // - https://www.w3.org/TR/sparql11-query/#evaluation
        return std::partial_ordering::unordered;
    }

    if (handle_ == other.handle_) {
        return std::partial_ordering::equivalent;
    }

    if (handle_.type() != other.handle_.type() || handle_.type() != storage::identifier::RDFNodeType::Literal) {
        // mismatched node types are not comparable
        // and nodes other than literals are not comparable with <,<=,>,>=
        return std::partial_ordering::unordered;
    }

    return Literal{handle_}.compare(Literal{other.handle_});
}

std::strong_ordering Node::order(Node const &other) const noexcept {
    if (this->handle_ == other.handle_) {
        return std::strong_ordering::equivalent;
    }

    // null nodes are the smallest nodes
    // https://www.w3.org/TR/sparql11-query/#modOrderBy
    if (this->null() && other.null()) {
        return this->handle_.type() <=> other.handle_.type();
    }
    if (this->null()) {
        return std::strong_ordering::less;
    }
    if (other.null()) {
        return std::strong_ordering::greater;
    }

    // different type
    if (std::strong_ordering const type_comp = this->handle_.type() <=> other.handle_.type(); type_comp != std::strong_ordering::equivalent) {
        return type_comp;
    }

    switch (this->handle_.type()) {
        case storage::identifier::RDFNodeType::IRI:
            return this->handle_.iri_backend() <=> other.handle_.iri_backend();
        case storage::identifier::RDFNodeType::BNode:
            return BlankNode{handle_}.order(BlankNode{other.handle_});
        case storage::identifier::RDFNodeType::Literal:
            return Literal{handle_}.order(Literal{other.handle_});
        case storage::identifier::RDFNodeType::Variable:
            return query::Variable{this->handle_}.order(query::Variable{other.handle_});
        default:{
            // this will never be reached because RDFNodeType has only 4 values.
            RDF4CPP_UNREACHABLE;
        }
    }
}

TriBool Node::eq(Node const &other) const noexcept {
    return eq_impl(other);
}
bool Node::order_eq(Node const &other) const noexcept {
    return order(other) == std::weak_ordering::equivalent;
}
TriBool Node::ne(Node const &other) const noexcept {
    return !eq_impl(other);
}
bool Node::order_ne(Node const &other) const noexcept {
    return order(other) != std::weak_ordering::equivalent;
}
TriBool Node::lt(Node const &other) const noexcept {
    return util::partial_weak_ordering_eq(compare_impl(other), std::weak_ordering::less);
}
bool Node::order_lt(Node const &other) const noexcept {
    return order(other) == std::weak_ordering::less;
}
TriBool Node::le(Node const &other) const noexcept {
    return !util::partial_weak_ordering_eq(compare_impl(other), std::weak_ordering::greater);
}
bool Node::order_le(Node const &other) const noexcept {
    return order(other) != std::weak_ordering::greater;
}
TriBool Node::gt(Node const &other) const noexcept {
    return util::partial_weak_ordering_eq(compare_impl(other), std::weak_ordering::greater);
}
bool Node::order_gt(Node const &other) const noexcept {
    return order(other) == std::weak_ordering::greater;
}
TriBool Node::ge(Node const &other) const noexcept {
    return !util::partial_weak_ordering_eq(compare_impl(other), std::weak_ordering::less);
}
bool Node::order_ge(Node const &other) const noexcept {
    return order(other) != std::weak_ordering::less;
}

Literal Node::as_eq(Node const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
    return Literal::make_boolean(eq(other), select_node_storage(node_storage));
}
Literal Node::as_order_eq(Node const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
    return Literal::make_boolean(order_eq(other), select_node_storage(node_storage));
}
Literal Node::as_ne(Node const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
    return Literal::make_boolean(ne(other), select_node_storage(node_storage));
}
Literal Node::as_order_ne(Node const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
    return Literal::make_boolean(order_ne(other), select_node_storage(node_storage));
}
Literal Node::as_lt(Node const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
    return Literal::make_boolean(lt(other), select_node_storage(node_storage));
}
Literal Node::as_order_lt(Node const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
    return Literal::make_boolean(order_lt(other), select_node_storage(node_storage));
}
Literal Node::as_le(Node const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
    return Literal::make_boolean(le(other), select_node_storage(node_storage));
}
Literal Node::as_order_le(Node const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
    return Literal::make_boolean(order_le(other), select_node_storage(node_storage));
}
Literal Node::as_gt(Node const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
    return Literal::make_boolean(gt(other), select_node_storage(node_storage));
}
Literal Node::as_order_gt(Node const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
    return Literal::make_boolean(order_gt(other), select_node_storage(node_storage));
}
Literal Node::as_ge(Node const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
    return Literal::make_boolean(ge(other), select_node_storage(node_storage));
}
Literal Node::as_order_ge(Node const &other, storage::DynNodeStoragePtr node_storage) const noexcept {
    return Literal::make_boolean(order_ge(other), select_node_storage(node_storage));
}

std::partial_ordering Node::operator<=>(Node const &other) const noexcept {
    return order(other);
}

bool Node::operator==(const Node &other) const noexcept {
    return order_eq(other);
}

BlankNode Node::as_blank_node() const noexcept {
    if (!this->is_blank_node()) {
        return BlankNode{};
    }

    return BlankNode{handle_};
}

IRI Node::as_iri() const noexcept {
    if (!this->is_iri()) {
        return IRI{};
    }

    return IRI{handle_};
}

Literal Node::as_literal() const noexcept {
    if (!this->is_literal()) {
        return Literal{};
    }

    return Literal{handle_};
}

query::Variable Node::as_variable() const noexcept {
    if (!this->is_variable()) {
        return query::Variable{};
    }

    return query::Variable{handle_};
}

bool Node::null() const noexcept {
    return handle_.null();
}
std::ostream &operator<<(std::ostream &os, Node const &node) {
    writer::BufOStreamWriter w{os};
    node.serialize(w);
    w.finalize();

    return os;
}
storage::identifier::NodeBackendHandle const &Node::backend_handle() const noexcept {
    return handle_;
}
storage::identifier::NodeBackendHandle &Node::backend_handle() noexcept {
    return handle_;
}

TriBool Node::ebv() const noexcept {
    if (this->null() || !this->is_literal()) {
        return TriBool::Err;
    }

    return Literal{handle_}.ebv();
}

Literal Node::as_ebv(storage::DynNodeStoragePtr node_storage) const noexcept {
    return this->as_literal().as_ebv(node_storage);
}

Node::operator bool() const noexcept {
    return ebv();
}

}  // namespace rdf4cpp

auto std::formatter<rdf4cpp::Node>::format(rdf4cpp::Node n, format_context &ctx) const -> decltype(ctx.out()) {
    rdf4cpp::writer::BufOutputIteratorWriter w{ctx.out()};
    n.serialize(w);
    w.finalize();
    return w.buffer().iter;
}