#ifndef ARABICA_XSLT_COMPILED_STYLESHEET_HPP #define ARABICA_XSLT_COMPILED_STYLESHEET_HPP #include #include #include #include "xslt_execution_context.hpp" #include "xslt_template.hpp" #include "xslt_top_level_param.hpp" #include "xslt_key.hpp" #include "xslt_stylesheet.hpp" namespace Arabica { namespace XSLT { template struct standard_stream { static std::basic_ostream& out(); static std::basic_ostream& err(); }; template<> struct standard_stream { static std::basic_ostream& out() { return std::cout; } static std::basic_ostream& err() { return std::cerr; } }; template<> struct standard_stream { static std::basic_ostream& out() { return std::wcout; } static std::basic_ostream& err() { return std::wcerr; } }; template class CompiledStylesheet : public Stylesheet { typedef standard_stream streams; public: typedef Arabica::XPath::BoolValue BoolValue; typedef Arabica::XPath::NumericValue NumericValue; typedef Arabica::XPath::StringValue StringValue; typedef Arabica::XPath::XPathValue Value; typedef Arabica::XPath::NodeSet NodeSet; typedef DOM::Node DOMNode; typedef DOM::NodeList DOMNodeList; CompiledStylesheet() : output_(new StreamSink(streams::out())), error_output_(&streams::err()) { } // CompiledStylesheet virtual ~CompiledStylesheet() { // let's clean up! for(VariableDeclListIterator ci = topLevelVars_.begin(), ce = topLevelVars_.end(); ci != ce; ++ci) delete *ci; for(ParamListIterator pi = params_.begin(), pe = params_.end(); pi != pe; ++pi) delete *pi; for(TemplateListIterator ti = all_templates_.begin(), te = all_templates_.end(); ti != te; ++ti) delete *ti; } // ~CompiledStylesheet virtual void set_parameter(const string_type& name, bool value) { set_parameter(name, Value(new BoolValue(value))); } // set_parameter virtual void set_parameter(const string_type& name, double value) { set_parameter(name, Value(new NumericValue(value))); } // set_parameter virtual void set_parameter(const string_type& name, const char* value) { set_parameter(name, Value(new StringValue(value))); } // set_parameter virtual void set_parameter(const string_type& name, const string_type& value) { set_parameter(name, Value(new StringValue(value))); } // set_parameter virtual void set_output(Sink& sink) { output_.reset(sink); } // set_output virtual void set_error_output(std::basic_ostream& os) { error_output_ = &os; } // set_error_output virtual void execute(const DOMNode& initialNode) const { if(initialNode == 0) throw std::runtime_error("Input document is empty"); NodeSet ns; ns.push_back(initialNode); ExecutionContext context(*this, output_.get(), *error_output_); // set up variables and so forth for(ParamListIterator pi = params_.begin(), pe = params_.end(); pi != pe; ++pi) (*pi)->declare(context); for(VariableDeclListIterator ci = topLevelVars_.begin(), ce = topLevelVars_.end(); ci != ce; ++ci) (*ci)->execute(initialNode, context); context.freezeTopLevel(); // go! output_.get().asOutput().start_document(output_settings_, output_cdata_elements_); applyTemplates(ns, context, string_adaptor::empty_string()); output_.get().asOutput().end_document(); } // execute //////////////////////////////////////// const DeclaredKeys& keys() const { return keys_; } void add_template(Template* templat) { typedef typename Template::MatchExprList MatchExprList; typedef typename MatchExprList::const_iterator MatchIterator; all_templates_.push_back(templat); for(MatchIterator e = templat->compiled_matches().begin(), ee = templat->compiled_matches().end(); e != ee; ++e) templates_[templat->precedence()][templat->mode()].push_back(MatchTemplate(*e, templat)); if(!templat->has_name()) return; NamedTemplatesIterator named_template = named_templates_.find(templat->name()); if(named_template != named_templates_.end()) { const Precedence& existing_precedence = named_template->second->precedence(); if(existing_precedence > templat->precedence()) return; if(existing_precedence == templat->precedence()) throw SAX::SAXException("Template named '" + string_adaptor::asStdString(templat->name()) + "' already defined"); } // if ... named_templates_[templat->name()] = templat; } // add_template void add_variable(Item* item) { topLevelVars_.push_back(item); } // add_item void add_key(const string_type& name, Key* key) { keys_.add(name, key); } // add_key void output_settings(const typename Output::Settings& settings, const typename Output::CDATAElements& cdata_elements) { output_settings_ = settings; output_cdata_elements_.insert(cdata_elements.begin(), cdata_elements.end()); } // output_settingsp void prepare() { for(typename TemplateStack::iterator ts = templates_.begin(), tse = templates_.end(); ts != tse; ++ts) for(typename ModeTemplates::iterator ms = ts->second.begin(), mse = ts->second.end(); ms != mse; ++ms) { MatchTemplates& matches = ms->second; std::reverse(matches.begin(), matches.end()); std::stable_sort(matches.begin(), matches.end()); } // for ... } // prepare //////////////////////////////////////// void applyTemplates(const NodeSet& nodes, ExecutionContext& context, const string_type& mode) const { // entirely simple so far LastFrame last(context, nodes.size()); int p = 1; for(typename NodeSet::const_iterator n = nodes.begin(), ne = nodes.end(); n != ne; ++n) { context.setPosition(*n, p++); doApplyTemplates(*n, context, mode, Precedence::FrozenPrecedence()); } } // applyTemplates void applyTemplates(const DOMNodeList& nodes, ExecutionContext& context, const string_type& mode) const { // entirely simple so far LastFrame last(context, (size_t)nodes.getLength()); for(int i = 0, ie = nodes.getLength(); i != ie; ++i) { context.setPosition(nodes.item(i), i+1); doApplyTemplates(nodes.item(i), context, mode, Precedence::FrozenPrecedence()); } } // applyTemplates void applyTemplates(const DOMNode& node, ExecutionContext& context, const string_type& mode) const { LastFrame last(context, -1); context.setPosition(node, 1); doApplyTemplates(node, context, mode, Precedence::FrozenPrecedence()); } // applyTemplates void callTemplate(const string_type& name, const DOMNode& node, ExecutionContext& context) const { StackFrame frame(context); NamedTemplatesIterator t = named_templates_.find(name); if(t == named_templates_.end()) { std::cerr << "No template named '"; std::cerr << string_adaptor::asStdString(name) << "'. I should be a compile-time error!" << std::endl; throw SAX::SAXException("No template named " + string_adaptor::asStdString(name) + ". I should be a compile-time error. Sorry!"); return; } t->second->execute(node, context); } // callTemplate void applyImports(const DOMNode& node, ExecutionContext& context) const { doApplyTemplates(node, context, current_mode_, current_generation_); } // applyImports private: void doApplyTemplates(const DOMNode& node, ExecutionContext& context, const string_type& mode, const Precedence& generation) const { StackFrame frame(context); std::vector lower_precedences; for(TemplateStackIterator ts = templates_.begin(), tse = templates_.end(); ts != tse; ++ts) if(generation.is_descendant(ts->first)) lower_precedences.push_back(ts->first); std::sort(lower_precedences.rbegin(), lower_precedences.rend()); current_mode_ = mode; for(std::vector::const_iterator p = lower_precedences.begin(), pe = lower_precedences.end(); p != pe; ++p) { current_generation_ = *p; ModeTemplates ts = templates_.find(current_generation_)->second; ModeTemplatesIterator mt = ts.find(mode); if(mt != ts.end()) { const MatchTemplates& templates = mt->second; for(MatchTemplatesIterator t = templates.begin(), te = templates.end(); t != te; ++t) if(t->match().evaluate(node, context.xpathContext())) { t->action()->execute(node, context); return; } // if ... } // if ... } // for ... defaultAction(node, context, mode); } // doApplyTemplates void defaultAction(const DOMNode& node, ExecutionContext& context, const string_type& mode) const { switch(node.getNodeType()) { case DOM::Node_base::DOCUMENT_NODE: case DOM::Node_base::DOCUMENT_FRAGMENT_NODE: case DOM::Node_base::ELEMENT_NODE: applyTemplates(node.getChildNodes(), context, mode); break; case DOM::Node_base::ATTRIBUTE_NODE: case DOM::Node_base::TEXT_NODE: case DOM::Node_base::CDATA_SECTION_NODE: context.sink().characters(node.getNodeValue()); /* { const string_type& ch = node.getNodeValue(); for(string_type::const_iterator i = ch.begin(), e = ch.end(); i != e; ++i) if(!Arabica::XML::is_space(*i)) { context.sink().characters(ch); return; } // if ... } */ break; default: ;// nothing! } // switch } // defaultAction void set_parameter(const string_type& name, Value value) { params_.push_back(new TopLevelParam(string_adaptor::empty_string(), name, value)); } // set_parameter void set_parameter(const string_type& namespace_uri, const string_type& name, Value value) { params_.push_back(new TopLevelParam(namespace_uri, name, value)); } // set_parameter private: class MatchTemplate { public: MatchTemplate(const Arabica::XPath::MatchExpr& matchExpr, Template* templat) : match_(matchExpr), template_(templat) { } // MatchTemplate MatchTemplate(const MatchTemplate& rhs) : match_(rhs.match_), template_(rhs.template_) { } // MatchTemplate const Arabica::XPath::MatchExpr& match() const { return match_; } Template* action() const { return template_; } bool operator<(const MatchTemplate& rhs) const { // high priority first! return match_.priority() > rhs.match_.priority(); } // operator< private: Arabica::XPath::MatchExpr match_; Template* template_; }; // struct MatchTemplate typedef std::vector*> TemplateList; typedef typename TemplateList::const_iterator TemplateListIterator; typedef std::vector MatchTemplates; typedef typename MatchTemplates::const_iterator MatchTemplatesIterator; typedef std::map ModeTemplates; typedef typename ModeTemplates::const_iterator ModeTemplatesIterator; typedef std::map TemplateStack; typedef typename TemplateStack::const_iterator TemplateStackIterator; typedef std::map*> NamedTemplates; typedef typename NamedTemplates::const_iterator NamedTemplatesIterator; typedef std::vector*> VariableDeclList; typedef typename std::vector*>::const_iterator VariableDeclListIterator; typedef std::vector*> ParamList; typedef typename ParamList::const_iterator ParamListIterator; TemplateList all_templates_; NamedTemplates named_templates_; TemplateStack templates_; VariableDeclList topLevelVars_; DeclaredKeys keys_; ParamList params_; mutable string_type current_mode_; mutable Precedence current_generation_; typename Output::Settings output_settings_; typename Output::CDATAElements output_cdata_elements_; SinkHolder output_; mutable std::basic_ostream* error_output_; }; // class CompiledStylesheet } // namespace XSLT } // namespace Arabica #endif // ARABICA_XSLT_COMPILED_STYLESHEET_HPP