diff options
Diffstat (limited to 'src/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java')
-rw-r--r-- | src/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java | 1396 |
1 files changed, 1396 insertions, 0 deletions
diff --git a/src/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java b/src/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java new file mode 100644 index 0000000..47d5e50 --- /dev/null +++ b/src/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java @@ -0,0 +1,1396 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* + * Copyright 2001-2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * $Id: Parser.java,v 1.2.4.1 2005/09/13 12:14:32 pvedula Exp $ + */ + +package com.sun.org.apache.xalan.internal.xsltc.compiler; + +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Properties; +import java.util.Stack; +import java.util.StringTokenizer; +import java.util.Vector; + +import com.sun.java_cup.internal.runtime.Symbol; +import javax.xml.XMLConstants; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg; +import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodType; +import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type; +import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError; +import org.xml.sax.Attributes; +import org.xml.sax.helpers.AttributesImpl; +import org.xml.sax.ContentHandler; +import org.xml.sax.InputSource; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.XMLReader; + +/** + * @author Jacek Ambroziak + * @author Santiago Pericas-Geertsen + * @author G. Todd Miller + * @author Morten Jorgensen + * @author Erwin Bolwidt <ejb@klomp.org> + */ +public class Parser implements Constants, ContentHandler { + + private static final String XSL = "xsl"; // standard prefix + private static final String TRANSLET = "translet"; // extension prefix + + private Locator _locator = null; + + private XSLTC _xsltc; // Reference to the compiler object. + private XPathParser _xpathParser; // Reference to the XPath parser. + private Vector _errors; // Contains all compilation errors + private Vector _warnings; // Contains all compilation errors + + private Hashtable _instructionClasses; // Maps instructions to classes + private Hashtable _instructionAttrs;; // reqd and opt attrs + private Hashtable _qNames; + private Hashtable _namespaces; + private QName _useAttributeSets; + private QName _excludeResultPrefixes; + private QName _extensionElementPrefixes; + private Hashtable _variableScope; + private Stylesheet _currentStylesheet; + private SymbolTable _symbolTable; // Maps QNames to syntax-tree nodes + private Output _output; + private Template _template; // Reference to the template being parsed. + + private boolean _rootNamespaceDef; // Used for validity check + + private SyntaxTreeNode _root; + + private String _target; + + private int _currentImportPrecedence; + + public Parser(XSLTC xsltc) { + _xsltc = xsltc; + } + + public void init() { + _qNames = new Hashtable(512); + _namespaces = new Hashtable(); + _instructionClasses = new Hashtable(); + _instructionAttrs = new Hashtable(); + _variableScope = new Hashtable(); + _template = null; + _errors = new Vector(); + _warnings = new Vector(); + _symbolTable = new SymbolTable(); + _xpathParser = new XPathParser(this); + _currentStylesheet = null; + _output = null; + _root = null; + _rootNamespaceDef = false; + _currentImportPrecedence = 1; + + initStdClasses(); + initInstructionAttrs(); + initExtClasses(); + initSymbolTable(); + + _useAttributeSets = + getQName(XSLT_URI, XSL, "use-attribute-sets"); + _excludeResultPrefixes = + getQName(XSLT_URI, XSL, "exclude-result-prefixes"); + _extensionElementPrefixes = + getQName(XSLT_URI, XSL, "extension-element-prefixes"); + } + + public void setOutput(Output output) { + if (_output != null) { + if (_output.getImportPrecedence() <= output.getImportPrecedence()) { + String cdata = _output.getCdata(); + output.mergeOutput(_output); + _output.disable(); + _output = output; + } + else { + output.disable(); + } + } + else { + _output = output; + } + } + + public Output getOutput() { + return _output; + } + + public Properties getOutputProperties() { + return getTopLevelStylesheet().getOutputProperties(); + } + + public void addVariable(Variable var) { + addVariableOrParam(var); + } + + public void addParameter(Param param) { + addVariableOrParam(param); + } + + private void addVariableOrParam(VariableBase var) { + Object existing = _variableScope.get(var.getName()); + if (existing != null) { + if (existing instanceof Stack) { + Stack stack = (Stack)existing; + stack.push(var); + } + else if (existing instanceof VariableBase) { + Stack stack = new Stack(); + stack.push(existing); + stack.push(var); + _variableScope.put(var.getName(), stack); + } + } + else { + _variableScope.put(var.getName(), var); + } + } + + public void removeVariable(QName name) { + Object existing = _variableScope.get(name); + if (existing instanceof Stack) { + Stack stack = (Stack)existing; + if (!stack.isEmpty()) stack.pop(); + if (!stack.isEmpty()) return; + } + _variableScope.remove(name); + } + + public VariableBase lookupVariable(QName name) { + Object existing = _variableScope.get(name); + if (existing instanceof VariableBase) { + return((VariableBase)existing); + } + else if (existing instanceof Stack) { + Stack stack = (Stack)existing; + return((VariableBase)stack.peek()); + } + return(null); + } + + public void setXSLTC(XSLTC xsltc) { + _xsltc = xsltc; + } + + public XSLTC getXSLTC() { + return _xsltc; + } + + public int getCurrentImportPrecedence() { + return _currentImportPrecedence; + } + + public int getNextImportPrecedence() { + return ++_currentImportPrecedence; + } + + public void setCurrentStylesheet(Stylesheet stylesheet) { + _currentStylesheet = stylesheet; + } + + public Stylesheet getCurrentStylesheet() { + return _currentStylesheet; + } + + public Stylesheet getTopLevelStylesheet() { + return _xsltc.getStylesheet(); + } + + public QName getQNameSafe(final String stringRep) { + // parse and retrieve namespace + final int colon = stringRep.lastIndexOf(':'); + if (colon != -1) { + final String prefix = stringRep.substring(0, colon); + final String localname = stringRep.substring(colon + 1); + String namespace = null; + + // Get the namespace uri from the symbol table + if (prefix.equals(XMLNS_PREFIX) == false) { + namespace = _symbolTable.lookupNamespace(prefix); + if (namespace == null) namespace = EMPTYSTRING; + } + return getQName(namespace, prefix, localname); + } + else { + final String uri = stringRep.equals(XMLNS_PREFIX) ? null + : _symbolTable.lookupNamespace(EMPTYSTRING); + return getQName(uri, null, stringRep); + } + } + + public QName getQName(final String stringRep) { + return getQName(stringRep, true, false); + } + + public QName getQNameIgnoreDefaultNs(final String stringRep) { + return getQName(stringRep, true, true); + } + + public QName getQName(final String stringRep, boolean reportError) { + return getQName(stringRep, reportError, false); + } + + private QName getQName(final String stringRep, boolean reportError, + boolean ignoreDefaultNs) + { + // parse and retrieve namespace + final int colon = stringRep.lastIndexOf(':'); + if (colon != -1) { + final String prefix = stringRep.substring(0, colon); + final String localname = stringRep.substring(colon + 1); + String namespace = null; + + // Get the namespace uri from the symbol table + if (prefix.equals(XMLNS_PREFIX) == false) { + namespace = _symbolTable.lookupNamespace(prefix); + if (namespace == null && reportError) { + final int line = getLineNumber(); + ErrorMsg err = new ErrorMsg(ErrorMsg.NAMESPACE_UNDEF_ERR, + line, prefix); + reportError(ERROR, err); + } + } + return getQName(namespace, prefix, localname); + } + else { + if (stringRep.equals(XMLNS_PREFIX)) { + ignoreDefaultNs = true; + } + final String defURI = ignoreDefaultNs ? null + : _symbolTable.lookupNamespace(EMPTYSTRING); + return getQName(defURI, null, stringRep); + } + } + + public QName getQName(String namespace, String prefix, String localname) { + if (namespace == null || namespace.equals(EMPTYSTRING)) { + QName name = (QName)_qNames.get(localname); + if (name == null) { + name = new QName(null, prefix, localname); + _qNames.put(localname, name); + } + return name; + } + else { + Dictionary space = (Dictionary)_namespaces.get(namespace); + if (space == null) { + final QName name = new QName(namespace, prefix, localname); + _namespaces.put(namespace, space = new Hashtable()); + space.put(localname, name); + return name; + } + else { + QName name = (QName)space.get(localname); + if (name == null) { + name = new QName(namespace, prefix, localname); + space.put(localname, name); + } + return name; + } + } + } + + public QName getQName(String scope, String name) { + return getQName(scope + name); + } + + public QName getQName(QName scope, QName name) { + return getQName(scope.toString() + name.toString()); + } + + public QName getUseAttributeSets() { + return _useAttributeSets; + } + + public QName getExtensionElementPrefixes() { + return _extensionElementPrefixes; + } + + public QName getExcludeResultPrefixes() { + return _excludeResultPrefixes; + } + + /** + * Create an instance of the <code>Stylesheet</code> class, + * and then parse, typecheck and compile the instance. + * Must be called after <code>parse()</code>. + */ + public Stylesheet makeStylesheet(SyntaxTreeNode element) + throws CompilerException { + try { + Stylesheet stylesheet; + + if (element instanceof Stylesheet) { + stylesheet = (Stylesheet)element; + } + else { + stylesheet = new Stylesheet(); + stylesheet.setSimplified(); + stylesheet.addElement(element); + stylesheet.setAttributes((AttributesImpl) element.getAttributes()); + + // Map the default NS if not already defined + if (element.lookupNamespace(EMPTYSTRING) == null) { + element.addPrefixMapping(EMPTYSTRING, EMPTYSTRING); + } + } + stylesheet.setParser(this); + return stylesheet; + } + catch (ClassCastException e) { + ErrorMsg err = new ErrorMsg(ErrorMsg.NOT_STYLESHEET_ERR, element); + throw new CompilerException(err.toString()); + } + } + + /** + * Instanciates a SAX2 parser and generate the AST from the input. + */ + public void createAST(Stylesheet stylesheet) { + try { + if (stylesheet != null) { + stylesheet.parseContents(this); + final int precedence = stylesheet.getImportPrecedence(); + final Enumeration elements = stylesheet.elements(); + while (elements.hasMoreElements()) { + Object child = elements.nextElement(); + if (child instanceof Text) { + final int l = getLineNumber(); + ErrorMsg err = + new ErrorMsg(ErrorMsg.ILLEGAL_TEXT_NODE_ERR,l,null); + reportError(ERROR, err); + } + } + if (!errorsFound()) { + stylesheet.typeCheck(_symbolTable); + } + } + } + catch (TypeCheckError e) { + reportError(ERROR, new ErrorMsg(e)); + } + } + + /** + * Parses a stylesheet and builds the internal abstract syntax tree + * @param reader A SAX2 SAXReader (parser) + * @param input A SAX2 InputSource can be passed to a SAX reader + * @return The root of the abstract syntax tree + */ + public SyntaxTreeNode parse(XMLReader reader, InputSource input) { + try { + // Parse the input document and build the abstract syntax tree + reader.setContentHandler(this); + reader.parse(input); + // Find the start of the stylesheet within the tree + return (SyntaxTreeNode)getStylesheet(_root); + } + catch (IOException e) { + if (_xsltc.debug()) e.printStackTrace(); + reportError(ERROR,new ErrorMsg(e)); + } + catch (SAXException e) { + Throwable ex = e.getException(); + if (_xsltc.debug()) { + e.printStackTrace(); + if (ex != null) ex.printStackTrace(); + } + reportError(ERROR, new ErrorMsg(e)); + } + catch (CompilerException e) { + if (_xsltc.debug()) e.printStackTrace(); + reportError(ERROR, new ErrorMsg(e)); + } + catch (Exception e) { + if (_xsltc.debug()) e.printStackTrace(); + reportError(ERROR, new ErrorMsg(e)); + } + return null; + } + + /** + * Parses a stylesheet and builds the internal abstract syntax tree + * @param input A SAX2 InputSource can be passed to a SAX reader + * @return The root of the abstract syntax tree + */ + public SyntaxTreeNode parse(InputSource input) { + try { + // Create a SAX parser and get the XMLReader object it uses + final SAXParserFactory factory = SAXParserFactory.newInstance(); + + if (_xsltc.isSecureProcessing()) { + try { + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + } + catch (SAXException e) {} + } + + try { + factory.setFeature(Constants.NAMESPACE_FEATURE,true); + } + catch (Exception e) { + factory.setNamespaceAware(true); + } + final SAXParser parser = factory.newSAXParser(); + final XMLReader reader = parser.getXMLReader(); + return(parse(reader, input)); + } + catch (ParserConfigurationException e) { + ErrorMsg err = new ErrorMsg(ErrorMsg.SAX_PARSER_CONFIG_ERR); + reportError(ERROR, err); + } + catch (SAXParseException e){ + reportError(ERROR, new ErrorMsg(e.getMessage(),e.getLineNumber())); + } + catch (SAXException e) { + reportError(ERROR, new ErrorMsg(e.getMessage())); + } + return null; + } + + public SyntaxTreeNode getDocumentRoot() { + return _root; + } + + private String _PImedia = null; + private String _PItitle = null; + private String _PIcharset = null; + + /** + * Set the parameters to use to locate the correct <?xml-stylesheet ...?> + * processing instruction in the case where the input document is an + * XML document with one or more references to a stylesheet. + * @param media The media attribute to be matched. May be null, in which + * case the prefered templates will be used (i.e. alternate = no). + * @param title The value of the title attribute to match. May be null. + * @param charset The value of the charset attribute to match. May be null. + */ + protected void setPIParameters(String media, String title, String charset) { + _PImedia = media; + _PItitle = title; + _PIcharset = charset; + } + + /** + * Extracts the DOM for the stylesheet. In the case of an embedded + * stylesheet, it extracts the DOM subtree corresponding to the + * embedded stylesheet that has an 'id' attribute whose value is the + * same as the value declared in the <?xml-stylesheet...?> processing + * instruction (P.I.). In the xml-stylesheet P.I. the value is labeled + * as the 'href' data of the P.I. The extracted DOM representing the + * stylesheet is returned as an Element object. + */ + private SyntaxTreeNode getStylesheet(SyntaxTreeNode root) + throws CompilerException { + + // Assume that this is a pure XSL stylesheet if there is not + // <?xml-stylesheet ....?> processing instruction + if (_target == null) { + if (!_rootNamespaceDef) { + ErrorMsg msg = new ErrorMsg(ErrorMsg.MISSING_XSLT_URI_ERR); + throw new CompilerException(msg.toString()); + } + return(root); + } + + // Find the xsl:stylesheet or xsl:transform with this reference + if (_target.charAt(0) == '#') { + SyntaxTreeNode element = findStylesheet(root, _target.substring(1)); + if (element == null) { + ErrorMsg msg = new ErrorMsg(ErrorMsg.MISSING_XSLT_TARGET_ERR, + _target, root); + throw new CompilerException(msg.toString()); + } + return(element); + } + else { + return(loadExternalStylesheet(_target)); + } + } + + /** + * Find a Stylesheet element with a specific ID attribute value. + * This method is used to find a Stylesheet node that is referred + * in a <?xml-stylesheet ... ?> processing instruction. + */ + private SyntaxTreeNode findStylesheet(SyntaxTreeNode root, String href) { + + if (root == null) return null; + + if (root instanceof Stylesheet) { + String id = root.getAttribute("id"); + if (id.equals(href)) return root; + } + Vector children = root.getContents(); + if (children != null) { + final int count = children.size(); + for (int i = 0; i < count; i++) { + SyntaxTreeNode child = (SyntaxTreeNode)children.elementAt(i); + SyntaxTreeNode node = findStylesheet(child, href); + if (node != null) return node; + } + } + return null; + } + + /** + * For embedded stylesheets: Load an external file with stylesheet + */ + private SyntaxTreeNode loadExternalStylesheet(String location) + throws CompilerException { + + InputSource source; + + // Check if the location is URL or a local file + if ((new File(location)).exists()) + source = new InputSource("file:"+location); + else + source = new InputSource(location); + + SyntaxTreeNode external = (SyntaxTreeNode)parse(source); + return(external); + } + + private void initAttrTable(String elementName, String[] attrs) { + _instructionAttrs.put(getQName(XSLT_URI, XSL, elementName), + attrs); + } + + private void initInstructionAttrs() { + initAttrTable("template", + new String[] {"match", "name", "priority", "mode"}); + initAttrTable("stylesheet", + new String[] {"id", "version", "extension-element-prefixes", + "exclude-result-prefixes"}); + initAttrTable("transform", + new String[] {"id", "version", "extension-element-prefixes", + "exclude-result-prefixes"}); + initAttrTable("text", new String[] {"disable-output-escaping"}); + initAttrTable("if", new String[] {"test"}); + initAttrTable("choose", new String[] {}); + initAttrTable("when", new String[] {"test"}); + initAttrTable("otherwise", new String[] {}); + initAttrTable("for-each", new String[] {"select"}); + initAttrTable("message", new String[] {"terminate"}); + initAttrTable("number", + new String[] {"level", "count", "from", "value", "format", "lang", + "letter-value", "grouping-separator", "grouping-size"}); + initAttrTable("comment", new String[] {}); + initAttrTable("copy", new String[] {"use-attribute-sets"}); + initAttrTable("copy-of", new String[] {"select"}); + initAttrTable("param", new String[] {"name", "select"}); + initAttrTable("with-param", new String[] {"name", "select"}); + initAttrTable("variable", new String[] {"name", "select"}); + initAttrTable("output", + new String[] {"method", "version", "encoding", + "omit-xml-declaration", "standalone", "doctype-public", + "doctype-system", "cdata-section-elements", "indent", + "media-type"}); + initAttrTable("sort", + new String[] {"select", "order", "case-order", "lang", "data-type"}); + initAttrTable("key", new String[] {"name", "match", "use"}); + initAttrTable("fallback", new String[] {}); + initAttrTable("attribute", new String[] {"name", "namespace"}); + initAttrTable("attribute-set", + new String[] {"name", "use-attribute-sets"}); + initAttrTable("value-of", + new String[] {"select", "disable-output-escaping"}); + initAttrTable("element", + new String[] {"name", "namespace", "use-attribute-sets"}); + initAttrTable("call-template", new String[] {"name"}); + initAttrTable("apply-templates", new String[] {"select", "mode"}); + initAttrTable("apply-imports", new String[] {}); + initAttrTable("decimal-format", + new String[] {"name", "decimal-separator", "grouping-separator", + "infinity", "minus-sign", "NaN", "percent", "per-mille", + "zero-digit", "digit", "pattern-separator"}); + initAttrTable("import", new String[] {"href"}); + initAttrTable("include", new String[] {"href"}); + initAttrTable("strip-space", new String[] {"elements"}); + initAttrTable("preserve-space", new String[] {"elements"}); + initAttrTable("processing-instruction", new String[] {"name"}); + initAttrTable("namespace-alias", + new String[] {"stylesheet-prefix", "result-prefix"}); + } + + + + /** + * Initialize the _instructionClasses Hashtable, which maps XSL element + * names to Java classes in this package. + */ + private void initStdClasses() { + initStdClass("template", "Template"); + initStdClass("stylesheet", "Stylesheet"); + initStdClass("transform", "Stylesheet"); + initStdClass("text", "Text"); + initStdClass("if", "If"); + initStdClass("choose", "Choose"); + initStdClass("when", "When"); + initStdClass("otherwise", "Otherwise"); + initStdClass("for-each", "ForEach"); + initStdClass("message", "Message"); + initStdClass("number", "Number"); + initStdClass("comment", "Comment"); + initStdClass("copy", "Copy"); + initStdClass("copy-of", "CopyOf"); + initStdClass("param", "Param"); + initStdClass("with-param", "WithParam"); + initStdClass("variable", "Variable"); + initStdClass("output", "Output"); + initStdClass("sort", "Sort"); + initStdClass("key", "Key"); + initStdClass("fallback", "Fallback"); + initStdClass("attribute", "XslAttribute"); + initStdClass("attribute-set", "AttributeSet"); + initStdClass("value-of", "ValueOf"); + initStdClass("element", "XslElement"); + initStdClass("call-template", "CallTemplate"); + initStdClass("apply-templates", "ApplyTemplates"); + initStdClass("apply-imports", "ApplyImports"); + initStdClass("decimal-format", "DecimalFormatting"); + initStdClass("import", "Import"); + initStdClass("include", "Include"); + initStdClass("strip-space", "Whitespace"); + initStdClass("preserve-space", "Whitespace"); + initStdClass("processing-instruction", "ProcessingInstruction"); + initStdClass("namespace-alias", "NamespaceAlias"); + } + + private void initStdClass(String elementName, String className) { + _instructionClasses.put(getQName(XSLT_URI, XSL, elementName), + COMPILER_PACKAGE + '.' + className); + } + + public boolean elementSupported(String namespace, String localName) { + return(_instructionClasses.get(getQName(namespace, XSL, localName)) != null); + } + + public boolean functionSupported(String fname) { + return(_symbolTable.lookupPrimop(fname) != null); + } + + private void initExtClasses() { + initExtClass("output", "TransletOutput"); + initExtClass(REDIRECT_URI, "write", "TransletOutput"); + } + + private void initExtClass(String elementName, String className) { + _instructionClasses.put(getQName(TRANSLET_URI, TRANSLET, elementName), + COMPILER_PACKAGE + '.' + className); + } + + private void initExtClass(String namespace, String elementName, String className) { + _instructionClasses.put(getQName(namespace, TRANSLET, elementName), + COMPILER_PACKAGE + '.' + className); + } + + /** + * Add primops and base functions to the symbol table. + */ + private void initSymbolTable() { + MethodType I_V = new MethodType(Type.Int, Type.Void); + MethodType I_R = new MethodType(Type.Int, Type.Real); + MethodType I_S = new MethodType(Type.Int, Type.String); + MethodType I_D = new MethodType(Type.Int, Type.NodeSet); + MethodType R_I = new MethodType(Type.Real, Type.Int); + MethodType R_V = new MethodType(Type.Real, Type.Void); + MethodType R_R = new MethodType(Type.Real, Type.Real); + MethodType R_D = new MethodType(Type.Real, Type.NodeSet); + MethodType R_O = new MethodType(Type.Real, Type.Reference); + MethodType I_I = new MethodType(Type.Int, Type.Int); + MethodType D_O = new MethodType(Type.NodeSet, Type.Reference); + MethodType D_V = new MethodType(Type.NodeSet, Type.Void); + MethodType D_S = new MethodType(Type.NodeSet, Type.String); + MethodType D_D = new MethodType(Type.NodeSet, Type.NodeSet); + MethodType A_V = new MethodType(Type.Node, Type.Void); + MethodType S_V = new MethodType(Type.String, Type.Void); + MethodType S_S = new MethodType(Type.String, Type.String); + MethodType S_A = new MethodType(Type.String, Type.Node); + MethodType S_D = new MethodType(Type.String, Type.NodeSet); + MethodType S_O = new MethodType(Type.String, Type.Reference); + MethodType B_O = new MethodType(Type.Boolean, Type.Reference); + MethodType B_V = new MethodType(Type.Boolean, Type.Void); + MethodType B_B = new MethodType(Type.Boolean, Type.Boolean); + MethodType B_S = new MethodType(Type.Boolean, Type.String); + MethodType D_X = new MethodType(Type.NodeSet, Type.Object); + MethodType R_RR = new MethodType(Type.Real, Type.Real, Type.Real); + MethodType I_II = new MethodType(Type.Int, Type.Int, Type.Int); + MethodType B_RR = new MethodType(Type.Boolean, Type.Real, Type.Real); + MethodType B_II = new MethodType(Type.Boolean, Type.Int, Type.Int); + MethodType S_SS = new MethodType(Type.String, Type.String, Type.String); + MethodType S_DS = new MethodType(Type.String, Type.Real, Type.String); + MethodType S_SR = new MethodType(Type.String, Type.String, Type.Real); + MethodType O_SO = new MethodType(Type.Reference, Type.String, Type.Reference); + + MethodType D_SS = + new MethodType(Type.NodeSet, Type.String, Type.String); + MethodType D_SD = + new MethodType(Type.NodeSet, Type.String, Type.NodeSet); + MethodType B_BB = + new MethodType(Type.Boolean, Type.Boolean, Type.Boolean); + MethodType B_SS = + new MethodType(Type.Boolean, Type.String, Type.String); + MethodType S_SD = + new MethodType(Type.String, Type.String, Type.NodeSet); + MethodType S_DSS = + new MethodType(Type.String, Type.Real, Type.String, Type.String); + MethodType S_SRR = + new MethodType(Type.String, Type.String, Type.Real, Type.Real); + MethodType S_SSS = + new MethodType(Type.String, Type.String, Type.String, Type.String); + + /* + * Standard functions: implemented but not in this table concat(). + * When adding a new function make sure to uncomment + * the corresponding line in <tt>FunctionAvailableCall</tt>. + */ + + // The following functions are inlined + + _symbolTable.addPrimop("current", A_V); + _symbolTable.addPrimop("last", I_V); + _symbolTable.addPrimop("position", I_V); + _symbolTable.addPrimop("true", B_V); + _symbolTable.addPrimop("false", B_V); + _symbolTable.addPrimop("not", B_B); + _symbolTable.addPrimop("name", S_V); + _symbolTable.addPrimop("name", S_A); + _symbolTable.addPrimop("generate-id", S_V); + _symbolTable.addPrimop("generate-id", S_A); + _symbolTable.addPrimop("ceiling", R_R); + _symbolTable.addPrimop("floor", R_R); + _symbolTable.addPrimop("round", R_R); + _symbolTable.addPrimop("contains", B_SS); + _symbolTable.addPrimop("number", R_O); + _symbolTable.addPrimop("number", R_V); + _symbolTable.addPrimop("boolean", B_O); + _symbolTable.addPrimop("string", S_O); + _symbolTable.addPrimop("string", S_V); + _symbolTable.addPrimop("translate", S_SSS); + _symbolTable.addPrimop("string-length", I_V); + _symbolTable.addPrimop("string-length", I_S); + _symbolTable.addPrimop("starts-with", B_SS); + _symbolTable.addPrimop("format-number", S_DS); + _symbolTable.addPrimop("format-number", S_DSS); + _symbolTable.addPrimop("unparsed-entity-uri", S_S); + _symbolTable.addPrimop("key", D_SS); + _symbolTable.addPrimop("key", D_SD); + _symbolTable.addPrimop("id", D_S); + _symbolTable.addPrimop("id", D_D); + _symbolTable.addPrimop("namespace-uri", S_V); + _symbolTable.addPrimop("function-available", B_S); + _symbolTable.addPrimop("element-available", B_S); + _symbolTable.addPrimop("document", D_S); + _symbolTable.addPrimop("document", D_V); + + // The following functions are implemented in the basis library + _symbolTable.addPrimop("count", I_D); + _symbolTable.addPrimop("sum", R_D); + _symbolTable.addPrimop("local-name", S_V); + _symbolTable.addPrimop("local-name", S_D); + _symbolTable.addPrimop("namespace-uri", S_V); + _symbolTable.addPrimop("namespace-uri", S_D); + _symbolTable.addPrimop("substring", S_SR); + _symbolTable.addPrimop("substring", S_SRR); + _symbolTable.addPrimop("substring-after", S_SS); + _symbolTable.addPrimop("substring-before", S_SS); + _symbolTable.addPrimop("normalize-space", S_V); + _symbolTable.addPrimop("normalize-space", S_S); + _symbolTable.addPrimop("system-property", S_S); + + // Extensions + _symbolTable.addPrimop("nodeset", D_O); + _symbolTable.addPrimop("objectType", S_O); + _symbolTable.addPrimop("cast", O_SO); + + // Operators +, -, *, /, % defined on real types. + _symbolTable.addPrimop("+", R_RR); + _symbolTable.addPrimop("-", R_RR); + _symbolTable.addPrimop("*", R_RR); + _symbolTable.addPrimop("/", R_RR); + _symbolTable.addPrimop("%", R_RR); + + // Operators +, -, * defined on integer types. + // Operators / and % are not defined on integers (may cause exception) + _symbolTable.addPrimop("+", I_II); + _symbolTable.addPrimop("-", I_II); + _symbolTable.addPrimop("*", I_II); + + // Operators <, <= >, >= defined on real types. + _symbolTable.addPrimop("<", B_RR); + _symbolTable.addPrimop("<=", B_RR); + _symbolTable.addPrimop(">", B_RR); + _symbolTable.addPrimop(">=", B_RR); + + // Operators <, <= >, >= defined on int types. + _symbolTable.addPrimop("<", B_II); + _symbolTable.addPrimop("<=", B_II); + _symbolTable.addPrimop(">", B_II); + _symbolTable.addPrimop(">=", B_II); + + // Operators <, <= >, >= defined on boolean types. + _symbolTable.addPrimop("<", B_BB); + _symbolTable.addPrimop("<=", B_BB); + _symbolTable.addPrimop(">", B_BB); + _symbolTable.addPrimop(">=", B_BB); + + // Operators 'and' and 'or'. + _symbolTable.addPrimop("or", B_BB); + _symbolTable.addPrimop("and", B_BB); + + // Unary minus. + _symbolTable.addPrimop("u-", R_R); + _symbolTable.addPrimop("u-", I_I); + } + + public SymbolTable getSymbolTable() { + return _symbolTable; + } + + public Template getTemplate() { + return _template; + } + + public void setTemplate(Template template) { + _template = template; + } + + private int _templateIndex = 0; + + public int getTemplateIndex() { + return(_templateIndex++); + } + + /** + * Creates a new node in the abstract syntax tree. This node can be + * o) a supported XSLT 1.0 element + * o) an unsupported XSLT element (post 1.0) + * o) a supported XSLT extension + * o) an unsupported XSLT extension + * o) a literal result element (not an XSLT element and not an extension) + * Unsupported elements do not directly generate an error. We have to wait + * until we have received all child elements of an unsupported element to + * see if any <xsl:fallback> elements exist. + */ + + private boolean versionIsOne = true; + + public SyntaxTreeNode makeInstance(String uri, String prefix, + String local, Attributes attributes) + { + SyntaxTreeNode node = null; + QName qname = getQName(uri, prefix, local); + String className = (String)_instructionClasses.get(qname); + + if (className != null) { + try { + final Class clazz = ObjectFactory.findProviderClass( + className, ObjectFactory.findClassLoader(), true); + node = (SyntaxTreeNode)clazz.newInstance(); + node.setQName(qname); + node.setParser(this); + if (_locator != null) { + node.setLineNumber(getLineNumber()); + } + if (node instanceof Stylesheet) { + _xsltc.setStylesheet((Stylesheet)node); + } + checkForSuperfluousAttributes(node, attributes); + } + catch (ClassNotFoundException e) { + ErrorMsg err = new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, node); + reportError(ERROR, err); + } + catch (Exception e) { + ErrorMsg err = new ErrorMsg(ErrorMsg.INTERNAL_ERR, + e.getMessage(), node); + reportError(FATAL, err); + } + } + else { + if (uri != null) { + // Check if the element belongs in our namespace + if (uri.equals(XSLT_URI)) { + node = new UnsupportedElement(uri, prefix, local, false); + UnsupportedElement element = (UnsupportedElement)node; + ErrorMsg msg = new ErrorMsg(ErrorMsg.UNSUPPORTED_XSL_ERR, + getLineNumber(),local); + element.setErrorMessage(msg); + if (versionIsOne) { + reportError(UNSUPPORTED,msg); + } + } + // Check if this is an XSLTC extension element + else if (uri.equals(TRANSLET_URI)) { + node = new UnsupportedElement(uri, prefix, local, true); + UnsupportedElement element = (UnsupportedElement)node; + ErrorMsg msg = new ErrorMsg(ErrorMsg.UNSUPPORTED_EXT_ERR, + getLineNumber(),local); + element.setErrorMessage(msg); + } + // Check if this is an extension of some other XSLT processor + else { + Stylesheet sheet = _xsltc.getStylesheet(); + if ((sheet != null) && (sheet.isExtension(uri))) { + if (sheet != (SyntaxTreeNode)_parentStack.peek()) { + node = new UnsupportedElement(uri, prefix, local, true); + UnsupportedElement elem = (UnsupportedElement)node; + ErrorMsg msg = + new ErrorMsg(ErrorMsg.UNSUPPORTED_EXT_ERR, + getLineNumber(), + prefix+":"+local); + elem.setErrorMessage(msg); + } + } + } + } + if (node == null) { + node = new LiteralElement(); + node.setLineNumber(getLineNumber()); + } + } + if ((node != null) && (node instanceof LiteralElement)) { + ((LiteralElement)node).setQName(qname); + } + return(node); + } + + /** + * checks the list of attributes against a list of allowed attributes + * for a particular element node. + */ + private void checkForSuperfluousAttributes(SyntaxTreeNode node, + Attributes attrs) + { + QName qname = node.getQName(); + boolean isStylesheet = (node instanceof Stylesheet); + String[] legal = (String[]) _instructionAttrs.get(qname); + if (versionIsOne && legal != null) { + int j; + final int n = attrs.getLength(); + + for (int i = 0; i < n; i++) { + final String attrQName = attrs.getQName(i); + + if (isStylesheet && attrQName.equals("version")) { + versionIsOne = attrs.getValue(i).equals("1.0"); + } + + // Ignore if special or if it has a prefix + if (attrQName.startsWith("xml") || + attrQName.indexOf(':') > 0) continue; + + for (j = 0; j < legal.length; j++) { + if (attrQName.equalsIgnoreCase(legal[j])) { + break; + } + } + if (j == legal.length) { + final ErrorMsg err = + new ErrorMsg(ErrorMsg.ILLEGAL_ATTRIBUTE_ERR, + attrQName, node); + // Workaround for the TCK failure ErrorListener.errorTests.error001.. + err.setWarningError(true); + reportError(WARNING, err); + } + } + } + } + + + /** + * Parse an XPath expression: + * @param parent - XSL element where the expression occured + * @param exp - textual representation of the expression + */ + public Expression parseExpression(SyntaxTreeNode parent, String exp) { + return (Expression)parseTopLevel(parent, "<EXPRESSION>"+exp, null); + } + + /** + * Parse an XPath expression: + * @param parent - XSL element where the expression occured + * @param attr - name of this element's attribute to get expression from + * @param def - default expression (if the attribute was not found) + */ + public Expression parseExpression(SyntaxTreeNode parent, + String attr, String def) { + // Get the textual representation of the expression (if any) + String exp = parent.getAttribute(attr); + // Use the default expression if none was found + if ((exp.length() == 0) && (def != null)) exp = def; + // Invoke the XPath parser + return (Expression)parseTopLevel(parent, "<EXPRESSION>"+exp, exp); + } + + /** + * Parse an XPath pattern: + * @param parent - XSL element where the pattern occured + * @param pattern - textual representation of the pattern + */ + public Pattern parsePattern(SyntaxTreeNode parent, String pattern) { + return (Pattern)parseTopLevel(parent, "<PATTERN>"+pattern, pattern); + } + + /** + * Parse an XPath pattern: + * @param parent - XSL element where the pattern occured + * @param attr - name of this element's attribute to get pattern from + * @param def - default pattern (if the attribute was not found) + */ + public Pattern parsePattern(SyntaxTreeNode parent, + String attr, String def) { + // Get the textual representation of the pattern (if any) + String pattern = parent.getAttribute(attr); + // Use the default pattern if none was found + if ((pattern.length() == 0) && (def != null)) pattern = def; + // Invoke the XPath parser + return (Pattern)parseTopLevel(parent, "<PATTERN>"+pattern, pattern); + } + + /** + * Parse an XPath expression or pattern using the generated XPathParser + * The method will return a Dummy node if the XPath parser fails. + */ + private SyntaxTreeNode parseTopLevel(SyntaxTreeNode parent, String text, + String expression) { + int line = getLineNumber(); + + try { + _xpathParser.setScanner(new XPathLexer(new StringReader(text))); + Symbol result = _xpathParser.parse(expression, line); + if (result != null) { + final SyntaxTreeNode node = (SyntaxTreeNode)result.value; + if (node != null) { + node.setParser(this); + node.setParent(parent); + node.setLineNumber(line); +// System.out.println("e = " + text + " " + node); + return node; + } + } + reportError(ERROR, new ErrorMsg(ErrorMsg.XPATH_PARSER_ERR, + expression, parent)); + } + catch (Exception e) { + if (_xsltc.debug()) e.printStackTrace(); + reportError(ERROR, new ErrorMsg(ErrorMsg.XPATH_PARSER_ERR, + expression, parent)); + } + + // Return a dummy pattern (which is an expression) + SyntaxTreeNode.Dummy.setParser(this); + return SyntaxTreeNode.Dummy; + } + + /************************ ERROR HANDLING SECTION ************************/ + + /** + * Returns true if there were any errors during compilation + */ + public boolean errorsFound() { + return _errors.size() > 0; + } + + /** + * Prints all compile-time errors + */ + public void printErrors() { + final int size = _errors.size(); + if (size > 0) { + System.err.println(new ErrorMsg(ErrorMsg.COMPILER_ERROR_KEY)); + for (int i = 0; i < size; i++) { + System.err.println(" " + _errors.elementAt(i)); + } + } + } + + /** + * Prints all compile-time warnings + */ + public void printWarnings() { + final int size = _warnings.size(); + if (size > 0) { + System.err.println(new ErrorMsg(ErrorMsg.COMPILER_WARNING_KEY)); + for (int i = 0; i < size; i++) { + System.err.println(" " + _warnings.elementAt(i)); + } + } + } + + /** + * Common error/warning message handler + */ + public void reportError(final int category, final ErrorMsg error) { + switch (category) { + case Constants.INTERNAL: + // Unexpected internal errors, such as null-ptr exceptions, etc. + // Immediately terminates compilation, no translet produced + _errors.addElement(error); + break; + case Constants.UNSUPPORTED: + // XSLT elements that are not implemented and unsupported ext. + // Immediately terminates compilation, no translet produced + _errors.addElement(error); + break; + case Constants.FATAL: + // Fatal error in the stylesheet input (parsing or content) + // Immediately terminates compilation, no translet produced + _errors.addElement(error); + break; + case Constants.ERROR: + // Other error in the stylesheet input (parsing or content) + // Does not terminate compilation, no translet produced + _errors.addElement(error); + break; + case Constants.WARNING: + // Other error in the stylesheet input (content errors only) + // Does not terminate compilation, a translet is produced + _warnings.addElement(error); + break; + } + } + + public Vector getErrors() { + return _errors; + } + + public Vector getWarnings() { + return _warnings; + } + + /************************ SAX2 ContentHandler INTERFACE *****************/ + + private Stack _parentStack = null; + private Hashtable _prefixMapping = null; + + /** + * SAX2: Receive notification of the beginning of a document. + */ + public void startDocument() { + _root = null; + _target = null; + _prefixMapping = null; + _parentStack = new Stack(); + } + + /** + * SAX2: Receive notification of the end of a document. + */ + public void endDocument() { } + + + /** + * SAX2: Begin the scope of a prefix-URI Namespace mapping. + * This has to be passed on to the symbol table! + */ + public void startPrefixMapping(String prefix, String uri) { + if (_prefixMapping == null) { + _prefixMapping = new Hashtable(); + } + _prefixMapping.put(prefix, uri); + } + + /** + * SAX2: End the scope of a prefix-URI Namespace mapping. + * This has to be passed on to the symbol table! + */ + public void endPrefixMapping(String prefix) { } + + /** + * SAX2: Receive notification of the beginning of an element. + * The parser may re-use the attribute list that we're passed so + * we clone the attributes in our own Attributes implementation + */ + public void startElement(String uri, String localname, + String qname, Attributes attributes) + throws SAXException { + final int col = qname.lastIndexOf(':'); + final String prefix = (col == -1) ? null : qname.substring(0, col); + + SyntaxTreeNode element = makeInstance(uri, prefix, + localname, attributes); + if (element == null) { + ErrorMsg err = new ErrorMsg(ErrorMsg.ELEMENT_PARSE_ERR, + prefix+':'+localname); + throw new SAXException(err.toString()); + } + + // If this is the root element of the XML document we need to make sure + // that it contains a definition of the XSL namespace URI + if (_root == null) { + if ((_prefixMapping == null) || + (_prefixMapping.containsValue(Constants.XSLT_URI) == false)) + _rootNamespaceDef = false; + else + _rootNamespaceDef = true; + _root = element; + } + else { + SyntaxTreeNode parent = (SyntaxTreeNode)_parentStack.peek(); + parent.addElement(element); + element.setParent(parent); + } + element.setAttributes(new AttributesImpl(attributes)); + element.setPrefixMapping(_prefixMapping); + + if (element instanceof Stylesheet) { + // Extension elements and excluded elements have to be + // handled at this point in order to correctly generate + // Fallback elements from <xsl:fallback>s. + getSymbolTable().setCurrentNode(element); + ((Stylesheet)element).excludeExtensionPrefixes(this); + } + + _prefixMapping = null; + _parentStack.push(element); + } + + /** + * SAX2: Receive notification of the end of an element. + */ + public void endElement(String uri, String localname, String qname) { + _parentStack.pop(); + } + + /** + * SAX2: Receive notification of character data. + */ + public void characters(char[] ch, int start, int length) { + String string = new String(ch, start, length); + SyntaxTreeNode parent = (SyntaxTreeNode)_parentStack.peek(); + + if (string.length() == 0) return; + + // If this text occurs within an <xsl:text> element we append it + // as-is to the existing text element + if (parent instanceof Text) { + ((Text)parent).setText(string); + return; + } + + // Ignore text nodes that occur directly under <xsl:stylesheet> + if (parent instanceof Stylesheet) return; + + SyntaxTreeNode bro = parent.lastChild(); + if ((bro != null) && (bro instanceof Text)) { + Text text = (Text)bro; + if (!text.isTextElement()) { + if ((length > 1) || ( ((int)ch[0]) < 0x100)) { + text.setText(string); + return; + } + } + } + + // Add it as a regular text node otherwise + parent.addElement(new Text(string)); + } + + private String getTokenValue(String token) { + final int start = token.indexOf('"'); + final int stop = token.lastIndexOf('"'); + return token.substring(start+1, stop); + } + + /** + * SAX2: Receive notification of a processing instruction. + * These require special handling for stylesheet PIs. + */ + public void processingInstruction(String name, String value) { + // We only handle the <?xml-stylesheet ...?> PI + if ((_target == null) && (name.equals("xml-stylesheet"))) { + + String href = null; // URI of stylesheet found + String media = null; // Media of stylesheet found + String title = null; // Title of stylesheet found + String charset = null; // Charset of stylesheet found + + // Get the attributes from the processing instruction + StringTokenizer tokens = new StringTokenizer(value); + while (tokens.hasMoreElements()) { + String token = (String)tokens.nextElement(); + if (token.startsWith("href")) + href = getTokenValue(token); + else if (token.startsWith("media")) + media = getTokenValue(token); + else if (token.startsWith("title")) + title = getTokenValue(token); + else if (token.startsWith("charset")) + charset = getTokenValue(token); + } + + // Set the target to this PI's href if the parameters are + // null or match the corresponding attributes of this PI. + if ( ((_PImedia == null) || (_PImedia.equals(media))) && + ((_PItitle == null) || (_PImedia.equals(title))) && + ((_PIcharset == null) || (_PImedia.equals(charset))) ) { + _target = href; + } + } + } + + /** + * IGNORED - all ignorable whitespace is ignored + */ + public void ignorableWhitespace(char[] ch, int start, int length) { } + + /** + * IGNORED - we do not have to do anything with skipped entities + */ + public void skippedEntity(String name) { } + + /** + * Store the document locator to later retrieve line numbers of all + * elements from the stylesheet + */ + public void setDocumentLocator(Locator locator) { + _locator = locator; + } + + /** + * Get the line number, or zero + * if there is no _locator. + */ + private int getLineNumber() { + int line = 0; + if (_locator != null) + line = _locator.getLineNumber(); + return line; + } + +} |