mirror of
https://github.com/jezhiggins/arabica
synced 2025-01-06 05:24:35 +01:00
f610d739fe
Templates are now constructed with their precedence. Variables are too, with a tweak to allow for the immediate evaluation of non-topl-level params and vars. The execution context now no longer needs to track variable precedence, which is good because it will be getting it wrong anyway. Corresponding simplifications follow to compliation context.
181 lines
5.3 KiB
C++
181 lines
5.3 KiB
C++
#ifndef ARABICA_XSLT_INCLUDE_HANDLER_HPP
|
|
#define ARABICA_XSLT_INCLUDE_HANDLER_HPP
|
|
|
|
#include <algorithm>
|
|
#include "xslt_constants.hpp"
|
|
#include "xslt_value_validation.hpp"
|
|
|
|
namespace Arabica
|
|
{
|
|
namespace XSLT
|
|
{
|
|
|
|
class IncludeHandler : public SAX::DefaultHandler<std::string>
|
|
{
|
|
public:
|
|
IncludeHandler() :
|
|
context_(0),
|
|
compiler_(0),
|
|
no_content_(false)
|
|
{
|
|
} // IncludeHandler
|
|
|
|
void context(CompilationContext& context, SAX::DefaultHandler<std::string>* compiler)
|
|
{
|
|
context_ = &context;
|
|
compiler_ = compiler;
|
|
} // context
|
|
|
|
void start_include(const std::string& namespaceURI,
|
|
const std::string& localName,
|
|
const std::string& qName,
|
|
const SAX::Attributes<std::string>& atts)
|
|
{
|
|
context_->parser().setContentHandler(*this);
|
|
startElement(namespaceURI, localName, qName, atts);
|
|
} // start_include
|
|
|
|
virtual void startDocument()
|
|
{
|
|
context_->parentHandler().startDocument();
|
|
} // startDocument
|
|
|
|
virtual void startElement(const std::string& namespaceURI,
|
|
const std::string& localName,
|
|
const std::string& qName,
|
|
const SAX::Attributes<std::string>& atts)
|
|
{
|
|
if(no_content_)
|
|
throw SAX::SAXException("xsl:include must be empty");
|
|
|
|
bool start_pass_through = false;
|
|
if(namespaceURI == StylesheetConstant::NamespaceURI())
|
|
{
|
|
if(localName == "import")
|
|
{
|
|
std::string href = validate_href(qName, atts);
|
|
check_for_loops(import_stack_, href);
|
|
import_stack_.push_back(href);
|
|
return;
|
|
} // if(localName == "import")
|
|
if(localName == "include")
|
|
{
|
|
href_.push_back(validate_href(qName, atts));
|
|
return;
|
|
} // if(localName == "include")
|
|
} // if ...
|
|
|
|
context_->parentHandler().startElement(namespaceURI,
|
|
localName,
|
|
qName,
|
|
atts);
|
|
} // startElement
|
|
|
|
virtual void startPrefixMapping(const std::string& prefix, const std::string& uri)
|
|
{
|
|
context_->parentHandler().startPrefixMapping(prefix, uri);
|
|
} // startPrefixMapping
|
|
|
|
|
|
virtual void endElement(const std::string& namespaceURI,
|
|
const std::string& localName,
|
|
const std::string& qName)
|
|
{
|
|
if(no_content_ &&
|
|
(namespaceURI == StylesheetConstant::NamespaceURI()))
|
|
{
|
|
no_content_ = false;
|
|
if(localName == "include")
|
|
{
|
|
include_stylesheet(href_.back());
|
|
href_.pop_back();
|
|
} // if ...
|
|
if(href_.empty())
|
|
context_->parser().setContentHandler(*compiler_);
|
|
return;
|
|
} // if ...
|
|
|
|
context_->parentHandler().endElement(namespaceURI,
|
|
localName,
|
|
qName);
|
|
} // endElement
|
|
|
|
virtual void characters(const std::string& ch)
|
|
{
|
|
if(no_content_)
|
|
throw SAX::SAXException("xsl:include/xsl:import must be empty");
|
|
context_->parentHandler().characters(ch);
|
|
} // characters
|
|
|
|
void unwind_imports()
|
|
{
|
|
while(!import_stack_.empty())
|
|
{
|
|
std::vector<std::string>::iterator import = import_stack_.end()-1;
|
|
size_t index = import_stack_.size() - 1;
|
|
context_->push_import_precedence();
|
|
include_stylesheet(import_stack_.back());
|
|
context_->pop_import_precedence();
|
|
import_stack_.erase(import_stack_.begin() + index);
|
|
} // while ...
|
|
} // unwind_imports
|
|
|
|
private:
|
|
std::string validate_href(const std::string& qName, const SAX::Attributes<std::string>& atts)
|
|
{
|
|
static const ValueRule rules[] = { { "href", true, 0 },
|
|
{ 0, false, 0 } };
|
|
std::string href = gatherAttributes(qName, atts, rules)["href"];
|
|
no_content_ = true;
|
|
return context_->makeAbsolute(href);
|
|
} // validate_href
|
|
|
|
void check_for_loops(const std::vector<std::string>& stack, const std::string& href)
|
|
{
|
|
if(std::find(stack.begin(), stack.end(), href) != stack.end())
|
|
{
|
|
std::string error = "Stylesheet '" + href + "' includes/imports itself ";
|
|
for(std::vector<std::string>::const_iterator i = stack.begin(), ie = stack.end(); i != ie; ++i)
|
|
error += "\n " + *i;
|
|
throw std::runtime_error(error);
|
|
} // if ...
|
|
} // check_for_loops
|
|
|
|
void include_stylesheet(const std::string& href)
|
|
{
|
|
check_for_loops(current_includes_, href);
|
|
current_includes_.push_back(href);
|
|
|
|
std::string prev = context_->setBase(href);
|
|
|
|
SAX::InputSource<std::string> source(href);
|
|
SAX::XMLReader<std::string> include_parser;
|
|
SAX::CatchErrorHandler<std::string> 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 '" + href + "' - " + errorHandler.errors());
|
|
|
|
current_includes_.pop_back();
|
|
} // include_stylesheet
|
|
|
|
SAX::DefaultHandler<std::string>* compiler_;
|
|
CompilationContext* context_;
|
|
bool no_content_;
|
|
std::vector<std::string> import_stack_;
|
|
std::vector<std::string> current_includes_;
|
|
|
|
std::vector<std::string> href_;
|
|
}; // class IncludeHandler
|
|
|
|
} // namespace XSLT
|
|
} // namespace Arabica
|
|
|
|
#endif
|
|
|