mirror of
https://github.com/jezhiggins/arabica
synced 2024-12-30 22:23:40 +01:00
441 lines
17 KiB
C++
441 lines
17 KiB
C++
#ifndef ARABICA_XSLT_FUNCTIONS_HPP
|
|
#define ARABICA_XSLT_FUNCTIONS_HPP
|
|
|
|
#include <DOM/SAX2DOM/SAX2DOM.hpp>
|
|
#include <SAX/helpers/CatchErrorHandler.hpp>
|
|
|
|
namespace Arabica
|
|
{
|
|
namespace XSLT
|
|
{
|
|
|
|
// node-set document(object, node-set?)
|
|
template<class string_type, class string_adaptor>
|
|
class DocumentFunction : public Arabica::XPath::NodeSetXPathFunction<string_type, string_adaptor>
|
|
{
|
|
typedef Arabica::XPath::NodeSetXPathFunction<string_type, string_adaptor> baseT;
|
|
|
|
typedef Arabica::XPath::XPathExpression<string_type, string_adaptor> XPathExpression;
|
|
typedef std::vector<XPathExpression> ArgList;
|
|
|
|
typedef Arabica::XPath::XPathValue<string_type, string_adaptor> XPathValue;
|
|
typedef Arabica::XPath::NodeSet<string_type, string_adaptor> NodeSet;
|
|
|
|
typedef Arabica::XPath::ExecutionContext<string_type, string_adaptor> XPathExecutionContext;
|
|
typedef DOM::Node<string_type, string_adaptor> DOMNode;
|
|
|
|
public:
|
|
static const string_type& name()
|
|
{
|
|
static const string_type n = string_adaptor::construct_from_utf8("document");
|
|
return n;
|
|
} // name
|
|
|
|
DocumentFunction(const string_type& currentBase,
|
|
const ArgList& args) :
|
|
baseT(1, 2, args),
|
|
baseURI_(currentBase)
|
|
{ }
|
|
|
|
protected:
|
|
virtual NodeSet doEvaluate(const DOMNode& context,
|
|
const XPathExecutionContext& executionContext) const
|
|
{
|
|
NodeSet nodes;
|
|
|
|
XPathValue a0 = arg(0, context, executionContext);
|
|
if(baseT::argCount() != 1)
|
|
throw Arabica::XPath::UnsupportedException("two arg version of document()");
|
|
if(a0.type() != Arabica::XPath::STRING)
|
|
throw Arabica::XPath::UnsupportedException("node-set arg version of document()");
|
|
|
|
load_document(a0.asString(), nodes);
|
|
return nodes;
|
|
} // doEvaluate
|
|
|
|
private:
|
|
void load_document(const string_type& location, NodeSet& nodes) const
|
|
{
|
|
SAX2DOM::Parser<string_type, string_adaptor> domParser;
|
|
SAX::CatchErrorHandler<string_type, string_adaptor> eh;
|
|
domParser.setErrorHandler(eh);
|
|
|
|
Arabica::io::URI base(string_adaptor::asStdString(baseURI_));
|
|
Arabica::io::URI absolute(base, string_adaptor::asStdString(location));
|
|
SAX::InputSource<string_type, string_adaptor> is(string_adaptor::construct_from_utf8(absolute.as_string().c_str()));
|
|
domParser.parse(is);
|
|
|
|
if(!eh.errorsReported())
|
|
nodes.push_back(domParser.getDocument());
|
|
else
|
|
std::cerr << eh.errors() << std::endl;
|
|
} // load_document
|
|
|
|
string_type baseURI_;
|
|
}; // DocumentFunction
|
|
|
|
// node-set key(string, object)
|
|
template<class string_type, class string_adaptor>
|
|
class KeyFunction : public Arabica::XPath::NodeSetXPathFunction<string_type, string_adaptor>
|
|
{
|
|
typedef Arabica::XPath::NodeSetXPathFunction<string_type, string_adaptor> baseT;
|
|
|
|
typedef Arabica::XPath::XPathExpression<string_type, string_adaptor> XPathExpression;
|
|
typedef std::vector<XPathExpression> ArgList;
|
|
typedef Arabica::XPath::XPathValue<string_type, string_adaptor> XPathValue;
|
|
typedef Arabica::XPath::NodeSet<string_type, string_adaptor> NodeSet;
|
|
typedef Arabica::XPath::ExecutionContext<string_type, string_adaptor> XPathExecutionContext;
|
|
typedef DOM::Node<string_type, string_adaptor> DOMNode;
|
|
typedef XML::QualifiedName<string_type, string_adaptor> QualifiedName;
|
|
public:
|
|
static const string_type& name()
|
|
{
|
|
static const string_type n = string_adaptor::construct_from_utf8("key");
|
|
return n;
|
|
} // name
|
|
|
|
KeyFunction(const DeclaredKeys<string_type, string_adaptor>& keys,
|
|
const std::map<string_type, string_type>& inscopeNamespaces,
|
|
const ArgList& args) :
|
|
baseT(2, 2, args),
|
|
keys_(keys),
|
|
namespaces_(inscopeNamespaces)
|
|
{
|
|
} // KeyFunction
|
|
|
|
protected:
|
|
virtual NodeSet doEvaluate(const DOMNode& context,
|
|
const XPathExecutionContext& executionContext) const
|
|
{
|
|
string_type keyname = argAsString(0, context, executionContext);
|
|
string_type keyClarkName = QualifiedName::parseQName(keyname, true, namespaces_).clarkName();
|
|
|
|
XPathValue a1 = baseT::arg(1, context, executionContext);
|
|
if(a1.type() == Arabica::XPath::NODE_SET)
|
|
return nodeSetUnion(keyClarkName, a1.asNodeSet(), executionContext);
|
|
|
|
return keys_.lookup(keyClarkName, a1.asString(), executionContext);
|
|
} // doEvaluate
|
|
|
|
NodeSet nodeSetUnion(const string_type& keyClarkName,
|
|
const NodeSet nodes,
|
|
const XPathExecutionContext& executionContext) const
|
|
{
|
|
NodeSet results;
|
|
for(typename NodeSet::const_iterator n = nodes.begin(), ne = nodes.end(); n != ne; ++n)
|
|
{
|
|
string_type id = Arabica::XPath::impl::nodeStringValue<string_type, string_adaptor>(*n);
|
|
results.push_back(keys_.lookup(keyClarkName, id, executionContext));
|
|
} // for ...
|
|
results.to_document_order();
|
|
return results;
|
|
} // nodeSetUnion
|
|
|
|
private:
|
|
const DeclaredKeys<string_type, string_adaptor>& keys_;
|
|
std::map<string_type, string_type> namespaces_;
|
|
}; // class KeyFunction
|
|
|
|
// string format-number(number, string, string?)
|
|
|
|
// node-set current()
|
|
template<class string_type, class string_adaptor>
|
|
class CurrentFunction : public Arabica::XPath::NodeSetXPathFunction<string_type, string_adaptor>
|
|
{
|
|
typedef Arabica::XPath::NodeSetXPathFunction<string_type, string_adaptor> baseT;
|
|
|
|
typedef Arabica::XPath::XPathExpression<string_type, string_adaptor> XPathExpression;
|
|
typedef std::vector<XPathExpression> ArgList;
|
|
typedef Arabica::XPath::NodeSet<string_type, string_adaptor> NodeSet;
|
|
typedef Arabica::XPath::ExecutionContext<string_type, string_adaptor> XPathExecutionContext;
|
|
typedef DOM::Node<string_type, string_adaptor> DOMNode;
|
|
public:
|
|
static const string_type& name()
|
|
{
|
|
static const string_type n = string_adaptor::construct_from_utf8("current");
|
|
return n;
|
|
} // name
|
|
|
|
CurrentFunction(const ArgList& args) :
|
|
baseT(0, 0, args) { }
|
|
|
|
protected:
|
|
virtual NodeSet doEvaluate(const DOMNode& /* context */,
|
|
const XPathExecutionContext& executionContext) const
|
|
{
|
|
NodeSet set;
|
|
set.push_back(executionContext.currentNode());
|
|
return set;
|
|
} // doEvaluate
|
|
}; // CurrentFunction
|
|
|
|
// string unparsed-entity-uri(string)
|
|
template<class string_type, class string_adaptor>
|
|
class UnparsedEntityUriFunction : public Arabica::XPath::StringXPathFunction<string_type, string_adaptor>
|
|
{
|
|
typedef Arabica::XPath::StringXPathFunction<string_type, string_adaptor> baseT;
|
|
|
|
typedef Arabica::XPath::XPathExpression<string_type, string_adaptor> XPathExpression;
|
|
typedef std::vector<XPathExpression> ArgList;
|
|
typedef Arabica::XPath::ExecutionContext<string_type, string_adaptor> XPathExecutionContext;
|
|
typedef DOM::Node<string_type, string_adaptor> DOMNode;
|
|
public:
|
|
static const string_type& name()
|
|
{
|
|
static const string_type n = string_adaptor::construct_from_utf8("unparsed-entity-uri");
|
|
return n;
|
|
} // name
|
|
|
|
UnparsedEntityUriFunction(const ArgList& args) :
|
|
baseT(1, 1, args) { }
|
|
|
|
protected:
|
|
virtual string_type doEvaluate(const DOMNode& /* context */,
|
|
const XPathExecutionContext& /* executionContext */) const
|
|
{
|
|
// This is a minimal, but I think conformant, implementation
|
|
return string_adaptor::empty_string();
|
|
} // evaluate
|
|
}; // UnparsedEntityUri
|
|
|
|
// string generate-id(node-set?)
|
|
template<class string_type, class string_adaptor>
|
|
class GenerateIdFunction : public Arabica::XPath::StringXPathFunction<string_type, string_adaptor>
|
|
{
|
|
typedef Arabica::XPath::StringXPathFunction<string_type, string_adaptor> baseT;
|
|
|
|
typedef Arabica::XPath::XPathExpression<string_type, string_adaptor> XPathExpression;
|
|
typedef std::vector<XPathExpression> ArgList;
|
|
typedef Arabica::XPath::NodeSet<string_type, string_adaptor> NodeSet;
|
|
typedef Arabica::XPath::ExecutionContext<string_type, string_adaptor> XPathExecutionContext;
|
|
typedef DOM::Node<string_type, string_adaptor> DOMNode;
|
|
public:
|
|
static const string_type& name()
|
|
{
|
|
static const string_type n = string_adaptor::construct_from_utf8("generate-id");
|
|
return n;
|
|
} // name
|
|
|
|
GenerateIdFunction(const ArgList& args) :
|
|
baseT(0, 1, args) { }
|
|
|
|
protected:
|
|
virtual string_type doEvaluate(const DOMNode& context,
|
|
const XPathExecutionContext& executionContext) const
|
|
{
|
|
DOMNode node;
|
|
if(baseT::argCount() == 0)
|
|
node = context;
|
|
else
|
|
{
|
|
NodeSet ns = baseT::argAsNodeSet(0, context, executionContext);
|
|
if(ns.size() == 0)
|
|
return string_adaptor::empty_string();
|
|
node = ns.top();
|
|
} // if ...
|
|
|
|
std::basic_ostringstream<typename string_adaptor::value_type> os;
|
|
os << node.underlying_impl();
|
|
return os.str();
|
|
} // doEvaluate
|
|
}; // class GenerateIdFunction
|
|
|
|
// object system-property(string)
|
|
template<class string_type, class string_adaptor>
|
|
class SystemPropertyFunction : public Arabica::XPath::StringXPathFunction<string_type, string_adaptor>
|
|
{
|
|
typedef Arabica::XPath::StringXPathFunction<string_type, string_adaptor> baseT;
|
|
|
|
typedef Arabica::XPath::XPathExpression<string_type, string_adaptor> XPathExpression;
|
|
typedef std::vector<XPathExpression> ArgList;
|
|
typedef Arabica::XPath::ExecutionContext<string_type, string_adaptor> XPathExecutionContext;
|
|
typedef DOM::Node<string_type, string_adaptor> DOMNode;
|
|
public:
|
|
static const string_type& name()
|
|
{
|
|
static const string_type n = string_adaptor::construct_from_utf8("system-property");
|
|
return n;
|
|
} // name
|
|
|
|
SystemPropertyFunction (const ArgList& args) :
|
|
baseT(1, 1, args) { }
|
|
|
|
protected:
|
|
virtual string_type doEvaluate(const DOMNode& context,
|
|
const XPathExecutionContext& executionContext) const
|
|
{
|
|
typedef StylesheetConstant<string_type, string_adaptor> SC;
|
|
|
|
string_type property = baseT::argAsString(0, context, executionContext);
|
|
string_type result;
|
|
if(property == SC::version_property)
|
|
return SC::Version;
|
|
if(property == SC::vendor_property)
|
|
return SC::Vendor;
|
|
else if(property == SC::vendor_url_property)
|
|
return SC::VendorUrl;
|
|
return string_adaptor::empty_string();
|
|
} // evaluate
|
|
}; // SystemPropertyFunction
|
|
|
|
// boolean element-available(string)
|
|
template<class string_type, class string_adaptor>
|
|
class ElementAvailableFunction : public Arabica::XPath::BooleanXPathFunction<string_type, string_adaptor>
|
|
{
|
|
typedef Arabica::XPath::BooleanXPathFunction<string_type, string_adaptor> baseT;
|
|
|
|
typedef Arabica::XPath::XPathExpression<string_type, string_adaptor> XPathExpression;
|
|
typedef std::vector<XPathExpression> ArgList;
|
|
typedef Arabica::XPath::ExecutionContext<string_type, string_adaptor> XPathExecutionContext;
|
|
typedef DOM::Node<string_type, string_adaptor> DOMNode;
|
|
typedef XML::QualifiedName<string_type, string_adaptor> QualifiedName;
|
|
public:
|
|
static const string_type& name()
|
|
{
|
|
static const string_type n = string_adaptor::construct_from_utf8("element_available");
|
|
return n;
|
|
} // name
|
|
|
|
ElementAvailableFunction(const std::vector<std::pair<string_type, string_type> >& names,
|
|
const std::map<string_type, string_type>& inscopeNamespaces,
|
|
const ArgList& args) :
|
|
baseT(1, 1, args),
|
|
namespaces_(inscopeNamespaces),
|
|
elementNames_(names)
|
|
{
|
|
} // ElementAvailableFunction
|
|
|
|
protected:
|
|
virtual bool doEvaluate(const DOMNode& context,
|
|
const XPathExecutionContext& executionContext) const
|
|
{
|
|
typedef StylesheetConstant<string_type, string_adaptor> SC;
|
|
|
|
const string_type functionName = baseT::argAsString(0, context, executionContext);
|
|
const QualifiedName expandedName = QualifiedName::parseQName(functionName, true, namespaces_);
|
|
|
|
if((expandedName.namespaceUri() != StylesheetConstant<string_type, string_adaptor>::NamespaceURI) &&
|
|
(!expandedName.namespaceUri().empty()))
|
|
return false;
|
|
|
|
static string_type XSLTNames[] = { SC::apply_imports,
|
|
SC::apply_templates,
|
|
SC::attribute,
|
|
SC::call_template,
|
|
SC::choose,
|
|
SC::comment,
|
|
SC::copy,
|
|
SC::copy_of,
|
|
SC::element,
|
|
SC::fallback,
|
|
SC::for_each,
|
|
SC::if_,
|
|
SC::message,
|
|
SC::number,
|
|
SC::processing_instruction,
|
|
SC::text,
|
|
SC::value_of,
|
|
SC::variable,
|
|
string_adaptor::empty_string() };
|
|
|
|
for(int i = 0; XSLTNames[i] != string_adaptor::empty_string(); ++i)
|
|
if(expandedName.localName() == XSLTNames[i])
|
|
return true;
|
|
|
|
return false;
|
|
} // doEvaluate
|
|
|
|
private:
|
|
const std::map<string_type, string_type> namespaces_;
|
|
std::vector<std::pair<string_type, string_type> > elementNames_;
|
|
}; // class ElementAvailableFunction
|
|
|
|
// boolean function-available(string)
|
|
template<class string_type, class string_adaptor>
|
|
class FunctionAvailableFunction : public Arabica::XPath::BooleanXPathFunction<string_type, string_adaptor>
|
|
{
|
|
typedef Arabica::XPath::BooleanXPathFunction<string_type, string_adaptor> baseT;
|
|
|
|
typedef Arabica::XPath::XPathExpression<string_type, string_adaptor> XPathExpression;
|
|
typedef std::vector<XPathExpression> ArgList;
|
|
typedef Arabica::XPath::ExecutionContext<string_type, string_adaptor> XPathExecutionContext;
|
|
typedef DOM::Node<string_type, string_adaptor> DOMNode;
|
|
typedef XML::QualifiedName<string_type, string_adaptor> QualifiedName;
|
|
public:
|
|
static const string_type& name()
|
|
{
|
|
static const string_type n = string_adaptor::construct_from_utf8("function-available");
|
|
return n;
|
|
} // name
|
|
|
|
FunctionAvailableFunction(const std::vector<std::pair<string_type, string_type> >& names,
|
|
const std::map<string_type, string_type>& inscopeNamespaces,
|
|
const ArgList& args) :
|
|
baseT(1, 1, args),
|
|
namespaces_(inscopeNamespaces),
|
|
functionNames_(names)
|
|
{
|
|
Arabica::XPath::StandardXPathFunctionResolver<string_type, string_adaptor> standardResolver;
|
|
const std::vector<std::pair<string_type, string_type> > standardNames = standardResolver.validNames();
|
|
functionNames_.insert(functionNames_.begin(), standardNames.begin(), standardNames.end());
|
|
} // FunctionAvailableFunction
|
|
|
|
protected:
|
|
virtual bool doEvaluate(const DOMNode& context,
|
|
const XPathExecutionContext& executionContext) const
|
|
{
|
|
const string_type functionName = baseT::argAsString(0, context, executionContext);
|
|
const QualifiedName expandedName = QualifiedName::parseQName(functionName, true, namespaces_);
|
|
|
|
const std::pair<string_type, string_type> name_to_check = std::make_pair(expandedName.namespaceUri(), expandedName.localName());
|
|
|
|
return (std::find(functionNames_.begin(), functionNames_.end(), name_to_check) != functionNames_.end());
|
|
} // doEvaluate
|
|
|
|
private:
|
|
const std::map<string_type, string_type> namespaces_;
|
|
std::vector<std::pair<string_type, string_type> > functionNames_;
|
|
}; // class FunctionAvailableFunction
|
|
|
|
template<class string_type, class string_adaptor>
|
|
class UndefinedFunction : public Arabica::XPath::BooleanXPathFunction<string_type, string_adaptor>
|
|
{
|
|
typedef Arabica::XPath::BooleanXPathFunction<string_type, string_adaptor> baseT;
|
|
|
|
typedef Arabica::XPath::XPathExpression<string_type, string_adaptor> XPathExpression;
|
|
typedef std::vector<XPathExpression> ArgList;
|
|
typedef Arabica::XPath::ExecutionContext<string_type, string_adaptor> XPathExecutionContext;
|
|
typedef DOM::Node<string_type, string_adaptor> DOMNode;
|
|
public:
|
|
UndefinedFunction(const string_type namespace_uri,
|
|
const string_type name,
|
|
const ArgList& args) :
|
|
baseT(-1, -1, args)
|
|
{
|
|
typedef Arabica::text::Unicode<string_adaptor::value_type> UnicodeT;
|
|
|
|
if(!namespace_uri.empty())
|
|
{
|
|
error_ += UnicodeT::LEFT_SQUARE_BRACKET;
|
|
error_ += namespace_uri;
|
|
error_ += UnicodeT::RIGHT_SQUARE_BRACKET;
|
|
} // if ..
|
|
|
|
error_ += name;
|
|
} // UndefinedFunction
|
|
|
|
protected:
|
|
virtual bool doEvaluate(const DOMNode&, const XPathExecutionContext&) const
|
|
{
|
|
throw Arabica::XPath::UndefinedFunctionException(string_adaptor::asStdString(error_));
|
|
} // doEvaluate
|
|
|
|
string_type error_;
|
|
}; // class UndefinedFunction
|
|
|
|
} // namespace XSLT
|
|
} // namespace Arabica
|
|
|
|
#endif // ARABICA_XSLT_FUNCTIONS_HPP
|
|
|
|
|