#ifndef ARABICA_XPATH_FUNCTION_HPP #define ARABICA_XPATH_FUNCTION_HPP #include #include #include #include #include #include "xpath_value.hpp" #include "xpath_execution_context.hpp" namespace Arabica { namespace XPath { template > class XPathFunction { protected: XPathFunction(int minArgs, int maxArgs, const std::vector >& args) : args_(args) { if(((minArgs != -1) && (static_cast(args.size()) < minArgs)) || ((maxArgs != -1) && (static_cast(args.size()) > maxArgs))) throw SyntaxException("wrong number of arguments to function"); } // XPathFunction public: virtual ~XPathFunction() { } virtual ValueType type() const = 0; virtual XPathValue_impl* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const = 0; protected: size_t argCount() const { return args_.size(); } XPathValue arg(size_t index, const DOM::Node& context, const ExecutionContext& executionContext) const { return args_[index].evaluate(context, executionContext); } // arg bool argAsBool(size_t index, const DOM::Node& context, const ExecutionContext& executionContext) const { return args_[index].evaluateAsBool(context, executionContext); } // argAsBool double argAsNumber(size_t index, const DOM::Node& context, const ExecutionContext& executionContext) const { return args_[index].evaluateAsNumber(context, executionContext); } // argAsNumber string_type argAsString(size_t index, const DOM::Node& context, const ExecutionContext& executionContext) const { return args_[index].evaluateAsString(context, executionContext); } // argAsString NodeSet argAsNodeSet(size_t index, const DOM::Node& context, const ExecutionContext& executionContext) const { return args_[index].evaluateAsNodeSet(context, executionContext); } // argAsNodeSet private: const std::vector > args_; }; // class XPathFunction namespace impl { template class BooleanFunction : public XPathFunction { public: BooleanFunction(int minArgs, int maxArgs, const std::vector >& args) : XPathFunction(minArgs, maxArgs, args) { } virtual ValueType type() const { return BOOL; } virtual XPathValue_impl* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return new BoolValue(doEvaluate(context, executionContext)); } // evaluate protected: virtual bool doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const = 0; }; // class BooleanFunction template class NumericFunction : public XPathFunction { public: NumericFunction(int minArgs, int maxArgs, const std::vector >& args) : XPathFunction(minArgs, maxArgs, args) { } virtual ValueType type() const { return NUMBER; } virtual XPathValue_impl* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return new NumericValue(doEvaluate(context, executionContext)); } // evaluate protected: virtual double doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const = 0; }; // class NumericFunction template class StringFunction : public XPathFunction { public: StringFunction(int minArgs, int maxArgs, const std::vector >& args) : XPathFunction(minArgs, maxArgs, args) { } virtual ValueType type() const { return STRING; } virtual XPathValue_impl* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return new StringValue(doEvaluate(context, executionContext)); } // evaluate protected: virtual string_type doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const = 0; }; // class StringFunction //////////////////////////////// // node-set functions // number last() template class LastFn : public NumericFunction { public: LastFn(const std::vector >& args) : NumericFunction(0, 0, args) { } protected: virtual double doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return executionContext.last(); } // evaluate }; // class LastFn // number position() template class PositionFn : public NumericFunction { public: PositionFn(const std::vector >& args) : NumericFunction(0, 0, args) { } protected: virtual double doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return executionContext.position(); } // evaluate }; // class PositionFn // number count(node-set) template class CountFn : public NumericFunction { typedef NumericFunction baseT; public: CountFn(const std::vector >& args) : NumericFunction(1, 1, args) { } virtual double doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return baseT::argAsNodeSet(0, context, executionContext).size(); } // evaluate }; // class CountFn // node-set id(object) // string local-name(node-set?) template class LocalNameFn : public StringFunction { typedef StringFunction baseT; public: LocalNameFn(const std::vector >& args) : StringFunction(0, 1, args) { } protected: virtual string_type doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { DOM::Node node; if(baseT::argCount() == 0) node = context; else { NodeSet ns = baseT::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: case NAMESPACE_NODE_TYPE: return node.hasNamespaceURI() ? node.getLocalName() : node.getNodeName(); default: // put this in to keep gcc quiet ; } // switch ... return string_adaptor::empty_string(); } // evaluate }; // class LocalNameFn // string namespace-uri(node-set?) template class NamespaceURIFn : public StringFunction { typedef StringFunction baseT; public: NamespaceURIFn(const std::vector >& args) : StringFunction(0, 1, args) { } protected: virtual string_type doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { DOM::Node node; if(baseT::argCount() == 0) node = context; else { NodeSet ns = baseT::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 node.getNamespaceURI(); default: // put this in to keep gcc quiet ; } // switch ... return string_adaptor::empty_string(); } // evaluate }; // class NamespaceURIFn // string name(node-set?) template class NameFn : public StringFunction { typedef StringFunction baseT; public: NameFn(const std::vector >& args) : StringFunction(0, 1, args) { } protected: virtual string_type doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { DOM::Node node; if(baseT::argCount() == 0) node = context; else { NodeSet ns = baseT::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: case NAMESPACE_NODE_TYPE: return node.getNodeName(); default: // stop gcc generating a warning about unhandled enum values ; } // switch ... return string_adaptor::empty_string(); } // evaluate }; // class NameFn /////////////////////////////////////////////// // string functions // string string(object?) template class StringFn : public StringFunction { typedef StringFunction baseT; public: StringFn(const std::vector >& args) : StringFunction(0, 1, args) { } protected: virtual string_type doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return (baseT::argCount() > 0) ? baseT::argAsString(0, context, executionContext) : nodeStringValue(context); } // doEvaluate }; // class StringFn // string concat(string, string, string*) template class ConcatFn : public StringFunction { typedef StringFunction baseT; public: ConcatFn(const std::vector >& args) : StringFunction(2, -1, args) { } protected: virtual string_type doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { string_type s; for(size_t a = 0, ae = baseT::argCount(); a < ae; ++a) string_adaptor::append(s, baseT::argAsString(a, context, executionContext)); return s; } // evaluate }; // ConcatFn // boolean starts-with(string, string) template class StartsWithFn : public BooleanFunction { typedef BooleanFunction baseT; public: StartsWithFn(const std::vector >& args) : BooleanFunction(2, 2, args) { } protected: virtual bool doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { string_type value = baseT::argAsString(0, context, executionContext); string_type start = baseT::argAsString(1, context, executionContext); if(string_adaptor::length(value) < string_adaptor::length(start)) return false; typename string_adaptor::const_iterator i = string_adaptor::begin(value); typename string_adaptor::const_iterator s = string_adaptor::begin(start); typename string_adaptor::const_iterator e = string_adaptor::end(start); for(; s != e; ++s, ++i) if(*i != *s) return false; return true; } // evaluate }; // StartsWithFn // boolean contains(string, string) template class ContainsFn : public BooleanFunction { typedef BooleanFunction baseT; public: ContainsFn(const std::vector >& args) : BooleanFunction(2, 2, args) { } protected: virtual bool doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return string_adaptor::find(baseT::argAsString(0, context, executionContext), baseT::argAsString(1, context, executionContext)) != string_adaptor::npos(); } // evaluate }; // class ContainsFn // string substring-before(string, string) template class SubstringBeforeFn : public StringFunction { typedef StringFunction baseT; public: SubstringBeforeFn(const std::vector >& args) : StringFunction(2, 2, args) { } protected: virtual string_type doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { string_type value = baseT::argAsString(0, context, executionContext); size_t splitAt = string_adaptor::find(value, baseT::argAsString(1, context, executionContext)); if(splitAt == string_adaptor::npos()) return string_adaptor::empty_string(); return string_adaptor::substr(value, 0, splitAt); } // evaluate }; // class SubstringBeforeFn // string substring-after(string, string) template class SubstringAfterFn : public StringFunction { typedef StringFunction baseT; public: SubstringAfterFn(const std::vector >& args) : StringFunction(2, 2, args) { } protected: virtual string_type doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { string_type value = baseT::argAsString(0, context, executionContext); string_type split = baseT::argAsString(1, context, executionContext); size_t splitAt = string_adaptor::find(value, split); if((splitAt == string_adaptor::npos()) || ((splitAt + string_adaptor::length(split)) >= string_adaptor::length(value))) return string_adaptor::empty_string(); return string_adaptor::substr(value, splitAt + string_adaptor::length(split)); } // evaluate }; // class SubstringAfterFn // string substring(string, number, number?) template class SubstringFn : public StringFunction { typedef StringFunction baseT; public: SubstringFn(const std::vector >& args) : StringFunction(2, 3, args) { } protected: virtual string_type doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { string_type value = baseT::argAsString(0, context, executionContext); if(string_adaptor::empty(value)) return string_adaptor::empty_string(); double startAt = roundNumber(baseT::argAsNumber(1, context, executionContext)) - 1; double endAt = roundNumber((baseT::argCount() == 3 ? baseT::argAsNumber(2, context, executionContext) : Infinity)) + startAt; if((endAt < 0) || (endAt < startAt) || (isNaN(endAt))) return string_adaptor::empty_string(); if(startAt < 0) startAt = 0; if((isInfinite(endAt)) || (endAt > string_adaptor::length(value))) endAt = string_adaptor::length(value); return string_adaptor::substr(value, static_cast(startAt), static_cast(endAt - startAt)); } // evaluate }; // SubstringFn // number string-length(string?) template class StringLengthFn : public NumericFunction { typedef NumericFunction baseT; public: StringLengthFn(const std::vector >& args) : NumericFunction(0, 1, args) { } virtual double doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { string_type v = (baseT::argCount() > 0) ? baseT::argAsString(0, context, executionContext) : nodeStringValue(context); return string_adaptor::length(v); } // evaluate }; // StringLengthFn // string normalize-space(string?) template class NormalizeSpaceFn : public StringFunction { typedef StringFunction baseT; public: NormalizeSpaceFn(const std::vector >& args) : StringFunction(0, 1, args) { } protected: virtual string_type doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { string_type initial = ((baseT::argCount() > 0) ? baseT::argAsString(0, context, executionContext) : nodeStringValue(context)); string_type value = Arabica::text::normalize_whitespace(initial); return value; } // evaluate }; // class NormalizeSpaceFn // string translate(string, string, string) template class TranslateFn : public StringFunction { typedef StringFunction baseT; public: TranslateFn(const std::vector >& args) : StringFunction(3, 3, args) { } protected: virtual string_type doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { string_type str = baseT::argAsString(0, context, executionContext); string_type from = baseT::argAsString(1, context, executionContext); string_type to = baseT::argAsString(2, context, executionContext); typename string_adaptor::mutable_iterator p = string_adaptor::begin(str); for(typename string_adaptor::mutable_iterator i = string_adaptor::begin(str), ie = string_adaptor::end(str); i != ie; ++i) { size_t r = string_adaptor::find(from, *i); if(r == string_adaptor::npos()) ++p; else if(r < string_adaptor::length(to)) *p++ = *(string_adaptor::begin(to) + r); } // for ... if(p != string_adaptor::end(str)) *p = 0; return string_adaptor::construct(string_adaptor::begin(str), p); } // evaluate }; // class TranslateFn /////////////////////////////////////////////////////// // boolean functions // boolean boolean(object) template class BooleanFn : public BooleanFunction { typedef BooleanFunction baseT; public: BooleanFn(const std::vector >& args) : BooleanFunction(1, 1, args) { } protected: virtual bool doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return baseT::argAsBool(0, context, executionContext); } // doEvaluate }; // class BooleanFn // boolean not(boolean) template class NotFn : public BooleanFunction { typedef BooleanFunction baseT; public: NotFn(const std::vector >& args) : BooleanFunction(1, 1, args) { } protected: virtual bool doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return !baseT::argAsBool(0, context, executionContext); } // doEvaluate }; // class NotFn // boolean true() template class TrueFn : public BooleanFunction { public: TrueFn(const std::vector >& args) : BooleanFunction(0, 0, args) { } protected: virtual bool doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return true; } // doEvaluate }; // TrueFn // boolean false() template class FalseFn : public BooleanFunction { public: FalseFn(const std::vector >& args) : BooleanFunction(0, 0, args) { } protected: virtual bool doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return false; } // evaluate }; // FalseFn // boolean lang(string) ///////////////////////////////////////////////// // number functions // number number(object?) template class NumberFn : public NumericFunction { typedef NumericFunction baseT; public: NumberFn(const std::vector >& args) : NumericFunction(0, 1, args) { } virtual double doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { double result = (baseT::argCount() > 0) ? baseT::argAsNumber(0, context, executionContext) : StringValue(nodeStringValue(context)).asNumber(); return result; } // evaluate }; // NumberFn // number sum(node-set) template class SumFn : public NumericFunction { typedef NumericFunction baseT; public: SumFn(const std::vector >& args) : NumericFunction(1, 1, args) { } protected: virtual double doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { double sum = 0; NodeSet ns = baseT::argAsNodeSet(0, context, executionContext); for(typename NodeSet::const_iterator n = ns.begin(), end = ns.end(); n != end; ++n) sum += nodeNumberValue(*n); return sum; } // doEvaluate }; // class SumFn // number floor(number) template class FloorFn : public NumericFunction { typedef NumericFunction baseT; public: FloorFn(const std::vector >& args) : NumericFunction(1, 1, args) { } protected: virtual double doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return std::floor(baseT::argAsNumber(0, context, executionContext)); } // doEvaluate }; // class FloorFn // number ceiling(number) template class CeilingFn : public NumericFunction { typedef NumericFunction baseT; public: CeilingFn(const std::vector >& args) : NumericFunction(1, 1, args) { } protected: virtual double doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return std::ceil(baseT::argAsNumber(0, context, executionContext)); } // doEvaluate }; // class CeilingFn // number round(number) template class RoundFn : public NumericFunction { typedef NumericFunction baseT; public: RoundFn(const std::vector >& args) : NumericFunction(1, 1, args) { } protected: virtual double doEvaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return roundNumber(baseT::argAsNumber(0, context, executionContext)); } // doEvaluate }; // class RoundFn } // namespace impl } // namespace XPath } // namespace Arabica #endif