diff --git a/XPath/XPath.hpp b/XPath/XPath.hpp new file mode 100644 index 00000000..b047e125 --- /dev/null +++ b/XPath/XPath.hpp @@ -0,0 +1,12 @@ +#ifndef ARABICA_XPATHIC_XPATH_HPP +#define ARABICA_XPATHIC_XPATH_HPP + +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/XPath/impl/xpath_arithmetic.hpp b/XPath/impl/xpath_arithmetic.hpp new file mode 100644 index 00000000..40b3c8f7 --- /dev/null +++ b/XPath/impl/xpath_arithmetic.hpp @@ -0,0 +1,92 @@ +#ifndef ARABICA_XPATHIC_XPATH_ARITHMETIC_HPP +#define ARABICA_XPATHIC_XPATH_ARITHMETIC_HPP + +#include "xpath_value.hpp" + +namespace Arabica +{ +namespace XPath +{ + +class PlusOperator : private BinaryExpression, public XPathExpression +{ +public: + PlusOperator(XPathExpression* lhs, XPathExpression* rhs) : + BinaryExpression(lhs, rhs) { } + + virtual XPathValuePtr evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return NumericValue::createValue(lhs()->evaluateAsNumber(context) + rhs()->evaluateAsNumber(context)); + } // evaluate +}; // class PlusOperator + +class MinusOperator : private BinaryExpression, public XPathExpression +{ +public: + MinusOperator(XPathExpression* lhs, XPathExpression* rhs) : + BinaryExpression(lhs, rhs) { } + + virtual XPathValuePtr evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return NumericValue::createValue(lhs()->evaluateAsNumber(context) - rhs()->evaluateAsNumber(context)); + } // evaluate +}; // class MinusOperator + +class MultiplyOperator : private BinaryExpression, public XPathExpression +{ +public: + MultiplyOperator(XPathExpression* lhs, XPathExpression* rhs) : + BinaryExpression(lhs, rhs) { } + + virtual XPathValuePtr evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return NumericValue::createValue(lhs()->evaluateAsNumber(context) * rhs()->evaluateAsNumber(context)); + } // evaluate +}; // class MultiplyOperator + +class DivideOperator : private BinaryExpression, public XPathExpression +{ +public: + DivideOperator(XPathExpression* lhs, XPathExpression* rhs) : + BinaryExpression(lhs, rhs) { } + + virtual XPathValuePtr evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return NumericValue::createValue(lhs()->evaluateAsNumber(context) / rhs()->evaluateAsNumber(context)); + } // evaluate +}; // class DivideOperator + +class ModOperator : private BinaryExpression, public XPathExpression +{ +public: + ModOperator(XPathExpression* lhs, XPathExpression* rhs) : + BinaryExpression(lhs, rhs) { } + + virtual XPathValuePtr evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return NumericValue::createValue(static_cast(lhs()->evaluateAsNumber(context)) % static_cast(rhs()->evaluateAsNumber(context))); + } // evaluate +}; // class ModOperator + +class UnaryNegative : private UnaryExpression, public XPathExpression +{ +public: + UnaryNegative(XPathExpression* expr) : + UnaryExpression(expr) { } + + virtual XPathValuePtr evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return NumericValue::createValue(-expr()->evaluate(context, executionContext)->asNumber()); + } // evaluate +}; // class UnaryNegative + +} // XPath +} // Arabica + +#endif diff --git a/XPath/impl/xpath_ast.hpp b/XPath/impl/xpath_ast.hpp new file mode 100644 index 00000000..a4e70b3a --- /dev/null +++ b/XPath/impl/xpath_ast.hpp @@ -0,0 +1,27 @@ +#ifndef ARABICA_XPATHIC_XPATH_AST_HPP +#define ARABICA_XPATHIC_XPATH_AST_HPP + +#include +#include + +namespace Arabica +{ +namespace XPath +{ + +typedef std::string::const_iterator str_iter_t; + +typedef boost::spirit::tree_match tree_match_t; +typedef tree_match_t::tree_iterator node_iter_t; +typedef boost::spirit::tree_parse_info tree_info_t; + +long getNodeId(node_iter_t const& node); +node_iter_t& skipWhitespace(node_iter_t& node); + +class XPathExpression; +class CompilationContext; +XPathExpression* compile_expression(node_iter_t const& i, CompilationContext& context); + +} // namespace XPath +} // namespace Arabica +#endif diff --git a/XPath/impl/xpath_ast_ids.hpp b/XPath/impl/xpath_ast_ids.hpp new file mode 100644 index 00000000..73f6263f --- /dev/null +++ b/XPath/impl/xpath_ast_ids.hpp @@ -0,0 +1,106 @@ +#ifndef ARABICA_XPATHIC_XPATH_AST_IDS_HPP +#define ARABICA_XPATHIC_XPATH_AST_IDS_HPP + +namespace Arabica +{ +namespace XPath +{ + +enum +{ + LocationPath_id = 1, + AbsoluteLocationPath_id, + RelativeLocationPath_id, + Step_id, + AxisSpecifier_id, + NodeTest_id, + Predicate_id, + PredicateExpr_id, + AbbreviatedAbsoluteLocationPath_id, + AbbreviatedStep_id, + AbbreviatedAxisSpecifier_id, + Expr_id, + PrimaryExpr_id, + FunctionCall_id, + Argument_id, + UnionExpr_id, + PathExpr_id, + FilterExpr_id, + OrExpr_id, + AndExpr_id, + EqualityExpr_id, + RelationalExpr_id, + AdditiveExpr_id, + MultiplicativeExpr_id, + UnaryExpr_id, + Literal_id, + Number_id, + Digits_id, + MultiplyOperator_id, + FunctionName_id, + VariableReference_id, + NameTest_id, + S_id, + NodeType_id, + AxisName_id, + + QName_id, + Prefix_id, + LocalPart_id, + NCName_id, + NCNameChar_id, + + Slash_id, + SlashSlash_id, + + AncestorOrSelf_id, + Ancestor_id, + Attribute_id, + Child_id, + DescendantOrSelf_id, + Descendant_id, + FollowingSibling_id, + Following_id, + Namespace_id, + Parent_id, + PrecedingSibling_id, + Preceding_id, + Self_id, + + Comment_id, + Text_id, + ProcessingInstruction_id, + Node_id, + AnyName_id, + + SelfSelect_id, + ParentSelect_id, + + LeftSquare_id, + RightSquare_id, + + LeftBracket_id, + RightBracket_id, + + PlusOperator_id, + MinusOperator_id, + ModOperator_id, + DivOperator_id, + EqualsOperator_id, + NotEqualsOperator_id, + LessThanOperator_id, + LessThanEqualsOperator_id, + GreaterThanOperator_id, + GreaterThanEqualsOperator_id, + + OrOperator_id, + AndOperator_id, + UnionOperator_id, + UnaryMinusOperator_id +}; + +} // namespace XPath + +} // namespace Arabica + +#endif diff --git a/XPath/impl/xpath_axis_enumerator.hpp b/XPath/impl/xpath_axis_enumerator.hpp new file mode 100644 index 00000000..7259a09f --- /dev/null +++ b/XPath/impl/xpath_axis_enumerator.hpp @@ -0,0 +1,531 @@ +#ifndef ARABICA_XPATHIC_XPATH_AXIS_ENUMERATOR_H +#define ARABICA_XPATHIC_XPATH_AXIS_ENUMERATOR_H + +#include +#include +#include +#include "xpath_namespace_node.hpp" + +namespace Arabica +{ +namespace XPath +{ + +enum Axis +{ + ANCESTOR, + ANCESTOR_OR_SELF, + ATTRIBUTE, + CHILD, + DESCENDANT, + DESCENDANT_OR_SELF, + FOLLOWING, + FOLLOWING_SIBLING, + NAMESPACE, + PARENT, + PRECEDING, + PRECEDING_SIBLING, + SELF +}; // Axis + +class AxisEnumerator +{ + class AxisWalker; + + template + static AxisWalker* CreateAxis(const DOM::Node& context) { return new axis_walker(context); } + + typedef AxisWalker* (*CreateAxisPtr)(const DOM::Node& context); + + struct NamedAxis { Axis name; CreateAxisPtr creator; }; + + static const NamedAxis AxisLookupTable[]; + +public: + AxisEnumerator(const DOM::Node& context, Axis axis) : + walker_(0) + { + for(const NamedAxis* ax = AxisLookupTable; ax->creator != 0; ++ax) + if(axis == ax->name) + walker_ = ax->creator(context); + + if(!walker_) + throw std::runtime_error("Unknown Axis specifier"); + } // AxisEnumerator + + AxisEnumerator(const AxisEnumerator& rhs) : + walker_(rhs.walker_->clone()) + { + } // AxisEnumerator + + AxisEnumerator& operator=(const AxisEnumerator& rhs) + { + AxisWalker* newwalker = rhs.walker_->clone(); + delete walker_; + walker_ = newwalker; + return *this; + } // operator= + + ~AxisEnumerator() + { + delete walker_; + } // ~AxisEnumerator + + bool forward() const { return walker_->forward(); } + bool reverse() const { return !walker_->forward(); } + const DOM::Node& operator*() const { return walker_->get(); } + const DOM::Node* const operator->() const { return &(walker_->get()); } + AxisEnumerator& operator++() { walker_->advance(); return *this; } + AxisEnumerator operator++(int) { AxisEnumerator copy(*this); walker_->advance(); return copy; } + +private: + AxisWalker* walker_; + + AxisEnumerator(); + + //////////////////////////////////////////////////// + class AxisWalker + { + public: + virtual ~AxisWalker() { } + const DOM::Node& get() const { return current_; } + virtual void advance() = 0; + bool forward() { return forward_; } + virtual AxisWalker* clone() const = 0; + + protected: + AxisWalker(bool forward) : forward_(forward) { } + AxisWalker(const AxisWalker& rhs) : current_(rhs.current_), forward_(rhs.forward_) { } + void set(const DOM::Node& current) { current_ = current; } + void end() { current_ = 0; } + + private: + DOM::Node current_; + bool forward_; + + AxisWalker& operator=(const AxisWalker&); + bool operator==(const AxisWalker&); + }; // AxisWalker + + class AncestorAxisWalker : public AxisWalker + { + public: + AncestorAxisWalker(const DOM::Node& context) : AxisWalker(false) + { + if(context == 0) + return; + + if(context.getNodeType() != DOM::Node::ATTRIBUTE_NODE) + set(context.getParentNode()); + else + set((static_cast >(context)).getOwnerElement()); + } // AncestorAxisWalker + + virtual void advance() + { + if(get() != 0) + set(get().getParentNode()); + } // advance + virtual AxisWalker* clone() const { return new AncestorAxisWalker(*this); } + + private: + AncestorAxisWalker(const AncestorAxisWalker& rhs) : AxisWalker(rhs) { } + }; // class AncestorAxisWalker + + class AncestorOrSelfAxisWalker : public AxisWalker + { + public: + AncestorOrSelfAxisWalker(const DOM::Node& context) : AxisWalker(false) + { + if(context != 0) + set(context); + } // AncestorAxisWalker + + virtual void advance() + { + if(get() == 0) + return; + + if(get().getNodeType() != DOM::Node::ATTRIBUTE_NODE) + set(get().getParentNode()); + else + set((static_cast >(get())).getOwnerElement()); + } // advance + virtual AxisWalker* clone() const { return new AncestorOrSelfAxisWalker(*this); } + + private: + AncestorOrSelfAxisWalker(const AncestorOrSelfAxisWalker& rhs) : AxisWalker(rhs) { } + }; // class AncestorOrSelfAxisWalker + + class AttributeAxisWalker : public AxisWalker + { + public: + AttributeAxisWalker(const DOM::Node& context) : AxisWalker(true), + index_(0), count_(0) + { + if((context != 0) && (context.hasAttributes())) + { + attrs_ = context.getAttributes(); + count_ = attrs_.getLength(); + set_next(); + } // if ... + } // AttributeAxisWalker + + virtual void advance() + { + if(get() == 0) + return; + set_next(); + } // advance + + virtual AxisWalker* clone() const { return new AttributeAxisWalker(*this); } + + private: + AttributeAxisWalker(const AttributeAxisWalker& rhs) : + AxisWalker(rhs), + attrs_(rhs.attrs_), + index_(rhs.index_), + count_(rhs.count_) { } + + DOM::NamedNodeMap attrs_; + unsigned int index_; + unsigned int count_; + + void set_next() + { + if(index_ == count_) + { + end(); + return; + } // if ... + + DOM::Node a; + do + { + a = attrs_.item(index_++); + } while ((a != 0) && (a.getNamespaceURI() == "http://www.w3.org/2000/xmlns/")); + + set(a); + } // set_next + }; // class AttributeAxisEnumerator + + class ChildAxisWalker : public AxisWalker + { + public: + ChildAxisWalker(const DOM::Node& context) : AxisWalker(true) + { + if(context != 0) + set(context.getFirstChild()); + } // ChildAxisWalker + + virtual void advance() + { + if(get() != 0) + set(get().getNextSibling()); + } // advance + virtual AxisWalker* clone() const { return new ChildAxisWalker(*this); } + + private: + ChildAxisWalker(const ChildAxisWalker& rhs) : AxisWalker(rhs) { } + }; // class ChildAxisWalker + + class DescendantAxisWalker : public AxisWalker + { + public: + DescendantAxisWalker(const DOM::Node& context) : AxisWalker(true), + origin_(context) + { + if((context != 0) && (context.getNodeType() != DOM::Node::ATTRIBUTE_NODE)) + set(context.getFirstChild()); + } // DescendantAxisWalker + + virtual void advance() + { + set(nextDescendant()); + } // advance + + virtual AxisWalker* clone() const { return new DescendantAxisWalker(*this); } + + private: + DOM::Node nextDescendant() + { + DOM::Node next = get().getFirstChild(); + if(next == 0) + next = get().getNextSibling(); + if(next != 0) + return next; + + DOM::Node parent = get().getParentNode(); + while(parent != origin_ && next == 0) + { + next = parent.getNextSibling(); + parent = parent.getParentNode(); + } // while ... + + return next; + } // nextDescendant + + DescendantAxisWalker(const DescendantAxisWalker& rhs) : AxisWalker(rhs), origin_(rhs.origin_) { } + const DOM::Node origin_; + }; // class DescendantAxisWalker + + class DescendantOrSelfAxisWalker : public AxisWalker + { + public: + DescendantOrSelfAxisWalker(const DOM::Node& context) : AxisWalker(true), + origin_(context) + { + if(context != 0) + set(context); + } // DescendantAxisWalker + + virtual void advance() + { + set(walkDown(get(), origin_)); + } // advance + + virtual AxisWalker* clone() const { return new DescendantOrSelfAxisWalker(*this); } + + private: + DescendantOrSelfAxisWalker(const DescendantOrSelfAxisWalker& rhs) : AxisWalker(rhs), origin_(rhs.origin_) { } + const DOM::Node origin_; + }; // class DescendantOrSelfAxisWalker + + class FollowingAxisWalker : public AxisWalker + { + public: + FollowingAxisWalker(const DOM::Node& context) : AxisWalker(true) + { + set(firstFollowing(context)); + } // FollowingAxisWalker + + virtual void advance() + { + set(walkDown(get(), get().getOwnerDocument())); + } // advance + virtual AxisWalker* clone() const { return new FollowingAxisWalker(*this); } + + private: + DOM::Node firstFollowing(const DOM::Node& context) const + { + if(context.getNodeType() == DOM::Node::ATTRIBUTE_NODE) + return 0; + + DOM::Node next = context.getNextSibling(); + if(next != 0) + return next; + + DOM::Node parent = context.getParentNode(); + while(parent != context.getOwnerDocument() && next == 0) + { + next = parent.getNextSibling(); + parent = parent.getParentNode(); + } // while ... + + return next; + } // firstFollowing + + FollowingAxisWalker(const FollowingAxisWalker& rhs) : AxisWalker(rhs) { } + }; // class FollowingAxisWalker + + class FollowingSiblingAxisWalker : public AxisWalker + { + public: + FollowingSiblingAxisWalker(const DOM::Node& context) : AxisWalker(true) + { + if(context != 0) + set(context.getNextSibling()); + } // FollowingSiblingAxisWalker + + virtual void advance() + { + if(get() != 0) + set(get().getNextSibling()); + } // advance + virtual AxisWalker* clone() const { return new FollowingSiblingAxisWalker(*this); } + + private: + FollowingSiblingAxisWalker(const FollowingSiblingAxisWalker& rhs) : AxisWalker(rhs) { } + }; // class FollowingSiblingAxisWalker + + class NamespaceAxisWalker : public AxisWalker + { + public: + NamespaceAxisWalker(const DOM::Node& context) : AxisWalker(true), + index_(0) + { + DOM::Node current = context; + while(current.getNodeType() == DOM::Node::ELEMENT_NODE) + { + for(unsigned int a = 0, ae = current.getAttributes().getLength(); a != ae; ++a) + { + DOM::Node attr = current.getAttributes().item(a); + if(attr.getPrefix() == "xmlns") + list_.push_back(DOM::Node( + new NamespaceNodeImpl(attr.getLocalName(), + attr.getNodeValue()) + ) + ); + } // for ... + current = current.getParentNode(); + } // while + list_.push_back(DOM::Node(0)); + set(list_[index_]); + } // NamespaceAxisWalker + + virtual void advance() + { + if(index_ != list_.size()) + set(list_[++index_]); + } // advance + + virtual AxisWalker* clone() const { return new NamespaceAxisWalker(*this); } + + private: + NamespaceAxisWalker(const NamespaceAxisWalker& rhs) : AxisWalker(rhs) { } + std::vector > list_; + unsigned int index_; + }; // class NamespaceAxisWalker + + class ParentAxisWalker : public AxisWalker + { + public: + ParentAxisWalker(const DOM::Node& context) : AxisWalker(false) + { + if(context == 0) + return; + + if(context.getNodeType() != DOM::Node::ATTRIBUTE_NODE) + set(context.getParentNode()); + else + set((static_cast >(context)).getOwnerElement()); + } // ParentAxisWalker + + virtual void advance() + { + if(get() != 0) + set(0); + } // advance + virtual AxisWalker* clone() const { return new ParentAxisWalker(*this); } + + private: + ParentAxisWalker(const ParentAxisWalker& rhs) : AxisWalker(rhs) { } + }; // class ParentAxisWalker + + class PrecedingAxisWalker : public AxisWalker + { + public: + PrecedingAxisWalker(const DOM::Node& context) : AxisWalker(false) + { + nextAncestor_ = context.getParentNode(); + set(previousInDocument(context)); + } // PrecedingAxisWalker + + virtual void advance() + { + set(previousInDocument(get())); + } // advance + virtual AxisWalker* clone() const { return new PrecedingAxisWalker(*this); } + + private: + DOM::Node previousInDocument(const DOM::Node& context) + { + DOM::Node next = context.getPreviousSibling(); + if(next != 0) + return getLastDescendant(next); + + next = context.getParentNode(); + if(next != nextAncestor_) + return next; + + // ancestor collision!! woorp, woorp! + nextAncestor_ = nextAncestor_.getParentNode(); + if(nextAncestor_ != 0) + return previousInDocument(next); + + return 0; + } // previousInDocument + + DOM::Node getLastDescendant(const DOM::Node& context) + { + if(context.getFirstChild() == 0) + return context; + + DOM::Node c = context.getFirstChild(); + while(c.getNextSibling() != 0) + c = c.getNextSibling(); + + return getLastDescendant(c); + } // getLastDescendant + + PrecedingAxisWalker(const PrecedingAxisWalker& rhs) : AxisWalker(rhs), nextAncestor_(rhs.nextAncestor_) { } + DOM::Node nextAncestor_; + }; // PrecedingAxisWalker + + class PrecedingSiblingAxisWalker : public AxisWalker + { + public: + PrecedingSiblingAxisWalker(const DOM::Node& context) : AxisWalker(false) + { + if(context != 0) + set(context.getPreviousSibling()); + } // PrecedingSiblingAxisWalker + + virtual void advance() + { + if(get() != 0) + set(get().getPreviousSibling()); + } // advance + virtual AxisWalker* clone() const { return new PrecedingSiblingAxisWalker(*this); } + + private: + PrecedingSiblingAxisWalker(const PrecedingSiblingAxisWalker& rhs) : AxisWalker(rhs) { } + }; // class PrecedingSiblingAxisWalker + + class SelfAxisWalker : public AxisWalker + { + public: + SelfAxisWalker(const DOM::Node& context) : AxisWalker(true) + { + set(context); + } // SelfAxisWalker + + virtual void advance() { end(); } + virtual AxisWalker* clone() const { return new SelfAxisWalker(*this); } + + private: + SelfAxisWalker(const SelfAxisWalker& rhs) : AxisWalker(rhs) { } + }; // class SelfAxisWalker + + static DOM::Node walkDown(const DOM::Node& context, const DOM::Node& origin) + { + if(context.getNodeType() == DOM::Node::ATTRIBUTE_NODE) + return 0; + + DOM::Node next = context.getFirstChild(); + if((next == 0) && (context == origin)) // node with no children + return 0; + + if(next != 0) + return next; + + next = context.getNextSibling(); + if(next != 0) + return next; + + DOM::Node parent = context.getParentNode(); + while(parent != origin && next == 0) + { + next = parent.getNextSibling(); + parent = parent.getParentNode(); + } // while ... + + return next; + } // walkDown +}; // class AxisEnumerator + +} // namespace XPath +} // namespace Arabica + +#endif + diff --git a/XPath/impl/xpath_compile_context.hpp b/XPath/impl/xpath_compile_context.hpp new file mode 100644 index 00000000..392c24bc --- /dev/null +++ b/XPath/impl/xpath_compile_context.hpp @@ -0,0 +1,42 @@ +#ifndef ARABICA_XPATH_COMPILE_CONTEXT_HPP +#define ARABICA_XPATH_COMPILE_CONTEXT_HPP + +namespace Arabica +{ +namespace XPath +{ + +class XPath; +class NamespaceContext; +class FunctionResolver; + +class CompilationContext +{ +public: + CompilationContext(const XPath& xpathCompiler, + const NamespaceContext& namespaceContext, + const FunctionResolver& functionResolver) : + xpath_(xpathCompiler), + namespaceContext_(namespaceContext), + functionResolver_(functionResolver) + { + } // CompilationContext + + const XPath& xpath() const { return xpath_; } + const NamespaceContext& namespaceContext() const { return namespaceContext_; } + const FunctionResolver& functionResolver() const { return functionResolver_; } + +private: + const XPath& xpath_; + const NamespaceContext& namespaceContext_; + const FunctionResolver& functionResolver_; + + CompilationContext(const CompilationContext&); + CompilationContext& operator=(const CompilationContext&); + bool operator==(const CompilationContext&) const; +}; // class CompilationContext + +} // namespace XPath +} // namespace Arabica + +#endif diff --git a/XPath/impl/xpath_execution_context.hpp b/XPath/impl/xpath_execution_context.hpp new file mode 100644 index 00000000..67f30aeb --- /dev/null +++ b/XPath/impl/xpath_execution_context.hpp @@ -0,0 +1,50 @@ +#ifndef ARABICA_XPATH_EXECUTION_CONTEXT_HPP +#define ARABICA_XPATH_EXECUTION_CONTEXT_HPP + +#include "xpath_variable_resolver.hpp" +#include "xpath_resolver_holder.hpp" + +namespace Arabica +{ +namespace XPath +{ + +class ExecutionContext +{ +public: + ExecutionContext() : + position_(-1), + last_(-1) + { + } // ExecutionContext + + ExecutionContext(size_t last, const ExecutionContext& parent) : + position_(-1), + last_(last), + variableResolver_(parent.variableResolver_) + { + } // ExecutionContext + + size_t position() const { return position_; } + size_t last() const { return last_; } + + void setPosition(unsigned int pos) { position_ = pos; } + + const VariableResolver& variableResolver() const { return variableResolver_.get(); } + void setVariableResolver(const VariableResolver& resolver) { variableResolver_.set(resolver); } + void setVariableResolver(VariableResolverPtr& resolver) { variableResolver_.set(resolver); } + +private: + size_t position_; + size_t last_; + ResolverHolder variableResolver_; + + ExecutionContext(const ExecutionContext&); + ExecutionContext& operator=(const ExecutionContext&); + bool operator==(const ExecutionContext&) const; +}; // class ExecutionContext + +} // namespace XPath +} // namespace Arabica + +#endif diff --git a/XPath/impl/xpath_function.hpp b/XPath/impl/xpath_function.hpp new file mode 100644 index 00000000..b877a27c --- /dev/null +++ b/XPath/impl/xpath_function.hpp @@ -0,0 +1,543 @@ +#ifndef ARABICA_XPATH_FUNCTION_HPP +#define ARABICA_XPATH_FUNCTION_HPP + +#include +#include +#include +#include +#include "xpath_value.hpp" +#include "xpath_execution_context.hpp" + +namespace Arabica +{ +namespace XPath +{ + +class XPathFunction +{ +protected: + XPathFunction(int minArgs, int maxArgs, const std::vector& args) : + args_(args) + { + if(((minArgs != -1) && (static_cast(args.size()) < minArgs)) || + ((maxArgs != -1) && (static_cast(args.size()) > maxArgs))) + throw SyntaxException("wrong number of arguments to function"); + } // XPathFunction + +public: + virtual ~XPathFunction() { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const = 0; + +protected: + size_t argCount() const { return args_.size(); } + + bool argAsBool(size_t index, + const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return args_[index]->evaluate(context, executionContext)->asBool(); + } // argAsBool + + double argAsNumber(size_t index, + const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return args_[index]->evaluate(context, executionContext)->asNumber(); + } // argAsNumber + + std::string argAsString(size_t index, + const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return args_[index]->evaluate(context, executionContext)->asString(); + } // argAsString + + NodeSet argAsNodeSet(size_t index, + const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return args_[index]->evaluate(context, executionContext)->asNodeSet(); + } // argAsNodeSet + +private: + const std::vector args_; +}; // class XPathFunction + +//////////////////////////////// +// node-set functions +// number last() +class LastFn : public XPathFunction +{ +public: + LastFn(const std::vector& args) : XPathFunction(0, 0, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return new NumericValue(executionContext.last()); + } // evaluate +}; // class LastFn + +// number position() +class PositionFn : public XPathFunction +{ +public: + PositionFn(const std::vector& args) : XPathFunction(0, 0, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return new NumericValue(executionContext.position()); + } // evaluate +}; // class PositionFn + +// number count(node-set) +class CountFn : public XPathFunction +{ +public: + CountFn(const std::vector& args) : XPathFunction(1, 1, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return new NumericValue(argAsNodeSet(0, context, executionContext).size()); + } // evaluate +}; // class CountFn + +// node-set id(object) +// string local-name(node-set?) +class LocalNameFn : public XPathFunction +{ +public: + LocalNameFn(const std::vector& args) : XPathFunction(0, 1, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + DOM::Node node; + if(argCount() == 0) + node = context; + else + { + NodeSet ns = argAsNodeSet(0, context, executionContext); + if(ns.size() != 0) + node = ns.top(); + } // if ... + + if(node != 0) + switch(node.getNodeType()) + { + case DOM::Node_base::ATTRIBUTE_NODE: + case DOM::Node_base::ELEMENT_NODE: + case DOM::Node_base::PROCESSING_INSTRUCTION_NODE: + return new StringValue(node.hasNamespaceURI() ? node.getLocalName() : node.getNodeName()); + } // switch ... + return new StringValue(""); + } // evaluate +}; // class LocalNameFn + +// string namespace-uri(node-set?) +class NamespaceURIFn : public XPathFunction +{ +public: + NamespaceURIFn(const std::vector& args) : XPathFunction(0, 1, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + DOM::Node node; + if(argCount() == 0) + node = context; + else + { + NodeSet ns = argAsNodeSet(0, context, executionContext); + if(ns.size() != 0) + node = ns.top(); + } // if ... + + if(node != 0) + switch(node.getNodeType()) + { + case DOM::Node_base::ATTRIBUTE_NODE: + case DOM::Node_base::ELEMENT_NODE: + return new StringValue(node.getNamespaceURI()); + } // switch ... + return new StringValue(""); + } // evaluate +}; // class NamespaceURIFn + +// string name(node-set?) +class NameFn : public XPathFunction +{ +public: + NameFn(const std::vector& args) : XPathFunction(0, 1, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + DOM::Node node; + if(argCount() == 0) + node = context; + else + { + NodeSet ns = argAsNodeSet(0, context, executionContext); + if(ns.size() != 0) + node = ns.top(); + } // if ... + + if(node != 0) + switch(node.getNodeType()) + { + case DOM::Node_base::ATTRIBUTE_NODE: + case DOM::Node_base::ELEMENT_NODE: + case DOM::Node_base::PROCESSING_INSTRUCTION_NODE: + return new StringValue(node.getNodeName()); + } // switch ... + return new StringValue(""); + } // evaluate +}; // class NameFn + +/////////////////////////////////////////////// +// string functions + +// string string(object?) +class StringFn : public XPathFunction +{ +public: + StringFn(const std::vector& args) : XPathFunction(0, 1, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return new StringValue((argCount() > 0) ? argAsString(0, context, executionContext) : nodeStringValue(context)); + } // evaluate +}; // class StringFn + +// string concat(string, string, string*) +class ConcatFn : public XPathFunction +{ +public: + ConcatFn(const std::vector& args) : XPathFunction(2, -1, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + std::string s; + for(size_t a = 0, ae = argCount(); a < ae; ++a) + s.append(argAsString(a, context, executionContext)); + return new StringValue(s); + } // evaluate +}; // ConcatFn + +// boolean starts-with(string, string) +class StartsWithFn : public XPathFunction +{ +public: + StartsWithFn(const std::vector& args) : XPathFunction(2, 2, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + std::string value = argAsString(0, context, executionContext); + std::string start = argAsString(1, context, executionContext); + + if(value.length() < start.length()) + return new BoolValue(false); + + for(size_t i = 0, ie = start.length(); i < ie; ++i) + if(value[i] != start[i]) + return new BoolValue(false); + + return new BoolValue(true); + } // evaluate +}; // StartsWithFn + +// boolean contains(string, string) +class ContainsFn : public XPathFunction +{ +public: + ContainsFn(const std::vector& args) : XPathFunction(2, 2, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return new BoolValue(argAsString(0, context, executionContext).find(argAsString(1, context, executionContext)) != std::string::npos); + } // evaluate +}; // class ContainsFn + +// string substring-before(string, string) +class SubstringBeforeFn : public XPathFunction +{ +public: + SubstringBeforeFn(const std::vector& args) : XPathFunction(2, 2, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + std::string value = argAsString(0, context, executionContext); + size_t splitAt = value.find(argAsString(1, context, executionContext)); + + if(splitAt == std::string::npos) + return new StringValue(""); + + return new StringValue(value.substr(0, splitAt)); + } // evaluate +}; // class SubstringBeforeFn + +// string substring-after(string, string) +class SubstringAfterFn : public XPathFunction +{ +public: + SubstringAfterFn(const std::vector& args) : XPathFunction(2, 2, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + std::string value = argAsString(0, context, executionContext); + std::string split = argAsString(1, context, executionContext); + size_t splitAt = value.find(split); + + if((splitAt == std::string::npos) || ((splitAt + split.length()) >= value.length())) + return new StringValue(""); + + return new StringValue(value.substr(splitAt + split.length())); + } // evaluate +}; // class SubstringAfterFn + +// string substring(string, number, number?) +class SubstringFn : public XPathFunction +{ +public: + SubstringFn(const std::vector& args) : XPathFunction(2, 3, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + std::string value = argAsString(0, context, executionContext); + double startAt = roundNumber(argAsNumber(1, context, executionContext)) - 1; + double endAt = roundNumber((argCount() == 3 ? argAsNumber(2, context, executionContext) : Infinity)) + startAt; + + if((endAt < 0) || (endAt < startAt)) + return new StringValue(""); + + if(startAt < 0) + startAt = 0; + if((isInfinite(endAt)) || (endAt > value.length())) + endAt = value.length(); + + return new StringValue(value.substr(static_cast(startAt), static_cast(endAt - startAt))); + } // evaluate +}; // SubstringFn + +// number string-length(string?) +class StringLengthFn : public XPathFunction +{ +public: + StringLengthFn(const std::vector& args) : XPathFunction(0, 1, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return new NumericValue(((argCount() > 0) ? argAsString(0, context, executionContext) : nodeStringValue(context)).length()); + } // evaluate +}; // StringLengthFn + +// string normalize-space(string?) +class NormalizeSpaceFn : public XPathFunction +{ +public: + NormalizeSpaceFn(const std::vector& args) : XPathFunction(0, 1, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + std::string value = ((argCount() > 0) ? argAsString(0, context, executionContext) : nodeStringValue(context)); + size_t i = 0, ie = value.length(); + + // string leading space + while((i != ie) && (XML::is_space(static_cast(value[i])))) + ++i; + + size_t p = 0; + while(i != ie) + { + while((i != ie) && (!XML::is_space(static_cast(value[i])))) + value[p++] = value[i++]; + while((i != ie) && (XML::is_space(static_cast(value[i])))) + ++i; + if(i != ie) + value[p++] = Unicode::SPACE; + } // while ... + if(p != ie) + value.resize(p); + + return new StringValue(value); + } // evaluate +}; // class NormalizeSpaceFn + +// string translate(string, string, string) +class TranslateFn : public XPathFunction +{ +public: + TranslateFn(const std::vector& args) : XPathFunction(3, 3, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + std::string str = argAsString(0, context, executionContext); + std::string from = argAsString(1, context, executionContext); + std::string to = argAsString(2, context, executionContext); + + size_t p = 0; + for(size_t i = 0, ie = str.length(); i != ie; ++i) + { + size_t r = from.find(str[i]); + if(r == std::string::npos) + ++p; + else if(r < to.length()) + str[p++] = to[r]; + } // for ... + if(p != str.length()) + str.resize(p); + + return new StringValue(str); + } // evaluate +}; // class TranslateFn + +/////////////////////////////////////////////////////// +// boolean functions + +// boolean boolean(object) +class BooleanFn : public XPathFunction +{ +public: + BooleanFn(const std::vector& args) : XPathFunction(1, 1, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return new BoolValue(argAsBool(0, context, executionContext)); + } // evaluate +}; // class BooleanFn + +// boolean not(boolean) +class NotFn : public XPathFunction +{ +public: + NotFn(const std::vector& args) : XPathFunction(1, 1, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return new BoolValue(!argAsBool(0, context, executionContext)); + } +}; // class NotFn + +// boolean true() +class TrueFn : public XPathFunction +{ +public: + TrueFn(const std::vector& args) : XPathFunction(0, 0, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return new BoolValue(true); + } // evaluate +}; // TrueFn + +// boolean false() +class FalseFn : public XPathFunction +{ +public: + FalseFn(const std::vector& args) : XPathFunction(0, 0, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return new BoolValue(false); + } // evaluate +}; // FalseFn + +// boolean lang(string) + +///////////////////////////////////////////////// +// number functions + +// number number(object?) +class NumberFn : public XPathFunction +{ +public: + NumberFn(const std::vector& args) : XPathFunction(0, 1, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + double result = (argCount() > 0) ? argAsNumber(0, context, executionContext) : + StringValue(nodeStringValue(context)).asNumber(); + return new NumericValue(result); + } // evaluate +}; // NumberFn + +// number sum(node-set) +class SumFn : public XPathFunction +{ +public: + SumFn(const std::vector& args) : XPathFunction(1, 1, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + double sum = 0; + NodeSet ns = argAsNodeSet(0, context, executionContext); + for(NodeSet::const_iterator n = ns.begin(), end = ns.end(); n != end; ++n) + sum += nodeNumberValue(*n); + return new NumericValue(sum); + } // evaluate +}; // class SumFn + +// number floor(number) +class FloorFn : public XPathFunction +{ +public: + FloorFn(const std::vector& args) : XPathFunction(1, 1, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return new NumericValue(std::floor(argAsNumber(0, context, executionContext))); + } // evaluate +}; // class FloorFn + +// number ceiling(number) +class CeilingFn : public XPathFunction +{ +public: + CeilingFn(const std::vector& args) : XPathFunction(1, 1, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return new NumericValue(std::ceil(argAsNumber(0, context, executionContext))); + } // evaluate +}; // class CeilingFn + +// number round(number) +class RoundFn : public XPathFunction +{ +public: + RoundFn(const std::vector& args) : XPathFunction(1, 1, args) { } + + virtual XPathValue* evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return new NumericValue(roundNumber(argAsNumber(0, context, executionContext))); + } // evaluate +}; // class RoundFn + +} // namespace XPath +} // namespace Arabica + +#endif diff --git a/XPath/impl/xpath_function_holder.hpp b/XPath/impl/xpath_function_holder.hpp new file mode 100644 index 00000000..b7f87874 --- /dev/null +++ b/XPath/impl/xpath_function_holder.hpp @@ -0,0 +1,92 @@ +#ifndef ARABICA_XPATH_FUNCTION_HOLDER_HPP +#define ARABICA_XPATH_FUNCTION_HOLDER_HPP + +#include +#include "xpath_value.hpp" +#include "xpath_function.hpp" + +namespace Arabica +{ +namespace XPath +{ + +template +XPathFunction* CreateFn(const std::vector& argExprs) { return new function_type(argExprs); } + +typedef XPathFunction* (*CreateFnPtr)(const std::vector& argExprs); + +struct NamedFunction { const char* name; CreateFnPtr creator; }; + +const NamedFunction FunctionLookupTable[] = { // node-set functions + { "position", CreateFn }, + { "last", CreateFn }, + { "count", CreateFn }, + { "local-name", CreateFn }, + { "namespace-uri", CreateFn }, + { "name", CreateFn }, + // string functions + {"string", CreateFn }, + {"concat", CreateFn }, + {"starts-with", CreateFn }, + {"contains", CreateFn }, + {"substring-before", CreateFn }, + {"substring-after", CreateFn }, + {"substring", CreateFn }, + {"string-length", CreateFn }, + {"normalize-space", CreateFn }, + {"translate", CreateFn }, + // boolean functions + {"boolean", CreateFn }, + {"not", CreateFn }, + {"true", CreateFn }, + {"false", CreateFn }, + // number functions + {"number", CreateFn }, + {"sum", CreateFn }, + {"floor", CreateFn }, + {"ceiling", CreateFn }, + {"round", CreateFn }, + {0, 0} + }; + +class FunctionHolder : public XPathExpression +{ +public: + FunctionHolder(XPathFunction* func) : + func_(func) + { + } // FunctionHolder + + virtual ~FunctionHolder() + { + delete func_; + } // ~FunctionHolder + + virtual XPathValuePtr evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return XPathValuePtr(func_->evaluate(context, executionContext)); + } // evaluate + + static FunctionHolder* createFunction(const std::string& name, + const std::vector& argExprs, + const CompilationContext& context) + { + for(const NamedFunction* fn = FunctionLookupTable; fn->name != 0; ++fn) + if(name == fn->name) + return new FunctionHolder(fn->creator(argExprs)); + + XPathFunction* func = context.functionResolver().resolveFunction(name, argExprs); + if(func == 0) + throw std::runtime_error("Function " + name + " not implemented"); + return new FunctionHolder(func); + } // createFunction + +private: + XPathFunction* func_; +}; // class FunctionResolver + +} // namespace XPath +} // namespace Arabica + +#endif diff --git a/XPath/impl/xpath_function_resolver.hpp b/XPath/impl/xpath_function_resolver.hpp new file mode 100644 index 00000000..77ada120 --- /dev/null +++ b/XPath/impl/xpath_function_resolver.hpp @@ -0,0 +1,44 @@ +#ifndef ARABICA_XPATH_FUNCTION_RESOLVER_HPP +#define ARABICA_XPATH_FUNCTION_RESOLVER_HPP + +#include + +namespace Arabica +{ +namespace XPath +{ + +class XPathFunction; +class XPathExpression; +typedef boost::shared_ptr XPathExpressionPtr; + +class UndefinedFunctionException : public std::runtime_error +{ +public: + UndefinedFunctionException(const std::string& thing) : std::runtime_error("The function '" + thing + "' is undefined.") { } +}; // class UndefinedFunctionException + +class FunctionResolver +{ +public: + // TODO: should make this a QName + virtual XPathFunction* resolveFunction(const std::string& name, + const std::vector& argExprs) const = 0; +}; // class FunctionResolver + +typedef boost::shared_ptr FunctionResolverPtr; + +class NullFunctionResolver : public FunctionResolver +{ +public: + virtual XPathFunction* resolveFunction(const std::string& name, + const std::vector& argExprs) const + { + throw UndefinedFunctionException(name); + } // resolveVariable +}; // NullFunctionResolver + +} // namespace XPath +} // namespace Arabica + +#endif diff --git a/XPath/impl/xpath_grammar.hpp b/XPath/impl/xpath_grammar.hpp new file mode 100644 index 00000000..f014a8d1 --- /dev/null +++ b/XPath/impl/xpath_grammar.hpp @@ -0,0 +1,329 @@ +#ifndef ARABICA_XPATHIC_XPATH_GRAMMER_HPP +#define ARABICA_XPATHIC_XPATH_GRAMMER_HPP + +#define BOOST_SPIRIT_DEBUG +#include +#include +#include +#include +#include + +#include "xpath_ast_ids.hpp" + +namespace Arabica +{ +namespace XPath +{ + +template +struct xpath_grammar_definition +{ + xpath_grammar_definition() + { + using namespace boost::spirit; + + // [1] + LocationPath = AbsoluteLocationPath | RelativeLocationPath; + // [2] + AbsoluteLocationPath = AbbreviatedAbsoluteLocationPath + | (Slash >> !RelativeLocationPath); + // [3] + RelativeLocationPath = Step >> *((SlashSlash | discard_node_d[Slash]) >> Step); + + // [4], [5] + Step = AxisSpecifier >> NodeTest >> *Predicate | AbbreviatedStep; + AxisSpecifier = S >> ( AxisName >> S >> "::" | AbbreviatedAxisSpecifier ) >> S; + + // [6] + AxisName = AncestorOrSelf + | Ancestor + | Attribute + | Child + | DescendantOrSelf + | Descendant + | FollowingSibling + | Following + | Namespace + | Parent + | PrecedingSibling + | Preceding + | Self; + + // [7] + NodeTest = S >>(ProcessingInstruction >> S >> discard_node_d[ch_p('(')] >> S >> Literal >> S >> discard_node_d[ch_p(')')] + | NodeType >> S >> discard_node_d[ch_p('(')] >> S >> discard_node_d[ch_p(')')] + | NameTest ) + >> S; + + // [8], [9] + Predicate = S >> LeftSquare >> PredicateExpr >> RightSquare >> S; + PredicateExpr = Expr; + + // [10] + AbbreviatedAbsoluteLocationPath = SlashSlash >> RelativeLocationPath; + // [11] AbbreviatedRelativeLocationPath eliminated + // [12], [13] + AbbreviatedStep = ParentSelect | SelfSelect; + AbbreviatedAxisSpecifier = !ch_p('@'); + + // [14], [15] + Expr = OrExpr; + PrimaryExpr = discard_node_d[S] >> + (VariableReference + | Number + | FunctionCall + | inner_node_d[ch_p('(') >> S >> Expr >> S >> ch_p(')')] + | Literal) >> + discard_node_d[S]; + + // [16], [17] + FunctionCall = FunctionName >> S >> LeftBracket >> !(Argument >> *(discard_node_d[ch_p(',')] >> S >> Argument)) >> S >> RightBracket >> S; + Argument = Expr; + + // [18], [19], [20] + // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr + UnionExpr = PathExpr >> *(UnionOperator >> PathExpr); + /* + LocationPath + | FilterExpr + | FilterExpr '/' RelativeLocationPath + | FilterExpr '//' RelativeLocationPath */ + PathExpr = discard_node_d[S] >> + (FilterExpr >> !((SlashSlash | Slash) >> RelativeLocationPath) + | LocationPath) >> + discard_node_d[S]; + // FilterExpr ::= PrimaryExpr | FilterExpr Predicate + FilterExpr = PrimaryExpr >> *Predicate; + + // [21], [22], [23], [24] + OrExpr = AndExpr >> *(OrOperator >> AndExpr); + AndExpr = EqualityExpr >> *(AndOperator >> EqualityExpr); + EqualityExpr = RelationalExpr >> *((EqualsOperator | NotEqualsOperator) >> RelationalExpr); + RelationalExpr = AdditiveExpr >> *((LessThanEqualsOperator | GreaterThanEqualsOperator | LessThanOperator |GreaterThanOperator) >> AdditiveExpr); + + // [25], [26], [27] + AdditiveExpr = MultiplicativeExpr >> *((PlusOperator | MinusOperator) >> MultiplicativeExpr); + MultiplicativeExpr = UnaryExpr >> *(token_node_d[(MultiplyOperator | DivOperator | ModOperator)] >> UnaryExpr); + UnaryExpr = discard_node_d[S] >> *(UnaryMinusOperator) >> UnionExpr; + + // [28] ExprToken not actually used + + //[29], [30], [31], + Literal = inner_node_d[ch_p('\"') >> token_node_d[*~ch_p('\"')] >> '\"'] + | inner_node_d[ch_p('\'') >> token_node_d[*~ch_p('\'')] >> '\'']; + Number = token_node_d[ch_p('.') >> Digits | Digits >> !('.' >> *Digits)]; + Digits = token_node_d[+digit_p]; + // [32] Operator not actually used + // [33] OperatorName not actually used + // [34], [35], [36], [37], [38], [39] + MultiplyOperator = ch_p('*'); + FunctionName = QName - NodeType; + VariableReference = token_node_d[ch_p('$') >> QName]; + NameTest = AnyName + | NCName >> discard_node_d[ch_p(':')] >> AnyName + | QName; + NodeType = Comment + | Text + | ProcessingInstruction + | Node; + + // These aren't correct to spec yet :) + S = *space_p; + QName = !(Prefix >> discard_node_d[ch_p(':')]) >> LocalPart; + Prefix = NCName; + LocalPart = NCName; + NCName = token_node_d[(alpha_p | '_') >> *NCNameChar]; + NCNameChar = alpha_p | digit_p | '.' | '-' | '_'; + + // things not defined in the spec, but which are just kind of handy :) + Slash = ch_p('/'); + SlashSlash = str_p("//"); + + AncestorOrSelf = str_p("ancestor-or-self"); + Ancestor = str_p("ancestor"); + Attribute = str_p("attribute"); + Child = str_p("child"); + DescendantOrSelf = str_p("descendant-or-self"); + Descendant = str_p("descendant"); + FollowingSibling = str_p("following-sibling"); + Following = str_p("following"); + Namespace = str_p("namespace"); + Parent = str_p("parent"); + PrecedingSibling = str_p("preceding-sibling"); + Preceding = str_p("preceding"); + Self = str_p("self"); + + Comment = str_p("comment"); + Text = str_p("text"); + ProcessingInstruction = str_p("processing-instruction"); + Node = str_p("node"); + AnyName = ch_p('*'); + + SelfSelect = ch_p('.'); + ParentSelect = str_p(".."); + + LeftSquare = ch_p('['); + RightSquare = ch_p(']'); + + LeftBracket = ch_p('('); + RightBracket = ch_p(')'); + + PlusOperator = ch_p('+'); + MinusOperator = ch_p('-'); + ModOperator = str_p("mod"); + DivOperator = str_p("div"); + EqualsOperator = ch_p('='); + NotEqualsOperator = str_p("!="); + LessThanOperator = ch_p('<'); + LessThanEqualsOperator = str_p("<="); + GreaterThanOperator = ch_p('>'); + GreaterThanEqualsOperator = str_p(">="); + + OrOperator = str_p("or"); + AndOperator = str_p("and"); + UnionOperator = ch_p('|'); + UnaryMinusOperator = ch_p('-'); + } // xpath_grammar_definition + + boost::spirit::rule > QName; + boost::spirit::rule > Prefix; + boost::spirit::rule > LocalPart; + boost::spirit::rule > NCName; + boost::spirit::rule > NCNameChar; + + boost::spirit::rule > AxisName; + boost::spirit::rule > NodeType; + + boost::spirit::rule > LocationPath; + boost::spirit::rule > AbsoluteLocationPath; + boost::spirit::rule > RelativeLocationPath; + + boost::spirit::rule > Step; + boost::spirit::rule > AxisSpecifier; + + boost::spirit::rule > NodeTest; + + boost::spirit::rule > Predicate; + boost::spirit::rule > PredicateExpr; + + boost::spirit::rule > AbbreviatedAbsoluteLocationPath; + boost::spirit::rule > AbbreviatedStep; + boost::spirit::rule > AbbreviatedAxisSpecifier; + + boost::spirit::rule > Expr; + boost::spirit::rule > PrimaryExpr; + + boost::spirit::rule > FunctionCall; + boost::spirit::rule > Argument; + + boost::spirit::rule > UnionExpr; + boost::spirit::rule > PathExpr; + boost::spirit::rule > FilterExpr; + + boost::spirit::rule > OrExpr; + boost::spirit::rule > AndExpr; + boost::spirit::rule > EqualityExpr; + boost::spirit::rule > RelationalExpr; + + boost::spirit::rule > AdditiveExpr; + boost::spirit::rule > MultiplicativeExpr; + boost::spirit::rule > UnaryExpr; + + boost::spirit::rule > Literal; + boost::spirit::rule > Number; + boost::spirit::rule > Digits; + boost::spirit::rule > MultiplyOperator; + boost::spirit::rule > FunctionName; + boost::spirit::rule > VariableReference; + boost::spirit::rule > NameTest; + boost::spirit::rule > S; // ExprWhitespace + + // bonus bits + boost::spirit::rule > Slash; + boost::spirit::rule > SlashSlash; + + boost::spirit::rule > AncestorOrSelf; + boost::spirit::rule > Ancestor; + boost::spirit::rule > Attribute; + boost::spirit::rule > Child; + boost::spirit::rule > DescendantOrSelf; + boost::spirit::rule > Descendant; + boost::spirit::rule > FollowingSibling; + boost::spirit::rule > Following; + boost::spirit::rule > Namespace; + boost::spirit::rule > Parent; + boost::spirit::rule > PrecedingSibling; + boost::spirit::rule > Preceding; + boost::spirit::rule > Self; + + boost::spirit::rule > Comment; + boost::spirit::rule > Text; + boost::spirit::rule > ProcessingInstruction; + boost::spirit::rule > Node; + boost::spirit::rule > AnyName; + + boost::spirit::rule > SelfSelect; + boost::spirit::rule > ParentSelect; + + boost::spirit::rule > LeftSquare; + boost::spirit::rule > RightSquare; + + boost::spirit::rule > LeftBracket; + boost::spirit::rule > RightBracket; + + boost::spirit::rule > PlusOperator; + boost::spirit::rule > MinusOperator; + boost::spirit::rule > ModOperator; + boost::spirit::rule > DivOperator; + boost::spirit::rule > EqualsOperator; + boost::spirit::rule > NotEqualsOperator; + boost::spirit::rule > LessThanOperator; + boost::spirit::rule > LessThanEqualsOperator; + boost::spirit::rule > GreaterThanOperator; + boost::spirit::rule > GreaterThanEqualsOperator; + + boost::spirit::rule > OrOperator; + boost::spirit::rule > AndOperator; + boost::spirit::rule > UnionOperator; + boost::spirit::rule > UnaryMinusOperator; +}; // xpath_grammar_definition + + +struct xpath_grammar : public boost::spirit::grammar +{ + template + struct definition : public xpath_grammar_definition + { + definition(xpath_grammar const& /* self */) + { + } // definition + + boost::spirit::rule > const& + start() const + { + return xpath_grammar_definition::LocationPath; + } // start + }; // definition +}; // xpath_grammar + +struct xpath_grammar_expr : public boost::spirit::grammar +{ + template + struct definition : public xpath_grammar_definition + { + definition(xpath_grammar_expr const& /* self */) + { + } // definition + + boost::spirit::rule > const& + start() const + { + return xpath_grammar_definition::Expr; + } // start + }; // definition +}; // xpath_grammar + +} // namespace XPath +} // namespace Arabica + +#endif diff --git a/XPath/impl/xpath_logical.hpp b/XPath/impl/xpath_logical.hpp new file mode 100644 index 00000000..77770146 --- /dev/null +++ b/XPath/impl/xpath_logical.hpp @@ -0,0 +1,53 @@ +#ifndef ARABICA_XPATHIC_XPATH_LOGICAL_HPP +#define ARABICA_XPATHIC_XPATH_LOGICAL_HPP + +#include "xpath_value.hpp" + +namespace Arabica +{ +namespace XPath +{ + +class OrOperator : private BinaryExpression, public XPathExpression +{ +public: + OrOperator(XPathExpression* lhs, XPathExpression* rhs) : + BinaryExpression(lhs, rhs) { } + + virtual XPathValuePtr evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + // From XPath 1.0 Rec, section 3.4 + // An or expression is evaluated by evaluating each operand and converting its value + // to a boolean as if by a call to the boolean function. The result is true if either + // value is true and false otherwise. The right operand is not evaluated if the + // left operand evaluates to true. + if(lhs()->evaluate(context, executionContext)->asBool()) + return BoolValue::createValue(true); + return BoolValue::createValue(rhs()->evaluate(context, executionContext)->asBool()); + } // evaluate +}; // class OrOperator + +class AndOperator : private BinaryExpression, public XPathExpression +{ +public: + AndOperator(XPathExpression* lhs, XPathExpression* rhs) : + BinaryExpression(lhs, rhs) { } + + virtual XPathValuePtr evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + // From XPath 1.0 Rec, section 3.4 + // An and expression is evaluated by evaluating each operand and converting its value + // to a boolean as if by a call to the boolean function. The result is true if both + // values are true and false otherwise. The right operand is not evaluated if the left + // operand evaluates to false. + if(!lhs()->evaluate(context, executionContext)->asBool()) + return BoolValue::createValue(false); + return BoolValue::createValue(rhs()->evaluate(context, executionContext)->asBool()); + } // evaluate +}; // class AndOperator + +} // namespace XPath +} // namespace Arabica +#endif diff --git a/XPath/impl/xpath_namespace_context.hpp b/XPath/impl/xpath_namespace_context.hpp new file mode 100644 index 00000000..d163563f --- /dev/null +++ b/XPath/impl/xpath_namespace_context.hpp @@ -0,0 +1,76 @@ +#ifndef ARABICA_XPATH_NAMESPACE_CONTEXT_HPP +#define ARABICA_XPATH_NAMESPACE_CONTEXT_HPP + +#include +#include +#include + +namespace Arabica +{ + +namespace XPath +{ + +class UnboundNamespacePrefixException : public std::runtime_error +{ +public: + UnboundNamespacePrefixException(const std::string& thing) : std::runtime_error("The prefix '" + thing + "' is not bound to a namespace URI") { } +}; // class UnboundNamespacePrefixException + +class NamespaceContext +{ + public: + NamespaceContext() { } + virtual ~NamespaceContext() = 0 { } + + virtual std::string namespaceURI(const std::string& prefix) const = 0; + + virtual NamespaceContext* clone() const = 0; + + private: + NamespaceContext(const NamespaceContext&); + NamespaceContext& operator=(const NamespaceContext&); + bool operator==(const NamespaceContext&) const; +}; // class NamespaceContext + +typedef boost::shared_ptr NamespaceContextPtr; + +class NullNamespaceContext : public NamespaceContext +{ + public: + virtual std::string namespaceURI(const std::string& prefix) const { throw UnboundNamespacePrefixException(prefix); } + virtual NamespaceContext* clone() const { return new NullNamespaceContext; } +}; // class NullNamespaceContext + +class StandardNamespaceContext : public NamespaceContext +{ + public: + StandardNamespaceContext() { } + ~StandardNamespaceContext() { } + + virtual std::string namespaceURI(const std::string& prefix) const + { + NSMap::const_iterator n = map_.find(prefix); + if(n == map_.end()) + throw UnboundNamespacePrefixException(prefix); + return (*n).second; + } // namespaceURI + + void addNamespaceDeclaration(const std::string& namespaceURI, const std::string& prefix) + { + map_[prefix] = namespaceURI; + } // addNamespaceDeclaration + + virtual StandardNamespaceContext* clone() const { return new StandardNamespaceContext(*this); } + + private: + StandardNamespaceContext(const StandardNamespaceContext& rhs) : map_(rhs.map_) { } + + typedef std::map NSMap; + NSMap map_; +}; // class StandardNamespaceContext + +} // namespace XPath + +} // namespace Arabica +#endif diff --git a/XPath/impl/xpath_namespace_node.hpp b/XPath/impl/xpath_namespace_node.hpp new file mode 100644 index 00000000..f09027dd --- /dev/null +++ b/XPath/impl/xpath_namespace_node.hpp @@ -0,0 +1,82 @@ +#ifndef ARABICA_XPATHIC_XPATH_NAMESPACE_NODE_HPP +#define ARABICA_XPATHIC_XPATH_NAMESPACE_NODE_HPP + +#pragma warning(disable: 4250) + +#include +#include +#include + +namespace Arabica +{ +namespace XPath +{ + +const DOM::Node_base::Type NAMESPACE_NODE_TYPE = static_cast(DOM::Node_base::MAX_TYPE + 27); // 27 is a random choice + +template > +class NamespaceNodeImpl : public SimpleDOM::ChildlessNodeImpl +{ + typedef SimpleDOM::ChildlessNodeImpl NodeT; + public: + NamespaceNodeImpl(stringT localname, + stringT value) : + SimpleDOM::ChildlessNodeImpl(0), + localname_(localname), + value_(value), + ref_(0) + { + this->setReadOnly(true); + } // NamespaceNodeImpl + + virtual ~NamespaceNodeImpl() { } + + /////////////////////////////////////////////////////// + // DOM::Node methods + virtual DOM::Node_base::Type getNodeType() const + { + return NAMESPACE_NODE_TYPE; + } // getNodeType + + virtual stringT getNodeName() const + { + return localname_; + } // getNodeName + + virtual stringT getNodeValue() const + { + return value_; + } // getNodeValue + + virtual stringT getLocalName() const + { + return localname_; + } // getNodeName + + virtual DOM::Node_impl* cloneNode(bool deep) const + { + return new NamespaceNodeImpl(localname_, value_); + } // cloneNode + + // not part of the document, so need to manage our own lifetime + virtual void addRef() + { + ++ref_; + } // addRef + + virtual void releaseRef() + { + if(!(--ref_)) + delete this; + } // releaseRef + +private: + stringT localname_; + stringT value_; + unsigned int ref_; +}; // class NamespaceNodeImpl + +} // namespace XPath +} // namespace Arabica + +#endif diff --git a/XPath/impl/xpath_node_test.hpp b/XPath/impl/xpath_node_test.hpp new file mode 100644 index 00000000..c56012cd --- /dev/null +++ b/XPath/impl/xpath_node_test.hpp @@ -0,0 +1,151 @@ +#ifndef ARABICA_XPATHIC_XPATH_NODE_TEST_HPP +#define ARABICA_XPATHIC_XPATH_NODE_TEST_HPP + +#include +#include "xpath_namespace_node.hpp" +#include + +namespace Arabica +{ +namespace XPath +{ + +class NodeTest +{ +protected: + NodeTest() { } +public: + virtual ~NodeTest() { } + virtual bool operator()(const DOM::Node& node) const = 0; + +private: + NodeTest(NodeTest&); + bool operator==(const NodeTest&); + NodeTest& operator=(const NodeTest&); +}; // class NodeTest + +class AnyNodeTest : public NodeTest +{ +public: + virtual bool operator()(const DOM::Node& node) const + { + return true; + } // matches +}; // class AnyNodeTest + +class NameNodeTest : public NodeTest +{ +public: + NameNodeTest(const std::string& name) : name_(name) { } + + virtual bool operator()(const DOM::Node& node) const + { + return (name_ == node.getNodeName()) && + (node.getPrefix().empty()); + } // test + +private: + std::string name_; +}; // NameNodeTest + +class QNameNodeTest : public NodeTest +{ +public: + QNameNodeTest(const std::string& namespace_uri, const std::string& name) : uri_(namespace_uri), name_(name) { } + + virtual bool operator()(const DOM::Node& node) const + { + return (name_ == node.getLocalName()) && + (uri_ == node.getNamespaceURI()); + } // test + +private: + std::string uri_; + std::string name_; +}; // QNameNodeTest + +class StarNodeTest : public NodeTest +{ +public: + virtual bool operator()(const DOM::Node& node) const + { + // match the primary node types on the various axis + // fortunately they are all independent + int type = node.getNodeType(); + return (type == DOM::Node_base::ELEMENT_NODE || + type == NAMESPACE_NODE_TYPE || + type == DOM::Node_base::ATTRIBUTE_NODE); + } // test +}; // class StarNodeTest + +class QStarNodeTest : public NodeTest +{ +public: + QStarNodeTest(const std::string& namespace_uri) : uri_(namespace_uri) { } + + virtual bool operator()(const DOM::Node& node) const + { + return (uri_ == node.getNamespaceURI()); + } // test + +private: + std::string uri_; +}; // clase QStarNodeTest + +class TextNodeTest : public NodeTest +{ +public: + virtual bool operator()(const DOM::Node& node) const + { + return node.getNodeType() == DOM::Node::TEXT_NODE; + } // test +}; // class TextNodeTest + +class CommentNodeTest : public NodeTest +{ +public: + virtual bool operator()(const DOM::Node& node) const + { + return node.getNodeType() == DOM::Node::COMMENT_NODE; + } // operator() +}; // CommentNodeTest + +class ProcessingInstructionNodeTest : public NodeTest +{ +public: + ProcessingInstructionNodeTest() : target_() { } + ProcessingInstructionNodeTest(const std::string& target) : target_(target) { } + + virtual bool operator()(const DOM::Node& node) const + { + if(node.getNodeType() != DOM::Node::PROCESSING_INSTRUCTION_NODE) + return false; + + if(target_.empty()) + return true; + + return node.getNodeName() == target_; + } // test + +private: + std::string target_; +}; // ProcessingInstructionNodeTest + +class RootNodeTest : public NodeTest +{ +public: + virtual bool operator()(const DOM::Node& node) const + { + int type = node.getNodeType(); + return (type == DOM::Node::DOCUMENT_NODE) || + (type == DOM::Node::DOCUMENT_FRAGMENT_NODE); + + } // operator() +}; // RootNodeTest + +} // namespace XPath + + +} // namespace Arabica + +#endif diff --git a/XPath/impl/xpath_object.hpp b/XPath/impl/xpath_object.hpp new file mode 100644 index 00000000..0ada956a --- /dev/null +++ b/XPath/impl/xpath_object.hpp @@ -0,0 +1,210 @@ +#ifndef ARABICA_XPATHIC_XPATH_OBJECT_H +#define ARABICA_XPATHIC_XPATH_OBJECT_H + +#include +#include +#include +#include +#include +#include +#include "xpath_execution_context.hpp" + +namespace Arabica +{ +namespace XPath +{ + +class XPathExpression; +typedef boost::shared_ptr XPathExpressionPtr; +class XPathValue; +typedef boost::shared_ptr XPathValuePtr; + +enum ValueType +{ + ANY , + BOOL, + NUMBER, + STRING, + NODE_SET +}; // ValueType + +int compareNodes(const DOM::Node& n1, const DOM::Node& n2); +bool nodes_less_than(const DOM::Node& n1, const DOM::Node& n2); + +class NodeSet : public std::vector > +{ +public: + NodeSet() : forward_(true), sorted_(false), std::vector >() { } + NodeSet(bool forward) : forward_(forward), sorted_(true), std::vector >() { } + NodeSet(const NodeSet& rhs) : forward_(rhs.forward_), sorted_(rhs.sorted_), std::vector >(rhs) { } + NodeSet& operator=(const NodeSet& rhs) + { + forward_ = rhs.forward_; + sorted_ = rhs.sorted_; + std::vector >::operator=(rhs); + return *this; + } // operator= + + void swap(NodeSet& rhs) + { + std::vector >::swap(rhs); + std::swap(forward_, rhs.forward_); + std::swap(sorted_, rhs.sorted_); + } // swap + + bool forward() const { return sorted_ && forward_; } + bool reverse() const { return sorted_ && !forward_; } + void forward(bool forward) { forward_ = forward; sorted_ = true; } + + void to_document_order() + { + if(!sorted_) + { + std::sort(begin(), end(), nodes_less_than); + sorted_ = true; + forward_ = true; + } // if(!sorted) + + if(!forward_) + { + std::reverse(begin(), end()); + forward_ = true; + } // if(!forward_) + } // to_document_order + + DOM::Node top() const + { + if(forward_) + return (*this)[0]; + return (*this)[size()-1]; + } // top() + +private: + bool forward_; + bool sorted_; +}; // NodeSet + +class XPathValue +{ +protected: + XPathValue() { } + +public: + virtual ~XPathValue() { } + + virtual bool asBool() const = 0; + virtual double asNumber() const = 0; + virtual std::string asString() const = 0; + virtual const NodeSet& asNodeSet() const = 0; + + virtual ValueType type() const = 0; + +private: + XPathValue(const XPathValue&); + bool operator==(const XPathValue&); + XPathValue& operator=(const XPathValue&); +}; // class XPathValue + +const double NaN = std::sqrt(-2.0); +const double Zero = 0.0; +const double Negative_Zero = -Zero; +const double Infinity = HUGE_VAL; +const double Negative_Infinity = -Infinity; + +inline bool isNaN(double value) { return (value != value); } +inline bool isInfinity(double value) { return (value == Infinity); } +inline bool isNegativeInfinity(double value) { return (value == Negative_Infinity); } +inline bool isInfinite(double value) { return isInfinity(value) || isNegativeInfinity(value); } + +inline double roundNumber(double value) +{ + if(!(isNaN(value) || isInfinite(value) || (std::fabs(value) == 0))) + if((value < 0.0) && (value > -0.5)) + value = -0.0; + else + value = std::floor(value + 0.5); + return value; +} // roundNumber + +double stringAsNumber(const std::string& str); +double nodeNumberValue(const DOM::Node& node); +std::string nodeStringValue(const DOM::Node& node); + +bool areEqual(const XPathValuePtr& lhs, const XPathValuePtr& rhs); +bool isLessThan(const XPathValuePtr& lhs, const XPathValuePtr& rhs); +bool isLessThanEquals(const XPathValuePtr& lhs, const XPathValuePtr& rhs); +bool isGreaterThan(const XPathValuePtr& lhs, const XPathValuePtr& rhs); +bool isGreaterThanEquals(const XPathValuePtr& lhs, const XPathValuePtr& rhs); + + +class XPathExpression +{ +protected: + XPathExpression() { } + +public: + virtual ~XPathExpression() { } + + XPathValuePtr evaluate(const DOM::Node& context) const + { + ExecutionContext executionContext; + return evaluate(context, executionContext); + } // evaluate + + virtual bool evaluateAsBool(const DOM::Node& context) const { return evaluate(context)->asBool(); } + virtual double evaluateAsNumber(const DOM::Node& context) const { return evaluate(context)->asNumber(); } + virtual std::string evaluateAsString(const DOM::Node& context) const { return evaluate(context)->asString(); } + virtual NodeSet evaluateAsNodeSet(const DOM::Node& context) const { return evaluate(context)->asNodeSet(); } + + virtual XPathValuePtr evaluate(const DOM::Node& context, + const Arabica::XPath::ExecutionContext& executionContext) const = 0; + +private: + XPathExpression(const XPathExpression&); + bool operator==(const XPathExpression&); + XPathExpression& operator=(const XPathExpression&); +}; // class XPathExpression + +class UnaryExpression +{ +public: + UnaryExpression(XPathExpression* expr) : + expr_(expr) { } + +protected: + ~UnaryExpression() + { + delete expr_; + } // ~UnaryExpression + + XPathExpression* expr() const { return expr_; } + +private: + XPathExpression* expr_; +}; // class UnaryExpression + +class BinaryExpression +{ +public: + BinaryExpression(XPathExpression* lhs, XPathExpression* rhs) : + lhs_(lhs), rhs_(rhs) { } + +protected: + ~BinaryExpression() + { + delete lhs_; + delete rhs_; + } // ~BinaryExpression + + XPathExpression* lhs() const { return lhs_; } + XPathExpression* rhs() const { return rhs_; } + +private: + XPathExpression* lhs_; + XPathExpression* rhs_; +}; // class BinaryExpression + +} // namespace XPath +} // namespace Arabica + +#endif diff --git a/XPath/impl/xpath_parser.hpp b/XPath/impl/xpath_parser.hpp new file mode 100644 index 00000000..c1a5cd5f --- /dev/null +++ b/XPath/impl/xpath_parser.hpp @@ -0,0 +1,97 @@ +#ifndef ARABICA_XPATHIC_XPATH_PARSER_HPP +#define ARABICA_XPATHIC_XPATH_PARSER_HPP + +#include +#include +#include +#include +#include "xpath_object.hpp" +#include "xpath_ast.hpp" +#include "xpath_grammar.hpp" +#include "xpath_namespace_context.hpp" +#include "xpath_function_resolver.hpp" +#include "xpath_resolver_holder.hpp" + +namespace Arabica +{ +namespace XPath +{ + +class SyntaxException : public std::runtime_error +{ +public: + SyntaxException(const std::string& thing) : std::runtime_error("Bad XPath: " + thing) { } +}; // class SyntaxException + +class RuntimeException : public std::runtime_error +{ +public: + RuntimeException(const std::string& thing) : std::runtime_error("Cannot evaluate XPath: " + thing) { } +}; // class RuntimeException + +class UnsupportedException : public std::runtime_error +{ +public: + UnsupportedException(const std::string& thing) : std::runtime_error("Sorry, haven't implemented '" + thing + "' yet") { } +}; // class UnsupportedException + +class CompilationContext; + +class XPath +{ +public: + XPath(); + ~XPath(); + + XPathExpressionPtr compile(const std::string& xpath) const; + XPathExpressionPtr compile_expr(const std::string& xpath) const; + + XPathValuePtr evaluate(const std::string& xpath, const DOM::Node& context) const; + XPathValuePtr evaluate_expr(const std::string& xpath, const DOM::Node& context) const; + + void setNamespaceContext(const NamespaceContext& namespaceContext) { namespaceContext_.set(namespaceContext); } + void setNamespaceContext(NamespaceContextPtr namespaceContext) { namespaceContext_.set(namespaceContext); } + const NamespaceContext& getNamespaceContext() const { return namespaceContext_.get(); } + void resetNamespaceContext() { namespaceContext_.set(NamespaceContextPtr(new NullNamespaceContext())); } + + void setVariableResolver(const VariableResolver& variableResolver) { variableResolver_.set(variableResolver); } + void setVariableResolver(VariableResolverPtr variableResolver) { variableResolver_.set(variableResolver); } + const VariableResolver& getVariableResolver() const { return variableResolver_.get(); } + void resetVariableResolver() { variableResolver_.set(VariableResolverPtr(new NullVariableResolver())); } + + void setFunctionResolver(const FunctionResolver& functionResolver) { functionResolver_.set(functionResolver); } + void setFunctionResolver(FunctionResolverPtr functionResolver) { functionResolver_.set(functionResolver); } + const FunctionResolver& getFunctionResolver() const { return functionResolver_.get(); } + void resetFunctionResolver() { functionResolver_.set(FunctionResolverPtr(new NullFunctionResolver())); } + +private: + XPathExpressionPtr do_compile(const std::string& xpath, tree_info_t(XPath::*fn)(const std::string& str) const) const; + tree_info_t parse_xpath(const std::string& str) const; + tree_info_t parse_xpath_expr(const std::string& str) const; + + xpath_grammar xpathg_; + + ResolverHolder namespaceContext_; + ResolverHolder variableResolver_; + ResolverHolder functionResolver_; + + typedef XPathExpression* (*compileFn)(node_iter_t const& i, CompilationContext& context); + static std::map factory_; + static std::map names_; + static const std::map createFunctions(); + static const std::map debugNames(); + static void dump(node_iter_t const& i, int depth); + + friend XPathExpression* Arabica::XPath::compile_expression(node_iter_t const& i, CompilationContext& context); + + + XPath(const XPath&); + XPath& operator=(const XPath&); + bool operator==(const XPath&) const; +}; // class XPath + +} // namespace XPath + +} // namespace Arabica + +#endif diff --git a/XPath/impl/xpath_relational.hpp b/XPath/impl/xpath_relational.hpp new file mode 100644 index 00000000..7e72008f --- /dev/null +++ b/XPath/impl/xpath_relational.hpp @@ -0,0 +1,97 @@ +#ifndef ARABICA_XPATHIC_XPATH_RELATIONAL_HPP +#define ARABICA_XPATHIC_XPATH_RELATIONAL_HPP + +#include "xpath_value.hpp" + +namespace Arabica +{ +namespace XPath +{ + +class EqualsOperator : private BinaryExpression, public XPathExpression +{ +public: + EqualsOperator(XPathExpression* lhs, XPathExpression* rhs) : + BinaryExpression(lhs, rhs) { } + + virtual XPathValuePtr evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return BoolValue::createValue(areEqual(lhs()->evaluate(context, executionContext), + rhs()->evaluate(context, executionContext))); + } // evaluate +}; // class EqualsOperator + +class NotEqualsOperator : private BinaryExpression, public XPathExpression +{ +public: + NotEqualsOperator(XPathExpression* lhs, XPathExpression* rhs) : + BinaryExpression(lhs, rhs) { } + + virtual XPathValuePtr evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return BoolValue::createValue(!areEqual(lhs()->evaluate(context, executionContext), + rhs()->evaluate(context, executionContext))); + } // evaluate +}; // class NotEqualsOperator + +class LessThanOperator : private BinaryExpression, public XPathExpression +{ +public: + LessThanOperator(XPathExpression* lhs, XPathExpression* rhs) : + BinaryExpression(lhs, rhs) { } + + virtual XPathValuePtr evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return BoolValue::createValue(isLessThan(lhs()->evaluate(context, executionContext), + rhs()->evaluate(context, executionContext))); + } // evaluate +}; // class LessThanOperator + +class LessThanEqualsOperator : private BinaryExpression, public XPathExpression +{ +public: + LessThanEqualsOperator(XPathExpression* lhs, XPathExpression* rhs) : + BinaryExpression(lhs, rhs) { } + + virtual XPathValuePtr evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return BoolValue::createValue(isLessThanEquals(lhs()->evaluate(context, executionContext), + rhs()->evaluate(context, executionContext))); + } // evaluate +}; // class LessThanEqualsOperator + +class GreaterThanOperator : private BinaryExpression, public XPathExpression +{ +public: + GreaterThanOperator(XPathExpression* lhs, XPathExpression* rhs) : + BinaryExpression(lhs, rhs) { } + + virtual XPathValuePtr evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return BoolValue::createValue(isGreaterThan(lhs()->evaluate(context, executionContext), + rhs()->evaluate(context, executionContext))); + } // evaluate +}; // class GreaterThanOperator + +class GreaterThanEqualsOperator : private BinaryExpression, public XPathExpression +{ +public: + GreaterThanEqualsOperator(XPathExpression* lhs, XPathExpression* rhs) : + BinaryExpression(lhs, rhs) { } + + virtual XPathValuePtr evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return BoolValue::createValue(isGreaterThanEquals(lhs()->evaluate(context, executionContext), + rhs()->evaluate(context, executionContext))); + } // evaluate +}; // class GreaterThanEqualsOperator + +} // namespace XPath +} // namespace Arabica +#endif diff --git a/XPath/impl/xpath_resolver_holder.hpp b/XPath/impl/xpath_resolver_holder.hpp new file mode 100644 index 00000000..5ff82bd2 --- /dev/null +++ b/XPath/impl/xpath_resolver_holder.hpp @@ -0,0 +1,69 @@ +#ifndef ARABICA_XPATH_RESOLVER_HOLDER_HPP +#define ARABICA_XPATH_RESOLVER_HOLDER_HPP + +#include + +namespace Arabica +{ +namespace XPath +{ + +template +class ResolverHolder +{ +public: + typedef boost::shared_ptr ResolverPtrT; + + ResolverHolder() : + unowned_(0), + shared_() + { + } // ResolverHolder + + ResolverHolder(const ResolverHolder& rhs) : + unowned_(rhs.unowned_), + shared_(rhs.shared_) + { + } // ResolverHolder + + ResolverHolder& operator=(const ResolverHolder& rhs) + { + if(rhs == *this) + return *this; + + unowned_ = rhs_.unowned_; + shared_ = rhs_.shared_; + + return *this; + } // operator= + + ResolverT& get() const + { + if(unowned_) + return *unowned_; + return *(shared_.get()); + } // get() + + void set(ResolverT& resolver) + { + unowned_ = &resolver; + shared_.reset(); + } // set + + void set(ResolverPtrT resolver) + { + unowned_ = 0; + shared_ = resolver; + } // set + +private: + ResolverT* unowned_; + ResolverPtrT shared_; + + bool operator==(const ResolverHolder&); +}; // ResolverHolder + +} // namespace XPath +} // namespace Arabica + +#endif diff --git a/XPath/impl/xpath_step.hpp b/XPath/impl/xpath_step.hpp new file mode 100644 index 00000000..9c7320e5 --- /dev/null +++ b/XPath/impl/xpath_step.hpp @@ -0,0 +1,416 @@ +#ifndef ARABICA_XPATHIC_XPATH_STEP_H +#define ARABICA_XPATHIC_XPATH_STEP_H + +#include +#include +#include "xpath_object.hpp" +#include "xpath_value.hpp" +#include "xpath_axis_enumerator.hpp" +#include "xpath_node_test.hpp" +#include "xpath_ast.hpp" +#include "xpath_ast_ids.hpp" +#include "xpath_namespace_context.hpp" +#include "xpath_compile_context.hpp" + +namespace Arabica +{ +namespace XPath +{ + +class StepExpression : public XPathExpression +{ +public: + StepExpression() { } + StepExpression(std::vector predicates) : predicates_(predicates) { } + + virtual ~StepExpression() + { + for(std::vector::iterator p = predicates_.begin(), e = predicates_.end(); p != e; ++p) + delete *p; + } // ~StepExpression + + virtual XPathValuePtr evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const = 0; + virtual XPathValuePtr evaluate(NodeSet& context, const ExecutionContext& executionContext) const = 0; + + bool has_predicates() const { return !predicates_.empty(); } + +protected: + NodeSet applyPredicates(NodeSet& nodes, const ExecutionContext& parentContext) const + { + for(std::vector::const_iterator p = predicates_.begin(), e = predicates_.end(); + (p != e) && (!nodes.empty()); ++p) + nodes = applyPredicate(nodes, *p, parentContext); + return nodes; + } // applyPredicates + +private: + NodeSet applyPredicate(NodeSet& nodes, XPathExpression* predicate, const ExecutionContext& parentContext) const + { + ExecutionContext executionContext(nodes.size(), parentContext); + NodeSet results(nodes.forward()); + unsigned int position = 1; + for(NodeSet::iterator i = nodes.begin(); i != nodes.end(); ++i, ++position) + { + executionContext.setPosition(position); + XPathValuePtr v = predicate->evaluate(*i, executionContext); + + if((v->type() == NUMBER) && (position != v->asNumber())) + continue; + if(v->asBool() == false) + continue; + + results.push_back(*i); + } // for ... + return results; + } // applyPredicate + + std::vector predicates_; +}; // StepExpression + +class TestStepExpression : public StepExpression +{ +public: + TestStepExpression(Axis axis, NodeTest* test) : + StepExpression(), + axis_(axis), + test_(test) + { + } // TestStepExpression + + TestStepExpression(Axis axis, NodeTest* test, std::vector predicates) : + StepExpression(predicates), + axis_(axis), + test_(test) + { + } // TestStepExpression + + virtual ~TestStepExpression() + { + delete test_; + } // StepExpression + + virtual XPathValuePtr evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const + { + NodeSet nodes; + enumerateOver(context, nodes, executionContext); + return XPathValuePtr(new NodeSetValue(nodes)); + } // evaluate + + virtual XPathValuePtr evaluate(NodeSet& context, const ExecutionContext& executionContext) const + { + NodeSet nodes; + for(NodeSet::iterator n = context.begin(); n != context.end(); ++n) + enumerateOver(*n, nodes, executionContext); + return XPathValuePtr(new NodeSetValue(nodes)); + } // evaluate + +private: + void enumerateOver(const DOM::Node& context, NodeSet& results, + const ExecutionContext& parentContext) const + { + AxisEnumerator enumerator(context, axis_); + NodeSet intermediate(enumerator.forward()); + NodeSet& d = (!has_predicates()) ? results : intermediate; + while(*enumerator != 0) + { + // if test + DOM::Node node = *enumerator; + if((*test_)(node)) + d.push_back(node); + ++enumerator; + } // while ... + + if(!has_predicates()) + { + results.forward(enumerator.forward()); + return; + } // if ... + + intermediate = applyPredicates(intermediate, parentContext); + + results.swap(intermediate); + } // enumerateOver + + Axis axis_; + NodeTest* test_; +}; // class TestStepExpression + +class ExprStepExpression : public StepExpression +{ +public: + ExprStepExpression(XPathExpression* expr, std::vector predicates) : + StepExpression(predicates), + expr_(expr) + { + } // ExprStepExpression + + virtual ~ExprStepExpression() + { + delete expr_; + } // ExprStepExpression + + virtual XPathValuePtr evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const + { + if(!has_predicates()) + return expr_->evaluate(context, executionContext); + + NodeSet ns = expr_->evaluate(context, executionContext)->asNodeSet(); + return XPathValuePtr(new NodeSetValue(applyPredicates(ns, executionContext))); + } // evaluate + + virtual XPathValuePtr evaluate(NodeSet& context, const ExecutionContext& executionContext) const + { + DOM::Node c = context.top(); + return evaluate(c, executionContext); + } // evaluate + +private: + XPathExpression* expr_; + std::vector predicates_; +}; // class ExprStepExpression + +class StepFactory +{ +public: + static StepExpression* createStep(node_iter_t& node, node_iter_t const& end, CompilationContext& context) + { + Axis axis = getAxis(node); + NodeTest* test = getTest(node, context.namespaceContext()); + XPathExpression* thing = 0; + if(!test) + thing = compile_expression(node++, context); + + std::vector preds; + + while((node != end) && (getNodeId(node) == Predicate_id)) + { + node_iter_t c = node->children.begin(); + assert(getNodeId(c) == LeftSquare_id); + ++c; + preds.push_back(compile_expression(c, context)); + ++c; + assert(getNodeId(c) == RightSquare_id); + + ++node; + } // if ... + if(!test) + return new ExprStepExpression(thing, preds); + return new TestStepExpression(axis, test, preds); + } // createStep + + static StepExpression* createStep(node_iter_t& node, CompilationContext& context) + { + Axis axis = getAxis(node); + NodeTest* test = getTest(node, context.namespaceContext()); + return new TestStepExpression(axis, test); + } // createStep + +private: + static Axis getAxis(node_iter_t& node) + { + long id = getNodeId(node); + + switch(id) + { + case Slash_id: + case SelfSelect_id: + return SELF; // don't advance node, SelfSelect is axis specifier and node test in one + case ParentSelect_id: + return PARENT; + case SlashSlash_id: + return DESCENDANT_OR_SELF; + + case AbbreviatedAxisSpecifier_id: + ++node; + return ATTRIBUTE; + + case AxisSpecifier_id: + // skip on to the next bit + break; + + default: + return CHILD; + } // switch(id) + + node_iter_t axis_node = node->children.begin(); + long axis = getNodeId(skipWhitespace(axis_node)); + ++node; + switch(axis) + { + case AncestorOrSelf_id: + return ANCESTOR_OR_SELF; + case Ancestor_id: + return ANCESTOR; + case AbbreviatedAxisSpecifier_id: + case Attribute_id: + return ATTRIBUTE; + case Child_id: + return CHILD; + case DescendantOrSelf_id: + return DESCENDANT_OR_SELF; + case Descendant_id: + return DESCENDANT; + case FollowingSibling_id: + return FOLLOWING_SIBLING; + case Following_id: + return FOLLOWING; + case Namespace_id: + return NAMESPACE; + case Parent_id: + return PARENT; + case PrecedingSibling_id: + return PRECEDING_SIBLING; + case Preceding_id: + return PRECEDING; + case Self_id: + return SELF; + } // switch ... + + assert(false); + return CHILD; + } // getAxis + + static NodeTest* getTest(node_iter_t& node, const NamespaceContext& namespaceContext) + { + long id = getNodeId(skipWhitespace(node)); + + switch(id) + { + case NodeTest_id: + { + node_iter_t c = node->children.begin(); + NodeTest* t = getTest(c, namespaceContext); + ++node; + return t; + } // case NodeTest_id + + case QName_id: + { + node_iter_t c = node->children.begin(); + std::string prefix(c->value.begin(), c->value.end()); + std::string uri = namespaceContext.namespaceURI(prefix); + ++c; + std::string name(c->value.begin(), c->value.end()); + ++node; + return new QNameNodeTest(uri, name); + } //case QName_id + + case NCName_id: + { + std::string name(node->value.begin(), node->value.end()); + ++node; + return new NameNodeTest(name); + } // case NameNodeTest + + case Comment_id: + { + ++node; + return new CommentNodeTest(); + } // case CommentTest_id + + case Text_id: + { + ++node; + return new TextNodeTest(); + } // case Text_id + + case ProcessingInstruction_id: + { + ++node; + if(getNodeId(node) != Literal_id) // not sure if this is always safe + return new ProcessingInstructionNodeTest(); + + std::string target(node->value.begin(), node->value.end()); + ++node; + return new ProcessingInstructionNodeTest(target); + } // case ProcessingInstruction_id + + case SlashSlash_id: + case Node_id: + { + ++node; + return new AnyNodeTest(); + } // case Node_id + + case Slash_id: + return new RootNodeTest(); + + case AnyName_id: + case SelfSelect_id: + case ParentSelect_id: + { + ++node; + return new StarNodeTest(); + } // case AnyName_id: + + case NameTest_id: + { + node_iter_t prefixNode = node->children.begin(); + ++node; + std::string prefix(prefixNode->value.begin(), prefixNode->value.end()); + std::string uri = namespaceContext.namespaceURI(prefix); + return new QStarNodeTest(uri); + } // case + } // switch(id) + + return 0; + } // getTest + + StepFactory(); +}; // class StepFactory + +class RelativeLocationPath : public XPathExpression +{ +public: + typedef std::vector StepList; + +public: + RelativeLocationPath(StepExpression* step) : steps_() { steps_.push_back(step); } + RelativeLocationPath(const StepList& steps) : steps_(steps) { } + + virtual ~RelativeLocationPath() + { + for(StepList::const_iterator i = steps_.begin(); i != steps_.end(); ++i) + delete *i; + } // ~LocationPath + + virtual XPathValuePtr evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const + { + NodeSet nodes; + nodes.push_back(context); + + for(StepList::const_iterator i = steps_.begin(); i != steps_.end(); ++i) + { + XPathValuePtr v = (*i)->evaluate(nodes, executionContext); + nodes = v->asNodeSet(); + } // for ... + + return XPathValuePtr(new NodeSetValue(nodes)); + } // do_evaluate + +private: + StepList steps_; +}; // LocationPath + +class AbsoluteLocationPath : public RelativeLocationPath +{ +public: + AbsoluteLocationPath(StepExpression* step) : RelativeLocationPath(step) { } + AbsoluteLocationPath(const RelativeLocationPath::StepList& steps) : RelativeLocationPath(steps) { } + + virtual XPathValuePtr evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const + { + int type = context.getNodeType(); + if((type == DOM::Node::DOCUMENT_NODE) || + (type == DOM::Node::DOCUMENT_FRAGMENT_NODE)) + return RelativeLocationPath::evaluate(context, executionContext); + + DOM::Document document = context.getOwnerDocument(); + return RelativeLocationPath::evaluate(document, executionContext); + } // evaluate +}; // class AbsoluteLocationPath + + +} // XPath +} // Arabica +#endif diff --git a/XPath/impl/xpath_union.hpp b/XPath/impl/xpath_union.hpp new file mode 100644 index 00000000..aec76f42 --- /dev/null +++ b/XPath/impl/xpath_union.hpp @@ -0,0 +1,75 @@ +#ifndef ARABICA_XPATHIC_XPATH_UNION_HPP +#define ARABICA_XPATHIC_XPATH_UNION_HPP + +#include "xpath_value.hpp" +#include + +namespace Arabica +{ +namespace XPath +{ + +class UnionExpression : private BinaryExpression, public XPathExpression +{ +public: + UnionExpression(XPathExpression* lhs, XPathExpression* rhs) : + BinaryExpression(lhs, rhs) { } + + virtual XPathValuePtr evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + XPathValuePtr p1 = lhs()->evaluate(context, executionContext); + if(p1->type() != NODE_SET) + throw RuntimeException("Union operator joins node-sets. First argument is not a node-set."); + XPathValuePtr p2 = rhs()->evaluate(context, executionContext); + if(p2->type() != NODE_SET) + throw RuntimeException("Union operator joins node-sets. First argument is not a node-set."); + + NodeSet ns1(p1->asNodeSet()); + NodeSet ns2(p2->asNodeSet()); + + // do the obvious optimizations + if(ns1.empty()) + return wrap(ns2); + if(ns2.empty()) + return wrap(ns1); + + ns1.to_document_order(); + ns2.to_document_order(); + NodeSet::const_iterator n1 = ns1.begin(), n1e = ns1.end(); + NodeSet::const_iterator n2 = ns2.begin(), n2e = ns2.end(); + + NodeSet result(true); + + while((n1 != n1e) && (n2 != n2e)) + { + int c = compareNodes(*n1, *n2); + if(c < 0) + result.push_back(*n1++); + else if(c > 0) + result.push_back(*n2++); + else + { // same node, so bin out duplicate + result.push_back(*n1++); + ++n2; + } // if ... + } // while ... + + // pop on any left overs, leftovers + std::copy(n1, n1e, std::back_inserter(result)); + std::copy(n2, n2e, std::back_inserter(result)); + + return wrap(result); + } // evaluate + +private: + XPathValuePtr wrap(const NodeSet& ns) const + { + return XPathValuePtr(new NodeSetValue(ns)); + } // wrap +}; // UnionExpression + +} // namespace XPath + +} // namespace Arabica +#endif diff --git a/XPath/impl/xpath_value.hpp b/XPath/impl/xpath_value.hpp new file mode 100644 index 00000000..217e44f9 --- /dev/null +++ b/XPath/impl/xpath_value.hpp @@ -0,0 +1,148 @@ +#ifndef ARABICA_XPATHIC_XPATH_VALUE_H +#define ARABICA_XPATHIC_XPATH_VALUE_H + +#include +#include +#include +#include +#include "xpath_object.hpp" + +namespace Arabica +{ +namespace XPath +{ + +class BoolValue : public XPathValue, public XPathExpression +{ +public: + BoolValue(bool value) : + value_(value) { } + + static XPathValuePtr createValue(bool value) { return XPathValuePtr(new BoolValue(value)); } + + virtual XPathValuePtr evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const + { + return XPathValuePtr(new BoolValue(value_)); + } // evaluate + virtual bool evaluateAsBool(const DOM::Node& context) { return asBool(); } + virtual double evaluateAsNumber(const DOM::Node& context) { return asNumber(); } + virtual std::string evaluateAsString(const DOM::Node& context) { return asString(); } + virtual NodeSet evaluateAsNodeSet(const DOM::Node& context) { return asNodeSet(); } + + virtual bool asBool() const { return value_; } + virtual double asNumber() const { return value_ ? 1 : 0; } + virtual std::string asString() const { return value_ ? "true" : "false"; } + virtual const NodeSet& asNodeSet() const { static NodeSet empty; return empty; } + + virtual ValueType type() const { return BOOL; } + +private: + bool value_; +}; // class BoolValue + +class NumericValue : public XPathValue, public XPathExpression +{ +public: + NumericValue(double value) : + value_(value) { } + + static XPathValuePtr createValue(double value) { return XPathValuePtr(new NumericValue(value)); } + + virtual XPathValuePtr evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const + { + return createValue(value_); + } // evaluate + virtual bool evaluateAsBool(const DOM::Node& context) { return asBool(); } + virtual double evaluateAsNumber(const DOM::Node& context) { return asNumber(); } + virtual std::string evaluateAsString(const DOM::Node& context) { return asString(); } + virtual NodeSet evaluateAsNodeSet(const DOM::Node& context) { return asNodeSet(); } + + virtual bool asBool() const { return (value_ != 0.0); } + virtual double asNumber() const { return value_; } + virtual std::string asString() const + { + if(isNaN(value_)) + return "NaN"; + if(isInfinity(value_)) + return "Infinity"; + if(isNegativeInfinity(value_)) + return "-Infinity"; + return boost::lexical_cast(value_); + } // asString + virtual const NodeSet& asNodeSet() const { static NodeSet empty; return empty; } + + virtual ValueType type() const { return NUMBER; } + +private: + double value_; +}; // class NumberValue + +class StringValue : public XPathValue, public XPathExpression +{ +public: + StringValue(const char* value) : + value_(value) { } + StringValue(const std::string& value) : + value_(value) { } + + virtual XPathValuePtr evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const + { + return XPathValuePtr(new StringValue(value_)); + } // evaluate + virtual bool evaluateAsBool(const DOM::Node& context) { return asBool(); } + virtual double evaluateAsNumber(const DOM::Node& context) { return asNumber(); } + virtual std::string evaluateAsString(const DOM::Node& context) { return asString(); } + virtual NodeSet evaluateAsNodeSet() const { return asNodeSet(); } + + virtual bool asBool() const { return !value_.empty(); } + virtual double asNumber() const + { + return stringAsNumber(value_); + } // asNumber + virtual std::string asString() const { return value_; } + virtual const NodeSet& asNodeSet() const { static NodeSet empty; return empty; } + + virtual ValueType type() const { return STRING; } + +private: + std::string value_; +}; // class StringValue + +class NodeSetValue : public XPathValue, public XPathExpression +{ +public: + NodeSetValue(const NodeSet& set) : set_(set) { } + + virtual XPathValuePtr evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const + { + return XPathValuePtr(this); + } // evaluate + virtual bool evaluateAsBool(const DOM::Node& context) const{ return asBool(); } + virtual double evaluateAsNumber(const DOM::Node& context) const { return asNumber(); } + virtual std::string evaluateAsString(const DOM::Node& context) const { return asString(); } + virtual const NodeSet& evaluateAsNodeSet() const { return asNodeSet(); } + + virtual bool asBool() const + { + return !set_.empty(); + } // asBool + virtual double asNumber() const + { + return stringAsNumber(asString()); + } // asNumber + virtual std::string asString() const + { + return !set_.empty() ? nodeStringValue(set_.top()) : ""; + } // asStringx + virtual const NodeSet& asNodeSet() const { return set_; } + + virtual ValueType type() const { return NODE_SET; } + +private: + NodeSet set_; +}; // NodeSetValue + +} // namespace XPath +} // namespace Arabica + +#endif diff --git a/XPath/impl/xpath_variable.hpp b/XPath/impl/xpath_variable.hpp new file mode 100644 index 00000000..e2be5db8 --- /dev/null +++ b/XPath/impl/xpath_variable.hpp @@ -0,0 +1,31 @@ +#ifndef ARABICA_XPATH_VARIABLE_HPP +#define ARABICA_XPATH_VARIABLE_HPP + +#include "xpath_value.hpp" +#include "xpath_execution_context.hpp" +#include "xpath_variable_resolver.hpp" + +namespace Arabica +{ +namespace XPath +{ + +class Variable : public XPathExpression +{ +public: + Variable(const std::string& name) : name_(name) { } + + virtual XPathValuePtr evaluate(const DOM::Node& context, + const ExecutionContext& executionContext) const + { + return executionContext.variableResolver().resolveVariable(name_); + } // evaluate + +private: + const std::string name_; +}; // class Variable + +} // namespace XPath +} // namespace Arabica + +#endif diff --git a/XPath/impl/xpath_variable_resolver.hpp b/XPath/impl/xpath_variable_resolver.hpp new file mode 100644 index 00000000..84df2130 --- /dev/null +++ b/XPath/impl/xpath_variable_resolver.hpp @@ -0,0 +1,42 @@ +#ifndef ARABICA_XPATH_VARIABLE_RESOLVER_HPP +#define ARABICA_XPATH_VARIABLE_RESOLVER_HPP + +#include + +namespace Arabica +{ +namespace XPath +{ + +class XPathValue; +typedef boost::shared_ptr XPathValuePtr; + +class UnboundVariableException : public std::runtime_error +{ +public: + UnboundVariableException(const std::string& thing) : std::runtime_error("The variable '" + thing + "' is undefined.") { } +}; // class UnboundVariableException + +class VariableResolver +{ +public: + virtual ~VariableResolver() { } + + virtual XPathValuePtr resolveVariable(const std::string& name) const = 0; +}; // class VariableResolver + +typedef boost::shared_ptr VariableResolverPtr; + +class NullVariableResolver : public VariableResolver +{ +public: + virtual XPathValuePtr resolveVariable(const std::string& name) const + { + throw UnboundVariableException(name); + } // resolveVariable +}; // NullVariableResolver + +} // namespace XPath +} // namespace Arabica + +#endif diff --git a/XPath/src/xpath_axis_enumerator.cpp b/XPath/src/xpath_axis_enumerator.cpp new file mode 100644 index 00000000..5223f510 --- /dev/null +++ b/XPath/src/xpath_axis_enumerator.cpp @@ -0,0 +1,20 @@ +#include + +using namespace Arabica::XPath; + +const AxisEnumerator::NamedAxis AxisEnumerator::AxisLookupTable[] = { + { ANCESTOR, AxisEnumerator::CreateAxis }, + { ANCESTOR_OR_SELF, AxisEnumerator::CreateAxis }, + { ATTRIBUTE, AxisEnumerator::CreateAxis }, + { CHILD, AxisEnumerator::CreateAxis }, + { DESCENDANT, AxisEnumerator::CreateAxis }, + { DESCENDANT_OR_SELF, AxisEnumerator::CreateAxis }, + { FOLLOWING, AxisEnumerator::CreateAxis }, + { FOLLOWING_SIBLING, AxisEnumerator::CreateAxis }, + { NAMESPACE, AxisEnumerator::CreateAxis }, + { PARENT, AxisEnumerator::CreateAxis }, + { PRECEDING, AxisEnumerator::CreateAxis }, + { PRECEDING_SIBLING, AxisEnumerator::CreateAxis }, + { SELF, AxisEnumerator::CreateAxis }, + { static_cast(0), 0 } + }; diff --git a/XPath/src/xpath_object.cpp b/XPath/src/xpath_object.cpp new file mode 100644 index 00000000..67dc4ac1 --- /dev/null +++ b/XPath/src/xpath_object.cpp @@ -0,0 +1,369 @@ +#include +#include +#include +#include +#include + +namespace Arabica +{ +namespace XPath +{ + +bool nodeSetsEqual(const XPathValuePtr& lhs, const XPathValuePtr& rhs); +bool nodeSetAndValueEqual(const XPathValuePtr& lhs, const XPathValuePtr& rhs); + +double minValue(const NodeSet& ns); +double maxValue(const NodeSet& ns); + +template T nodeValue(const DOM::Node& node); +template<> std::string nodeValue(const DOM::Node& node) { return nodeStringValue(node); } +template<> double nodeValue(const DOM::Node& node) { return nodeNumberValue(node); } + +template +class compareNodeWith +{ + typedef typename Op::first_argument_type T; +public: + compareNodeWith(const T& value) : value_(value) { } + compareNodeWith(const compareNodeWith& rhs) : value_(rhs.value_) { } + + bool operator()(const DOM::Node& node) + { + return Op()(nodeValue(node), value_); + } // operator() + +private: + T value_; + bool operator==(const compareNodeWith&); + compareNodeWith& operator=(const compareNodeWith&); +}; // class compareNodeWith + +template +bool compareNodeSets(const XPathValuePtr& lhs, const XPathValuePtr& rhs) +{ + return Op()(minValue(lhs->asNodeSet()), maxValue(rhs->asNodeSet())); +} // compareNodeSets + +template +bool compareNodeSetWith(const XPathValuePtr& lhs, const XPathValuePtr& rhs) +{ + const NodeSet& lns = lhs->asNodeSet(); + return std::find_if(lns.begin(), + lns.end(), + compareNodeWith(rhs->asNumber())) != lns.end(); +} // compareNodeSetAndValue + +/////////////////////////////////////////////////// +bool areEqual(const XPathValuePtr& lhs, const XPathValuePtr& rhs) +{ + ValueType lt = lhs->type(); + ValueType rt = rhs->type(); + + if((lt == NODE_SET) && (rt == NODE_SET)) + return nodeSetsEqual(lhs, rhs); + + if(lt == NODE_SET) + return nodeSetAndValueEqual(lhs, rhs); + if(rt == NODE_SET) + return nodeSetAndValueEqual(rhs, lhs); + + if((lt == BOOL) || (rt == BOOL)) + return lhs->asBool() == rhs->asBool(); + + if((lt == NUMBER) || (rt == NUMBER)) + return lhs->asNumber() == rhs->asNumber(); + + if((lt == STRING) || (rt == STRING)) + return lhs->asString() == rhs->asString(); + + return false; +} // operator== + +bool nodeSetsEqual(const XPathValuePtr& lhs, const XPathValuePtr& rhs) +{ + const NodeSet& lns = lhs->asNodeSet(); + const NodeSet& rns = rhs->asNodeSet(); + + if((lns.size() == 0) || (rns.size() == 0)) + return false; + + std::set values; + NodeSet::const_iterator l = lns.begin(); + std::string lvalue = nodeStringValue(*l); + + for(NodeSet::const_iterator r = rns.begin(), rend = rns.end(); r != rend; ++r) + { + std::string rvalue = nodeStringValue(*r); + if(lvalue == rvalue) + return true; + values.insert(rvalue); + } // for ... + + ++l; + for(NodeSet::const_iterator lend = lns.end(); l != lend; ++l) + if(values.find(nodeStringValue(*l)) != values.end()) + return true; + + return false; +} // nodeSetsEqual + +bool nodeSetAndValueEqual(const XPathValuePtr& lhs, const XPathValuePtr& rhs) +{ + const NodeSet& lns = lhs->asNodeSet(); + + switch(rhs->type()) + { + case BOOL: + { + bool l = !lns.empty(); + bool r = rhs->asBool(); + + return l == r; + } // case BOOL + case STRING: + return std::find_if(lns.begin(), lns.end(), compareNodeWith >(rhs->asString())) != lns.end(); + + case NUMBER: + return std::find_if(lns.begin(), lns.end(), compareNodeWith >(rhs->asNumber())) != lns.end(); + + default: + throw std::runtime_error("Node set == not yet implemented for type " + boost::lexical_cast(rhs->type())); + } // switch +} // nodeSetAndValueEqual + +/////////////////////////////// +bool isLessThan(const XPathValuePtr& lhs, const XPathValuePtr& rhs) +{ + ValueType lt = lhs->type(); + ValueType rt = rhs->type(); + + if((lt == NODE_SET) && (rt == NODE_SET)) + return compareNodeSets >(lhs, rhs); + + if(lt == NODE_SET) + return compareNodeSetWith >(lhs, rhs); + + if(rt == NODE_SET) + return compareNodeSetWith >(rhs, lhs); + + return lhs->asNumber() < rhs->asNumber(); +} // isLessThan + +bool isLessThanEquals(const XPathValuePtr& lhs, const XPathValuePtr& rhs) +{ + ValueType lt = lhs->type(); + ValueType rt = rhs->type(); + + if((lt == NODE_SET) && (rt == NODE_SET)) + return compareNodeSets >(lhs, rhs); + + if(lt == NODE_SET) + return compareNodeSetWith >(lhs, rhs); + + if(rt == NODE_SET) + return compareNodeSetWith >(rhs, lhs); + + return lhs->asNumber() <= rhs->asNumber(); +} // isLessThanEquals + +bool isGreaterThan(const XPathValuePtr& lhs, const XPathValuePtr& rhs) +{ + return isLessThan(rhs, lhs); +} // isGreaterThan + +bool isGreaterThanEquals(const XPathValuePtr& lhs, const XPathValuePtr& rhs) +{ + return isLessThanEquals(rhs, lhs); +} // isGreaterThanEquals + +double minValue(const NodeSet& ns) +{ + double v = nodeNumberValue(ns[0]); + for(NodeSet::const_iterator i = ns.begin(), ie = ns.end(); i != ie; ++i) + { + double vt = nodeNumberValue(*i); + if(isNaN(vt)) + continue; + if(!(vt > v)) // looks weird, but should account for infinity + v = vt; + } // for ... + return v; +} // minValue + +double maxValue(const NodeSet& ns) +{ + double v = nodeNumberValue(ns[0]); + for(NodeSet::const_iterator i = ns.begin(), ie = ns.end(); i != ie; ++i) + { + double vt = nodeNumberValue(*i); + if(isNaN(vt)) + continue; + if(!(vt < v)) + v = vt; + } // for ... + return v; +} // maxValue + +//////////////////////////////////// +double stringAsNumber(const std::string& str) +{ + try { + return boost::lexical_cast(str); + } // try + catch(const boost::bad_lexical_cast&) { + return NaN; + } // catch +} // stringAsNumber + +double nodeNumberValue(const DOM::Node& node) +{ + return stringAsNumber(nodeStringValue(node)); +} // nodeNumberValue + +std::string nodeStringValue(const DOM::Node& node) +{ + switch(node.getNodeType()) + { + case DOM::Node::DOCUMENT_NODE: + case DOM::Node::DOCUMENT_FRAGMENT_NODE: + case DOM::Node::ELEMENT_NODE: + { + std::ostringstream os; + AxisEnumerator ae(node, DESCENDANT); + while(*ae != 0) + { + if((ae->getNodeType() == DOM::Node::TEXT_NODE) || + (ae->getNodeType() == DOM::Node::CDATA_SECTION_NODE)) + os << ae->getNodeValue(); + ++ae; + } // while + return os.str(); + } // case + + case DOM::Node::ATTRIBUTE_NODE: + case DOM::Node::PROCESSING_INSTRUCTION_NODE: + case DOM::Node::COMMENT_NODE: + case DOM::Node::TEXT_NODE: + case DOM::Node::CDATA_SECTION_NODE: + return node.getNodeValue(); + + default: + throw std::runtime_error("Don't know how to calculate string-value of " + node.getNodeName()); + } // switch +} // nodeStringValue + +DOM::Node node_parent_or_owner(const DOM::Node& node) +{ + if(node.getNodeType() == DOM::Node::ATTRIBUTE_NODE) + return (static_cast >(node)).getOwnerElement(); + return node.getParentNode(); +} // node_parent_or_owner + +DOM::Node ultimate_parent(const DOM::Node& origin) +{ + DOM::Node n = origin; + DOM::Node p = node_parent_or_owner(n); + while(p != 0) + { + n = p; + p = node_parent_or_owner(n); + } // while ... + return n; +} // ultimate_parent + +unsigned int node_attribute_index(const DOM::Attr& attr) +{ + DOM::NamedNodeMap attrs = attr.getOwnerElement().getAttributes(); + unsigned int p = 0; + for(unsigned int pe = attrs.getLength(); p != pe; ++p) + if(attrs.item(p) == attr) + break; + return p; +} // node_attribute_index + +unsigned int node_child_position(const DOM::Node& node) +{ + if(node.getNodeType() == DOM::Node::ATTRIBUTE_NODE) + return node_attribute_index(static_cast >(node)); + + unsigned int pos = 0; + DOM::Node n = node; + do + { + n = n.getPreviousSibling(); + pos += 1000; + } while(n != 0); + return pos; +} // node_child_position + +std::vector node_position(const DOM::Node& node) +{ + std::vector pos; + DOM::Node n = node; + do + { + pos.push_back(node_child_position(n)); + n = node_parent_or_owner(n); + } while(n != 0); + + return pos; +} // node_position + +int resolve_different_subtrees(const DOM::Node& lhs, const DOM::Node& rhs) +{ + // if we have something in the document, and a document fragment, + // sort the doc ahead of the fragment + DOM::Node lp = ultimate_parent(lhs); + if(lp.getNodeType() == DOM::Node::DOCUMENT_NODE) + return -1; + DOM::Node rp = ultimate_parent(rhs); + if(rp.getNodeType() == DOM::Node::DOCUMENT_NODE) + return 1; + + // otherwise, sort the frags + return (lp.unlying_impl() < lp.unlying_impl()) ? -1 : 1; +} // resolve_different_subtrees + +int compareNodes(const DOM::Node& lhs, const DOM::Node& rhs) +{ + if(lhs == rhs) + return 0; + + // different documents + if(lhs.getOwnerDocument() != rhs.getOwnerDocument()) + return (lhs.getOwnerDocument().unlying_impl() < rhs.getOwnerDocument().unlying_impl()) ? -1 : 1; + + // ok, nodes belong to the same document, but do they belong to the document itself, or a document fragment, + // or is it just floating free? if they both belong to a document fragment, is it the same fragment? + if(ultimate_parent(lhs) != ultimate_parent(rhs)) + return resolve_different_subtrees(lhs, rhs); + + std::vector pos1 = node_position(lhs); + std::vector pos2 = node_position(rhs); + + std::vector::const_reverse_iterator l = pos1.rbegin(), le = pos1.rend(); + std::vector::const_reverse_iterator r = pos2.rbegin(), re = pos2.rend(); + + while(l != le && r != re) + { + if(*l != *r) + return *l - *r; + + ++l; + ++r; + } // while + + if(l != le) + return 1; + if(r != re) + return -1; + return 0; +} // compareNodes + +bool nodes_less_than(const DOM::Node& n1, const DOM::Node& n2) +{ + return compareNodes(n1, n2) < 0; +} // nodes_less_than + +} // namespace XPath +} // namespace Arabica diff --git a/XPath/src/xpath_parser.cpp b/XPath/src/xpath_parser.cpp new file mode 100644 index 00000000..45ac09e7 --- /dev/null +++ b/XPath/src/xpath_parser.cpp @@ -0,0 +1,462 @@ +#pragma warning(disable:4224 4180 4244) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Arabica::XPath; + +long Arabica::XPath::getNodeId(node_iter_t const& node) +{ + return static_cast(node->value.id().to_long()); +} // getNodeId + +node_iter_t& Arabica::XPath::skipWhitespace(node_iter_t& node) +{ + while(getNodeId(node) == S_id) + ++node; + return node; +} // skipWhitespace + +XPath::XPath() +{ + resetNamespaceContext(); + resetVariableResolver(); + resetFunctionResolver(); +} // XPath + +XPath::~XPath() +{ +} // ~XPath + +std::map XPath::names_ = debugNames(); +std::map XPath::factory_ = createFunctions(); + +void XPath::dump(node_iter_t const& i, int depth) +{ + long id = static_cast(i->value.id().to_long()); + + for(int d = 0; d < depth; ++d) + std::cout << ' '; + std::cout << names_[id] << " - " << std::string(i->value.begin(), i->value.end()) << std::endl; + + for(node_iter_t c = i->children.begin(); c != i->children.end(); ++c) + dump(c, depth+2); +} // dump + +XPathExpression* createExpression(node_iter_t const& i, CompilationContext& context) +{ + node_iter_t c = i->children.begin(); + skipWhitespace(c); + return compile_expression(c, context); +} // createExpression + +XPathExpression* createFunction(node_iter_t const& i, CompilationContext& context) +{ +// dump(i); + node_iter_t c = i->children.begin(); + std::string name(c->value.begin(), c->value.end()); + ++c; + skipWhitespace(c); + assert(getNodeId(c) == LeftBracket_id); + ++c; + skipWhitespace(c); + + std::vector args; + while(getNodeId(c) != RightBracket_id) + { + XPathExpressionPtr arg(compile_expression(c++, context)); + args.push_back(arg); + + skipWhitespace(c); + } // while ... + // maybe trailing whitespace ... + + return FunctionHolder::createFunction(name, args, context); +} // createFunction + +XPathExpression* createNumber(node_iter_t const& i, CompilationContext& context) +{ + return new NumericValue(boost::lexical_cast(std::string(i->value.begin(), i->value.end()))); +} // createNumber + +XPathExpression* createVariable(node_iter_t const& i, CompilationContext& context) +{ + return new Variable(std::string(i->value.begin()+1, i->value.end())); // skip $ +} // createVariable + +XPathExpression* createLiteral(node_iter_t const& i, CompilationContext& context) +{ + std::string str(i->value.begin(), i->value.end()); + return new StringValue(str); +} // createLiteral + +XPathExpression* createBinaryExpression(node_iter_t const& i, CompilationContext& context) +{ + node_iter_t c = i->children.begin(); + XPathExpression* p1 = compile_expression(c, context); + ++c; + + do + { + long op = getNodeId(c); + ++c; + XPathExpression* p2 = compile_expression(c, context); + + switch(op) + { + case PlusOperator_id: + p1 = new PlusOperator(p1, p2); + break; + case MinusOperator_id: + p1 = new MinusOperator(p1, p2); + break; + case MultiplyOperator_id: + p1 = new MultiplyOperator(p1, p2); + break; + case DivOperator_id: + p1 = new DivideOperator(p1, p2); + break; + case ModOperator_id: + p1 = new ModOperator(p1, p2); + break; + case EqualsOperator_id: + p1 = new EqualsOperator(p1, p2); + break; + case NotEqualsOperator_id: + p1 = new NotEqualsOperator(p1, p2); + break; + case LessThanOperator_id: + p1 = new LessThanOperator(p1, p2); + break; + case LessThanEqualsOperator_id: + p1 = new LessThanEqualsOperator(p1, p2); + break; + case GreaterThanOperator_id: + p1 = new GreaterThanOperator(p1, p2); + break; + case GreaterThanEqualsOperator_id: + p1 = new GreaterThanEqualsOperator(p1, p2); + break; + case OrOperator_id: + p1 = new OrOperator(p1, p2); + break; + case AndOperator_id: + p1 = new AndOperator(p1, p2); + break; + case UnionOperator_id: + p1 = new UnionExpression(p1, p2); + break; + default: + throw UnsupportedException(boost::lexical_cast(op)); + } // switch + } + while(++c != i->children.end()); + + return p1; +} // createBinaryExpression + +RelativeLocationPath::StepList createStepList(node_iter_t const& from, node_iter_t const& to, CompilationContext& context) +{ + RelativeLocationPath::StepList steps; + + node_iter_t c = from; + node_iter_t end = to; + + while(c != end) + switch(getNodeId(c)) + { + case S_id: + case Slash_id: + ++c; // just drop it + break; + case RelativeLocationPath_id: + // might get here when handling an absolute path + end = c->children.end(); + c = c->children.begin(); + break; + case Step_id: + { + node_iter_t step = c->children.begin(); + steps.push_back(StepFactory::createStep(step, c->children.end(), context)); + ++c; + } + break; + default: + steps.push_back(StepFactory::createStep(c, end, context)); + } // switch(getNodeId(c)) + + return steps; +} // createStepList + +XPathExpression* createAbsoluteLocationPath(node_iter_t const& i, CompilationContext& context) +{ + return new AbsoluteLocationPath(createStepList(i->children.begin(), i->children.end(), context)); +} // createAbsoluteLocationPath + +XPathExpression* createRelativeLocationPath(node_iter_t const& i, CompilationContext& context) +{ + return new RelativeLocationPath(createStepList(i->children.begin(), i->children.end(), context)); +} // createRelativeLocationPath + +XPathExpression* createSingleStepRelativeLocationPath(node_iter_t const& i, CompilationContext& context) +{ + node_iter_t n = i; + return new RelativeLocationPath(StepFactory::createStep(n, context)); +} // createSingleStepRelativeLocationPath + +XPathExpression* createSingleStepAbsoluteLocationPath(node_iter_t const& i, CompilationContext& context) +{ + node_iter_t n = i; + return new AbsoluteLocationPath(StepFactory::createStep(n, context)); +} // createSingleStepAbsoluteLocationPath + +XPathExpression* createUnaryExpression(node_iter_t const& i, CompilationContext& context) +{ + return compile_expression(i->children.begin(), context); +} // createUnaryExpression + +XPathExpression* createUnaryNegativeExpr(node_iter_t const& i, CompilationContext& context) +{ + return new UnaryNegative(compile_expression(i+1, context)); +} // createUnaryNegativeExpr + +XPathExpressionPtr XPath::compile(const std::string& xpath) const +{ + return do_compile(xpath, &XPath::parse_xpath); +} // compile + +XPathExpressionPtr XPath::compile_expr(const std::string& xpath) const +{ + return do_compile(xpath, &XPath::parse_xpath_expr); +} // compile_expr + +XPathExpressionPtr XPath::do_compile(const std::string& xpath, tree_info_t(XPath::*fn)(const std::string& str) const) const +{ + debugNames(); + createFunctions(); + tree_info_t ast; + try { + ast = (this->*fn)(xpath); + if(!ast.full) + throw SyntaxException(xpath); + + CompilationContext context(*this, getNamespaceContext(), getFunctionResolver()); + return XPathExpressionPtr(compile_expression(ast.trees.begin(), context)); + } // try + catch(std::exception& ex) + { + //dump(ast.trees.begin()); + std::cerr << "\n\t'" << xpath << "' threw " << ex.what() << '\n' << std::endl; + throw SyntaxException(xpath); + } + catch(...) + { + std::cerr << "\n\t'" << xpath << "' threw something\n" << std::endl; + throw SyntaxException(xpath); + } +} // compile + +tree_info_t XPath::parse_xpath(const std::string& str) const +{ + str_iter_t first = str.begin(); + str_iter_t last = str.end(); + + return ast_parse(first, last, xpathg_); +} // parse_xpath + +tree_info_t XPath::parse_xpath_expr(const std::string& str) const +{ + str_iter_t first = str.begin(); + str_iter_t last = str.end(); + + xpath_grammar_expr xpathg; + return ast_parse(first, last, xpathg); +} // parse_xpath_expr + +XPathValuePtr XPath::evaluate(const std::string& xpath, const DOM::Node& context) const +{ + ExecutionContext executionContext; + executionContext.setVariableResolver(getVariableResolver()); + + return compile(xpath)->evaluate(context, executionContext); +} // evaluate + +XPathValuePtr XPath::evaluate_expr(const std::string& xpath, const DOM::Node& context) const +{ + ExecutionContext executionContext; + executionContext.setVariableResolver(getVariableResolver()); + + return compile_expr(xpath)->evaluate(context, executionContext); +} // evaluate_expr + +XPathExpression* Arabica::XPath::compile_expression(node_iter_t const& i, CompilationContext& context) +{ + //dump(i); + + long id = getNodeId(i); + + if(XPath::factory_.find(id) == XPath::factory_.end()) + { + //return XPathExpressionPtr(); + XPath::dump(i, 0); + throw UnsupportedException(XPath::names_[id]); + } + + return XPath::factory_[id](i, context); +} // compile_expression + +const std::map XPath::createFunctions() +{ + std::map factory; + + factory[AbsoluteLocationPath_id] = createAbsoluteLocationPath; + factory[RelativeLocationPath_id] = createRelativeLocationPath; + factory[AbbreviatedAbsoluteLocationPath_id] = createAbsoluteLocationPath; + factory[Step_id] = createRelativeLocationPath; + factory[PathExpr_id] = createRelativeLocationPath; + factory[FilterExpr_id] = createRelativeLocationPath; + + factory[PrimaryExpr_id] = createExpression; + + factory[FunctionCall_id] = createFunction; + + factory[AdditiveExpr_id] = createBinaryExpression; + factory[MultiplicativeExpr_id] = createBinaryExpression; + factory[EqualityExpr_id] = createBinaryExpression; + factory[RelationalExpr_id] = createBinaryExpression; + factory[OrExpr_id] = createBinaryExpression; + factory[AndExpr_id] = createBinaryExpression; + factory[UnionExpr_id] = createBinaryExpression; + + factory[Literal_id] = createLiteral; + factory[Number_id] = createNumber; + factory[Digits_id] = createNumber; + + factory[VariableReference_id] = createVariable; + + factory[NodeTest_id] = createSingleStepRelativeLocationPath; + factory[QName_id] = createSingleStepRelativeLocationPath; + factory[NCName_id] = createSingleStepRelativeLocationPath; + factory[AnyName_id] = createSingleStepRelativeLocationPath; + factory[Text_id] = createSingleStepRelativeLocationPath; + factory[Comment_id] = createSingleStepRelativeLocationPath; + factory[ProcessingInstruction_id] = createSingleStepRelativeLocationPath; + factory[Slash_id] = createSingleStepAbsoluteLocationPath; + + factory[SelfSelect_id] = createSingleStepRelativeLocationPath; + factory[ParentSelect_id] = createSingleStepRelativeLocationPath; + + factory[UnaryExpr_id] = createUnaryExpression; + factory[UnaryMinusOperator_id] = createUnaryNegativeExpr; + + return factory; +} // createFunctions + +const std::map XPath::debugNames() +{ + std::map names; + + names[LocationPath_id] = "LocationPath"; + names[AbsoluteLocationPath_id] = "AbsoluteLocationPath"; + names[RelativeLocationPath_id] = "RelativeLocationPath"; + names[Step_id] = "Step"; + names[AxisSpecifier_id] = "AxisSpecifier"; + names[NodeTest_id] = "NodeTest"; + names[Predicate_id] = "Predicate"; + names[PredicateExpr_id] = "PredicateExpr"; + names[AbbreviatedAbsoluteLocationPath_id] = "AbbreviatedAbsoluteLocationPath"; + names[AbbreviatedStep_id] = "AbbreviatedStep"; + names[AbbreviatedAxisSpecifier_id] = "AbbreviatedAxisSpecifier"; + names[Expr_id] = "Expr"; + names[PrimaryExpr_id] = "PrimaryExpr"; + names[FunctionCall_id] = "FunctionCall"; + names[Argument_id] = "Argument"; + names[UnionExpr_id] = "UnionExpr"; + names[PathExpr_id] = "PathExpr"; + names[FilterExpr_id] = "FilterExpr"; + names[OrExpr_id] = "OrExpr"; + names[AndExpr_id] = "AndExpr"; + names[EqualityExpr_id] = "EqualityExpr"; + names[RelationalExpr_id] = "RelationalExpr"; + names[AdditiveExpr_id] = "AdditiveExpr"; + names[MultiplicativeExpr_id] = "MultiplicativeExpr"; + names[UnaryExpr_id] = "UnaryExpr"; + names[Literal_id] = "Literal"; + names[Number_id] = "Number"; + names[Digits_id] = "Digits"; + names[MultiplyOperator_id] = "MultiplyOperator"; + names[FunctionName_id] = "FunctionName"; + names[VariableReference_id] = "VariableReference"; + names[NameTest_id] = "NameTest"; + names[S_id] = "S"; + names[NodeType_id] = "NodeType"; + names[AxisName_id] = "AxisName"; + + names[QName_id] = "QName"; + names[Prefix_id] = "Prefix"; + names[LocalPart_id] = "LocalPart"; + names[NCName_id] = "NCName"; + names[NCNameChar_id] = "NCNameChar"; + + names[Slash_id] = "/"; + names[SlashSlash_id] = "//"; + + names[AncestorOrSelf_id] = "ancestor-or-self::"; + names[Ancestor_id] = "ancestor::"; + names[Attribute_id] = "attribute::"; + names[Child_id] = "child::"; + names[DescendantOrSelf_id] = "descendant-or-self::"; + names[Descendant_id] = "descendant::"; + names[FollowingSibling_id] = "following-sibling::"; + names[Following_id] = "following::"; + names[Namespace_id] = "namespace::"; + names[Parent_id] = "parent::"; + names[PrecedingSibling_id] = "preceding-sibling::"; + names[Preceding_id] = "preceding::"; + names[Self_id] = "self::"; + + names[Comment_id] = "comment()"; + names[Text_id] = "text()"; + names[ProcessingInstruction_id] = "processing-instruction()"; + names[Node_id] = "node()"; + names[AnyName_id] = "AnyName"; + + names[SelfSelect_id] = "SelfSelect"; + names[ParentSelect_id] = "ParentSelect"; + + names[LeftSquare_id] = "["; + names[RightSquare_id] = "]"; + + names[LeftBracket_id] = "("; + names[RightBracket_id] = ")"; + + names[PlusOperator_id] = "+"; + names[MinusOperator_id] = "-"; + names[ModOperator_id] = "mod"; + names[DivOperator_id] = "div"; + names[EqualsOperator_id] = "="; + names[NotEqualsOperator_id] = "!="; + names[LessThanOperator_id] = "<"; + names[LessThanEqualsOperator_id] = "<="; + names[GreaterThanOperator_id] = ">"; + names[GreaterThanEqualsOperator_id] = ">="; + + names[OrOperator_id] = "or"; + names[AndOperator_id] = "and"; + names[UnionOperator_id] = "union"; + names[UnaryMinusOperator_id] = "minus"; + + return names; +} // debugNames + +// end