mirror of
https://github.com/jezhiggins/arabica
synced 2025-01-29 08:36:45 +01:00
moved here from xpath-dev-sandbox
This commit is contained in:
parent
50f169b1c7
commit
b18f90ce9e
27 changed files with 4266 additions and 0 deletions
12
XPath/XPath.hpp
Normal file
12
XPath/XPath.hpp
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef ARABICA_XPATHIC_XPATH_HPP
|
||||||
|
#define ARABICA_XPATHIC_XPATH_HPP
|
||||||
|
|
||||||
|
#include <XPath/impl/xpath_parser.hpp>
|
||||||
|
#include <XPath/impl/xpath_value.hpp>
|
||||||
|
#include <XPath/impl/xpath_function.hpp>
|
||||||
|
#include <XPath/impl/xpath_step.hpp>
|
||||||
|
#include <XPath/impl/xpath_relational.hpp>
|
||||||
|
#include <XPath/impl/xpath_logical.hpp>
|
||||||
|
#include <XPath/impl/xpath_arithmetic.hpp>
|
||||||
|
|
||||||
|
#endif
|
92
XPath/impl/xpath_arithmetic.hpp
Normal file
92
XPath/impl/xpath_arithmetic.hpp
Normal file
|
@ -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<std::string>& 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<std::string>& 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<std::string>& 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<std::string>& 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<std::string>& context,
|
||||||
|
const ExecutionContext& executionContext) const
|
||||||
|
{
|
||||||
|
return NumericValue::createValue(static_cast<long>(lhs()->evaluateAsNumber(context)) % static_cast<long>(rhs()->evaluateAsNumber(context)));
|
||||||
|
} // evaluate
|
||||||
|
}; // class ModOperator
|
||||||
|
|
||||||
|
class UnaryNegative : private UnaryExpression, public XPathExpression
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UnaryNegative(XPathExpression* expr) :
|
||||||
|
UnaryExpression(expr) { }
|
||||||
|
|
||||||
|
virtual XPathValuePtr evaluate(const DOM::Node<std::string>& context,
|
||||||
|
const ExecutionContext& executionContext) const
|
||||||
|
{
|
||||||
|
return NumericValue::createValue(-expr()->evaluate(context, executionContext)->asNumber());
|
||||||
|
} // evaluate
|
||||||
|
}; // class UnaryNegative
|
||||||
|
|
||||||
|
} // XPath
|
||||||
|
} // Arabica
|
||||||
|
|
||||||
|
#endif
|
27
XPath/impl/xpath_ast.hpp
Normal file
27
XPath/impl/xpath_ast.hpp
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef ARABICA_XPATHIC_XPATH_AST_HPP
|
||||||
|
#define ARABICA_XPATHIC_XPATH_AST_HPP
|
||||||
|
|
||||||
|
#include <boost/spirit/core.hpp>
|
||||||
|
#include <boost/spirit/tree/ast.hpp>
|
||||||
|
|
||||||
|
namespace Arabica
|
||||||
|
{
|
||||||
|
namespace XPath
|
||||||
|
{
|
||||||
|
|
||||||
|
typedef std::string::const_iterator str_iter_t;
|
||||||
|
|
||||||
|
typedef boost::spirit::tree_match<str_iter_t> tree_match_t;
|
||||||
|
typedef tree_match_t::tree_iterator node_iter_t;
|
||||||
|
typedef boost::spirit::tree_parse_info<str_iter_t> 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
|
106
XPath/impl/xpath_ast_ids.hpp
Normal file
106
XPath/impl/xpath_ast_ids.hpp
Normal file
|
@ -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
|
531
XPath/impl/xpath_axis_enumerator.hpp
Normal file
531
XPath/impl/xpath_axis_enumerator.hpp
Normal file
|
@ -0,0 +1,531 @@
|
||||||
|
#ifndef ARABICA_XPATHIC_XPATH_AXIS_ENUMERATOR_H
|
||||||
|
#define ARABICA_XPATHIC_XPATH_AXIS_ENUMERATOR_H
|
||||||
|
|
||||||
|
#include <DOM/Node.h>
|
||||||
|
#include <DOM/Document.h>
|
||||||
|
#include <DOM/NamedNodeMap.h>
|
||||||
|
#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<class axis_walker>
|
||||||
|
static AxisWalker* CreateAxis(const DOM::Node<std::string>& context) { return new axis_walker(context); }
|
||||||
|
|
||||||
|
typedef AxisWalker* (*CreateAxisPtr)(const DOM::Node<std::string>& context);
|
||||||
|
|
||||||
|
struct NamedAxis { Axis name; CreateAxisPtr creator; };
|
||||||
|
|
||||||
|
static const NamedAxis AxisLookupTable[];
|
||||||
|
|
||||||
|
public:
|
||||||
|
AxisEnumerator(const DOM::Node<std::string>& 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<std::string>& operator*() const { return walker_->get(); }
|
||||||
|
const DOM::Node<std::string>* 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<std::string>& 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<std::string>& current) { current_ = current; }
|
||||||
|
void end() { current_ = 0; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
DOM::Node<std::string> current_;
|
||||||
|
bool forward_;
|
||||||
|
|
||||||
|
AxisWalker& operator=(const AxisWalker&);
|
||||||
|
bool operator==(const AxisWalker&);
|
||||||
|
}; // AxisWalker
|
||||||
|
|
||||||
|
class AncestorAxisWalker : public AxisWalker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AncestorAxisWalker(const DOM::Node<std::string>& context) : AxisWalker(false)
|
||||||
|
{
|
||||||
|
if(context == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(context.getNodeType() != DOM::Node<std::string>::ATTRIBUTE_NODE)
|
||||||
|
set(context.getParentNode());
|
||||||
|
else
|
||||||
|
set((static_cast<DOM::Attr<std::string> >(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<std::string>& context) : AxisWalker(false)
|
||||||
|
{
|
||||||
|
if(context != 0)
|
||||||
|
set(context);
|
||||||
|
} // AncestorAxisWalker
|
||||||
|
|
||||||
|
virtual void advance()
|
||||||
|
{
|
||||||
|
if(get() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(get().getNodeType() != DOM::Node<std::string>::ATTRIBUTE_NODE)
|
||||||
|
set(get().getParentNode());
|
||||||
|
else
|
||||||
|
set((static_cast<DOM::Attr<std::string> >(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<std::string>& 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<std::string> attrs_;
|
||||||
|
unsigned int index_;
|
||||||
|
unsigned int count_;
|
||||||
|
|
||||||
|
void set_next()
|
||||||
|
{
|
||||||
|
if(index_ == count_)
|
||||||
|
{
|
||||||
|
end();
|
||||||
|
return;
|
||||||
|
} // if ...
|
||||||
|
|
||||||
|
DOM::Node<std::string> 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<std::string>& 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<std::string>& context) : AxisWalker(true),
|
||||||
|
origin_(context)
|
||||||
|
{
|
||||||
|
if((context != 0) && (context.getNodeType() != DOM::Node<std::string>::ATTRIBUTE_NODE))
|
||||||
|
set(context.getFirstChild());
|
||||||
|
} // DescendantAxisWalker
|
||||||
|
|
||||||
|
virtual void advance()
|
||||||
|
{
|
||||||
|
set(nextDescendant());
|
||||||
|
} // advance
|
||||||
|
|
||||||
|
virtual AxisWalker* clone() const { return new DescendantAxisWalker(*this); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
DOM::Node<std::string> nextDescendant()
|
||||||
|
{
|
||||||
|
DOM::Node<std::string> next = get().getFirstChild();
|
||||||
|
if(next == 0)
|
||||||
|
next = get().getNextSibling();
|
||||||
|
if(next != 0)
|
||||||
|
return next;
|
||||||
|
|
||||||
|
DOM::Node<std::string> 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<std::string> origin_;
|
||||||
|
}; // class DescendantAxisWalker
|
||||||
|
|
||||||
|
class DescendantOrSelfAxisWalker : public AxisWalker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DescendantOrSelfAxisWalker(const DOM::Node<std::string>& 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<std::string> origin_;
|
||||||
|
}; // class DescendantOrSelfAxisWalker
|
||||||
|
|
||||||
|
class FollowingAxisWalker : public AxisWalker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FollowingAxisWalker(const DOM::Node<std::string>& 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<std::string> firstFollowing(const DOM::Node<std::string>& context) const
|
||||||
|
{
|
||||||
|
if(context.getNodeType() == DOM::Node<std::string>::ATTRIBUTE_NODE)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
DOM::Node<std::string> next = context.getNextSibling();
|
||||||
|
if(next != 0)
|
||||||
|
return next;
|
||||||
|
|
||||||
|
DOM::Node<std::string> 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<std::string>& 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<std::string>& context) : AxisWalker(true),
|
||||||
|
index_(0)
|
||||||
|
{
|
||||||
|
DOM::Node<std::string> current = context;
|
||||||
|
while(current.getNodeType() == DOM::Node<std::string>::ELEMENT_NODE)
|
||||||
|
{
|
||||||
|
for(unsigned int a = 0, ae = current.getAttributes().getLength(); a != ae; ++a)
|
||||||
|
{
|
||||||
|
DOM::Node<std::string> attr = current.getAttributes().item(a);
|
||||||
|
if(attr.getPrefix() == "xmlns")
|
||||||
|
list_.push_back(DOM::Node<std::string>(
|
||||||
|
new NamespaceNodeImpl<std::string>(attr.getLocalName(),
|
||||||
|
attr.getNodeValue())
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} // for ...
|
||||||
|
current = current.getParentNode();
|
||||||
|
} // while
|
||||||
|
list_.push_back(DOM::Node<std::string>(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<DOM::Node<std::string> > list_;
|
||||||
|
unsigned int index_;
|
||||||
|
}; // class NamespaceAxisWalker
|
||||||
|
|
||||||
|
class ParentAxisWalker : public AxisWalker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ParentAxisWalker(const DOM::Node<std::string>& context) : AxisWalker(false)
|
||||||
|
{
|
||||||
|
if(context == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(context.getNodeType() != DOM::Node<std::string>::ATTRIBUTE_NODE)
|
||||||
|
set(context.getParentNode());
|
||||||
|
else
|
||||||
|
set((static_cast<DOM::Attr<std::string> >(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<std::string>& 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<std::string> previousInDocument(const DOM::Node<std::string>& context)
|
||||||
|
{
|
||||||
|
DOM::Node<std::string> 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<std::string> getLastDescendant(const DOM::Node<std::string>& context)
|
||||||
|
{
|
||||||
|
if(context.getFirstChild() == 0)
|
||||||
|
return context;
|
||||||
|
|
||||||
|
DOM::Node<std::string> 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<std::string> nextAncestor_;
|
||||||
|
}; // PrecedingAxisWalker
|
||||||
|
|
||||||
|
class PrecedingSiblingAxisWalker : public AxisWalker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PrecedingSiblingAxisWalker(const DOM::Node<std::string>& 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<std::string>& 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<std::string> walkDown(const DOM::Node<std::string>& context, const DOM::Node<std::string>& origin)
|
||||||
|
{
|
||||||
|
if(context.getNodeType() == DOM::Node<std::string>::ATTRIBUTE_NODE)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
DOM::Node<std::string> 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<std::string> 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
|
||||||
|
|
42
XPath/impl/xpath_compile_context.hpp
Normal file
42
XPath/impl/xpath_compile_context.hpp
Normal file
|
@ -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
|
50
XPath/impl/xpath_execution_context.hpp
Normal file
50
XPath/impl/xpath_execution_context.hpp
Normal file
|
@ -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<const VariableResolver> variableResolver_;
|
||||||
|
|
||||||
|
ExecutionContext(const ExecutionContext&);
|
||||||
|
ExecutionContext& operator=(const ExecutionContext&);
|
||||||
|
bool operator==(const ExecutionContext&) const;
|
||||||
|
}; // class ExecutionContext
|
||||||
|
|
||||||
|
} // namespace XPath
|
||||||
|
} // namespace Arabica
|
||||||
|
|
||||||
|
#endif
|
543
XPath/impl/xpath_function.hpp
Normal file
543
XPath/impl/xpath_function.hpp
Normal file
|
@ -0,0 +1,543 @@
|
||||||
|
#ifndef ARABICA_XPATH_FUNCTION_HPP
|
||||||
|
#define ARABICA_XPATH_FUNCTION_HPP
|
||||||
|
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
#include <cmath>
|
||||||
|
#include <XML/XMLCharacterClasses.h>
|
||||||
|
#include <XML/UnicodeCharacters.h>
|
||||||
|
#include "xpath_value.hpp"
|
||||||
|
#include "xpath_execution_context.hpp"
|
||||||
|
|
||||||
|
namespace Arabica
|
||||||
|
{
|
||||||
|
namespace XPath
|
||||||
|
{
|
||||||
|
|
||||||
|
class XPathFunction
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
XPathFunction(int minArgs, int maxArgs, const std::vector<XPathExpressionPtr>& args) :
|
||||||
|
args_(args)
|
||||||
|
{
|
||||||
|
if(((minArgs != -1) && (static_cast<int>(args.size()) < minArgs)) ||
|
||||||
|
((maxArgs != -1) && (static_cast<int>(args.size()) > maxArgs)))
|
||||||
|
throw SyntaxException("wrong number of arguments to function");
|
||||||
|
} // XPathFunction
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~XPathFunction() { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& context,
|
||||||
|
const ExecutionContext& executionContext) const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
size_t argCount() const { return args_.size(); }
|
||||||
|
|
||||||
|
bool argAsBool(size_t index,
|
||||||
|
const DOM::Node<std::string>& context,
|
||||||
|
const ExecutionContext& executionContext) const
|
||||||
|
{
|
||||||
|
return args_[index]->evaluate(context, executionContext)->asBool();
|
||||||
|
} // argAsBool
|
||||||
|
|
||||||
|
double argAsNumber(size_t index,
|
||||||
|
const DOM::Node<std::string>& context,
|
||||||
|
const ExecutionContext& executionContext) const
|
||||||
|
{
|
||||||
|
return args_[index]->evaluate(context, executionContext)->asNumber();
|
||||||
|
} // argAsNumber
|
||||||
|
|
||||||
|
std::string argAsString(size_t index,
|
||||||
|
const DOM::Node<std::string>& context,
|
||||||
|
const ExecutionContext& executionContext) const
|
||||||
|
{
|
||||||
|
return args_[index]->evaluate(context, executionContext)->asString();
|
||||||
|
} // argAsString
|
||||||
|
|
||||||
|
NodeSet argAsNodeSet(size_t index,
|
||||||
|
const DOM::Node<std::string>& context,
|
||||||
|
const ExecutionContext& executionContext) const
|
||||||
|
{
|
||||||
|
return args_[index]->evaluate(context, executionContext)->asNodeSet();
|
||||||
|
} // argAsNodeSet
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::vector<XPathExpressionPtr> args_;
|
||||||
|
}; // class XPathFunction
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// node-set functions
|
||||||
|
// number last()
|
||||||
|
class LastFn : public XPathFunction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LastFn(const std::vector<XPathExpressionPtr>& args) : XPathFunction(0, 0, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& context,
|
||||||
|
const ExecutionContext& executionContext) const
|
||||||
|
{
|
||||||
|
return new NumericValue(executionContext.last());
|
||||||
|
} // evaluate
|
||||||
|
}; // class LastFn
|
||||||
|
|
||||||
|
// number position()
|
||||||
|
class PositionFn : public XPathFunction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PositionFn(const std::vector<XPathExpressionPtr>& args) : XPathFunction(0, 0, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& 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<XPathExpressionPtr>& args) : XPathFunction(1, 1, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& 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<XPathExpressionPtr>& args) : XPathFunction(0, 1, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& context,
|
||||||
|
const ExecutionContext& executionContext) const
|
||||||
|
{
|
||||||
|
DOM::Node<std::string> 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<XPathExpressionPtr>& args) : XPathFunction(0, 1, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& context,
|
||||||
|
const ExecutionContext& executionContext) const
|
||||||
|
{
|
||||||
|
DOM::Node<std::string> 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<XPathExpressionPtr>& args) : XPathFunction(0, 1, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& context,
|
||||||
|
const ExecutionContext& executionContext) const
|
||||||
|
{
|
||||||
|
DOM::Node<std::string> 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<XPathExpressionPtr>& args) : XPathFunction(0, 1, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& 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<XPathExpressionPtr>& args) : XPathFunction(2, -1, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& 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<XPathExpressionPtr>& args) : XPathFunction(2, 2, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& 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<XPathExpressionPtr>& args) : XPathFunction(2, 2, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& 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<XPathExpressionPtr>& args) : XPathFunction(2, 2, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& 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<XPathExpressionPtr>& args) : XPathFunction(2, 2, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& 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<XPathExpressionPtr>& args) : XPathFunction(2, 3, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& 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<int>(startAt), static_cast<int>(endAt - startAt)));
|
||||||
|
} // evaluate
|
||||||
|
}; // SubstringFn
|
||||||
|
|
||||||
|
// number string-length(string?)
|
||||||
|
class StringLengthFn : public XPathFunction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StringLengthFn(const std::vector<XPathExpressionPtr>& args) : XPathFunction(0, 1, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& 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<XPathExpressionPtr>& args) : XPathFunction(0, 1, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& 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<wchar_t>(value[i]))))
|
||||||
|
++i;
|
||||||
|
|
||||||
|
size_t p = 0;
|
||||||
|
while(i != ie)
|
||||||
|
{
|
||||||
|
while((i != ie) && (!XML::is_space(static_cast<wchar_t>(value[i]))))
|
||||||
|
value[p++] = value[i++];
|
||||||
|
while((i != ie) && (XML::is_space(static_cast<wchar_t>(value[i]))))
|
||||||
|
++i;
|
||||||
|
if(i != ie)
|
||||||
|
value[p++] = Unicode<char>::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<XPathExpressionPtr>& args) : XPathFunction(3, 3, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& 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<XPathExpressionPtr>& args) : XPathFunction(1, 1, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& 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<XPathExpressionPtr>& args) : XPathFunction(1, 1, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& 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<XPathExpressionPtr>& args) : XPathFunction(0, 0, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& context,
|
||||||
|
const ExecutionContext& executionContext) const
|
||||||
|
{
|
||||||
|
return new BoolValue(true);
|
||||||
|
} // evaluate
|
||||||
|
}; // TrueFn
|
||||||
|
|
||||||
|
// boolean false()
|
||||||
|
class FalseFn : public XPathFunction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FalseFn(const std::vector<XPathExpressionPtr>& args) : XPathFunction(0, 0, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& 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<XPathExpressionPtr>& args) : XPathFunction(0, 1, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& 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<XPathExpressionPtr>& args) : XPathFunction(1, 1, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& 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<XPathExpressionPtr>& args) : XPathFunction(1, 1, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& 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<XPathExpressionPtr>& args) : XPathFunction(1, 1, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& 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<XPathExpressionPtr>& args) : XPathFunction(1, 1, args) { }
|
||||||
|
|
||||||
|
virtual XPathValue* evaluate(const DOM::Node<std::string>& context,
|
||||||
|
const ExecutionContext& executionContext) const
|
||||||
|
{
|
||||||
|
return new NumericValue(roundNumber(argAsNumber(0, context, executionContext)));
|
||||||
|
} // evaluate
|
||||||
|
}; // class RoundFn
|
||||||
|
|
||||||
|
} // namespace XPath
|
||||||
|
} // namespace Arabica
|
||||||
|
|
||||||
|
#endif
|
92
XPath/impl/xpath_function_holder.hpp
Normal file
92
XPath/impl/xpath_function_holder.hpp
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
#ifndef ARABICA_XPATH_FUNCTION_HOLDER_HPP
|
||||||
|
#define ARABICA_XPATH_FUNCTION_HOLDER_HPP
|
||||||
|
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
#include "xpath_value.hpp"
|
||||||
|
#include "xpath_function.hpp"
|
||||||
|
|
||||||
|
namespace Arabica
|
||||||
|
{
|
||||||
|
namespace XPath
|
||||||
|
{
|
||||||
|
|
||||||
|
template<class function_type>
|
||||||
|
XPathFunction* CreateFn(const std::vector<XPathExpressionPtr>& argExprs) { return new function_type(argExprs); }
|
||||||
|
|
||||||
|
typedef XPathFunction* (*CreateFnPtr)(const std::vector<XPathExpressionPtr>& argExprs);
|
||||||
|
|
||||||
|
struct NamedFunction { const char* name; CreateFnPtr creator; };
|
||||||
|
|
||||||
|
const NamedFunction FunctionLookupTable[] = { // node-set functions
|
||||||
|
{ "position", CreateFn<PositionFn> },
|
||||||
|
{ "last", CreateFn<LastFn> },
|
||||||
|
{ "count", CreateFn<CountFn> },
|
||||||
|
{ "local-name", CreateFn<LocalNameFn> },
|
||||||
|
{ "namespace-uri", CreateFn<NamespaceURIFn> },
|
||||||
|
{ "name", CreateFn<NameFn> },
|
||||||
|
// string functions
|
||||||
|
{"string", CreateFn<StringFn> },
|
||||||
|
{"concat", CreateFn<ConcatFn> },
|
||||||
|
{"starts-with", CreateFn<StartsWithFn> },
|
||||||
|
{"contains", CreateFn<ContainsFn> },
|
||||||
|
{"substring-before", CreateFn<SubstringBeforeFn> },
|
||||||
|
{"substring-after", CreateFn<SubstringAfterFn> },
|
||||||
|
{"substring", CreateFn<SubstringFn> },
|
||||||
|
{"string-length", CreateFn<StringLengthFn> },
|
||||||
|
{"normalize-space", CreateFn<NormalizeSpaceFn> },
|
||||||
|
{"translate", CreateFn<TranslateFn> },
|
||||||
|
// boolean functions
|
||||||
|
{"boolean", CreateFn<BooleanFn> },
|
||||||
|
{"not", CreateFn<NotFn> },
|
||||||
|
{"true", CreateFn<TrueFn> },
|
||||||
|
{"false", CreateFn<FalseFn> },
|
||||||
|
// number functions
|
||||||
|
{"number", CreateFn<NumberFn> },
|
||||||
|
{"sum", CreateFn<SumFn> },
|
||||||
|
{"floor", CreateFn<FloorFn> },
|
||||||
|
{"ceiling", CreateFn<CeilingFn> },
|
||||||
|
{"round", CreateFn<RoundFn> },
|
||||||
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
class FunctionHolder : public XPathExpression
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FunctionHolder(XPathFunction* func) :
|
||||||
|
func_(func)
|
||||||
|
{
|
||||||
|
} // FunctionHolder
|
||||||
|
|
||||||
|
virtual ~FunctionHolder()
|
||||||
|
{
|
||||||
|
delete func_;
|
||||||
|
} // ~FunctionHolder
|
||||||
|
|
||||||
|
virtual XPathValuePtr evaluate(const DOM::Node<std::string>& context,
|
||||||
|
const ExecutionContext& executionContext) const
|
||||||
|
{
|
||||||
|
return XPathValuePtr(func_->evaluate(context, executionContext));
|
||||||
|
} // evaluate
|
||||||
|
|
||||||
|
static FunctionHolder* createFunction(const std::string& name,
|
||||||
|
const std::vector<XPathExpressionPtr>& 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
|
44
XPath/impl/xpath_function_resolver.hpp
Normal file
44
XPath/impl/xpath_function_resolver.hpp
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#ifndef ARABICA_XPATH_FUNCTION_RESOLVER_HPP
|
||||||
|
#define ARABICA_XPATH_FUNCTION_RESOLVER_HPP
|
||||||
|
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
|
namespace Arabica
|
||||||
|
{
|
||||||
|
namespace XPath
|
||||||
|
{
|
||||||
|
|
||||||
|
class XPathFunction;
|
||||||
|
class XPathExpression;
|
||||||
|
typedef boost::shared_ptr<XPathExpression> 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<XPathExpressionPtr>& argExprs) const = 0;
|
||||||
|
}; // class FunctionResolver
|
||||||
|
|
||||||
|
typedef boost::shared_ptr<FunctionResolver> FunctionResolverPtr;
|
||||||
|
|
||||||
|
class NullFunctionResolver : public FunctionResolver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual XPathFunction* resolveFunction(const std::string& name,
|
||||||
|
const std::vector<XPathExpressionPtr>& argExprs) const
|
||||||
|
{
|
||||||
|
throw UndefinedFunctionException(name);
|
||||||
|
} // resolveVariable
|
||||||
|
}; // NullFunctionResolver
|
||||||
|
|
||||||
|
} // namespace XPath
|
||||||
|
} // namespace Arabica
|
||||||
|
|
||||||
|
#endif
|
329
XPath/impl/xpath_grammar.hpp
Normal file
329
XPath/impl/xpath_grammar.hpp
Normal file
|
@ -0,0 +1,329 @@
|
||||||
|
#ifndef ARABICA_XPATHIC_XPATH_GRAMMER_HPP
|
||||||
|
#define ARABICA_XPATHIC_XPATH_GRAMMER_HPP
|
||||||
|
|
||||||
|
#define BOOST_SPIRIT_DEBUG
|
||||||
|
#include <boost/spirit/core.hpp>
|
||||||
|
#include <boost/spirit/phoenix/primitives.hpp>
|
||||||
|
#include <boost/spirit/symbols/symbols.hpp>
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "xpath_ast_ids.hpp"
|
||||||
|
|
||||||
|
namespace Arabica
|
||||||
|
{
|
||||||
|
namespace XPath
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename ScannerT>
|
||||||
|
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<ScannerT, boost::spirit::parser_tag<QName_id> > QName;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<Prefix_id> > Prefix;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<LocalPart_id> > LocalPart;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<NCName_id> > NCName;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<NCNameChar_id> > NCNameChar;
|
||||||
|
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<AxisName_id> > AxisName;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<NodeType_id> > NodeType;
|
||||||
|
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<LocationPath_id> > LocationPath;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<AbsoluteLocationPath_id> > AbsoluteLocationPath;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<RelativeLocationPath_id> > RelativeLocationPath;
|
||||||
|
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<Step_id> > Step;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<AxisSpecifier_id> > AxisSpecifier;
|
||||||
|
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<NodeTest_id> > NodeTest;
|
||||||
|
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<Predicate_id> > Predicate;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<PredicateExpr_id> > PredicateExpr;
|
||||||
|
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<AbbreviatedAbsoluteLocationPath_id> > AbbreviatedAbsoluteLocationPath;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<AbbreviatedStep_id> > AbbreviatedStep;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<AbbreviatedAxisSpecifier_id> > AbbreviatedAxisSpecifier;
|
||||||
|
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<Expr_id> > Expr;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<PrimaryExpr_id> > PrimaryExpr;
|
||||||
|
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<FunctionCall_id> > FunctionCall;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<Argument_id> > Argument;
|
||||||
|
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<UnionExpr_id> > UnionExpr;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<PathExpr_id> > PathExpr;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<FilterExpr_id> > FilterExpr;
|
||||||
|
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<OrExpr_id> > OrExpr;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<AndExpr_id> > AndExpr;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<EqualityExpr_id> > EqualityExpr;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<RelationalExpr_id> > RelationalExpr;
|
||||||
|
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<AdditiveExpr_id> > AdditiveExpr;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<MultiplicativeExpr_id> > MultiplicativeExpr;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<UnaryExpr_id> > UnaryExpr;
|
||||||
|
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<Literal_id> > Literal;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<Number_id> > Number;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<Digits_id> > Digits;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<MultiplyOperator_id> > MultiplyOperator;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<FunctionName_id> > FunctionName;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<VariableReference_id> > VariableReference;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<NameTest_id> > NameTest;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<S_id> > S; // ExprWhitespace
|
||||||
|
|
||||||
|
// bonus bits
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<Slash_id> > Slash;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<SlashSlash_id> > SlashSlash;
|
||||||
|
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<AncestorOrSelf_id> > AncestorOrSelf;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<Ancestor_id> > Ancestor;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<Attribute_id> > Attribute;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<Child_id> > Child;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<DescendantOrSelf_id> > DescendantOrSelf;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<Descendant_id> > Descendant;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<FollowingSibling_id> > FollowingSibling;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<Following_id> > Following;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<Namespace_id> > Namespace;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<Parent_id> > Parent;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<PrecedingSibling_id> > PrecedingSibling;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<Preceding_id> > Preceding;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<Self_id> > Self;
|
||||||
|
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<Comment_id> > Comment;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<Text_id> > Text;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<ProcessingInstruction_id> > ProcessingInstruction;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<Node_id> > Node;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<AnyName_id> > AnyName;
|
||||||
|
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<SelfSelect_id> > SelfSelect;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<ParentSelect_id> > ParentSelect;
|
||||||
|
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<LeftSquare_id> > LeftSquare;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<RightSquare_id> > RightSquare;
|
||||||
|
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<LeftBracket_id> > LeftBracket;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<RightBracket_id> > RightBracket;
|
||||||
|
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<PlusOperator_id> > PlusOperator;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<MinusOperator_id> > MinusOperator;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<ModOperator_id> > ModOperator;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<DivOperator_id> > DivOperator;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<EqualsOperator_id> > EqualsOperator;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<NotEqualsOperator_id> > NotEqualsOperator;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<LessThanOperator_id> > LessThanOperator;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<LessThanEqualsOperator_id> > LessThanEqualsOperator;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<GreaterThanOperator_id> > GreaterThanOperator;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<GreaterThanEqualsOperator_id> > GreaterThanEqualsOperator;
|
||||||
|
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<OrOperator_id> > OrOperator;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<AndOperator_id> > AndOperator;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<UnionOperator_id> > UnionOperator;
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<UnaryMinusOperator_id> > UnaryMinusOperator;
|
||||||
|
}; // xpath_grammar_definition
|
||||||
|
|
||||||
|
|
||||||
|
struct xpath_grammar : public boost::spirit::grammar<xpath_grammar>
|
||||||
|
{
|
||||||
|
template<typename ScannerT>
|
||||||
|
struct definition : public xpath_grammar_definition<ScannerT>
|
||||||
|
{
|
||||||
|
definition(xpath_grammar const& /* self */)
|
||||||
|
{
|
||||||
|
} // definition
|
||||||
|
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<LocationPath_id> > const&
|
||||||
|
start() const
|
||||||
|
{
|
||||||
|
return xpath_grammar_definition<ScannerT>::LocationPath;
|
||||||
|
} // start
|
||||||
|
}; // definition<ScannerT>
|
||||||
|
}; // xpath_grammar
|
||||||
|
|
||||||
|
struct xpath_grammar_expr : public boost::spirit::grammar<xpath_grammar_expr>
|
||||||
|
{
|
||||||
|
template<typename ScannerT>
|
||||||
|
struct definition : public xpath_grammar_definition<ScannerT>
|
||||||
|
{
|
||||||
|
definition(xpath_grammar_expr const& /* self */)
|
||||||
|
{
|
||||||
|
} // definition
|
||||||
|
|
||||||
|
boost::spirit::rule<ScannerT, boost::spirit::parser_tag<Expr_id> > const&
|
||||||
|
start() const
|
||||||
|
{
|
||||||
|
return xpath_grammar_definition<ScannerT>::Expr;
|
||||||
|
} // start
|
||||||
|
}; // definition<ScannerT>
|
||||||
|
}; // xpath_grammar
|
||||||
|
|
||||||
|
} // namespace XPath
|
||||||
|
} // namespace Arabica
|
||||||
|
|
||||||
|
#endif
|
53
XPath/impl/xpath_logical.hpp
Normal file
53
XPath/impl/xpath_logical.hpp
Normal file
|
@ -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<std::string>& 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<std::string>& 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
|
76
XPath/impl/xpath_namespace_context.hpp
Normal file
76
XPath/impl/xpath_namespace_context.hpp
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
#ifndef ARABICA_XPATH_NAMESPACE_CONTEXT_HPP
|
||||||
|
#define ARABICA_XPATH_NAMESPACE_CONTEXT_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
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<NamespaceContext> 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<std::string, std::string> NSMap;
|
||||||
|
NSMap map_;
|
||||||
|
}; // class StandardNamespaceContext
|
||||||
|
|
||||||
|
} // namespace XPath
|
||||||
|
|
||||||
|
} // namespace Arabica
|
||||||
|
#endif
|
82
XPath/impl/xpath_namespace_node.hpp
Normal file
82
XPath/impl/xpath_namespace_node.hpp
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
#ifndef ARABICA_XPATHIC_XPATH_NAMESPACE_NODE_HPP
|
||||||
|
#define ARABICA_XPATHIC_XPATH_NAMESPACE_NODE_HPP
|
||||||
|
|
||||||
|
#pragma warning(disable: 4250)
|
||||||
|
|
||||||
|
#include <DOM/Simple/DocumentImpl.h>
|
||||||
|
#include <DOM/Simple/NodeImpl.h>
|
||||||
|
#include <SAX/helpers/StringAdaptor.h>
|
||||||
|
|
||||||
|
namespace Arabica
|
||||||
|
{
|
||||||
|
namespace XPath
|
||||||
|
{
|
||||||
|
|
||||||
|
const DOM::Node_base::Type NAMESPACE_NODE_TYPE = static_cast<DOM::Node_base::Type>(DOM::Node_base::MAX_TYPE + 27); // 27 is a random choice
|
||||||
|
|
||||||
|
template<class stringT, class string_adaptorT = SAX::default_string_adaptor<stringT> >
|
||||||
|
class NamespaceNodeImpl : public SimpleDOM::ChildlessNodeImpl<stringT, string_adaptorT>
|
||||||
|
{
|
||||||
|
typedef SimpleDOM::ChildlessNodeImpl<stringT, string_adaptorT> NodeT;
|
||||||
|
public:
|
||||||
|
NamespaceNodeImpl(stringT localname,
|
||||||
|
stringT value) :
|
||||||
|
SimpleDOM::ChildlessNodeImpl<stringT, string_adaptorT>(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<stringT>* cloneNode(bool deep) const
|
||||||
|
{
|
||||||
|
return new NamespaceNodeImpl<stringT, string_adaptorT>(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
|
151
XPath/impl/xpath_node_test.hpp
Normal file
151
XPath/impl/xpath_node_test.hpp
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
#ifndef ARABICA_XPATHIC_XPATH_NODE_TEST_HPP
|
||||||
|
#define ARABICA_XPATHIC_XPATH_NODE_TEST_HPP
|
||||||
|
|
||||||
|
#include <DOM/Node.h>
|
||||||
|
#include "xpath_namespace_node.hpp"
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
|
namespace Arabica
|
||||||
|
{
|
||||||
|
namespace XPath
|
||||||
|
{
|
||||||
|
|
||||||
|
class NodeTest
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
NodeTest() { }
|
||||||
|
public:
|
||||||
|
virtual ~NodeTest() { }
|
||||||
|
virtual bool operator()(const DOM::Node<std::string>& 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<std::string>& 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<std::string>& 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<std::string>& 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<std::string>& 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<std::string>& node) const
|
||||||
|
{
|
||||||
|
return (uri_ == node.getNamespaceURI());
|
||||||
|
} // test
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string uri_;
|
||||||
|
}; // clase QStarNodeTest
|
||||||
|
|
||||||
|
class TextNodeTest : public NodeTest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual bool operator()(const DOM::Node<std::string>& node) const
|
||||||
|
{
|
||||||
|
return node.getNodeType() == DOM::Node<std::string>::TEXT_NODE;
|
||||||
|
} // test
|
||||||
|
}; // class TextNodeTest
|
||||||
|
|
||||||
|
class CommentNodeTest : public NodeTest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual bool operator()(const DOM::Node<std::string>& node) const
|
||||||
|
{
|
||||||
|
return node.getNodeType() == DOM::Node<std::string>::COMMENT_NODE;
|
||||||
|
} // operator()
|
||||||
|
}; // CommentNodeTest
|
||||||
|
|
||||||
|
class ProcessingInstructionNodeTest : public NodeTest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ProcessingInstructionNodeTest() : target_() { }
|
||||||
|
ProcessingInstructionNodeTest(const std::string& target) : target_(target) { }
|
||||||
|
|
||||||
|
virtual bool operator()(const DOM::Node<std::string>& node) const
|
||||||
|
{
|
||||||
|
if(node.getNodeType() != DOM::Node<std::string>::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<std::string>& node) const
|
||||||
|
{
|
||||||
|
int type = node.getNodeType();
|
||||||
|
return (type == DOM::Node<std::string>::DOCUMENT_NODE) ||
|
||||||
|
(type == DOM::Node<std::string>::DOCUMENT_FRAGMENT_NODE);
|
||||||
|
|
||||||
|
} // operator()
|
||||||
|
}; // RootNodeTest
|
||||||
|
|
||||||
|
} // namespace XPath
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace Arabica
|
||||||
|
|
||||||
|
#endif
|
210
XPath/impl/xpath_object.hpp
Normal file
210
XPath/impl/xpath_object.hpp
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
#ifndef ARABICA_XPATHIC_XPATH_OBJECT_H
|
||||||
|
#define ARABICA_XPATHIC_XPATH_OBJECT_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <utility>
|
||||||
|
#include <DOM/Node.h>
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
#include <cmath>
|
||||||
|
#include "xpath_execution_context.hpp"
|
||||||
|
|
||||||
|
namespace Arabica
|
||||||
|
{
|
||||||
|
namespace XPath
|
||||||
|
{
|
||||||
|
|
||||||
|
class XPathExpression;
|
||||||
|
typedef boost::shared_ptr<XPathExpression> XPathExpressionPtr;
|
||||||
|
class XPathValue;
|
||||||
|
typedef boost::shared_ptr<const XPathValue> XPathValuePtr;
|
||||||
|
|
||||||
|
enum ValueType
|
||||||
|
{
|
||||||
|
ANY ,
|
||||||
|
BOOL,
|
||||||
|
NUMBER,
|
||||||
|
STRING,
|
||||||
|
NODE_SET
|
||||||
|
}; // ValueType
|
||||||
|
|
||||||
|
int compareNodes(const DOM::Node<std::string>& n1, const DOM::Node<std::string>& n2);
|
||||||
|
bool nodes_less_than(const DOM::Node<std::string>& n1, const DOM::Node<std::string>& n2);
|
||||||
|
|
||||||
|
class NodeSet : public std::vector<DOM::Node<std::string> >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NodeSet() : forward_(true), sorted_(false), std::vector<DOM::Node<std::string> >() { }
|
||||||
|
NodeSet(bool forward) : forward_(forward), sorted_(true), std::vector<DOM::Node<std::string> >() { }
|
||||||
|
NodeSet(const NodeSet& rhs) : forward_(rhs.forward_), sorted_(rhs.sorted_), std::vector<DOM::Node<std::string> >(rhs) { }
|
||||||
|
NodeSet& operator=(const NodeSet& rhs)
|
||||||
|
{
|
||||||
|
forward_ = rhs.forward_;
|
||||||
|
sorted_ = rhs.sorted_;
|
||||||
|
std::vector<DOM::Node<std::string> >::operator=(rhs);
|
||||||
|
return *this;
|
||||||
|
} // operator=
|
||||||
|
|
||||||
|
void swap(NodeSet& rhs)
|
||||||
|
{
|
||||||
|
std::vector<DOM::Node<std::string> >::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<std::string> 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<std::string>& node);
|
||||||
|
std::string nodeStringValue(const DOM::Node<std::string>& 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<std::string>& context) const
|
||||||
|
{
|
||||||
|
ExecutionContext executionContext;
|
||||||
|
return evaluate(context, executionContext);
|
||||||
|
} // evaluate
|
||||||
|
|
||||||
|
virtual bool evaluateAsBool(const DOM::Node<std::string>& context) const { return evaluate(context)->asBool(); }
|
||||||
|
virtual double evaluateAsNumber(const DOM::Node<std::string>& context) const { return evaluate(context)->asNumber(); }
|
||||||
|
virtual std::string evaluateAsString(const DOM::Node<std::string>& context) const { return evaluate(context)->asString(); }
|
||||||
|
virtual NodeSet evaluateAsNodeSet(const DOM::Node<std::string>& context) const { return evaluate(context)->asNodeSet(); }
|
||||||
|
|
||||||
|
virtual XPathValuePtr evaluate(const DOM::Node<std::string>& 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
|
97
XPath/impl/xpath_parser.hpp
Normal file
97
XPath/impl/xpath_parser.hpp
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
#ifndef ARABICA_XPATHIC_XPATH_PARSER_HPP
|
||||||
|
#define ARABICA_XPATHIC_XPATH_PARSER_HPP
|
||||||
|
|
||||||
|
#include <boost/spirit/core.hpp>
|
||||||
|
#include <boost/spirit/tree/ast.hpp>
|
||||||
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
#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<std::string>& context) const;
|
||||||
|
XPathValuePtr evaluate_expr(const std::string& xpath, const DOM::Node<std::string>& 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<const NamespaceContext> namespaceContext_;
|
||||||
|
ResolverHolder<const VariableResolver> variableResolver_;
|
||||||
|
ResolverHolder<const FunctionResolver> functionResolver_;
|
||||||
|
|
||||||
|
typedef XPathExpression* (*compileFn)(node_iter_t const& i, CompilationContext& context);
|
||||||
|
static std::map<int, compileFn> factory_;
|
||||||
|
static std::map<int, std::string> names_;
|
||||||
|
static const std::map<int, compileFn> createFunctions();
|
||||||
|
static const std::map<int, std::string> 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
|
97
XPath/impl/xpath_relational.hpp
Normal file
97
XPath/impl/xpath_relational.hpp
Normal file
|
@ -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<std::string>& 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<std::string>& 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<std::string>& 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<std::string>& 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<std::string>& 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<std::string>& 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
|
69
XPath/impl/xpath_resolver_holder.hpp
Normal file
69
XPath/impl/xpath_resolver_holder.hpp
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
#ifndef ARABICA_XPATH_RESOLVER_HOLDER_HPP
|
||||||
|
#define ARABICA_XPATH_RESOLVER_HOLDER_HPP
|
||||||
|
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
|
namespace Arabica
|
||||||
|
{
|
||||||
|
namespace XPath
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename ResolverT>
|
||||||
|
class ResolverHolder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef boost::shared_ptr<ResolverT> 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
|
416
XPath/impl/xpath_step.hpp
Normal file
416
XPath/impl/xpath_step.hpp
Normal file
|
@ -0,0 +1,416 @@
|
||||||
|
#ifndef ARABICA_XPATHIC_XPATH_STEP_H
|
||||||
|
#define ARABICA_XPATHIC_XPATH_STEP_H
|
||||||
|
|
||||||
|
#include <DOM/Document.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#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<XPathExpression*> predicates) : predicates_(predicates) { }
|
||||||
|
|
||||||
|
virtual ~StepExpression()
|
||||||
|
{
|
||||||
|
for(std::vector<XPathExpression*>::iterator p = predicates_.begin(), e = predicates_.end(); p != e; ++p)
|
||||||
|
delete *p;
|
||||||
|
} // ~StepExpression
|
||||||
|
|
||||||
|
virtual XPathValuePtr evaluate(const DOM::Node<std::string>& 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<XPathExpression*>::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<XPathExpression*> 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<XPathExpression*> predicates) :
|
||||||
|
StepExpression(predicates),
|
||||||
|
axis_(axis),
|
||||||
|
test_(test)
|
||||||
|
{
|
||||||
|
} // TestStepExpression
|
||||||
|
|
||||||
|
virtual ~TestStepExpression()
|
||||||
|
{
|
||||||
|
delete test_;
|
||||||
|
} // StepExpression
|
||||||
|
|
||||||
|
virtual XPathValuePtr evaluate(const DOM::Node<std::string>& 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<std::string>& 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<std::string> 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<XPathExpression*> predicates) :
|
||||||
|
StepExpression(predicates),
|
||||||
|
expr_(expr)
|
||||||
|
{
|
||||||
|
} // ExprStepExpression
|
||||||
|
|
||||||
|
virtual ~ExprStepExpression()
|
||||||
|
{
|
||||||
|
delete expr_;
|
||||||
|
} // ExprStepExpression
|
||||||
|
|
||||||
|
virtual XPathValuePtr evaluate(const DOM::Node<std::string>& 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<std::string> c = context.top();
|
||||||
|
return evaluate(c, executionContext);
|
||||||
|
} // evaluate
|
||||||
|
|
||||||
|
private:
|
||||||
|
XPathExpression* expr_;
|
||||||
|
std::vector<XPathExpression*> 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<XPathExpression*> 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<StepExpression*> 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<std::string>& 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<std::string>& context, const ExecutionContext& executionContext) const
|
||||||
|
{
|
||||||
|
int type = context.getNodeType();
|
||||||
|
if((type == DOM::Node<std::string>::DOCUMENT_NODE) ||
|
||||||
|
(type == DOM::Node<std::string>::DOCUMENT_FRAGMENT_NODE))
|
||||||
|
return RelativeLocationPath::evaluate(context, executionContext);
|
||||||
|
|
||||||
|
DOM::Document<std::string> document = context.getOwnerDocument();
|
||||||
|
return RelativeLocationPath::evaluate(document, executionContext);
|
||||||
|
} // evaluate
|
||||||
|
}; // class AbsoluteLocationPath
|
||||||
|
|
||||||
|
|
||||||
|
} // XPath
|
||||||
|
} // Arabica
|
||||||
|
#endif
|
75
XPath/impl/xpath_union.hpp
Normal file
75
XPath/impl/xpath_union.hpp
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
#ifndef ARABICA_XPATHIC_XPATH_UNION_HPP
|
||||||
|
#define ARABICA_XPATHIC_XPATH_UNION_HPP
|
||||||
|
|
||||||
|
#include "xpath_value.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
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<std::string>& 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
|
148
XPath/impl/xpath_value.hpp
Normal file
148
XPath/impl/xpath_value.hpp
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
#ifndef ARABICA_XPATHIC_XPATH_VALUE_H
|
||||||
|
#define ARABICA_XPATHIC_XPATH_VALUE_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <DOM/Node.h>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#include <vector>
|
||||||
|
#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<std::string>& context, const ExecutionContext& executionContext) const
|
||||||
|
{
|
||||||
|
return XPathValuePtr(new BoolValue(value_));
|
||||||
|
} // evaluate
|
||||||
|
virtual bool evaluateAsBool(const DOM::Node<std::string>& context) { return asBool(); }
|
||||||
|
virtual double evaluateAsNumber(const DOM::Node<std::string>& context) { return asNumber(); }
|
||||||
|
virtual std::string evaluateAsString(const DOM::Node<std::string>& context) { return asString(); }
|
||||||
|
virtual NodeSet evaluateAsNodeSet(const DOM::Node<std::string>& 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<std::string>& context, const ExecutionContext& executionContext) const
|
||||||
|
{
|
||||||
|
return createValue(value_);
|
||||||
|
} // evaluate
|
||||||
|
virtual bool evaluateAsBool(const DOM::Node<std::string>& context) { return asBool(); }
|
||||||
|
virtual double evaluateAsNumber(const DOM::Node<std::string>& context) { return asNumber(); }
|
||||||
|
virtual std::string evaluateAsString(const DOM::Node<std::string>& context) { return asString(); }
|
||||||
|
virtual NodeSet evaluateAsNodeSet(const DOM::Node<std::string>& 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<std::string>(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<std::string>& context, const ExecutionContext& executionContext) const
|
||||||
|
{
|
||||||
|
return XPathValuePtr(new StringValue(value_));
|
||||||
|
} // evaluate
|
||||||
|
virtual bool evaluateAsBool(const DOM::Node<std::string>& context) { return asBool(); }
|
||||||
|
virtual double evaluateAsNumber(const DOM::Node<std::string>& context) { return asNumber(); }
|
||||||
|
virtual std::string evaluateAsString(const DOM::Node<std::string>& 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<std::string>& context, const ExecutionContext& executionContext) const
|
||||||
|
{
|
||||||
|
return XPathValuePtr(this);
|
||||||
|
} // evaluate
|
||||||
|
virtual bool evaluateAsBool(const DOM::Node<std::string>& context) const{ return asBool(); }
|
||||||
|
virtual double evaluateAsNumber(const DOM::Node<std::string>& context) const { return asNumber(); }
|
||||||
|
virtual std::string evaluateAsString(const DOM::Node<std::string>& 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
|
31
XPath/impl/xpath_variable.hpp
Normal file
31
XPath/impl/xpath_variable.hpp
Normal file
|
@ -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<std::string>& context,
|
||||||
|
const ExecutionContext& executionContext) const
|
||||||
|
{
|
||||||
|
return executionContext.variableResolver().resolveVariable(name_);
|
||||||
|
} // evaluate
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string name_;
|
||||||
|
}; // class Variable
|
||||||
|
|
||||||
|
} // namespace XPath
|
||||||
|
} // namespace Arabica
|
||||||
|
|
||||||
|
#endif
|
42
XPath/impl/xpath_variable_resolver.hpp
Normal file
42
XPath/impl/xpath_variable_resolver.hpp
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef ARABICA_XPATH_VARIABLE_RESOLVER_HPP
|
||||||
|
#define ARABICA_XPATH_VARIABLE_RESOLVER_HPP
|
||||||
|
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
|
namespace Arabica
|
||||||
|
{
|
||||||
|
namespace XPath
|
||||||
|
{
|
||||||
|
|
||||||
|
class XPathValue;
|
||||||
|
typedef boost::shared_ptr<const XPathValue> 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<VariableResolver> VariableResolverPtr;
|
||||||
|
|
||||||
|
class NullVariableResolver : public VariableResolver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual XPathValuePtr resolveVariable(const std::string& name) const
|
||||||
|
{
|
||||||
|
throw UnboundVariableException(name);
|
||||||
|
} // resolveVariable
|
||||||
|
}; // NullVariableResolver
|
||||||
|
|
||||||
|
} // namespace XPath
|
||||||
|
} // namespace Arabica
|
||||||
|
|
||||||
|
#endif
|
20
XPath/src/xpath_axis_enumerator.cpp
Normal file
20
XPath/src/xpath_axis_enumerator.cpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#include <XPath/impl/xpath_axis_enumerator.hpp>
|
||||||
|
|
||||||
|
using namespace Arabica::XPath;
|
||||||
|
|
||||||
|
const AxisEnumerator::NamedAxis AxisEnumerator::AxisLookupTable[] = {
|
||||||
|
{ ANCESTOR, AxisEnumerator::CreateAxis<AxisEnumerator::AncestorAxisWalker> },
|
||||||
|
{ ANCESTOR_OR_SELF, AxisEnumerator::CreateAxis<AxisEnumerator::AncestorOrSelfAxisWalker> },
|
||||||
|
{ ATTRIBUTE, AxisEnumerator::CreateAxis<AxisEnumerator::AttributeAxisWalker> },
|
||||||
|
{ CHILD, AxisEnumerator::CreateAxis<AxisEnumerator::ChildAxisWalker> },
|
||||||
|
{ DESCENDANT, AxisEnumerator::CreateAxis<AxisEnumerator::DescendantAxisWalker> },
|
||||||
|
{ DESCENDANT_OR_SELF, AxisEnumerator::CreateAxis<AxisEnumerator::DescendantOrSelfAxisWalker> },
|
||||||
|
{ FOLLOWING, AxisEnumerator::CreateAxis<AxisEnumerator::FollowingAxisWalker> },
|
||||||
|
{ FOLLOWING_SIBLING, AxisEnumerator::CreateAxis<AxisEnumerator::FollowingSiblingAxisWalker> },
|
||||||
|
{ NAMESPACE, AxisEnumerator::CreateAxis<AxisEnumerator::NamespaceAxisWalker> },
|
||||||
|
{ PARENT, AxisEnumerator::CreateAxis<AxisEnumerator::ParentAxisWalker> },
|
||||||
|
{ PRECEDING, AxisEnumerator::CreateAxis<AxisEnumerator::PrecedingAxisWalker> },
|
||||||
|
{ PRECEDING_SIBLING, AxisEnumerator::CreateAxis<AxisEnumerator::PrecedingSiblingAxisWalker> },
|
||||||
|
{ SELF, AxisEnumerator::CreateAxis<AxisEnumerator::SelfAxisWalker> },
|
||||||
|
{ static_cast<Axis>(0), 0 }
|
||||||
|
};
|
369
XPath/src/xpath_object.cpp
Normal file
369
XPath/src/xpath_object.cpp
Normal file
|
@ -0,0 +1,369 @@
|
||||||
|
#include <XPath/impl/xpath_object.hpp>
|
||||||
|
#include <XPath/impl/xpath_axis_enumerator.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#include <set>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
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<class T> T nodeValue(const DOM::Node<std::string>& node);
|
||||||
|
template<> std::string nodeValue(const DOM::Node<std::string>& node) { return nodeStringValue(node); }
|
||||||
|
template<> double nodeValue(const DOM::Node<std::string>& node) { return nodeNumberValue(node); }
|
||||||
|
|
||||||
|
template<class Op>
|
||||||
|
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<std::string>& node)
|
||||||
|
{
|
||||||
|
return Op()(nodeValue<T>(node), value_);
|
||||||
|
} // operator()
|
||||||
|
|
||||||
|
private:
|
||||||
|
T value_;
|
||||||
|
bool operator==(const compareNodeWith&);
|
||||||
|
compareNodeWith& operator=(const compareNodeWith&);
|
||||||
|
}; // class compareNodeWith
|
||||||
|
|
||||||
|
template<class Op>
|
||||||
|
bool compareNodeSets(const XPathValuePtr& lhs, const XPathValuePtr& rhs)
|
||||||
|
{
|
||||||
|
return Op()(minValue(lhs->asNodeSet()), maxValue(rhs->asNodeSet()));
|
||||||
|
} // compareNodeSets
|
||||||
|
|
||||||
|
template<class Op>
|
||||||
|
bool compareNodeSetWith(const XPathValuePtr& lhs, const XPathValuePtr& rhs)
|
||||||
|
{
|
||||||
|
const NodeSet& lns = lhs->asNodeSet();
|
||||||
|
return std::find_if(lns.begin(),
|
||||||
|
lns.end(),
|
||||||
|
compareNodeWith<Op>(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<std::string> 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<std::equal_to<std::string> >(rhs->asString())) != lns.end();
|
||||||
|
|
||||||
|
case NUMBER:
|
||||||
|
return std::find_if(lns.begin(), lns.end(), compareNodeWith<std::equal_to<double> >(rhs->asNumber())) != lns.end();
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Node set == not yet implemented for type " + boost::lexical_cast<std::string>(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<std::less<double> >(lhs, rhs);
|
||||||
|
|
||||||
|
if(lt == NODE_SET)
|
||||||
|
return compareNodeSetWith<std::less<double> >(lhs, rhs);
|
||||||
|
|
||||||
|
if(rt == NODE_SET)
|
||||||
|
return compareNodeSetWith<std::greater<double> >(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<std::less_equal<double> >(lhs, rhs);
|
||||||
|
|
||||||
|
if(lt == NODE_SET)
|
||||||
|
return compareNodeSetWith<std::less_equal<double> >(lhs, rhs);
|
||||||
|
|
||||||
|
if(rt == NODE_SET)
|
||||||
|
return compareNodeSetWith<std::greater_equal<double> >(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<double>(str);
|
||||||
|
} // try
|
||||||
|
catch(const boost::bad_lexical_cast&) {
|
||||||
|
return NaN;
|
||||||
|
} // catch
|
||||||
|
} // stringAsNumber
|
||||||
|
|
||||||
|
double nodeNumberValue(const DOM::Node<std::string>& node)
|
||||||
|
{
|
||||||
|
return stringAsNumber(nodeStringValue(node));
|
||||||
|
} // nodeNumberValue
|
||||||
|
|
||||||
|
std::string nodeStringValue(const DOM::Node<std::string>& node)
|
||||||
|
{
|
||||||
|
switch(node.getNodeType())
|
||||||
|
{
|
||||||
|
case DOM::Node<std::string>::DOCUMENT_NODE:
|
||||||
|
case DOM::Node<std::string>::DOCUMENT_FRAGMENT_NODE:
|
||||||
|
case DOM::Node<std::string>::ELEMENT_NODE:
|
||||||
|
{
|
||||||
|
std::ostringstream os;
|
||||||
|
AxisEnumerator ae(node, DESCENDANT);
|
||||||
|
while(*ae != 0)
|
||||||
|
{
|
||||||
|
if((ae->getNodeType() == DOM::Node<std::string>::TEXT_NODE) ||
|
||||||
|
(ae->getNodeType() == DOM::Node<std::string>::CDATA_SECTION_NODE))
|
||||||
|
os << ae->getNodeValue();
|
||||||
|
++ae;
|
||||||
|
} // while
|
||||||
|
return os.str();
|
||||||
|
} // case
|
||||||
|
|
||||||
|
case DOM::Node<std::string>::ATTRIBUTE_NODE:
|
||||||
|
case DOM::Node<std::string>::PROCESSING_INSTRUCTION_NODE:
|
||||||
|
case DOM::Node<std::string>::COMMENT_NODE:
|
||||||
|
case DOM::Node<std::string>::TEXT_NODE:
|
||||||
|
case DOM::Node<std::string>::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<std::string> node_parent_or_owner(const DOM::Node<std::string>& node)
|
||||||
|
{
|
||||||
|
if(node.getNodeType() == DOM::Node<std::string>::ATTRIBUTE_NODE)
|
||||||
|
return (static_cast<DOM::Attr<std::string> >(node)).getOwnerElement();
|
||||||
|
return node.getParentNode();
|
||||||
|
} // node_parent_or_owner
|
||||||
|
|
||||||
|
DOM::Node<std::string> ultimate_parent(const DOM::Node<std::string>& origin)
|
||||||
|
{
|
||||||
|
DOM::Node<std::string> n = origin;
|
||||||
|
DOM::Node<std::string> 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<std::string>& attr)
|
||||||
|
{
|
||||||
|
DOM::NamedNodeMap<std::string> 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<std::string>& node)
|
||||||
|
{
|
||||||
|
if(node.getNodeType() == DOM::Node<std::string>::ATTRIBUTE_NODE)
|
||||||
|
return node_attribute_index(static_cast<DOM::Attr<std::string> >(node));
|
||||||
|
|
||||||
|
unsigned int pos = 0;
|
||||||
|
DOM::Node<std::string> n = node;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
n = n.getPreviousSibling();
|
||||||
|
pos += 1000;
|
||||||
|
} while(n != 0);
|
||||||
|
return pos;
|
||||||
|
} // node_child_position
|
||||||
|
|
||||||
|
std::vector<unsigned int> node_position(const DOM::Node<std::string>& node)
|
||||||
|
{
|
||||||
|
std::vector<unsigned int> pos;
|
||||||
|
DOM::Node<std::string> 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<std::string>& lhs, const DOM::Node<std::string>& rhs)
|
||||||
|
{
|
||||||
|
// if we have something in the document, and a document fragment,
|
||||||
|
// sort the doc ahead of the fragment
|
||||||
|
DOM::Node<std::string> lp = ultimate_parent(lhs);
|
||||||
|
if(lp.getNodeType() == DOM::Node<std::string>::DOCUMENT_NODE)
|
||||||
|
return -1;
|
||||||
|
DOM::Node<std::string> rp = ultimate_parent(rhs);
|
||||||
|
if(rp.getNodeType() == DOM::Node<std::string>::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<std::string>& lhs, const DOM::Node<std::string>& 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<unsigned int> pos1 = node_position(lhs);
|
||||||
|
std::vector<unsigned int> pos2 = node_position(rhs);
|
||||||
|
|
||||||
|
std::vector<unsigned int>::const_reverse_iterator l = pos1.rbegin(), le = pos1.rend();
|
||||||
|
std::vector<unsigned int>::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<std::string>& n1, const DOM::Node<std::string>& n2)
|
||||||
|
{
|
||||||
|
return compareNodes(n1, n2) < 0;
|
||||||
|
} // nodes_less_than
|
||||||
|
|
||||||
|
} // namespace XPath
|
||||||
|
} // namespace Arabica
|
462
XPath/src/xpath_parser.cpp
Normal file
462
XPath/src/xpath_parser.cpp
Normal file
|
@ -0,0 +1,462 @@
|
||||||
|
#pragma warning(disable:4224 4180 4244)
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#include <XPath/impl/xpath_parser.hpp>
|
||||||
|
#include <XPath/impl/xpath_value.hpp>
|
||||||
|
#include <XPath/impl/xpath_arithmetic.hpp>
|
||||||
|
#include <XPath/impl/xpath_relational.hpp>
|
||||||
|
#include <XPath/impl/xpath_logical.hpp>
|
||||||
|
#include <XPath/impl/xpath_step.hpp>
|
||||||
|
#include <XPath/impl/xpath_compile_context.hpp>
|
||||||
|
#include <XPath/impl/xpath_variable.hpp>
|
||||||
|
#include <XPath/impl/xpath_function_holder.hpp>
|
||||||
|
#include <XPath/impl/xpath_union.hpp>
|
||||||
|
|
||||||
|
using namespace Arabica::XPath;
|
||||||
|
|
||||||
|
long Arabica::XPath::getNodeId(node_iter_t const& node)
|
||||||
|
{
|
||||||
|
return static_cast<long>(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<int, std::string> XPath::names_ = debugNames();
|
||||||
|
std::map<int, XPath::compileFn> XPath::factory_ = createFunctions();
|
||||||
|
|
||||||
|
void XPath::dump(node_iter_t const& i, int depth)
|
||||||
|
{
|
||||||
|
long id = static_cast<long>(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<XPathExpressionPtr> 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<double>(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<std::string>(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<std::string>& 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<std::string>& 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<int, XPath::compileFn> XPath::createFunctions()
|
||||||
|
{
|
||||||
|
std::map<int, XPath::compileFn> 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<int, std::string> XPath::debugNames()
|
||||||
|
{
|
||||||
|
std::map<int, std::string> 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
|
Loading…
Add table
Reference in a new issue