moved here from xpath-dev-sandbox

This commit is contained in:
jez_higgins 2005-08-04 20:42:30 +00:00
parent 50f169b1c7
commit b18f90ce9e
27 changed files with 4266 additions and 0 deletions

12
XPath/XPath.hpp Normal file
View 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

View 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
View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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
View 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

View 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

View 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

View 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
View 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

View 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
View 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

View 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

View 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

View 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
View 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
View 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