#ifndef ARABICA_XPATH_FUNCTION_HPP #define ARABICA_XPATH_FUNCTION_HPP #include #include #include #include #include "xpath_value.hpp" #include "xpath_execution_context.hpp" namespace Arabica { namespace XPath { 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(); } 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 //////////////////////////////// // 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: 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?) class StringFn : public XPathFunction > { public: StringFn(const std::vector > >& args) : XPathFunction >(0, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext >& executionContext) const { return new StringValue >((argCount() > 0) ? argAsString(0, context, executionContext) : nodeStringValue(context)); } // evaluate }; // class StringFn // string concat(string, string, string*) class ConcatFn : public XPathFunction > { public: ConcatFn(const std::vector > >& args) : XPathFunction >(2, -1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext >& executionContext) const { std::string s; for(size_t a = 0, ae = argCount(); a < ae; ++a) s.append(argAsString(a, context, executionContext)); return new StringValue >(s); } // evaluate }; // ConcatFn // boolean starts-with(string, string) class StartsWithFn : public XPathFunction > { public: StartsWithFn(const std::vector > >& args) : XPathFunction >(2, 2, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext >& executionContext) const { std::string value = argAsString(0, context, executionContext); std::string start = argAsString(1, context, executionContext); if(value.length() < start.length()) return new BoolValue >(false); for(size_t i = 0, ie = start.length(); i < ie; ++i) if(value[i] != start[i]) return new BoolValue >(false); return new BoolValue >(true); } // evaluate }; // StartsWithFn // boolean contains(string, string) class ContainsFn : public XPathFunction > { public: ContainsFn(const std::vector > >& args) : XPathFunction >(2, 2, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext >& executionContext) const { return new BoolValue >(argAsString(0, context, executionContext).find(argAsString(1, context, executionContext)) != std::string::npos); } // evaluate }; // class ContainsFn // string substring-before(string, string) class SubstringBeforeFn : public XPathFunction > { public: SubstringBeforeFn(const std::vector > >& args) : XPathFunction >(2, 2, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext >& executionContext) const { std::string value = argAsString(0, context, executionContext); size_t splitAt = value.find(argAsString(1, context, executionContext)); if(splitAt == std::string::npos) return new StringValue >(""); return new StringValue >(value.substr(0, splitAt)); } // evaluate }; // class SubstringBeforeFn // string substring-after(string, string) class SubstringAfterFn : public XPathFunction > { public: SubstringAfterFn(const std::vector > >& args) : XPathFunction >(2, 2, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext >& executionContext) const { std::string value = argAsString(0, context, executionContext); std::string split = argAsString(1, context, executionContext); size_t splitAt = value.find(split); if((splitAt == std::string::npos) || ((splitAt + split.length()) >= value.length())) return new StringValue >(""); return new StringValue >(value.substr(splitAt + split.length())); } // evaluate }; // class SubstringAfterFn // string substring(string, number, number?) class SubstringFn : public XPathFunction > { public: SubstringFn(const std::vector > >& args) : XPathFunction >(2, 3, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext >& executionContext) const { std::string value = argAsString(0, context, executionContext); double startAt = roundNumber(argAsNumber(1, context, executionContext)) - 1; double endAt = roundNumber((argCount() == 3 ? argAsNumber(2, context, executionContext) : Infinity)) + startAt; if((endAt < 0) || (endAt < startAt) || (isNaN(endAt))) return new StringValue >(""); if(startAt < 0) startAt = 0; if((isInfinite(endAt)) || (endAt > value.length())) endAt = value.length(); return new StringValue >(value.substr(static_cast(startAt), static_cast(endAt - startAt))); } // evaluate }; // SubstringFn // number string-length(string?) class StringLengthFn : public XPathFunction > { public: StringLengthFn(const std::vector > >& args) : XPathFunction >(0, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext >& executionContext) const { return new NumericValue >(((argCount() > 0) ? argAsString(0, context, executionContext) : nodeStringValue(context)).length()); } // evaluate }; // StringLengthFn // string normalize-space(string?) class NormalizeSpaceFn : public XPathFunction > { public: NormalizeSpaceFn(const std::vector > >& args) : XPathFunction >(0, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext >& executionContext) const { std::string value = ((argCount() > 0) ? argAsString(0, context, executionContext) : nodeStringValue(context)); size_t i = 0, ie = value.length(); // string leading space while((i != ie) && (XML::is_space(static_cast(value[i])))) ++i; size_t p = 0; while(i != ie) { while((i != ie) && (!XML::is_space(static_cast(value[i])))) value[p++] = value[i++]; while((i != ie) && (XML::is_space(static_cast(value[i])))) ++i; if(i != ie) value[p++] = Unicode::SPACE; } // while ... if(p != ie) value.resize(p); return new StringValue >(value); } // evaluate }; // class NormalizeSpaceFn // string translate(string, string, string) class TranslateFn : public XPathFunction > { public: TranslateFn(const std::vector > >& args) : XPathFunction >(3, 3, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext >& executionContext) const { std::string str = argAsString(0, context, executionContext); std::string from = argAsString(1, context, executionContext); std::string to = argAsString(2, context, executionContext); size_t p = 0; for(size_t i = 0, ie = str.length(); i != ie; ++i) { size_t r = from.find(str[i]); if(r == std::string::npos) ++p; else if(r < to.length()) str[p++] = to[r]; } // for ... if(p != str.length()) str.resize(p); return new StringValue >(str); } // evaluate }; // class TranslateFn /////////////////////////////////////////////////////// // boolean functions // boolean boolean(object) class BooleanFn : public XPathFunction > { public: BooleanFn(const std::vector > >& args) : XPathFunction >(1, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext >& executionContext) const { return new BoolValue >(argAsBool(0, context, executionContext)); } // evaluate }; // class BooleanFn // boolean not(boolean) class NotFn : public XPathFunction > { public: NotFn(const std::vector > >& args) : XPathFunction >(1, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext >& executionContext) const { return new BoolValue >(!argAsBool(0, context, executionContext)); } }; // class NotFn // boolean true() class TrueFn : public XPathFunction > { public: TrueFn(const std::vector > >& args) : XPathFunction >(0, 0, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext >& executionContext) const { return new BoolValue >(true); } // evaluate }; // TrueFn // boolean false() class FalseFn : public XPathFunction > { public: FalseFn(const std::vector > >& args) : XPathFunction >(0, 0, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext >& executionContext) const { return new BoolValue >(false); } // evaluate }; // FalseFn // boolean lang(string) ///////////////////////////////////////////////// // number functions // number number(object?) class NumberFn : public XPathFunction > { public: NumberFn(const std::vector > >& args) : XPathFunction >(0, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext >& executionContext) const { double result = (argCount() > 0) ? argAsNumber(0, context, executionContext) : StringValue >(nodeStringValue(context)).asNumber(); return new NumericValue >(result); } // evaluate }; // NumberFn // number sum(node-set) class SumFn : public XPathFunction > { public: SumFn(const std::vector > >& args) : XPathFunction >(1, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext >& executionContext) const { double sum = 0; NodeSet ns = argAsNodeSet(0, context, executionContext); for(NodeSet::const_iterator n = ns.begin(), end = ns.end(); n != end; ++n) sum += nodeNumberValue(*n); return new NumericValue >(sum); } // evaluate }; // class SumFn // number floor(number) class FloorFn : public XPathFunction > { public: FloorFn(const std::vector > >& args) : XPathFunction >(1, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext >& executionContext) const { return new NumericValue >(std::floor(argAsNumber(0, context, executionContext))); } // evaluate }; // class FloorFn // number ceiling(number) class CeilingFn : public XPathFunction > { public: CeilingFn(const std::vector > >& args) : XPathFunction >(1, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext >& executionContext) const { return new NumericValue >(std::ceil(argAsNumber(0, context, executionContext))); } // evaluate }; // class CeilingFn // number round(number) class RoundFn : public XPathFunction > { public: RoundFn(const std::vector > >& args) : XPathFunction >(1, 1, args) { } virtual XPathValue* evaluate(const DOM::Node& context, const ExecutionContext >& executionContext) const { return new NumericValue >(roundNumber(argAsNumber(0, context, executionContext))); } // evaluate }; // class RoundFn } // namespace XPath } // namespace Arabica #endif