#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 { typedef StylesheetConstant SC; public: StreamSink(std::basic_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() { flush(); } // ~StreamSink virtual Output& asOutput() { return *this; } void flush() { stream_ << std::endl; } protected: typedef typename Output::Settings Settings; void do_start_document(const Settings& settings) { seen_root_ = false; settings_ = settings; if(setting(SC::indent) == SC::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(string_adaptor::begin(ch), string_adaptor::end(ch), 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(string_adaptor::empty_string()); 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(string_adaptor::begin(ch), string_adaptor::end(ch), Arabica::XML::text_escaper(stream_)); else if(in_cdata_) { size_t breakAt = string_adaptor::find(ch, SC::CDATAEnd); if(breakAt == string_adaptor::npos()) { stream_ << ch; return; } size_t start = 0; do { breakAt += 2; stream_ << string_adaptor::substr(ch, start, breakAt); do_end_CDATA(); start = breakAt; do_start_CDATA(); breakAt = string_adaptor::find(ch, SC::CDATAEnd, breakAt); } while(breakAt != string_adaptor::npos()); stream_ << string_adaptor::substr(ch, start); } else stream_ << ch; } // characters void do_start_CDATA() { close_element_if_empty(); in_cdata_ = true; stream_ << SC::CDATAStart; } // do_start_CDATA void do_end_CDATA() { in_cdata_ = false; stream_ << SC::CDATAEnd; } // do_end_CDATA void do_comment(const string_type& ch) { close_element_if_empty(); stream_ << SC::CommentStart << ch << SC::CommentEnd; } // do_comment void do_processing_instruction(const string_type& target, const string_type& data) { close_element_if_empty(); stream_ << SC::PIStart << target << " " << data << SC::PIEnd; } // 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(string_adaptor::empty_string()); } // 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(SC::method) == SC::text) || (setting(SC::omit_xml_declaration) == SC::yes)) return; { string_type version = setting(SC::version); if(string_adaptor::empty(version)) version = SC::Version; stream_ << "\n"; if(!string_adaptor::empty(qName)) { string_type pub = setting(SC::doctype_public); string_type sys = setting(SC::doctype_system); if(!string_adaptor::empty(sys)) { stream_ << "\n"; else stream_ << " SYSTEM \"" << sys << "\">\n"; } // if(!sys.empty()) } seen_root_ = true; } // do_decl string_type setting(const string_type& name) { typename Settings::const_iterator i = settings_.find(name); if(i == settings_.end()) return string_adaptor::empty_string(); return i->second; } // setting std::basic_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 { typedef StylesheetConstant SC; public: DOMSink() : indent_(-1), out_again_(false) { } // DOMSink ~DOMSink() { } // ~DOMSink virtual Output& asOutput() { return *this; } DOM::Node node() const { return documentFrag_; } protected: typedef typename Output::Settings Settings; void do_start_document(const Settings& settings) { typename Settings::const_iterator i = settings.find(SC::indent); if((i != settings.end()) && (i->second == SC::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(string_adaptor::concat(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(string_adaptor::empty_string(), string_adaptor::empty_string(), 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) { std::basic_string sps; sps += SC::CARRIAGE_RETURN; std::fill_n(std::back_inserter >(sps), indent_, SC::SPACE); do_characters(string_adaptor::construct(sps)); } // if ... indent_ += 2; out_again_ = false; } // indent void outdent() { if(indent_ == -1) return; if(out_again_) do_characters(SC::newline); 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