com.caucho.xsl.Generator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of resin Show documentation
Show all versions of resin Show documentation
Resin Java Application Server
/*
* Copyright (c) 1998-2018 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
* Free SoftwareFoundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.xsl;
import com.caucho.java.JavaWriter;
import com.caucho.java.LineMap;
import com.caucho.util.CharBuffer;
import com.caucho.util.CharScanner;
import com.caucho.util.IntArray;
import com.caucho.util.IntMap;
import com.caucho.util.L10N;
import com.caucho.util.StringCharCursor;
import com.caucho.vfs.Path;
import com.caucho.vfs.ReadStream;
import com.caucho.xml.CauchoDocument;
import com.caucho.xml.QAbstractNode;
import com.caucho.xml.QElement;
import com.caucho.xml.QName;
import com.caucho.xml.Xml;
import com.caucho.xml.XmlChar;
import com.caucho.xpath.Expr;
import com.caucho.xpath.NamespaceContext;
import com.caucho.xpath.XPath;
import com.caucho.xpath.pattern.AbstractPattern;
import com.caucho.xpath.pattern.UnionPattern;
import com.caucho.xsl.fun.FormatNumberFun;
import com.caucho.xsl.fun.KeyFun;
import com.caucho.xsl.java.XslAttributeSet;
import com.caucho.xsl.java.XslNode;
import com.caucho.xsl.java.XslStylesheet;
import com.caucho.xsl.java.XslTemplate;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.Text;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Base class for generating code from an XSL tree. JavaGenerator and
* JavaScriptGenerator extend this class for language-specific code
* generation.
*/
abstract class Generator {
private static final Logger log
= Logger.getLogger(Generator.class.getName());
protected static final L10N L = new L10N(Generator.class);
public static final String XSLNS = "http://www.w3.org/1999/XSL/Transform";
public static final String XTPNS = "http://www.caucho.com/XTP/1.0";
private static final int STYLESHEET = 0;
private static final int OUTPUT = STYLESHEET + 1;
private static final int IMPORT = OUTPUT + 1;
private static final int INCLUDE = IMPORT + 1;
private static final int TEMPLATE = INCLUDE + 1;
private static final int STRIP_SPACE = TEMPLATE + 1;
private static final int PRESERVE_SPACE = STRIP_SPACE + 1;
private static final int KEY = PRESERVE_SPACE + 1;
private static final int LOCALE = KEY + 1;
private static final int ATTRIBUTE_SET = LOCALE + 1;
private static final int NAMESPACE_ALIAS = ATTRIBUTE_SET + 1;
private static final int APPLY_TEMPLATES = NAMESPACE_ALIAS + 1;
private static final int APPLY_IMPORTS = APPLY_TEMPLATES + 1;
private static final int CALL_TEMPLATE = APPLY_IMPORTS + 1;
private static final int PARAM = CALL_TEMPLATE + 1;
private static final int VARIABLE = PARAM + 1;
private static final int VALUE_OF = VARIABLE + 1;
private static final int COPY_OF = VALUE_OF + 1;
private static final int FOR_EACH = COPY_OF + 1;
private static final int IF = FOR_EACH + 1;
private static final int CHOOSE = IF + 1;
private static final int TEXT = CHOOSE + 1;
private static final int XSL_TEXT = TEXT + 1;
private static final int NUMBER = XSL_TEXT + 1;
private static final int COPY = NUMBER + 1;
private static final int COPY_ELEMENT = COPY + 1;
private static final int ELEMENT = COPY_ELEMENT + 1;
private static final int ATTRIBUTE = ELEMENT + 1;
private static final int PI = ATTRIBUTE + 1;
private static final int COMMENT = PI + 1;
private static final int MESSAGE = COMMENT + 1;
private static final int EXPRESSION = MESSAGE + 1;
private static final int SCRIPTLET = EXPRESSION + 1;
private static final int DECLARATION = SCRIPTLET + 1;
private static final int DIRECTIVE_CACHE = DECLARATION + 1;
private static final int DIRECTIVE_PAGE = DIRECTIVE_CACHE + 1;
private static final int WHILE = DIRECTIVE_PAGE + 1;
private static final int ASSIGN = WHILE + 1;
private static final int IGNORE = ASSIGN + 1;
// xslt 2.0
private static final int RESULT_DOCUMENT = IGNORE + 1;
private static IntMap _tags;
private static IntMap _xtpTags;
private String _version = "1.0";
String _xslName;
// the root context
Path _topContext;
// the pwd for the file
Path _baseURL;
Path _context;
CharBuffer _text;
HashMap _names = new HashMap();
int _loopDepth;
Path _workPath;
int _uniqueId;
protected HashMap _preserve = new HashMap();
protected HashMap _strip = new HashMap();
HashMap _attributeSets =
new HashMap();
protected HashMap _namespaceAliases =
new HashMap();
protected HashMap _excludedNamespaces =
new HashMap();
protected KeyFun _keyFun;
protected FormatNumberFun _formatNumberFun;
protected NamespaceContext _namespace;
protected ArrayList _globalActions = new ArrayList();
protected ArrayList _globalParameters =
new ArrayList();
protected Document _doc;
protected CauchoDocument _qDoc;
protected Path _path;
boolean _lineContent;
int _lineWs;
String _systemId;
String _filename;
int _line;
protected LineMap _lineMap;
private ArrayList _frags;
protected int _destLine = 1;
boolean _defaultCacheable = true;
boolean _isCacheable;
protected String _encoding;
HashMap> _templates = new HashMap>();
int _minImportance; // for included files, the minimum importance
int _importance;
int _templateCount;
private IntArray _vars = new IntArray();
private ArrayList _inits = new ArrayList();
protected ArrayList _depends = new ArrayList();
protected ArrayList _cacheDepends = new ArrayList();
private boolean _isCauchoXsl;
protected boolean _isRawText;
protected String _errorPage;
boolean _hasSession;
protected AbstractPattern _nodeListContext;
private boolean _isTop;
private ClassLoader _loader;
protected boolean _isSpecial;
protected boolean _isStyleScript;
HashMap _outputAttributes = new HashMap();
HashMap _macros;
HashMap _files;
protected AbstractStylesheetFactory _xslGenerator;
protected ArrayList _imports = new ArrayList();
Generator(AbstractStylesheetFactory xslGenerator)
{
_xslGenerator = xslGenerator;
_workPath = xslGenerator.getWorkPath();
Path path = xslGenerator.getStylePath();
_context = path;
_topContext = _context;
_loader = xslGenerator.getClassLoader();
if (_loader == null)
_loader = Thread.currentThread().getContextClassLoader();
_text = new CharBuffer();
_frags = new ArrayList();
_macros = new HashMap();
_keyFun = new KeyFun();
_formatNumberFun = new FormatNumberFun();
}
void init(String filename)
{
_lineMap = new LineMap(filename);
}
public void setErrorPage(String errorPage)
{
_errorPage = errorPage;
}
public void setStyleScript(boolean stylescript)
{
_isStyleScript = stylescript;
}
/**
* Adds a Java import to the generated stylesheet.
*/
public void addImport(String pkg)
{
if (! _imports.contains(pkg))
_imports.add(pkg);
}
public void setContentType(String type)
{
// contentType = type;
}
void setPath(Path path)
{
_path = path;
_context = path;
}
void setWorkPath(Path path)
{
_workPath = path;
}
public int getMinImportance()
{
return _minImportance;
}
public int getMaxImportance()
{
return _importance;
}
public NamespaceContext getNamespace()
{
return _namespace;
}
public AbstractPattern getNodeListContext()
{
return _nodeListContext;
}
public void addLocale(String name, DecimalFormatSymbols format)
{
_formatNumberFun.addLocale(name, format);
}
/**
* Generates a uniqueId
*/
public int uniqueId()
{
return _uniqueId++;
}
/**
* Starts the generation from the top of the document.
*
* @param xsl the stylesheet document.
*/
public StylesheetImpl generate(Node node) throws Exception
{
Document xsl = node.getOwnerDocument();
if (xsl == null)
xsl = (Document) node;
DocumentType dtd = xsl.getDoctype();
if (dtd != null && dtd.getSystemId() != null) {
_context = _path.lookup(dtd.getSystemId());
_topContext = _context;
}
Element top = (Element) xsl.getDocumentElement();
if (top == null)
throw error(xsl, L.l("xsl:stylesheet must be top element."));
_doc = xsl;
if (_doc instanceof CauchoDocument)
_qDoc = (CauchoDocument) _doc;
QElement qTop = null;
if (top instanceof QElement)
qTop = (QElement) top;
/*
if (qTop != null && qTop.getFilename() != null)
context = topContext.lookup(qTop.getFilename()).getParent();
*/
_isTop = true;
_files = new HashMap();
scanFiles(top);
if (_qDoc != null) {
ArrayList depends = (ArrayList) _qDoc.getProperty(CauchoDocument.DEPENDS);
for (int i = 0; depends != null && i < depends.size(); i++) {
Path path = (Path) depends.get(i);
addDepend(path);
}
}
else {
addDepend(_path);
}
if ("stylesheet".equals(getXslLocal(top)) ||
"transform".equals(getXslLocal(top))) {
generateStylesheet(top, true);
}
else {
// literal result element
printHeader();
boolean oldCacheable = _isCacheable;
boolean oldDefaultCacheable = _defaultCacheable;
_isCacheable = true;
XslNode literal = createChild(top);
XslTemplate template = new XslTemplate();
template.setGenerator((JavaGenerator) this);
template.addAttribute(new QName("match"), "/");
template.addChild(literal);
template.generateDeclaration(getOut());
template.generate(getOut());
// printTemplate(top, null, "/", null, 0.0/0.0);
_isCacheable = oldCacheable;
_defaultCacheable = oldDefaultCacheable;
}
addNamespace(top);
StylesheetImpl stylesheet = completeGenerate(_inits, _globalActions);
return stylesheet;
}
private static CharScanner commaDelimScanner = new CharScanner(" \t\n\r,");
/**
* Scan the stylesheet for imported packages and files. The imported
* stylesheets will be read and stored in the 'files' HashMap for when
* they're actually needed.
*/
private void scanFiles(Element top)
throws XslParseException, IOException
{
_isCauchoXsl = ! top.getAttribute("xsl-caucho").equals("");
Iterator iter;
try {
iter = XPath.select("//xtp:directive.page/@*", top);
} catch (Exception e) {
throw new XslParseException(e);
}
while (iter.hasNext()) {
Attr attr = (Attr) iter.next();
String name = attr.getNodeName();
String value = attr.getNodeValue();
if (name.equals("import")) {
StringCharCursor cursor = new StringCharCursor(value);
CharBuffer cb = new CharBuffer();
while (cursor.current() != cursor.DONE) {
char ch;
commaDelimScanner.skip(cursor);
cb.clear();
ch = commaDelimScanner.scan(cursor, cb);
if (cb.length() != 0) {
addImport(cb.toString());
}
else if (ch != cursor.DONE)
throw new IOException(L.l("illegal `import' directive"));
}
}
}
try {
iter = XPath.select("//xsl:import|xsl:include", top);
} catch (Exception e) {
throw new XslParseException(e);
}
while (iter.hasNext()) {
Element elt = (Element) iter.next();
String href = elt.getAttribute("href");
ReadStream rs;
try {
rs = _xslGenerator.openPath(href, _context.getURL());
} catch (Exception e) {
throw new XslParseException(e);
}
Path path = rs.getPath();
Document xsl = readXsl(rs);
Element subtop = xsl.getDocumentElement();
if (subtop == null)
throw error(elt, L.l("xsl:import file {0} is empty", path.getFullPath()));
Path oldContext = _context;
Path virtualPath = _context.getParent().lookup(href);
_context = virtualPath;
_files.put(virtualPath.getPath(), xsl);
scanFiles(subtop);
_context = oldContext;
}
}
public void addImportList(String value)
throws XslParseException
{
StringCharCursor cursor = new StringCharCursor(value);
CharBuffer cb = new CharBuffer();
while (cursor.current() != cursor.DONE) {
char ch;
commaDelimScanner.skip(cursor);
cb.clear();
ch = commaDelimScanner.scan(cursor, cb);
if (cb.length() != 0) {
addImport(cb.toString());
}
else if (ch != cursor.DONE)
throw error(L.l("illegal `import' directive"));
}
}
/**
* Read in an imported or included XSL file.
*
* @param path Path to the include files.
*
* @return XML tree describing the XSL.
*/
Document readXsl(Path path)
throws IOException, XslParseException
{
return readXsl(path.openRead());
}
/**
* Read in an imported or included XSL file.
*
* @param path Path to the include files.
*
* @return XML tree describing the XSL.
*/
Document readXsl(ReadStream file)
throws IOException, XslParseException
{
try {
addDepend(file.getPath());
if (_isStyleScript) {
XslParser parser = new XslParser();
return parser.parse(file);
}
else
return new Xml().parseDocument(file);
} catch (org.xml.sax.SAXException e) {
throw new XslParseException(e);
} finally {
file.close();
}
}
/**
* Start the top-level stylesheet generation.
*/
private void generateStylesheet(Element elt, boolean isTop)
throws Exception
{
QElement element = (QElement) elt;
_isCauchoXsl = ! element.getAttribute("xsl-caucho").equals("");
String systemId = element.getBaseURI();
Path oldContext = _context;
if (systemId != null)
_context = _context.lookup(systemId);
XslNode stylesheet = createChild(element);
addNamespace(element);
if (isTop)
printHeader();
stylesheet.generateDeclaration(getOut());
stylesheet.generate(getOut());
_context = oldContext;
/*
_version = element.getAttribute("version");
if (_version.equals(""))
_version = "1.0";
// generateAttributeSets(element);
excludeNamespaces(element);
String xslSpace = element.getAttribute("xsl-space");
ArrayList children = new ArrayList();
for (Node child = element.getFirstChild();
child != null;
child = child.getNextSibling()) {
if (! (child instanceof Element))
continue;
int code = -1;
String name = getXslLocal(child);
if (name != null)
code = _tags.get(name);
else if ((name = getXtpLocal(child)) != null)
code = _xtpTags.get(name);
else {
String childName = child.getNodeName();
if (childName.startsWith("jsp:directive.") ||
childName.equals("jsp:declaration") ||
childName.equals("jsp:scriptlet"))
addGlobalAction((Element) child);
continue;
}
NamespaceContext oldNamespace = addNamespace((Element) child);
// generateTopLevelNode(code, (Element) child);
XslNode node = createChild(child);
children.add(node);
_namespace = oldNamespace;
}
for (int i = 0; i < children.size(); i++) {
XslNode node = children.get(i);
node.generate(getOut());
}
*/
}
abstract protected JavaWriter getOut();
abstract protected XslNode createChild(Node child)
throws Exception;
abstract protected XslNode createChild(XslNode parent, Node child)
throws Exception;
private void addGlobalAction(Element elt)
{
_globalActions.add(elt);
}
/**
* Converts the exclude-result-prefixes into the "excludedNamespaces"
* hashMap for later use.
*/
private void excludeNamespaces(Element element)
throws Exception
{
if (! (element instanceof QElement))
return;
QElement elt = (QElement) element;
String excludeNamespace;
excludeNamespace = element.getAttribute("exclude-result-prefixes");
if (! excludeNamespace.equals("")) {
for (String prefix : excludeNamespace.split("[,\\s]+")) {
String ns = elt.getNamespace(prefix);
if (ns == null)
throw error(elt, L.l("`{0}' must be a namespace prefix",
prefix));
_excludedNamespaces.put(ns, "");
}
}
}
public void addExcludedNamespace(String ns)
{
_excludedNamespaces.put(ns, "");
}
public void addInit(XslNode node)
{
_inits.add(node);
}
public void addGlobalParameter(String param)
{
_globalParameters.add(param);
}
/**
* Adds a file dependency for cacheable references to the output of
* the stylesheet.
*/
private void addCacheDepends(String attr)
{
if (attr.equals(""))
return;
int i = 0;
int ch = 0;
int len = attr.length();
for (;
i < len && XmlChar.isWhitespace((ch = attr.charAt(i))) || ch == ',';
i++) {
}
CharBuffer cb = new CharBuffer();
while (i < len) {
cb.clear();
for (;
i < len && ! XmlChar.isWhitespace((ch = attr.charAt(i))) &&
ch != ',';
i++) {
cb.append((char) ch);
}
_cacheDepends.add(cb.toString());
for (;
i < len && XmlChar.isWhitespace((ch = attr.charAt(i))) || ch == ',';
i++) {
}
}
}
private void generateCacheDepends(String attr)
throws Exception
{
if (attr.equals(""))
return;
int i = 0;
int ch = 0;
int len = attr.length();
for (;
i < len && XmlChar.isWhitespace((ch = attr.charAt(i))) || ch == ',';
i++) {
}
CharBuffer cb = new CharBuffer();
while (i < len) {
cb.clear();
for (;
i < len && ! XmlChar.isWhitespace((ch = attr.charAt(i))) &&
ch != ',';
i++) {
cb.append((char) ch);
}
printCacheDepends(cb.toString());
for (;
i < len && XmlChar.isWhitespace((ch = attr.charAt(i))) || ch == ',';
i++) {
}
}
}
/**
* Generate code for xsl:template
*/
void generateTemplate(Element element)
throws Exception
{
String name = element.getAttribute("name");
String patternString = element.getAttribute("match");
String mode = element.getAttribute("mode");
String priority = element.getAttribute("priority");
double dPriority = 0.0/0.0;
if (! name.equals(""))
_macros.put(name, name);
if (name.equals("") && patternString.equals(""))
throw error("xsl:template expects a `name' or a `match' attribute.");
if (! priority.equals("")) {
try {
dPriority = Double.valueOf(priority).doubleValue();
} catch (Exception e) {
throw error("xsl:template expects `priority' must be a double.");
}
}
boolean oldCacheable = _isCacheable;
boolean oldDefaultCacheable = _defaultCacheable;
AbstractPattern oldNodeListContext = _nodeListContext;
if (! patternString.equals(""))
_nodeListContext = parseMatch(patternString);
_isCacheable = true;
printTemplate(element, name, patternString, mode, dPriority);
_nodeListContext = oldNodeListContext;
_isCacheable = oldCacheable;
_defaultCacheable = oldDefaultCacheable;
}
public XslNode generateImport(String href)
throws Exception
{
Path path = lookupPath(href);
if (_files.get(path.getPath()) != null)
return null;
Document xsl = readFile(href, path);
if (xsl == null)
throw new FileNotFoundException(href);
QElement top = (QElement) xsl.getDocumentElement();
if (top == null ||
! "stylesheet".equals(getXslLocal(top)) &&
! "transform".equals(getXslLocal(top))) {
throw error("imported stylesheet `" + href +
"' missing xsl:stylesheet.");
}
int oldMinImportance = _minImportance;
Path oldContext = _context;
Path virtualPath = _context.getParent().lookup(href);
_context = virtualPath;
_minImportance = _importance;
boolean oldTop = _isTop;
boolean oldRaw = _isRawText;
_isTop = false;
_isRawText = false;
String systemId = top.getBaseURI();
if (systemId != null)
_context = _context.lookup(systemId);
XslStylesheet stylesheet = (XslStylesheet) createChild(top);
_isRawText = oldRaw;
_isTop = oldTop;
_minImportance = oldMinImportance;
_context = oldContext;
incrementImportance();
return stylesheet;
}
/**
* Generates code for xsl:include. The included file has the same
* importance as the containing file.
*/
void generateInclude(Element element)
throws Exception
{
String href = element.getAttribute("href");
if (href.equals(""))
throw error("xsl:include expects `href' attribute.");
if (element.getFirstChild() != null)
throw error("xsl:include must be empty");
}
public void generateInclude(XslNode parent, String href)
throws Exception
{
Path path = lookupPath(href);
if (_files.get(path.getPath()) != null)
return;
Document xsl = readFile(href, path);
Element top = (Element) xsl.getDocumentElement();
if (top == null ||
! "stylesheet".equals(getXslLocal(top)) &&
! "transform".equals(getXslLocal(top))) {
throw error("imported stylesheet `" + href +
"' missing xsl:stylesheet.");
}
Path oldContext = _context;
_context = path;
for (Node node = top.getFirstChild();
node != null;
node = node.getNextSibling()) {
XslNode child = createChild(parent, node);
if (child != null)
parent.addChild(child);
}
// generateStylesheet(top, false);
_context = oldContext;
}
/**
* Returns the actual path for the relative href.
*/
private Path lookupPath(String href)
{
return _context.getParent().lookup(href);
}
private Document readFile(String href, Path virtualPath)
throws Exception
{
Document xsl = (Document) _files.get(virtualPath.getPath());
if (xsl != null)
throw new IllegalStateException(L.l("'{0}' is a duplicated path",
virtualPath.getPath()));
ReadStream rs;
try {
rs = _xslGenerator.openPath(href, _context.getURL());
} catch (Exception e) {
throw new XslParseException(e);
}
Path path = rs.getPath();
xsl = readXsl(rs);
Element subtop = xsl.getDocumentElement();
if (subtop == null)
throw error(L.l("xsl:import file {0} is empty", path.getFullPath()));
Path oldContext = _context;
_context = virtualPath;
_files.put(virtualPath.getPath(), xsl);
_context = oldContext;
return xsl;
}
void generateKey(Element element)
throws Exception
{
String name = element.getAttribute("name");
if (name.equals(""))
throw error("xsl:key expects `name' attribute.");
String match = element.getAttribute("match");
if (match.equals(""))
throw error("xsl:key expects `match' attribute.");
String use = element.getAttribute("use");
if (use.equals(""))
throw error("xsl:key expects `use' attribute.");
if (element.getFirstChild() != null)
throw error("xsl:key must be empty");
_keyFun.add(name, parseMatch(match), parseExpr(use));
}
public void addKey(String name, AbstractPattern match, Expr use)
{
_keyFun.add(name, match, use);
}
void generateLocale(Element element)
throws Exception
{
String name = element.getAttribute("name");
if (name.equals(""))
name = "*";
DecimalFormatSymbols format = new DecimalFormatSymbols();
String value = element.getAttribute("decimal-separator");
if (value.length() > 0)
format.setDecimalSeparator(value.charAt(0));
value = element.getAttribute("grouping-separator");
if (value.length() > 0)
format.setGroupingSeparator(value.charAt(0));
value = element.getAttribute("infinity");
if (! value.equals(""))
format.setInfinity(value);
value = element.getAttribute("minus-sign");
if (value.length() > 0)
format.setMinusSign(value.charAt(0));
value = element.getAttribute("NaN");
if (! value.equals(""))
format.setNaN(value);
value = element.getAttribute("percent");
if (value.length() > 0)
format.setPercent(value.charAt(0));
value = element.getAttribute("per-mille");
if (value.length() > 0)
format.setPerMill(value.charAt(0));
value = element.getAttribute("zero-digit");
if (value.length() > 0)
format.setZeroDigit(value.charAt(0));
value = element.getAttribute("digit");
if (value.length() > 0)
format.setDigit(value.charAt(0));
value = element.getAttribute("pattern-separator");
if (value.length() > 0)
format.setPatternSeparator(value.charAt(0));
_formatNumberFun.addLocale(name, format);
}
void generateNamespaceAlias(Element element)
throws Exception
{
if (! (element instanceof QElement))
return;
QElement elt = (QElement) element;
String stylesheetPrefix;
String resultPrefix;
stylesheetPrefix = (String) element.getAttribute("stylesheet-prefix");
resultPrefix = (String) element.getAttribute("result-prefix");
if (stylesheetPrefix.equals(""))
throw error(element, "xsl:namespace-alias needs `stylesheet-prefix'");
if (resultPrefix.equals(""))
throw error(element, "xsl:namespace-alias needs `result-prefix'");
}
public void addNamespaceAlias(String stylesheetPrefix,
String resultPrefix)
{
/*
String stylesheetNs = getNamespace(stylesheetPrefix);
if (stylesheetPrefix.equals("#default"))
stylesheetNs = "";
else if (stylesheetNs.equals(""))
throw error("`" + stylesheetPrefix + "' is not a valid namespace prefix");
String resultNs = getNamespace(resultPrefix);
if (resultPrefix.equals("#default")) {
resultPrefix = "";
resultNs = "";
}
else if (resultNs.equals(""))
throw error("`" + resultPrefix + "' is not a valid namespace prefix");
String result[] = new String[] { resultPrefix, resultNs };
_namespaceAliases.put(stylesheetNs, result);
*/
}
public void addNamespaceAlias(String namespace, String []result)
{
_namespaceAliases.put(namespace, result);
}
public String []getNamespaceAlias(String namespace)
{
return _namespaceAliases.get(namespace);
}
/**
* Scans through the stylesheet, grabbing the attribute set
* definitions.
*
* @param element the current nost
*/
/*
void generateAttributeSets(Element element)
throws Exception
{
Node child = element.getFirstChild();
for (; child != null; child = child.getNextSibling()) {
if (! "attribute-set".equals(getXslLocal(child)))
continue;
QElement elt = (QElement) child;
String name = elt.getAttribute("name");
if (name.equals(""))
throw error(L.l("xsl:attribute-set expects `name' attribute."));
generateAttributeSet(element, name);
}
}
*/
/**
* Scans through the stylesheet, grabbing the attribute set
* definitions.
*
* @param element the current node
*/
/*
HashMap generateAttributeSet(Element element, String setName)
throws Exception
{
Node child = element.getFirstChild();
for (; child != null; child = child.getNextSibling()) {
if (! "attribute-set".equals(getXslLocal(child)))
continue;
QElement elt = (QElement) child;
String name = elt.getAttribute("name");
if (name.equals(""))
throw error(L.l("xsl:attribute-set expects `name' attribute."));
if (! name.equals(setName))
continue;
HashMap set = _attributeSets.get(name);
if (set != null)
return set;
set = new HashMap();
_attributeSets.put(name, set);
// add any attributes from use-attribute-sets
for (Node attr = elt.getFirstAttribute();
attr != null;
attr = attr.getNextSibling()) {
if (attr.getNodeName().equals("use-attribute-sets")) {
HashMap subset = generateAttributeSet(element, attr.getNodeValue());
if (subset == null)
throw error(elt, L.l("Unknown attribute-set `{0}'. Each use-attribute-sets needs a matching xsl:attribute-set.", attr.getNodeValue()));
Iterator iter = subset.keySet().iterator();
while (iter.hasNext()) {
String key = iter.next();
set.put(key, subset.get(key));
}
}
}
for (Node attr = elt.getFirstChild();
attr != null;
attr = attr.getNextSibling()) {
if (! "attribute".equals(getXslLocal(attr)))
continue;
Element attrElt = (Element) attr;
String attrName = attrElt.getAttribute("name");
if (attrName.equals(""))
throw error(L.l("xsl:attribute expects `name' attribute."));
set.put(attrName, ((QElement) attr).getTextValue());
}
for (Attr attr = ((QElement) elt).getFirstAttribute();
attr != null;
attr = (Attr) attr.getNextSibling()) {
if (attr.getNodeName().equals("name") ||
attr.getNodeName().equals("use-attribute-sets"))
continue;
set.put(attr.getNodeName(), attr.getNodeValue());
}
return set;
}
return null;
}
*/
public void addAttributeSet(String name, XslAttributeSet attributeSet)
{
_attributeSets.put(name, attributeSet);
}
/*
public XslAttributeSet getAttributeSet(String name)
{
return _attributeSets.get(name);
}
*/
public void setDisableOutputEscaping(boolean disable)
{
_isRawText = disable;
}
public boolean getDisableOutputEscaping()
{
return _isRawText;
}
private void generateOutput(Element element)
throws Exception
{
Node attr = ((QElement) element).getFirstAttribute();
if (element.getFirstChild() != null)
throw error("xsl:output must be empty");
String disableEscaping;
disableEscaping = element.getAttribute("resin:disable-output-escaping");
if (disableEscaping.equals(""))
disableEscaping = element.getAttribute("disable-output-escaping");
if (disableEscaping.equals("no") ||
disableEscaping.equals("false"))
_isRawText = false;
else if (! disableEscaping.equals(""))
_isRawText = true;
// Only top-level xsl:output matters (XXX: spec?)
if (! _isTop)
return;
if (_outputAttributes == null)
_outputAttributes = new HashMap();
for (; attr != null; attr = attr.getNextSibling()) {
_outputAttributes.put(attr.getNodeName(), attr.getNodeValue());
}
}
public void setOutputAttribute(String name, String value)
{
_outputAttributes.put(name, value);
}
private void generatePreserveSpace(Element element)
throws Exception
{
String elements = element.getAttribute("elements");
if (elements.equals(""))
throw error("xsl:preserve-space expects `elements' attribute.");
if (element.getFirstChild() != null)
throw error("xsl:preserve-space must be empty");
int i = 0;
int len = elements.length();
for (; i < len && XmlChar.isWhitespace(elements.charAt(i)); i++) {
}
CharBuffer cb = new CharBuffer();
while (i < len) {
cb.clear();
for (; i < len && ! XmlChar.isWhitespace(elements.charAt(i)); i++)
cb.append(elements.charAt(i));
_preserve.put(cb.toString(), "true");
for (; i < len && XmlChar.isWhitespace(elements.charAt(i)); i++) {
}
}
}
private void generateStripSpace(Element element)
throws Exception
{
throw new UnsupportedOperationException();
/*
String elements = element.getAttribute("elements");
if (elements.equals(""))
throw error("xsl:strip-space expects `elements' attribute.");
if (element.getFirstChild() != null)
throw error("xsl:strip-space must be empty");
*/
}
public void addStripSpace(String elements)
{
int i = 0;
int len = elements.length();
for (; i < len && XmlChar.isWhitespace(elements.charAt(i)); i++) {
}
CharBuffer cb = new CharBuffer();
while (i < len) {
cb.clear();
for (; i < len && ! XmlChar.isWhitespace(elements.charAt(i)); i++) {
cb.append(elements.charAt(i));
}
_strip.put(cb.toString(), "true");
for (; i < len && XmlChar.isWhitespace(elements.charAt(i)); i++) {
}
}
}
public void addPreserveSpace(String elements)
{
int i = 0;
int len = elements.length();
for (; i < len && XmlChar.isWhitespace(elements.charAt(i)); i++) {
}
CharBuffer cb = new CharBuffer();
while (i < len) {
cb.clear();
for (; i < len && ! XmlChar.isWhitespace(elements.charAt(i)); i++) {
cb.append(elements.charAt(i));
}
_preserve.put(cb.toString(), "true");
for (; i < len && XmlChar.isWhitespace(elements.charAt(i)); i++) {
}
}
}
protected void generateChildren(Node node)
throws Exception
{
_vars.add(0);
for (Node child = node.getFirstChild();
child != null;
child = child.getNextSibling()) {
generateChild(child);
}
int count = _vars.pop();
if (count > 0 && _vars.size() > 0)
printPopScope(count);
}
protected void generateChild(Node child)
throws Exception
{
generateChildImpl(child);
}
public void generateChildImpl(Node child)
throws Exception
{
String nodeName = getXslLocal(child);
int code = -1;
if (nodeName != null)
code = _tags.get(nodeName);
else if ((nodeName = getXtpLocal(child)) != null)
code = _xtpTags.get(nodeName);
/* XXX: xsl/04al
else if (macros.get(child.getNodeName()) != null) {
generateMacro((Element) child);
return;
}
*/
if (nodeName == null) {
if (child.getNodeType() == child.TEXT_NODE)
generateText(child);
else if (child.getNodeType() == child.ELEMENT_NODE) {
NamespaceContext oldNamespace = addNamespace((Element) child);
printElement((Element) child);
_namespace = oldNamespace;
}
return;
}
if (child instanceof QElement) {
NamespaceContext oldNamespace = addNamespace((QElement) child);
generateChild(child, code);
_namespace = oldNamespace;
}
else
generateChild(child, code);
}
public void generateChild(Node child, int code)
throws Exception
{
if (child instanceof QAbstractNode) {
QAbstractNode qChild = (QAbstractNode) child;
setLocation(qChild.getBaseURI(), qChild.getFilename(), qChild.getLine());
}
switch (code) {
case TEXT:
generateText(child);
break;
case XSL_TEXT:
generateXslText((Element) child);
break;
case APPLY_TEMPLATES:
generateApplyTemplates((Element) child);
break;
case APPLY_IMPORTS:
generateApplyImports((Element) child);
break;
case CALL_TEMPLATE:
generateCallTemplate((Element) child);
break;
case PARAM:
generateParamVariable((Element) child);
break;
case VARIABLE:
generateVariable((Element) child);
break;
case VALUE_OF:
generateValueOf((Element) child);
break;
case COPY_OF:
generateCopyOf((Element) child);
break;
case FOR_EACH:
generateForEach((Element) child);
break;
case IF:
generateIf((Element) child);
break;
case CHOOSE:
generateChoose((Element) child);
break;
case NUMBER:
generateNumber((Element) child);
break;
case COPY:
printCopy((Element) child);
break;
case COPY_ELEMENT:
printCopyElement((Element) child);
break;
case ELEMENT:
generateElement((Element) child);
break;
case ATTRIBUTE:
generateAttribute((Element) child);
break;
case PI:
printPi((Element) child);
break;
case COMMENT:
printComment((Element) child);
break;
case MESSAGE:
printMessage((Element) child);
break;
case EXPRESSION:
if (! _defaultCacheable)
_isCacheable = false;
printExpression((Element) child);
break;
case SCRIPTLET:
if (! _defaultCacheable)
_isCacheable = false;
printScriptlet((Element) child);
break;
case DIRECTIVE_CACHE:
generateCacheDepends(((Element) child).getAttribute("file"));
if (! ((Element) child).getAttribute("no-cache").equals("")) {
_isCacheable = false;
_defaultCacheable = false;
}
else
_defaultCacheable = true;
break;
case WHILE:
generateWhile((Element) child);
break;
case ASSIGN:
generateAssign((Element) child);
break;
case RESULT_DOCUMENT:
generateResultDocument((Element) child);
break;
case IGNORE:
break;
default:
if (child instanceof QElement &&
XSLNS.equals(((QElement) child).getNamespaceURI()) &&
_version != null && _version.equals("1.0"))
throw error(child, "unknown XSL element `" +
child.getNodeName() + "'");
else {
Node subchild = child.getFirstChild();
boolean hasFallback = false;
for (; subchild != null; subchild = subchild.getNextSibling()) {
String local = getXslLocal(subchild);
if (local != null && local.equals("fallback")) {
hasFallback = true;
generateChildren(subchild);
}
}
if (! hasFallback) // && child.getNamespace().equals(XSLNS))
printError(L.l("expected xsl tag at `{0}'",
child.getNodeName()));
}
break;
}
}
private void generateText(Node node)
throws Exception
{
String data = node.getNodeValue();
int length = data.length();
if (length == 0)
return;
int i = 0;
for (; i < length && XmlChar.isWhitespace(data.charAt(i)); i++) {
}
if (i == length && stripNode(node))
return;
if (data != null && data.length() > 0 && node instanceof QAbstractNode) {
setLocation(node);
writeText(data);
}
}
private boolean stripNode(Node node)
{
for (node = node.getParentNode();
node != null;
node = node.getParentNode()) {
if (node instanceof Element) {
Element elt = (Element) node;
String space = elt.getAttribute("xml:space");
if (! space.equals(""))
return ! space.equals("preserve");
}
}
return true;
}
void generateXslText(Element element)
throws Exception
{
_text.clear();
for (Node child = element.getFirstChild();
child != null;
child = child.getNextSibling()) {
if (! (child instanceof Text))
continue;
String data = child.getNodeValue();
int length = data.length();
_text.append(data);
}
String disableEscaping = element.getAttribute("disable-output-escaping");
if (disableEscaping.equals(""))
disableEscaping = "no";
if (_text.length() <= 0) {
}
else if (! disableEscaping.equals("yes") &&
! disableEscaping.equals("true"))
writeText(_text.toString());
else {
startDisableEscaping();
writeText(_text.toString());
endDisableEscaping();
}
}
private void generateApplyTemplates(Node node)
throws Exception
{
QElement element = (QElement) node;
String select = element.getAttribute("select");
String mode = element.getAttribute("mode");
AbstractPattern selectPattern = null;
if (! select.equals(""))
selectPattern = parseSelect(select, node);
Sort []sort = generateSort(node);
if (sort != null && selectPattern == null)
selectPattern = parseSelect("*", node);
pushCall();
generateArgs(element);
printApplyTemplates(selectPattern, mode, sort);
popCall();
}
private void generateApplyImports(Node node)
throws Exception
{
QElement element = (QElement) node;
String mode = element.getAttribute("mode");
if (element.getFirstChild() != null)
throw error(L.l("xsl:apply-imports must be empty"));
pushCall();
generateArgs(element);
printApplyImports(mode, _minImportance, _importance);
popCall();
}
private void generateCallTemplate(Element element)
throws Exception
{
String name = element.getAttribute("name");
String mode = element.getAttribute("mode");
if (name.equals(""))
throw error(L.l("{0} expects `{1}' attribute",
"xsl:call-template", "name"));
if (findMacro(name) == null)
throw error(element, L.l("`{0}' is an unknown macro for xsl:call-template. All macros must be defined in an element.", name));
pushCall();
generateArgs(element);
printCallTemplate(name, mode);
popCall();
}
private Element findMacro(String name)
throws Exception
{
Element template = findMacroInDocument(_doc, name);
if (template != null)
return template;
Iterator iter = _files.values().iterator();
while (iter.hasNext()) {
Document doc = (Document) iter.next();
template = findMacroInDocument(doc, name);
if (template != null)
return template;
}
return null;
}
private Element findMacroInDocument(Document doc, String name)
{
Element elt = doc.getDocumentElement();
for (Node child = elt.getFirstChild();
child != null;
child = child.getNextSibling()) {
if (! child.getNodeName().equals("xsl:template"))
continue;
Element template = (Element) child;
if (template.getAttribute("name").equals(name))
return template;
}
return null;
}
private void generateMacro(Element element)
throws Exception
{
QElement elt = (QElement) element;
String name = element.getNodeName();
String mode = element.getAttribute("mode");
pushCall();
for (Node node = elt.getFirstAttribute();
node != null;
node = node.getNextSibling()) {
String argName = node.getNodeName();
String argValue = node.getNodeValue();
printParam(argName, argValue, elt);
}
printParam("contents", elt);
printCallTemplate(name, mode);
popCall();
}
private void generateArgs(Element element)
throws Exception
{
for (Node node = element.getFirstChild(); node != null;
node = node.getNextSibling()) {
String localName = getXslLocal(node);
if ("with-param".equals(localName)) {
String key = ((Element) node).getAttribute("name");
String expr = ((Element) node).getAttribute("select");
if (key.equals(""))
throw error(L.l("{0} requires `{1}' attribute",
"xsl:with-param", "name"));
if (! expr.equals(""))
printParam(key, parseExpr(expr));
else
printParam(key, node);
}
}
}
private void generateParamVariable(Element element)
throws Exception
{
int i = _vars.size() - 1;
_vars.set(i, _vars.get(i) + 1);
String name = element.getAttribute("name");
String expr = element.getAttribute("select");
if (name.equals(""))
throw error(L.l("{0} expects `{1}' attribute",
"xsl:param", "name"));
if (! expr.equals(""))
printParamVariable(name, parseExpr(expr));
else
printParamVariable(name, element);
}
private void generateVariable(Element element)
throws Exception
{
int i = _vars.size() - 1;
_vars.set(i, _vars.get(i) + 1);
String name = element.getAttribute("name");
String expr = element.getAttribute("select");
if (name.equals(""))
throw error(L.l("{0} expects `{1}' attribute.",
"xsl:variable", "name"));
if (! expr.equals(""))
printVariable(name, parseExpr(expr));
else
printVariable(name, element);
}
private void generateAssign(Element element)
throws Exception
{
String name = element.getAttribute("name");
String expr = element.getAttribute("select");
if (name.equals(""))
throw error(L.l("{0} expects `{1}' attribute.",
"xtp:assign", "name"));
if (! expr.equals(""))
printAssign(name, parseExpr(expr));
else
printAssign(name, element);
}
private void generateResultDocument(Element element)
throws Exception
{
String href = element.getAttribute("href");
String format = element.getAttribute("format");
if (href.equals(""))
throw error(L.l("{0} expects `{1}' attribute.",
"xtp:result-document", "href"));
printResultDocument(element, href, format);
}
private void generateValueOf(Element element)
throws Exception
{
String select = element.getAttribute("select");
if (select.equals(""))
throw error(L.l("{0} expects `{1}' attribute.",
"xsl:value-of", "select"));
if (element.getFirstChild() != null)
throw error(L.l("{0} must be empty", "xsl:value-of"));
String disable = element.getAttribute("disable-output-escaping");
boolean isDisabled = disable.equals("yes");
if (isDisabled)
startDisableEscaping();
printSelectValue(select, element);
if (isDisabled)
endDisableEscaping();
}
private void generateCopyOf(Element element)
throws Exception
{
String select = element.getAttribute("select");
if (select.equals(""))
throw error(L.l("{0} expects `{1}' attribute",
"xsl:copy-of", "select"));
if (element.getFirstChild() != null)
throw error(L.l("{0} must be empty", "xsl:copy-of"));
printCopyOf(select, element);
}
/**
* Generates code for the xsl:for-each element
*/
void generateForEach(Element element)
throws Exception
{
String select = element.getAttribute("select");
if (select.equals(""))
throw error(L.l("{0} expects `{1}' attribute",
"xsl:for-each", "select"));
Sort []sort = generateSort(element);
if (sort != null)
printForEach(element, select, sort);
else
printForEach(element, select);
}
private Sort []generateSort(Node node)
throws XslParseException, IOException
{
ArrayList sorts = new ArrayList();
sort:
for (Node child = node.getFirstChild();
child != null;
child = child.getNextSibling()) {
if (child.getNodeType() == child.TEXT_NODE) {
String data = child.getNodeValue();
for (int i = 0; i < data.length(); i++) {
if (! XmlChar.isWhitespace(data.charAt(i)))
break sort;
}
continue;
}
else if (child.getNodeType() == child.COMMENT_NODE ||
child.getNodeType() == child.PROCESSING_INSTRUCTION_NODE)
continue;
String name = getXslLocal(child);
if (! "sort".equals(name))
break;
Element elt = (Element) child;
String select = elt.getAttribute("select");
if (select.equals(""))
throw error(L.l("{0} expects attribute `{1}'",
"xsl:sort", "select"));
Expr expr = parseExpr(select);
String order = elt.getAttribute("order");
Expr isAscending = null;
if (order.equals("")) {
isAscending = parseExpr("true()");
}
else if (order.startsWith("{") && order.endsWith("}")) {
order = order.substring(1, order.length() - 1);
isAscending = parseExpr(order + " = 'ascending'");
}
else if (order.equals("ascending"))
isAscending = parseExpr("true()");
else
isAscending = parseExpr("false()");
String dataType = elt.getAttribute("data-type");
boolean isText = true;
if (dataType.equals("number"))
isText = false;
String lang = elt.getAttribute("lang");
if (lang.equals("")) {
sorts.add(Sort.create(expr, isAscending, isText));
}
else {
if (lang.startsWith("{") && lang.endsWith("}"))
lang = lang.substring(1, lang.length() - 1);
else
lang = "'" + lang + "'";
sorts.add(Sort.create(expr, isAscending, parseExpr(lang)));
/*
int p = lang.indexOf('-');
Locale locale = null;
if (p < 0) {
locale = new Locale(lang, "");
}
else {
String language = lang.substring(0, p);
int q = lang.indexOf(p + 1, '-');
if (q < 0) {
String country = lang.substring(p + 1);
locale = new Locale(language, country);
}
else {
String country = lang.substring(p + 1, q);
String variant = lang.substring(q);
locale = new Locale(language, country, variant);
}
}
sorts.add(Sort.create(expr, isAscending, lang));
*/
}
}
if (sorts.size() > 0)
return (Sort []) sorts.toArray(new Sort[sorts.size()]);
else
return null;
}
void generateIf(Element element)
throws Exception
{
String test = (String) element.getAttribute("test");
if (test.equals(""))
throw error(L.l("{0} expects `{1}' attribute", "xsl:if", "test"));
printIf(element, parseExpr(test));
}
void generateWhile(Element element)
throws Exception
{
String test = (String) element.getAttribute("test");
if (test.equals(""))
throw error(L.l("{0} expects `{1}' attribute", "xsl:while", "test"));
printWhile(element, parseExpr(test));
}
void generateChoose(Element element)
throws Exception
{
boolean first = true;
for (Node child = element.getFirstChild();
child != null;
child = child.getNextSibling()) {
if (! (child instanceof Element))
continue;
String name = getXslLocal(child);
if ("when".equals(name)) {
Element elt = (Element) child;
String test = elt.getAttribute("test");
if (test.equals(""))
throw error(L.l("{0} expects `{1}' attribute", "xsl:when", "test"));
printChoose(elt, parseExpr(test), first);
first = false;
}
else if ("otherwise".equals(name)) {
printOtherwise((Element) child, first);
}
else
throw error(L.l("xsl:choose expects `xsl:when' or `xsl:otherwise' at `{0}'",
child.getNodeName()));
}
}
void generateElement(Element element)
throws Exception
{
String name = (String) element.getAttribute("name");
if (name.equals(""))
throw error(L.l("{0} expects `{1}' attribute.", "xsl:element", "name"));
Attr nsAttr = element.getAttributeNode("namespace");
if (nsAttr == null)
printElement(element, name);
else
printElement(element, name, nsAttr.getNodeValue());
}
void generateAttribute(Element element)
throws Exception
{
String name = (String) element.getAttribute("name");
if (name.equals(""))
throw error(L.l("{0} expects `{1}' attribute",
"xsl:attribute", "name"));
Attr nsAttr = element.getAttributeNode("namespace");
boolean oldSpecial = _isSpecial;
_isSpecial = true;
if (nsAttr == null)
printAttribute(element, name);
else
printAttribute(element, name, nsAttr.getNodeValue());
_isSpecial = oldSpecial;
}
void generateNumber(Element element)
throws Exception
{
String value = element.getAttribute("value");
String count = element.getAttribute("count");
String from = element.getAttribute("from");
String level = element.getAttribute("level");
String format = element.getAttribute("format");
String letter = element.getAttribute("letter-value");
String separator = element.getAttribute("grouping-separator");
String lang = element.getAttribute("lang");
String size_name = element.getAttribute("grouping-size");
int size = 0;
for (int i = 0; i < size_name.length(); i++) {
char ch = size_name.charAt(i);
if (ch >= '0' && ch <= '9')
size = 10 * size + ch - '0';
}
boolean isAlphabetic = true;
if (! letter.equals("alphabetic"))
isAlphabetic = false;
AbstractPattern countPattern = null;
if (! count.equals(""))
countPattern = parseMatch(count);
AbstractPattern fromPattern = null;
if (! from.equals(""))
fromPattern = parseMatch(from);
if (level.equals("") || level.equals("single"))
level = "single";
else if (level.equals("multiple")) {
}
else if (level.equals("any")) {
}
else
throw error(L.l("xsl:number can't understand level=`{0}'",
level));
XslNumberFormat xslFormat;
xslFormat = new XslNumberFormat(format, lang, isAlphabetic,
separator, size);
if (! value.equals(""))
printNumber(parseExpr(value), xslFormat);
else
printNumber(level, countPattern, fromPattern, xslFormat);
}
void printNumber(Expr expr, XslNumberFormat format)
throws Exception
{
}
void printNumber(String level,
AbstractPattern countPattern,
AbstractPattern fromPattern,
XslNumberFormat format)
throws Exception
{
}
/**
* Sets location code for the node.
*/
void setLocation(Node node)
throws Exception
{
if (node instanceof QAbstractNode) {
setLocation(((QAbstractNode) node).getBaseURI(),
((QAbstractNode) node).getFilename(),
((QAbstractNode) node).getLine());
}
}
public void setLocation(String systemId, String filename, int line)
throws XslParseException, IOException
{
if (filename != null) {
_systemId = systemId;
_filename = filename;
_line = line;
// _lineMap.add(filename, line, getOut().getDestLine());
}
}
int getTextLength()
{
return _text.length();
}
protected void printHeader()
throws XslParseException, IOException
{
}
abstract protected void startDisableEscaping()
throws Exception;
abstract protected void endDisableEscaping()
throws Exception;
abstract protected void writeText(String text)
throws Exception;
abstract protected void printTemplate(Element node,
String name, String pattern,
String mode, double priority)
throws Exception;
/**
* Prints location code for the node.
*/
void printLocation(Node node)
throws Exception
{
if (node instanceof QAbstractNode) {
printLocation(((QAbstractNode) node).getBaseURI(),
((QAbstractNode) node).getFilename(),
((QAbstractNode) node).getLine());
}
}
abstract protected void printLocation(String systemId, String filename, int line)
throws Exception;
abstract protected void printElement(Node node)
throws Exception;
abstract protected void
printApplyTemplates(AbstractPattern select, String mode, Sort []sort)
throws Exception;
abstract protected void
printApplyImports(String mode, int min, int max)
throws Exception;
abstract protected void
printCallTemplate(String name, String mode)
throws Exception;
abstract protected void pushCall()
throws Exception;
abstract protected void popCall()
throws Exception;
abstract protected void printParam(String name, Object value)
throws Exception;
abstract protected void printParam(String name, String value, Element elt)
throws Exception;
abstract protected void printParamVariable(String name, Expr expr)
throws Exception;
abstract protected void printParamVariable(String name, Element elt)
throws Exception;
abstract protected void printVariable(String name, Object value)
throws Exception;
protected void printAssign(String name, Object value)
throws Exception
{
printVariable(name, value);
}
abstract protected void printPopScope(int count)
throws Exception;
abstract protected void printCopyOf(String select, Element element)
throws Exception;
abstract protected void printSelectValue(String select, Element element)
throws Exception;
abstract protected void printForEach(Element element, String select)
throws Exception;
abstract protected void printForEach(Element element, String select,
Sort []sort)
throws Exception;
protected void printIf(Element element, Expr expr)
throws Exception
{
}
protected void printChoose(Element element, Expr expr, boolean first)
throws Exception
{
}
protected void printOtherwise(Element element, boolean first)
throws Exception
{
}
protected void printCopy(Element element)
throws Exception
{
}
protected void printCopyElement(Element element)
throws Exception
{
}
protected void printElement(Element element, String name)
throws Exception
{
}
protected void printElement(Element element, String name, String namespace)
throws Exception
{
}
protected void printAttribute(Element node, String name)
throws Exception
{
}
protected void printAttribute(Element node, String name, String namespace)
throws Exception
{
}
protected void printPi(Element node)
throws Exception
{
}
protected void printComment(Element node)
throws Exception
{
}
protected void printError(String msg)
throws Exception
{
}
protected void printMessage(Element node)
throws Exception
{
}
// extension
protected void printExpression(Element node)
throws Exception
{
}
protected void printScriptlet(Element node)
throws Exception
{
}
protected void printDeclaration(Element node)
throws Exception
{
}
protected void printCacheDepends(String path)
throws Exception
{
}
protected void printWhile(Element element, Expr expr)
throws Exception
{
}
protected void printResultDocument(Element element, String href,
String format)
throws Exception
{
}
public int getImportance()
{
return _importance;
}
public void setMinImportance(int importance)
{
_minImportance = importance;
}
public void incrementImportance()
{
_importance++;
}
/**
* Adds a new template pattern.
*
* @param pattern the match pattern.
* @param mode the template mode.
* @param priority the template priority.
* @param function the associated function name.
* @param funId the function id.
*/
Template addPattern(AbstractPattern pattern, String mode, double priority,
String function, int funId)
{
if (pattern instanceof UnionPattern) {
UnionPattern union = (UnionPattern) pattern;
addPattern(union.getLeft(), mode, priority, function, funId);
return addPattern(union.getRight(), mode, priority, function, funId);
}
if (Double.isNaN(priority))
priority = pattern.getPriority();
if (log.isLoggable(Level.FINER))
log.finer("add " + pattern.getNodeName() + " " + pattern + " fun:" +
function + " mode:" + mode + " priority:" + priority);
Template template = new Template(pattern, mode,
_minImportance, _importance,
priority, _templateCount++,
function, funId);
addTemplate(pattern.getNodeName(), template);
return template;
}
private void addTemplate(String nodeName, Template template)
{
ArrayList templateList = _templates.get(nodeName);
if (templateList == null) {
templateList = new ArrayList();
_templates.put(nodeName, templateList);
}
for (int i = templateList.size() - 1; i >= 0; i--) {
Template item = templateList.get(i);
if (template.compareTo(item) <= 0) {
templateList.add(i + 1, template);
return;
}
}
templateList.add(0, template);
}
public AbstractPattern parseMatch(String pattern)
throws XslParseException, IOException
{
if (true)
throw new RuntimeException();
try {
return XPath.parseMatch(pattern, _namespace).getPattern();
} catch (Exception e) {
throw error(L.l("{0} in pattern `{1}'",
e.toString(), pattern));
}
}
public AbstractPattern parseSelect(String pattern)
throws IOException, XslParseException
{
if (true)
throw new RuntimeException();
try {
return XPath.parseSelect(pattern, _namespace).getPattern();
} catch (Exception e) {
throw error(e);
}
}
protected AbstractPattern parseSelect(String pattern, Node node)
throws IOException, XslParseException
{
if (true)
throw new UnsupportedOperationException();
try {
return XPath.parseSelect(pattern, _namespace).getPattern();
} catch (Exception e) {
throw error(node, e);
}
}
public Expr parseExpr(String pattern)
throws XslParseException
{
if (true)
throw new UnsupportedOperationException();
try {
return XPath.parseExpr(pattern, _namespace, _nodeListContext);
} catch (Exception e) {
throw error(e);
}
}
XslParseException error(Exception e)
{
if (e.getMessage() != null)
return error(e.getMessage());
else {
log.log(Level.WARNING, e.toString(), e);
return error(e.toString());
}
}
XslParseException error(Node node, Exception e)
{
if (e.getMessage() != null)
return error(node, e.getMessage());
else {
log.log(Level.WARNING, e.toString(), e);
return error(e.toString());
}
}
XslParseException error(String message)
{
return new XslParseException(_filename + ":" + _line + ": " + message);
}
/**
* Creates an error message with filename and line number based on
* the source node.
*
* @param node XML node of the source XSL.
* @param message the error message.
*/
XslParseException error(Node node, String message)
{
if (! (node instanceof QAbstractNode))
return error(message);
QAbstractNode qnode = (QAbstractNode) node;
String filename = qnode.getFilename();
int line = qnode.getLine();
if (filename != null)
return new XslParseException(filename + ":" +
line + ": " +
message);
else
return error(message);
}
/**
* Returns the local name of an XSL element. Non-XSL elements return
* null. So xsl:copy will return "copy", while "foo:bar" returns null.
*
* @param node the XSL source node
* @return the local part of the XSL name.
*/
protected String getXslLocal(Node node)
{
if (! (node instanceof Element))
return null;
QElement elt = (QElement) node;
String ns = elt.getNamespaceURI();
String prefix = elt.getPrefix();
if (ns == null || ns.equals("")) {
return (elt.getNodeName().startsWith("xsl:") ?
elt.getNodeName().substring(4) :
null);
}
else if (ns.startsWith(XSLNS) &&
(ns.length() == XSLNS.length() || ns.charAt(XSLNS.length()) == '/'))
return elt.getLocalName();
else
return null;
}
protected String getXtpLocal(Node node)
{
if (! (node instanceof Element))
return null;
QElement elt = (QElement) node;
String ns = elt.getNamespaceURI();
String prefix = elt.getPrefix();
if (ns == null || ns.equals("")) {
return (elt.getNodeName().startsWith("xtp:") ?
elt.getNodeName().substring(4) :
null);
}
else if (ns.startsWith(XTPNS))
return elt.getLocalName();
else
return null;
}
/**
* Parses an expression in a context
*/
private Expr parseExpr(Node node, String expr)
throws Exception
{
try {
return XPath.parseExpr(expr, _namespace, _nodeListContext);
} catch (Exception e) {
throw error(node, e.getMessage());
}
}
/**
* Adds the namespaces in the element to the current NamespaceContext.
* The XPath pattern parsing uses NamespaceContext to associate the right
* context with element patterns.
*
* @param elt the XSL element being processed.
*
* @return the old namespace context
*/
protected NamespaceContext addNamespace(Element elt)
{
NamespaceContext oldNamespace = _namespace;
Node attr = ((QElement) elt).getFirstAttribute();
for (; attr != null; attr = attr.getNextSibling()) {
String name = attr.getNodeName();
if (name.startsWith("xmlns:"))
name = name.substring(6);
else if (name.equals("xmlns"))
name = "";
else
continue;
// Note: according to the spec, the default namespace is not used
String url = attr.getNodeValue();
if (url.equals(XSLNS) || url.equals(XTPNS))
continue;
if (url.startsWith("quote:"))
url = url.substring(6);
_namespace = new NamespaceContext(_namespace, name, url);
}
return oldNamespace;
}
void addDepend(Path depend)
{
if (depend != null)
_depends.add(depend);
}
abstract protected StylesheetImpl completeGenerate(ArrayList inits,
ArrayList globals)
throws Exception;
/**
* Close call when an error occurs.
*/
public void close()
throws IOException, XslParseException
{
}
static {
_tags = new IntMap();
_tags.put("stylesheet", STYLESHEET);
_tags.put("transform", STYLESHEET);
_tags.put("output", OUTPUT);
_tags.put("template", TEMPLATE);
_tags.put("preserve-space", PRESERVE_SPACE);
_tags.put("strip-space", STRIP_SPACE);
_tags.put("import", IMPORT);
_tags.put("include", INCLUDE);
_tags.put("key", KEY);
_tags.put("decimal-format", LOCALE);
_tags.put("attribute-set", ATTRIBUTE_SET);
_tags.put("namespace-alias", NAMESPACE_ALIAS);
_tags.put("apply-templates", APPLY_TEMPLATES);
_tags.put("apply-imports", APPLY_IMPORTS);
_tags.put("call-template", CALL_TEMPLATE);
_tags.put("param", PARAM);
_tags.put("variable", VARIABLE);
_tags.put("for-each", FOR_EACH);
_tags.put("if", IF);
_tags.put("choose", CHOOSE);
_tags.put("value-of", VALUE_OF);
_tags.put("copy-of", COPY_OF);
_tags.put("text", XSL_TEXT);
_tags.put("#text", TEXT);
_tags.put("number", NUMBER);
_tags.put("copy", COPY);
_tags.put("element", ELEMENT);
_tags.put("attribute", ATTRIBUTE);
_tags.put("pi", PI);
_tags.put("processing-instruction", PI);
_tags.put("comment", COMMENT);
_tags.put("message", MESSAGE);
_tags.put("sort", IGNORE);
_tags.put("fallback", IGNORE);
// xslt 2.0
_tags.put("result-document", RESULT_DOCUMENT);
_xtpTags = new IntMap();
_xtpTags.put("expression", EXPRESSION);
_xtpTags.put("expr", EXPRESSION);
_xtpTags.put("eval", EXPRESSION);
_xtpTags.put("scriptlet", SCRIPTLET);
_xtpTags.put("script", SCRIPTLET);
_xtpTags.put("decl", DECLARATION);
_xtpTags.put("declaration", DECLARATION);
_xtpTags.put("directive.cache", DIRECTIVE_CACHE);
_xtpTags.put("while", WHILE);
_xtpTags.put("assign", ASSIGN);
}
}