#ifndef ARABICA_XSLT_SINK_HPP #define ARABICA_XSLT_SINK_HPP #include #include #include #include #include #include #include "xslt_output.hpp" #include "handler/xslt_constants.hpp" namespace Arabica { namespace XSLT { template > class Sink { public: virtual ~Sink() { } virtual Output& asOutput() = 0; }; // class Sink template class SinkHolder { public: SinkHolder(Sink* sink) : sink_(sink), owned_(true) { } // SinkHolder SinkHolder(Sink& sink) : sink_(&sink), owned_(false) { } // SinkHolder ~SinkHolder() { clear(); } // ~SinkHolder void reset(Sink* sink) { clear(); sink_ = sink; owned_ = true; } // assign void reset(Sink& sink) { clear(); sink_ = &sink; owned_ = false; } // reset Sink& get() const { return *sink_; } // get private: void clear() { if(owned_) delete sink_; } // clear Sink* sink_; bool owned_; SinkHolder(const SinkHolder&); bool operator==(const SinkHolder&) const; SinkHolder& operator=(const SinkHolder&); }; // class SinkHolder template > class StreamSink : public Sink, private Output { public: StreamSink(std::ostream& stream) : stream_(stream), disable_output_escaping_(false), in_cdata_(false), empty_(false), seen_root_(true), indent_(-1), out_again_(false) { } // StreamSink StreamSink(const StreamSink& rhs) : stream_(rhs.stream_), disable_output_escaping_(rhs.disable_output_escaping_), in_cdata_(rhs.in_cdata_), empty_(false), seen_root_(rhs.seen_root_), indent_(rhs.indent_), out_again_(rhs.out_again_), settings_(rhs.settings_) { } // StreamSink ~StreamSink() { stream_.flush(); } // ~StreamSink virtual Output& asOutput() { return *this; } protected: void do_start_document(const Settings& settings) { seen_root_ = false; settings_ = settings; if(setting("indent") == "yes") indent_ = 0; } // do_start_document void do_end_document() { } // do_end_document void do_start_element(const string_type& qName, const string_type& /* namespaceURI */, const SAX::Attributes& atts) { if(!seen_root_) do_decl(qName); if(empty_) stream_ << ">"; indent(); stream_ << '<' << qName; for(int a = 0; a < atts.getLength(); ++a) { stream_ << ' ' << atts.getQName(a) << '=' << '\"'; string_type ch = atts.getValue(a); std::for_each(ch.begin(), ch.end(), Arabica::XML::attribute_escaper(stream_)); stream_ << '\"'; } empty_ = true; } // do_start_element void do_end_element(const string_type& qName, const string_type& /* namespaceURI */) { if(!seen_root_) do_decl(""); preoutdent(empty_); if(empty_) stream_ << "/>"; else stream_ << ""; outdent(empty_); empty_ = false; } // do_end_element void do_characters(const string_type& ch) { close_element_if_empty(); if(!disable_output_escaping_ && !in_cdata_) std::for_each(ch.begin(), ch.end(), Arabica::XML::text_escaper(stream_)); else if(in_cdata_) { size_t breakAt = ch.find("]]>"); if(breakAt == string_type::npos) { stream_ << ch; return; } size_t start = 0; do { breakAt += 2; stream_ << ch.substr(start, breakAt); do_end_CDATA(); start = breakAt; do_start_CDATA(); breakAt = ch.find("]]>", breakAt); } while(breakAt != string_type::npos); stream_ << ch.substr(start); } else stream_ << ch; } // characters void do_start_CDATA() { close_element_if_empty(); in_cdata_ = true; stream_ << ""; } // do_end_CDATA void do_comment(const string_type& ch) { close_element_if_empty(); stream_ << ""; } // do_comment void do_processing_instruction(const string_type& target, const string_type& data) { close_element_if_empty(); stream_ << ""; } // do_processing_instruction void do_disableOutputEscaping(bool disable) { disable_output_escaping_ = disable; } bool want_namespace_declarations() const { return true; } private: void close_element_if_empty() { if(empty_) { stream_ << '>'; empty_ = false; } if(!seen_root_) do_decl(""); } // close_element_if_empty void indent() { if(indent_ == -1) return; if(indent_ != 0) stream_ << "\n"; for(int i = 0; i != indent_; ++i) stream_ << " "; indent_ += 2; out_again_ = false; } // indent void preoutdent(bool empty) { if(out_again_ && !empty) stream_ << "\n"; } // preoutdent void outdent(bool empty) { if(indent_ == -1) return; if(out_again_ && empty) stream_ << "\n"; indent_ -= 2; out_again_ = true; } // outdent void do_decl(const string_type& qName) { if((setting("method") == "text") || (setting("omit-xml-declaration") == "yes")) return; { string_type version = setting("version"); if(version.empty()) version = "1.0"; stream_ << "\n"; if(!qName.empty()) { string_type pub = setting("doctype-public"); string_type sys = setting("doctype-system"); if(!sys.empty()) { stream_ << "\n"; else stream_ << " SYSTEM \"" << sys << "\">\n"; } // if(!sys.empty()) } seen_root_ = true; } // do_decl string_type setting(const string_type& name) { Settings::const_iterator i = settings_.find(name); if(i == settings_.end()) return ""; return i->second; } // setting std::ostream& stream_; bool disable_output_escaping_; bool in_cdata_; bool empty_; bool seen_root_; int indent_; bool out_again_; Settings settings_; bool operator==(const StreamSink&) const; StreamSink& operator=(const StreamSink&); }; // class StreamSink template > class DOMSink : public Sink, private Output { public: DOMSink() : indent_(-1), out_again_(false) { } // DOMSink ~DOMSink() { } // ~DOMSink virtual Output& asOutput() { return *this; } DOM::Node node() const { return documentFrag_; } protected: void do_start_document(const Settings& settings) { Settings::const_iterator i = settings.find("indent"); if((i != settings.end()) && (i->second == "yes")) indent_ = 0; } // do_start_document void do_end_document() { } // do_end_document void do_characters(const string_type& ch) { DOM::Node lc = current().getLastChild(); if(lc == 0 || lc.getNodeType() != DOM::Node_base::TEXT_NODE) current().appendChild(document().createTextNode(ch)); else lc.setNodeValue(lc.getNodeValue() + ch); } // do_characters void do_start_CDATA() { } // do_start_CDATA void do_end_CDATA() { DOM::Node lc = current().getLastChild(); if(lc.getNodeType() == DOM::Node_base::TEXT_NODE) current().replaceChild(document().createCDATASection(lc.getNodeValue()), lc); } // do_end_CDATA void do_comment(const string_type& ch) { current().appendChild(document().createComment(ch)); } // do_comment void do_processing_instruction(const string_type& target, const string_type& data) { current().appendChild(document().createProcessingInstruction(target, data)); } // do_processing_instruction void do_disableOutputEscaping(bool /* disable */) { } bool want_namespace_declarations() const { return false; } void do_start_element(const string_type& qName, const string_type& namespaceURI, const SAX::Attributes& atts) { indent(); DOM::Element elem = document().createElementNS(namespaceURI, qName); current().appendChild(elem); // attributes here for(int i = 0; i < atts.getLength(); ++i) elem.setAttributeNS(atts.getURI(i), atts.getQName(i), atts.getValue(i)); current_ = elem; } // do_start_element void do_end_element(const string_type& /* qName */, const string_type& /* namespaceURI */) { outdent(); current_ = current_.getParentNode(); } // do_end_element private: DOM::Document& document() { if(document_ != 0) return document_; DOM::DOMImplementation di = SimpleDOM::DOMImplementation::getDOMImplementation(); document_ = di.createDocument("", "", 0); return document_; } // document DOM::Node& current() { if(current_ != 0) return current_; documentFrag_ = document().createDocumentFragment(); current_ = documentFrag_; return current_; } // current void indent() { if(indent_ == -1) return; string_type sps; if(indent_ != 0) { sps += '\n'; std::fill_n(std::back_inserter(sps), indent_, ' '); do_characters(sps); } // if ... indent_ += 2; out_again_ = false; } // indent void outdent() { if(indent_ == -1) return; if(out_again_) do_characters("\n"); indent_ -= 2; out_again_ = true; } // outdent DOM::Document document_; DOM::DocumentFragment documentFrag_; DOM::Node current_; int indent_; bool out_again_; }; // DOMSink } // namespace XSLT } // namespace Arabica #endif // ARABICA_XSLT_SINK_HPP