arabica/include/XSLT/impl/xslt_sink.hpp

503 lines
12 KiB
C++
Raw Normal View History

2007-07-19 19:01:42 +02:00
#ifndef ARABICA_XSLT_SINK_HPP
#define ARABICA_XSLT_SINK_HPP
#include <ostream>
#include <sstream>
#include <XML/escaper.hpp>
2007-09-05 00:55:47 +02:00
#include <SAX/ContentHandler.hpp>
#include <SAX/ext/LexicalHandler.hpp>
#include <SAX/helpers/AttributesImpl.hpp>
2009-03-31 21:06:06 +02:00
#include "xslt_output.hpp"
#include "handler/xslt_constants.hpp"
2007-07-19 19:01:42 +02:00
namespace Arabica
{
namespace XSLT
{
2012-11-06 20:29:22 +01:00
template<class string_type, class string_adaptor = Arabica::default_string_adaptor<string_type> >
2007-07-19 19:01:42 +02:00
class Sink
{
public:
virtual ~Sink() { }
2012-11-06 20:29:22 +01:00
virtual Output<string_type, string_adaptor>& asOutput() = 0;
2007-07-19 19:01:42 +02:00
}; // class Sink
2012-11-06 20:29:22 +01:00
template<class string_type, class string_adaptor>
2007-07-19 19:01:42 +02:00
class SinkHolder
{
public:
2012-11-06 20:29:22 +01:00
SinkHolder(Sink<string_type, string_adaptor>* sink) :
2007-07-19 19:01:42 +02:00
sink_(sink),
owned_(true)
{
} // SinkHolder
2012-11-06 20:29:22 +01:00
SinkHolder(Sink<string_type, string_adaptor>& sink) :
2007-07-19 19:01:42 +02:00
sink_(&sink),
owned_(false)
{
} // SinkHolder
~SinkHolder()
{
clear();
} // ~SinkHolder
2012-11-06 20:29:22 +01:00
void reset(Sink<string_type, string_adaptor>* sink)
2007-07-19 19:01:42 +02:00
{
clear();
sink_ = sink;
owned_ = true;
} // assign
2012-11-06 20:29:22 +01:00
void reset(Sink<string_type, string_adaptor>& sink)
2007-07-19 19:01:42 +02:00
{
clear();
sink_ = &sink;
owned_ = false;
} // reset
2012-11-06 20:29:22 +01:00
Sink<string_type, string_adaptor>& get() const
2007-07-19 19:01:42 +02:00
{
return *sink_;
} // get
private:
void clear()
{
if(owned_)
delete sink_;
} // clear
2012-11-06 20:29:22 +01:00
Sink<string_type, string_adaptor>* sink_;
2007-07-19 19:01:42 +02:00
bool owned_;
SinkHolder(const SinkHolder&);
bool operator==(const SinkHolder&) const;
SinkHolder& operator=(const SinkHolder&);
}; // class SinkHolder
2012-11-06 20:29:22 +01:00
template<class string_type, class string_adaptor = Arabica::default_string_adaptor<string_type> >
class StreamSink : public Sink<string_type, string_adaptor>, private Output<string_type, string_adaptor>
2007-07-19 19:01:42 +02:00
{
typedef StylesheetConstant<string_type, string_adaptor> SC;
2007-07-19 19:01:42 +02:00
public:
StreamSink(std::basic_ostream<typename string_adaptor::value_type>& stream) :
2007-07-19 19:01:42 +02:00
stream_(stream),
disable_output_escaping_(false),
in_cdata_(false),
2010-01-11 10:02:17 +01:00
empty_(false),
2007-07-19 19:01:42 +02:00
seen_root_(true),
indent_(-1),
2010-01-11 10:02:17 +01:00
out_again_(false)
2007-07-19 19:01:42 +02:00
{
} // StreamSink
StreamSink(const StreamSink& rhs) :
stream_(rhs.stream_),
disable_output_escaping_(rhs.disable_output_escaping_),
2010-01-11 10:02:17 +01:00
in_cdata_(rhs.in_cdata_),
empty_(false),
2007-07-19 19:01:42 +02:00
seen_root_(rhs.seen_root_),
indent_(rhs.indent_),
2010-01-11 10:02:17 +01:00
out_again_(rhs.out_again_),
settings_(rhs.settings_)
2007-07-19 19:01:42 +02:00
{
} // StreamSink
~StreamSink()
{
flush();
2007-07-19 19:01:42 +02:00
} // ~StreamSink
2012-11-06 20:29:22 +01:00
virtual Output<string_type, string_adaptor>& asOutput() { return *this; }
2007-07-19 19:01:42 +02:00
void flush() { stream_ << std::endl; }
2007-07-19 19:01:42 +02:00
protected:
2012-11-06 20:31:41 +01:00
typedef typename Output<string_type, string_adaptor>::Settings Settings;
2007-07-19 19:01:42 +02:00
void do_start_document(const Settings& settings)
{
seen_root_ = false;
settings_ = settings;
if(setting(SC::indent) == SC::yes)
2007-07-19 19:01:42 +02:00
indent_ = 0;
} // do_start_document
void do_end_document()
{
} // do_end_document
2012-11-06 20:29:22 +01:00
void do_start_element(const string_type& qName,
const string_type& /* namespaceURI */,
const SAX::Attributes<string_type, string_adaptor>& atts)
2007-07-19 19:01:42 +02:00
{
if(!seen_root_)
do_decl(qName);
if(empty_)
stream_ << ">";
indent();
stream_ << '<' << qName;
for(int a = 0; a < atts.getLength(); ++a)
{
stream_ << ' ' << atts.getQName(a) << '=' << '\"';
2012-11-06 20:29:22 +01:00
string_type ch = atts.getValue(a);
2012-11-21 07:59:40 +01:00
std::for_each(string_adaptor::begin(ch), string_adaptor::end(ch), Arabica::XML::attribute_escaper<typename string_adaptor::value_type>(stream_));
2007-07-19 19:01:42 +02:00
stream_ << '\"';
}
empty_ = true;
} // do_start_element
2012-11-06 20:29:22 +01:00
void do_end_element(const string_type& qName,
const string_type& /* namespaceURI */)
2007-07-19 19:01:42 +02:00
{
if(!seen_root_)
do_decl(string_adaptor::empty_string());
2007-07-19 19:01:42 +02:00
preoutdent(empty_);
if(empty_)
stream_ << "/>";
else
stream_ << "</" << qName << ">";
outdent(empty_);
empty_ = false;
} // do_end_element
2012-11-06 20:29:22 +01:00
void do_characters(const string_type& ch)
2007-07-19 19:01:42 +02:00
{
close_element_if_empty();
if(!disable_output_escaping_ && !in_cdata_)
2012-11-21 07:59:40 +01:00
std::for_each(string_adaptor::begin(ch), string_adaptor::end(ch), Arabica::XML::text_escaper<typename string_adaptor::value_type>(stream_));
else if(in_cdata_)
{
2012-11-21 07:59:40 +01:00
size_t breakAt = string_adaptor::find(ch, SC::CDATAEnd);
if(breakAt == string_adaptor::npos())
{
stream_ << ch;
return;
}
size_t start = 0;
do
{
breakAt += 2;
2012-11-21 07:59:40 +01:00
stream_ << string_adaptor::substr(ch, start, breakAt);
do_end_CDATA();
start = breakAt;
do_start_CDATA();
2012-11-21 07:59:40 +01:00
breakAt = string_adaptor::find(ch, SC::CDATAEnd, breakAt);
}
2012-11-21 07:59:40 +01:00
while(breakAt != string_adaptor::npos());
stream_ << string_adaptor::substr(ch, start);
}
2007-07-19 19:01:42 +02:00
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
2012-11-06 20:29:22 +01:00
void do_comment(const string_type& ch)
2007-07-19 19:01:42 +02:00
{
close_element_if_empty();
stream_ << SC::CommentStart
2007-07-19 19:01:42 +02:00
<< ch
<< SC::CommentEnd;
2007-07-19 19:01:42 +02:00
} // do_comment
2012-11-06 20:29:22 +01:00
void do_processing_instruction(const string_type& target,
const string_type& data)
2007-07-19 19:01:42 +02:00
{
close_element_if_empty();
stream_ << SC::PIStart
2007-07-19 19:01:42 +02:00
<< target
<< " "
<< data
<< SC::PIEnd;
2007-07-19 19:01:42 +02:00
} // 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());
2007-07-19 19:01:42 +02:00
} // 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
2012-11-06 20:29:22 +01:00
void do_decl(const string_type& qName)
2007-07-19 19:01:42 +02:00
{
if((setting(SC::method) == SC::text) || (setting(SC::omit_xml_declaration) == SC::yes))
2007-07-19 19:01:42 +02:00
return;
{
string_type version = setting(SC::version);
2012-11-21 07:59:40 +01:00
if(string_adaptor::empty(version))
version = SC::Version;
2007-07-19 19:01:42 +02:00
stream_ << "<?xml version=\"" << version << "\"";
}
{
string_type s = setting(SC::standalone);
2012-11-21 07:59:40 +01:00
if(!string_adaptor::empty(s))
2007-07-19 19:01:42 +02:00
stream_ << " standalone=\"" << s << "\"";
}
stream_ << "?>\n";
2012-11-21 07:59:40 +01:00
if(!string_adaptor::empty(qName))
2007-07-19 19:01:42 +02:00
{
string_type pub = setting(SC::doctype_public);
string_type sys = setting(SC::doctype_system);
2007-07-19 19:01:42 +02:00
2012-11-21 07:59:40 +01:00
if(!string_adaptor::empty(sys))
2007-07-19 19:01:42 +02:00
{
stream_ << "<!DOCTYPE " << qName << "\n";
2012-11-21 07:59:40 +01:00
if(!string_adaptor::empty(pub))
2007-07-19 19:01:42 +02:00
stream_ << " PUBLIC \"" << pub << "\" \"" << sys << "\">\n";
else
stream_ << " SYSTEM \"" << sys << "\">\n";
} // if(!sys.empty())
}
seen_root_ = true;
} // do_decl
2012-11-06 20:29:22 +01:00
string_type setting(const string_type& name)
2007-07-19 19:01:42 +02:00
{
2012-11-06 20:35:28 +01:00
typename Settings::const_iterator i = settings_.find(name);
2007-07-19 19:01:42 +02:00
if(i == settings_.end())
return string_adaptor::empty_string();
2007-07-19 19:01:42 +02:00
return i->second;
} // setting
std::basic_ostream<typename string_adaptor::value_type>& stream_;
2007-07-19 19:01:42 +02:00
bool disable_output_escaping_;
bool in_cdata_;
2007-07-19 19:01:42 +02:00
bool empty_;
bool seen_root_;
int indent_;
bool out_again_;
2010-01-11 10:02:17 +01:00
Settings settings_;
2007-07-19 19:01:42 +02:00
bool operator==(const StreamSink&) const;
StreamSink& operator=(const StreamSink&);
}; // class StreamSink
2012-11-06 20:29:22 +01:00
template<class string_type, class string_adaptor = Arabica::default_string_adaptor<string_type> >
class DOMSink : public Sink<string_type, string_adaptor>, private Output<string_type, string_adaptor>
2007-07-19 19:01:42 +02:00
{
2012-11-15 23:11:54 +01:00
typedef StylesheetConstant<string_type, string_adaptor> SC;
2007-07-19 19:01:42 +02:00
public:
DOMSink() :
indent_(-1),
out_again_(false)
{
} // DOMSink
~DOMSink()
{
} // ~DOMSink
2012-11-06 20:39:39 +01:00
virtual Output<string_type, string_adaptor>& asOutput() { return *this; }
2012-11-06 20:29:22 +01:00
DOM::Node<string_type, string_adaptor> node() const { return documentFrag_; }
2007-07-19 19:01:42 +02:00
protected:
2012-11-06 20:36:58 +01:00
typedef typename Output<string_type, string_adaptor>::Settings Settings;
2007-07-19 19:01:42 +02:00
void do_start_document(const Settings& settings)
{
typename Settings::const_iterator i = settings.find(SC::indent);
2007-07-19 19:01:42 +02:00
if((i != settings.end()) &&
(i->second == SC::yes))
2007-07-19 19:01:42 +02:00
indent_ = 0;
} // do_start_document
void do_end_document()
{
} // do_end_document
2012-11-06 20:29:22 +01:00
void do_characters(const string_type& ch)
2007-07-19 19:01:42 +02:00
{
2012-11-21 12:12:35 +01:00
DOM::Node<string_type, string_adaptor> lc = current().getLastChild();
2007-07-19 19:01:42 +02:00
if(lc == 0 || lc.getNodeType() != DOM::Node_base::TEXT_NODE)
current().appendChild(document().createTextNode(ch));
else
2012-11-21 07:59:40 +01:00
lc.setNodeValue(string_adaptor::concat(lc.getNodeValue(), ch));
2007-07-19 19:01:42 +02:00
} // do_characters
void do_start_CDATA()
{
} // do_start_CDATA
void do_end_CDATA()
{
2012-11-21 12:12:35 +01:00
DOM::Node<string_type, string_adaptor> lc = current().getLastChild();
if(lc.getNodeType() == DOM::Node_base::TEXT_NODE)
current().replaceChild(document().createCDATASection(lc.getNodeValue()), lc);
} // do_end_CDATA
2012-11-06 20:29:22 +01:00
void do_comment(const string_type& ch)
2007-07-19 19:01:42 +02:00
{
current().appendChild(document().createComment(ch));
} // do_comment
2012-11-06 20:29:22 +01:00
void do_processing_instruction(const string_type& target,
const string_type& data)
2007-07-19 19:01:42 +02:00
{
current().appendChild(document().createProcessingInstruction(target, data));
} // do_processing_instruction
void do_disableOutputEscaping(bool /* disable */) { }
2007-07-19 19:01:42 +02:00
bool want_namespace_declarations() const { return false; }
2012-11-06 20:29:22 +01:00
void do_start_element(const string_type& qName,
const string_type& namespaceURI,
const SAX::Attributes<string_type, string_adaptor>& atts)
2007-07-19 19:01:42 +02:00
{
indent();
2012-11-06 20:29:22 +01:00
DOM::Element<string_type, string_adaptor> elem = document().createElementNS(namespaceURI, qName);
2007-07-19 19:01:42 +02:00
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
2012-11-06 20:29:22 +01:00
void do_end_element(const string_type& /* qName */,
const string_type& /* namespaceURI */)
2007-07-19 19:01:42 +02:00
{
outdent();
current_ = current_.getParentNode();
} // do_end_element
private:
2012-11-21 12:12:35 +01:00
DOM::Document<string_type, string_adaptor>& document()
2007-07-19 19:01:42 +02:00
{
if(document_ != 0)
return document_;
2012-11-06 20:29:22 +01:00
DOM::DOMImplementation<string_type, string_adaptor> di = SimpleDOM::DOMImplementation<string_type, string_adaptor>::getDOMImplementation();
document_ = di.createDocument(string_adaptor::empty_string(), string_adaptor::empty_string(), 0);
2007-07-19 19:01:42 +02:00
return document_;
} // document
2012-11-21 12:12:35 +01:00
DOM::Node<string_type, string_adaptor>& current()
2007-07-19 19:01:42 +02:00
{
if(current_ != 0)
return current_;
documentFrag_ = document().createDocumentFragment();
current_ = documentFrag_;
return current_;
} // current
void indent()
{
if(indent_ == -1)
return;
2012-11-06 20:29:22 +01:00
string_type sps;
2007-07-19 19:01:42 +02:00
if(indent_ != 0)
{
2012-11-21 07:59:40 +01:00
std::basic_string<typename string_adaptor::value_type> sps;
sps += SC::CARRIAGE_RETURN;
std::fill_n(std::back_inserter<std::basic_string<typename string_adaptor::value_type> >(sps), indent_, SC::SPACE);
do_characters(string_adaptor::construct(sps));
2007-07-19 19:01:42 +02:00
} // if ...
indent_ += 2;
out_again_ = false;
} // indent
void outdent()
{
if(indent_ == -1)
return;
if(out_again_)
do_characters(SC::newline);
2007-07-19 19:01:42 +02:00
indent_ -= 2;
out_again_ = true;
} // outdent
2012-11-06 20:29:22 +01:00
DOM::Document<string_type, string_adaptor> document_;
DOM::DocumentFragment<string_type, string_adaptor> documentFrag_;
DOM::Node<string_type, string_adaptor> current_;
2007-07-19 19:01:42 +02:00
int indent_;
bool out_again_;
}; // DOMSink
} // namespace XSLT
} // namespace Arabica
#endif // ARABICA_XSLT_SINK_HPP