arabica/include/XSLT/impl/xslt_stylesheet_compiler.hpp

271 lines
10 KiB
C++

#ifndef ARABICA_XSLT_STYLESHEETCONSTANT_HPP
#define ARABICA_XSLT_STYLESHEETCONSTANT_HPP
#include <XML/XMLCharacterClasses.hpp>
#include <XPath/impl/xpath_namespace_context.hpp>
#include <memory>
#include "xslt_stylesheet_parser.hpp"
#include "xslt_compiled_stylesheet.hpp"
#include "xslt_compilation_context.hpp"
#include "handler/xslt_value_validation.hpp"
#include "handler/xslt_template_handler.hpp"
#include "handler/xslt_include_handler.hpp"
#include "handler/xslt_output_handler.hpp"
#include "handler/xslt_namespace_alias_handler.hpp"
#include "handler/xslt_key_handler.hpp"
#include "handler/xslt_foreign_element_handler.hpp"
namespace Arabica
{
namespace XSLT
{
template<class string_type, class string_adaptor>
class StylesheetHandler : public SAX::DefaultHandler<string_type, string_adaptor>
{
typedef StylesheetConstant<string_type, string_adaptor> SC;
typedef AttributeValidators<string_type, string_adaptor> AV;
public:
typedef string_type stringT;
typedef string_adaptor string_adaptorT;
typedef SAX::Attributes<string_type, string_adaptor> AttributesT;
typedef SAX::DefaultHandler<string_type, string_adaptor> DefaultHandlerT;
typedef CompilationContext<string_type, string_adaptor> CompilationContextT;
StylesheetHandler(CompilationContextT& context) :
context_(context),
top_(false)
{
context_.root(*this);
includer_.context(context_, this);
} // StylesheetHandler
virtual void startDocument()
{
top_ = true;
} // startDocument
virtual void startElement(const string_type& namespaceURI,
const string_type& localName,
const string_type& qName,
const AttributesT& atts)
{
if(top_)
{
top_ = false;
if(namespaceURI == SC::NamespaceURI)
startStylesheet(namespaceURI, localName, qName, atts);
else
startLREAsStylesheet(namespaceURI, localName, qName, atts);
return;
} // if(top_)
if(namespaceURI == SC::NamespaceURI)
startXSLTElement(namespaceURI, localName, qName, atts);
else if(!string_adaptor::empty(namespaceURI))
startForeignElement(namespaceURI, localName, qName, atts);
else
oops(qName);
} // startElement
virtual void endElement(const string_type& /* namespaceURI */,
const string_type& /* localName */,
const string_type& /* qName */)
{
} // endElement
virtual void characters(const string_type& ch)
{
verifyNoCharacterData<string_type, string_adaptor>(ch, SC::stylesheet);
} // characters
virtual void endDocument()
{
includer_.unwind_imports();
context_.stylesheet().prepare();
} // endDocument
private:
void startStylesheet(const string_type& /* namespaceURI */,
const string_type& localName,
const string_type& qName,
const AttributesT& atts)
{
if(localName != SC::stylesheet && localName != SC::transform)
throw SAX::SAXException("Top-level element must be 'stylesheet' or 'transform'.");
static const AV rules = AV::rule(SC::version, true)
.rule(SC::extension_element_prefixes, false)
.rule(SC::exclude_result_prefixes, false)
.rule(SC::id, false);
std::map<string_type, string_type> attributes = rules.gather(qName, atts);
if(attributes[SC::version] != SC::Version)
throw SAX::SAXException("I'm only a poor version 1.0 XSLT Transformer.");
if(!string_adaptor::empty(attributes[SC::extension_element_prefixes]))
throw SAX::SAXException("Haven't implemented extension-element-prefixes yet");
} // startStylesheet
void startLREAsStylesheet(const string_type& namespaceURI,
const string_type& localName,
const string_type& qName,
const AttributesT& atts)
{
string_type version;
for(int a = 0; a != atts.getLength(); ++a)
if((SC::NamespaceURI == atts.getURI(a)) &&
(SC::version == atts.getLocalName(a)))
{
version = atts.getValue(a);
break;
}
if(string_adaptor::empty(version))
throw SAX::SAXException("The source file does not look like a stylesheet.");
if(version != SC::Version)
throw SAX::SAXException("I'm only a poor version 1.0 XSLT Transformer.");
Template<string_type, string_adaptor>* lreStylesheet =
new Template<string_type, string_adaptor>(context_.xpath_match(SC::root_xpath),
string_adaptor::empty_string(),
string_adaptor::empty_string(),
string_adaptor::empty_string(),
context_.precedence());
context_.push(lreStylesheet,
new LREStylesheetHandler<string_type, string_adaptor>(context_, lreStylesheet),
namespaceURI,
localName,
qName,
atts);
} // startLREAsStylesheet
void startXSLTElement(const string_type& namespaceURI,
const string_type& localName,
const string_type& qName,
const AttributesT& atts)
{
if((localName == SC::import) || (localName == SC::include))
{
include_stylesheet(namespaceURI, localName, qName, atts);
return;
} // if ...
SAX::DefaultHandler<string_type, string_adaptor>* child = allowedChildren().create(localName, context_);
if(child != 0)
context_.push(0, child, namespaceURI, localName, qName, atts);
else
oops(qName);
} // startXSLTElement
void startForeignElement(const string_type& namespaceURI,
const string_type& localName,
const string_type& qName,
const AttributesT& atts)
{
context_.push(0,
new ForeignElementHandler<string_type, string_adaptor>(context_),
namespaceURI,
localName,
qName,
atts);
} // startForeignElement
void include_stylesheet(const string_type& namespaceURI,
const string_type& localName,
const string_type& qName,
const AttributesT& atts)
{
includer_.start_include(namespaceURI, localName, qName, atts);
} // include_stylesheet
void oops(const string_type& qName) const
{
throw SAX::SAXException("xsl:stylesheet does not allow " + string_adaptor::asStdString(qName) + " here.");
} // oops
ChildElements<string_type, string_adaptor>& allowedChildren()
{
static ChildElements<string_type, string_adaptor> allowed =
ChildElements<string_type, string_adaptor>::add(SC::template_, CreateHandler<TemplateHandler<string_type, string_adaptor> > )
.add(SC::param, CreateHandler<TopLevelVariableHandler<Param<string_type, string_adaptor> > >)
.add(SC::variable, CreateHandler<TopLevelVariableHandler<Variable<string_type, string_adaptor> > > )
.add(SC::output, CreateHandler<OutputHandler<string_type, string_adaptor> >)
.add(SC::attribute_set, CreateHandler<NotImplementedYetHandler<string_type, string_adaptor> >)
.add(SC::decimal_format, CreateHandler<NotImplementedYetHandler<string_type, string_adaptor> >)
//"import"
//"include"
.add(SC::key, CreateHandler<KeyHandler<string_type, string_adaptor> >)
.add(SC::namespace_alias, CreateHandler<NamespaceAliasHandler<string_type, string_adaptor> >)
.add(SC::preserve_space, CreateHandler<NotImplementedYetHandler<string_type, string_adaptor> >)
.add(SC::strip_space, CreateHandler<NotImplementedYetHandler<string_type, string_adaptor> >);
return allowed;
} // allowedChildren
CompilationContextT& context_;
DefaultHandlerT* child_;
IncludeHandler<string_type, string_adaptor> includer_;
bool top_;
}; // class StylesheetHandler
template<class string_type, class string_adaptor = Arabica::default_string_adaptor<string_type> >
class StylesheetCompiler
{
public:
typedef string_type string_typeT;
typedef string_adaptor string_adaptorT;
typedef SAX::InputSource<string_type, string_adaptor> InputSourceT;
StylesheetCompiler()
{
} // StylesheetCompiler
~StylesheetCompiler()
{
} // ~StylesheetCompiler
std::unique_ptr<Stylesheet<string_type, string_adaptor> > compile(InputSourceT& source)
{
error_ = "";
auto stylesheet = std::make_unique<CompiledStylesheet<string_type, string_adaptor>>();
StylesheetParser<string_type, string_adaptor> parser;
CompilationContext<string_type, string_adaptor> context(parser, *stylesheet.get());
StylesheetHandler<string_type, string_adaptor> stylesheetHandler(context);
parser.setContentHandler(stylesheetHandler);
//parser.setErrorHandler(*this);
//if(entityResolver_)
// parser.setEntityResolver(*entityResolver_);
try {
parser.parse(source);
} // try
catch(std::exception& ex)
{
error_ = ex.what();
//std::cerr << "Compilation Failed : " << ex.what() << std::endl;
stylesheet.reset();
} // catch
return std::unique_ptr<Stylesheet<string_type, string_adaptor> >(stylesheet.release());
} // compile
const std::string& error() const
{
return error_;
} // error
private:
std::string error_;
}; // class StylesheetCompiler
} // namespace XSLT
} // namespace Arabica
#endif // ARABICA_XSLT_STYLESHEETCOMPILER_HPP