arabica/include/XSLT/impl/xslt_sink.hpp
2009-03-31 20:06:06 +01:00

438 lines
9.3 KiB
C++
Executable file

#ifndef ARABICA_XSLT_SINK_HPP
#define ARABICA_XSLT_SINK_HPP
#include <ostream>
#include <sstream>
#include <XML/escaper.hpp>
#include <SAX/ContentHandler.hpp>
#include <SAX/ext/LexicalHandler.hpp>
#include <SAX/helpers/AttributesImpl.hpp>
#include "xslt_output.hpp"
#include "handler/xslt_constants.hpp"
namespace Arabica
{
namespace XSLT
{
class Sink
{
public:
virtual ~Sink() { }
virtual Output& asOutput() = 0;
}; // class Sink
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
class StreamSink : public Sink, private Output
{
public:
StreamSink(std::ostream& stream) :
stream_(stream),
disable_output_escaping_(false),
seen_root_(true),
out_again_(false),
indent_(-1),
empty_(false)
{
} // StreamSink
StreamSink(const StreamSink& rhs) :
stream_(rhs.stream_),
disable_output_escaping_(rhs.disable_output_escaping_),
seen_root_(rhs.seen_root_),
settings_(rhs.settings_),
indent_(rhs.indent_),
empty_(false),
out_again_(rhs.out_again_)
{
} // 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 std::string& qName,
const std::string& namespaceURI,
const SAX::Attributes<std::string>& 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) << '=' << '\"';
std::string ch = atts.getValue(a);
std::for_each(ch.begin(), ch.end(), Arabica::XML::attribute_escaper<char>(stream_));
stream_ << '\"';
}
empty_ = true;
} // do_start_element
void do_end_element(const std::string& qName, const std::string& namespaceURI)
{
if(!seen_root_)
do_decl("");
preoutdent(empty_);
if(empty_)
stream_ << "/>";
else
stream_ << "</" << qName << ">";
outdent(empty_);
empty_ = false;
} // do_end_element
void do_characters(const std::string& ch)
{
close_element_if_empty();
if(!disable_output_escaping_)
std::for_each(ch.begin(), ch.end(), Arabica::XML::text_escaper<char>(stream_));
else
stream_ << ch;
} // characters
void do_comment(const std::string& ch)
{
close_element_if_empty();
stream_ << "<!--"
<< ch
<< "-->";
} // do_comment
void do_processing_instruction(const std::string& target,
const std::string& data)
{
close_element_if_empty();
stream_ << "<?"
<< target
<< " "
<< data
<< "?>";
} // 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 std::string& qName)
{
if((setting("method") == "text") || (setting("omit-xml-declaration") == "yes"))
return;
{
std::string version = setting("version");
if(version.empty())
version = "1.0";
stream_ << "<?xml version=\"" << version << "\"";
}
{
std::string s = setting("standalone");
if(!s.empty())
stream_ << " standalone=\"" << s << "\"";
}
stream_ << "?>\n";
if(!qName.empty())
{
std::string pub = setting("doctype-public");
std::string sys = setting("doctype-system");
if(!sys.empty())
{
stream_ << "<!DOCTYPE " << qName << "\n";
if(!pub.empty())
stream_ << " PUBLIC \"" << pub << "\" \"" << sys << "\">\n";
else
stream_ << " SYSTEM \"" << sys << "\">\n";
} // if(!sys.empty())
}
seen_root_ = true;
} // do_decl
std::string setting(const std::string& 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 empty_;
bool seen_root_;
Settings settings_;
int indent_;
bool out_again_;
bool operator==(const StreamSink&) const;
StreamSink& operator=(const StreamSink&);
}; // class StreamSink
class DOMSink : public Sink, private Output
{
public:
DOMSink() :
indent_(-1),
out_again_(false)
{
} // DOMSink
~DOMSink()
{
} // ~DOMSink
virtual Output& asOutput() { return *this; }
DOM::Node<std::string> 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 std::string& ch)
{
DOM::Node<std::string> 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_comment(const std::string& ch)
{
current().appendChild(document().createComment(ch));
} // do_comment
void do_processing_instruction(const std::string& target,
const std::string& 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 std::string& qName,
const std::string& namespaceURI,
const SAX::Attributes<std::string>& atts)
{
indent();
DOM::Element<std::string> 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 std::string& qName,
const std::string& namespaceURI)
{
outdent();
current_ = current_.getParentNode();
} // do_end_element
private:
DOM::Document<std::string>& document()
{
if(document_ != 0)
return document_;
DOM::DOMImplementation<std::string> di = SimpleDOM::DOMImplementation<std::string>::getDOMImplementation();
document_ = di.createDocument("", "", 0);
return document_;
} // document
DOM::Node<std::string>& current()
{
if(current_ != 0)
return current_;
documentFrag_ = document().createDocumentFragment();
current_ = documentFrag_;
return current_;
} // current
void indent()
{
if(indent_ == -1)
return;
std::string sps;
if(indent_ != 0)
{
sps += '\n';
std::fill_n(std::back_inserter<std::string>(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<std::string> document_;
DOM::DocumentFragment<std::string> documentFrag_;
DOM::Node<std::string> current_;
int indent_;
bool out_again_;
}; // DOMSink
} // namespace XSLT
} // namespace Arabica
#endif // ARABICA_XSLT_SINK_HPP