arabica/include/XSLT/impl/handler/xslt_include_handler.hpp
2012-11-21 06:59:40 +00:00

243 lines
7.2 KiB
C++

#ifndef ARABICA_XSLT_INCLUDE_HANDLER_HPP
#define ARABICA_XSLT_INCLUDE_HANDLER_HPP
#include <algorithm>
#include "xslt_constants.hpp"
namespace Arabica
{
namespace XSLT
{
template<class string_type, class string_adaptor>
class IncludeHandler : public SAX::DefaultHandler<string_type, string_adaptor>
{
struct ImportHref;
typedef StylesheetConstant<string_type, string_adaptor> SC;
typedef AttributeValidators<string_type, string_adaptor> AV;
public:
IncludeHandler() :
context_(0),
compiler_(0),
no_content_(false)
{
} // IncludeHandler
void context(CompilationContext<string_type, string_adaptor>& context,
SAX::DefaultHandler<string_type, string_adaptor>* compiler)
{
context_ = &context;
compiler_ = compiler;
} // context
void start_include(const string_type& namespaceURI,
const string_type& localName,
const string_type& qName,
const SAX::Attributes<string_type, string_adaptor>& atts)
{
context_->parser().setContentHandler(*this);
startElement(namespaceURI, localName, qName, atts);
} // start_include
virtual void startDocument()
{
context_->parentHandler().startDocument();
} // startDocument
virtual void startElement(const string_type& namespaceURI,
const string_type& localName,
const string_type& qName,
const SAX::Attributes<string_type, string_adaptor>& atts)
{
if(no_content_)
throw SAX::SAXException("xsl:include must be empty");
if(namespaceURI == StylesheetConstant<string_type, string_adaptor>::NamespaceURI)
{
if(localName == SC::import)
{
string_type href = validate_href(qName, atts);
import_stack_.push_back(href, context_->next_precedence(), current_includes_);
return;
} // if ...
if(localName == SC::include)
{
href_.push_back(validate_href(qName, atts));
return;
} // if ...
} // if ...
context_->parentHandler().startElement(namespaceURI,
localName,
qName,
atts);
} // startElement
virtual void startPrefixMapping(const string_type& prefix, const string_type& uri)
{
context_->parentHandler().startPrefixMapping(prefix, uri);
} // startPrefixMapping
virtual void endElement(const string_type& namespaceURI,
const string_type& localName,
const string_type& qName)
{
if(no_content_ &&
(namespaceURI == StylesheetConstant<string_type, string_adaptor>::NamespaceURI))
{
no_content_ = false;
if(localName == SC::include)
{
include_stylesheet(href_.back(), context_->precedence());
href_.pop_back();
} // if ...
if(href_.empty())
context_->parser().setContentHandler(*compiler_);
return;
} // if ...
context_->parentHandler().endElement(namespaceURI,
localName,
qName);
} // endElement
virtual void characters(const string_type& ch)
{
if(no_content_)
verifyNoCharacterData<string_type, string_adaptor>(ch, SC::include); // + SC::import
context_->parentHandler().characters(ch);
} // characters
void unwind_imports()
{
while(!import_stack_.empty())
{
import_stylesheet(import_stack_.current());
import_stack_.pop();
} // while ...
} // unwind_imports
private:
string_type validate_href(const string_type& qName, const SAX::Attributes<string_type, string_adaptor>& atts)
{
static const AV rules = AV::rule(SC::href, true);
string_type href = rules.gather(qName, atts)[SC::href];
no_content_ = true;
// std::cout << "Base : " << context_->currentBase() << ", href : " << href << "\n";
return context_->makeAbsolute(href);
} // validate_href
void check_for_loops(const string_type& href)
{
if(std::find(current_includes_.begin(), current_includes_.end(), href) != current_includes_.end())
{
std::string error = "Stylesheet '" + string_adaptor::asStdString(href) + "' includes/imports itself ";
for(typename HrefStack::const_iterator i = current_includes_.begin(), ie = current_includes_.end(); i != ie; ++i)
error += "\n " + string_adaptor::asStdString(*i);
throw std::runtime_error(error);
} // if ...
} // check_for_loops
void import_stylesheet(const ImportHref& import)
{
current_includes_ = import.includes;
include_stylesheet(import.href, import.precedence);
} // import_stylesheet
void include_stylesheet(const string_type& href, const Precedence& precedence)
{
check_for_loops(href);
current_includes_.push_back(href);
string_type prev = context_->setBase(href);
context_->set_precedence(precedence);
SAX::InputSource<string_type, string_adaptor> source(href);
SAX::XMLReader<string_type, string_adaptor> include_parser;
SAX::CatchErrorHandler<string_type, string_adaptor> errorHandler;
include_parser.setContentHandler(*this);
include_parser.setErrorHandler(errorHandler);
include_parser.parse(source);
context_->setBase(prev);
if(errorHandler.errorsReported())
throw std::runtime_error("Could not import/include stylesheet '" + string_adaptor::asStdString(href) + "' - " + errorHandler.errors());
current_includes_.pop_back();
} // include_stylesheet
CompilationContext<string_type, string_adaptor>* context_;
SAX::DefaultHandler<string_type, string_adaptor>* compiler_;
bool no_content_;
typedef std::vector<string_type> HrefStack;
struct ImportHref
{
string_type href;
Precedence precedence;
HrefStack includes;
ImportHref() { }
ImportHref(const string_type& h, const Precedence& p, const HrefStack& i) :
href(h),
precedence(p),
includes(i)
{
} // ImportHref
ImportHref& operator=(const ImportHref& rhs)
{
href = rhs.href;
precedence = rhs.precedence;
includes = rhs.includes;
return *this;
} // operator=
}; // string ImportHref
class ImportStack
{
public:
ImportStack() { }
void push_back(const string_type& href, const Precedence& precedence, const HrefStack& includes)
{
stack_.push_back(ImportHref(href, precedence, includes));
} // push_back
bool empty() const { return stack_.empty(); }
const ImportHref& current() const
{
most_recent_ = stack_.size() - 1;
return stack_[most_recent_];
} // current
void pop()
{
stack_.erase(stack_.begin() + most_recent_);
} // pop
private:
std::vector<ImportHref> stack_;
mutable size_t most_recent_;
}; // class ImportStack
ImportStack import_stack_;
HrefStack current_includes_;
HrefStack href_;
}; // class IncludeHandler
} // namespace XSLT
} // namespace Arabica
#endif