#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 XPathValue* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const = 0; protected: size_t argCount() const { return args_.size(); } XPathValuePtr arg(size_t index, const DOM::Node& context, const ExecutionContext& executionContext) const { return args_[index]->evaluate(context, executionContext); } // argAsBool bool argAsBool(size_t index, const DOM::Node& context, const ExecutionContext& executionContext) const { return args_[index]->evaluate(context, executionContext)->asBool(); } // argAsBool double argAsNumber(size_t index, const DOM::Node& context, const ExecutionContext& executionContext) const { return args_[index]->evaluate(context, executionContext)->asNumber(); } // argAsNumber string_type argAsString(size_t index, const DOM::Node& context, const ExecutionContext& executionContext) const { return args_[index]->evaluate(context, executionContext)->asString(); } // argAsString NodeSet argAsNodeSet(size_t index, const DOM::Node& context, const ExecutionContext& executionContext) const { return args_[index]->evaluate(context, executionContext)->asNodeSet(); } // argAsNodeSet private: const std::vector > args_; }; // class XPathFunction namespace impl { //////////////////////////////// // node-set functions // number last() template class LastFn : public XPathFunction { public: LastFn(const std::vector >& args) : XPathFunction(0, 0, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return new NumericValue(executionContext.last()); } // evaluate }; // class LastFn // number position() template class PositionFn : public XPathFunction { public: PositionFn(const std::vector >& args) : XPathFunction(0, 0, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return new NumericValue(executionContext.position()); } // evaluate }; // class PositionFn // number count(node-set) template class CountFn : public XPathFunction { typedef XPathFunction baseT; public: CountFn(const std::vector >& args) : XPathFunction(1, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return new NumericValue(baseT::argAsNodeSet(0, context, executionContext).size()); } // evaluate }; // class CountFn // node-set id(object) // string local-name(node-set?) template class LocalNameFn : public XPathFunction { typedef XPathFunction baseT; public: LocalNameFn(const std::vector >& args) : XPathFunction(0, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { DOM::Node node; if(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: return new StringValue(node.hasNamespaceURI() ? node.getLocalName() : node.getNodeName()); default: // put this in to keep gcc quiet ; } // switch ... return new StringValue(""); } // evaluate }; // class LocalNameFn // string namespace-uri(node-set?) template class NamespaceURIFn : public XPathFunction { typedef XPathFunction baseT; public: NamespaceURIFn(const std::vector >& args) : XPathFunction(0, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { DOM::Node node; if(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 new StringValue(node.getNamespaceURI()); default: // put this in to keep gcc quiet ; } // switch ... return new StringValue(""); } // evaluate }; // class NamespaceURIFn // string name(node-set?) template class NameFn : public XPathFunction { typedef XPathFunction baseT; public: NameFn(const std::vector >& args) : XPathFunction(0, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { DOM::Node node; if(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 new StringValue(node.getNodeName()); default: // stop gcc generating a warning about unhandled enum values ; } // switch ... return new StringValue(""); } // evaluate }; // class NameFn /////////////////////////////////////////////// // string functions // string string(object?) template class StringFn : public XPathFunction { typedef XPathFunction baseT; public: StringFn(const std::vector >& args) : XPathFunction(0, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return new StringValue((baseT::argCount() > 0) ? baseT::argAsString(0, context, executionContext) : nodeStringValue(context)); } // evaluate }; // class StringFn // string concat(string, string, string*) template class ConcatFn : public XPathFunction { typedef XPathFunction baseT; public: ConcatFn(const std::vector >& args) : XPathFunction(2, -1, args) { } virtual XPathValue* evaluate(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 new StringValue(s); } // evaluate }; // ConcatFn // boolean starts-with(string, string) template class StartsWithFn : public XPathFunction { typedef XPathFunction baseT; public: StartsWithFn(const std::vector >& args) : XPathFunction(2, 2, args) { } virtual XPathValue* evaluate(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 new BoolValue(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 new BoolValue(false); return new BoolValue(true); } // evaluate }; // StartsWithFn // boolean contains(string, string) template class ContainsFn : public XPathFunction { typedef XPathFunction baseT; public: ContainsFn(const std::vector >& args) : XPathFunction(2, 2, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return new BoolValue(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 XPathFunction { typedef XPathFunction baseT; public: SubstringBeforeFn(const std::vector >& args) : XPathFunction(2, 2, args) { } virtual XPathValue* evaluate(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 new StringValue(""); return new StringValue(string_adaptor::substr(value, 0, splitAt)); } // evaluate }; // class SubstringBeforeFn // string substring-after(string, string) template class SubstringAfterFn : public XPathFunction { typedef XPathFunction baseT; public: SubstringAfterFn(const std::vector >& args) : XPathFunction(2, 2, args) { } virtual XPathValue* evaluate(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 new StringValue(""); return new StringValue(string_adaptor::substr(value, splitAt + string_adaptor::length(split))); } // evaluate }; // class SubstringAfterFn // string substring(string, number, number?) template class SubstringFn : public XPathFunction { typedef XPathFunction baseT; public: SubstringFn(const std::vector >& args) : XPathFunction(2, 3, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { string_type value = baseT::argAsString(0, context, executionContext); if(string_adaptor::empty(value)) return new StringValue(value); 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 new StringValue(""); if(startAt < 0) startAt = 0; if((isInfinite(endAt)) || (endAt > string_adaptor::length(value))) endAt = string_adaptor::length(value); return new StringValue(string_adaptor::substr(value, static_cast(startAt), static_cast(endAt - startAt))); } // evaluate }; // SubstringFn // number string-length(string?) template class StringLengthFn : public XPathFunction { typedef XPathFunction baseT; public: StringLengthFn(const std::vector >& args) : XPathFunction(0, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { string_type v = (baseT::argCount() > 0) ? baseT::argAsString(0, context, executionContext) : nodeStringValue(context); return new NumericValue(string_adaptor::length(v)); } // evaluate }; // StringLengthFn // string normalize-space(string?) template class NormalizeSpaceFn : public XPathFunction { typedef XPathFunction baseT; public: NormalizeSpaceFn(const std::vector >& args) : XPathFunction(0, 1, args) { } virtual XPathValue* evaluate(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 new StringValue(value); } // evaluate }; // class NormalizeSpaceFn // string translate(string, string, string) template class TranslateFn : public XPathFunction { typedef XPathFunction baseT; public: TranslateFn(const std::vector >& args) : XPathFunction(3, 3, args) { } virtual XPathValue* evaluate(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 new StringValue(string_adaptor::construct(string_adaptor::begin(str), p)); } // evaluate }; // class TranslateFn /////////////////////////////////////////////////////// // boolean functions // boolean boolean(object) template class BooleanFn : public XPathFunction { typedef XPathFunction baseT; public: BooleanFn(const std::vector >& args) : XPathFunction(1, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return new BoolValue(baseT::argAsBool(0, context, executionContext)); } // evaluate }; // class BooleanFn // boolean not(boolean) template class NotFn : public XPathFunction { typedef XPathFunction baseT; public: NotFn(const std::vector >& args) : XPathFunction(1, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return new BoolValue(!baseT::argAsBool(0, context, executionContext)); } }; // class NotFn // boolean true() template class TrueFn : public XPathFunction { public: TrueFn(const std::vector >& args) : XPathFunction(0, 0, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return new BoolValue(true); } // evaluate }; // TrueFn // boolean false() template class FalseFn : public XPathFunction { public: FalseFn(const std::vector >& args) : XPathFunction(0, 0, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return new BoolValue(false); } // evaluate }; // FalseFn // boolean lang(string) ///////////////////////////////////////////////// // number functions // number number(object?) template class NumberFn : public XPathFunction { typedef XPathFunction baseT; public: NumberFn(const std::vector >& args) : XPathFunction(0, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { double result = (baseT::argCount() > 0) ? baseT::argAsNumber(0, context, executionContext) : StringValue(nodeStringValue(context)).asNumber(); return new NumericValue(result); } // evaluate }; // NumberFn // number sum(node-set) template class SumFn : public XPathFunction { typedef XPathFunction baseT; public: SumFn(const std::vector >& args) : XPathFunction(1, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { double sum = 0; NodeSet ns = baseT::argAsNodeSet(0, context, executionContext); for(typename 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) template class FloorFn : public XPathFunction { typedef XPathFunction baseT; public: FloorFn(const std::vector >& args) : XPathFunction(1, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return new NumericValue(std::floor(baseT::argAsNumber(0, context, executionContext))); } // evaluate }; // class FloorFn // number ceiling(number) template class CeilingFn : public XPathFunction { typedef XPathFunction baseT; public: CeilingFn(const std::vector >& args) : XPathFunction(1, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return new NumericValue(std::ceil(baseT::argAsNumber(0, context, executionContext))); } // evaluate }; // class CeilingFn // number round(number) template class RoundFn : public XPathFunction { typedef XPathFunction baseT; public: RoundFn(const std::vector >& args) : XPathFunction(1, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext& executionContext) const { return new NumericValue(roundNumber(baseT::argAsNumber(0, context, executionContext))); } // evaluate }; // class RoundFn } // namespace impl } // namespace XPath } // namespace Arabica #endif