1 /*
2  * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
3  */
4 /*
5  * Licensed to the Apache Software Foundation (ASF) under one or more
6  * contributor license agreements.  See the NOTICE file distributed with
7  * this work for additional information regarding copyright ownership.
8  * The ASF licenses this file to You under the Apache License, Version 2.0
9  * (the "License"); you may not use this file except in compliance with
10  * the License.  You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 package com.sun.org.apache.xalan.internal.xsltc.compiler;
22 
23 import com.sun.java_cup.internal.runtime.Symbol;
24 import com.sun.org.apache.xalan.internal.XalanConstants;
25 import com.sun.org.apache.xalan.internal.utils.ObjectFactory;
26 import com.sun.org.apache.xalan.internal.utils.SecuritySupport;
27 import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager;
28 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
29 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodType;
30 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
31 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
32 import com.sun.org.apache.xml.internal.serializer.utils.SystemIDResolver;
33 import java.io.File;
34 import java.io.IOException;
35 import java.io.StringReader;
36 import java.util.ArrayList;
37 import java.util.HashMap;
38 import java.util.Iterator;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Properties;
42 import java.util.Stack;
43 import java.util.StringTokenizer;
44 import javax.xml.XMLConstants;
45 import jdk.xml.internal.JdkXmlUtils;
46 import org.xml.sax.Attributes;
47 import org.xml.sax.ContentHandler;
48 import org.xml.sax.InputSource;
49 import org.xml.sax.Locator;
50 import org.xml.sax.SAXException;
51 import org.xml.sax.XMLReader;
52 import org.xml.sax.helpers.AttributesImpl;
53 
54 /**
55  * @author Jacek Ambroziak
56  * @author Santiago Pericas-Geertsen
57  * @author G. Todd Miller
58  * @author Morten Jorgensen
59  * @author Erwin Bolwidt <ejb@klomp.org>
60  */
61 public class Parser implements Constants, ContentHandler {
62 
63     private static final String XSL = "xsl";           // standard prefix
64     private static final String TRANSLET = "translet"; // extension prefix
65 
66     private Locator _locator = null;
67 
68     private XSLTC _xsltc;                  // Reference to the compiler object.
69     private XPathParser _xpathParser;      // Reference to the XPath parser.
70     private ArrayList<ErrorMsg> _errors;   // Contains all compilation errors
71     private ArrayList<ErrorMsg> _warnings; // Contains all compilation warnings
72 
73     private Map<String, String>   _instructionClasses; // Maps instructions to classes
74     private Map<String, String[]> _instructionAttrs;  // reqd and opt attrs
75     private Map<String, QName>    _qNames;
76     private Map<String, Map<String, QName>> _namespaces;
77     private QName       _useAttributeSets;
78     private QName       _excludeResultPrefixes;
79     private QName       _extensionElementPrefixes;
80     private Map<String, Object>   _variableScope;
81     private Stylesheet  _currentStylesheet;
82     private SymbolTable _symbolTable; // Maps QNames to syntax-tree nodes
83     private Output      _output;
84     private Template    _template;    // Reference to the template being parsed.
85 
86     private boolean     _rootNamespaceDef; // Used for validity check
87 
88     private SyntaxTreeNode _root;
89 
90     private String _target;
91 
92     private int _currentImportPrecedence;
93 
94     private boolean _overrideDefaultParser;
95 
Parser(XSLTC xsltc, boolean useOverrideDefaultParser)96     public Parser(XSLTC xsltc, boolean useOverrideDefaultParser) {
97         _xsltc = xsltc;
98         _overrideDefaultParser = useOverrideDefaultParser;
99     }
100 
init()101     public void init() {
102         _qNames              = new HashMap<>(512);
103         _namespaces          = new HashMap<>();
104         _instructionClasses  = new HashMap<>();
105         _instructionAttrs    = new HashMap<>();
106         _variableScope       = new HashMap<>();
107         _template            = null;
108         _errors              = new ArrayList<>();
109         _warnings            = new ArrayList<>();
110         _symbolTable         = new SymbolTable();
111         _xpathParser         = new XPathParser(this);
112         _currentStylesheet   = null;
113         _output              = null;
114         _root                = null;
115         _rootNamespaceDef    = false;
116         _currentImportPrecedence = 1;
117 
118         initStdClasses();
119         initInstructionAttrs();
120         initExtClasses();
121         initSymbolTable();
122 
123         _useAttributeSets =
124             getQName(XSLT_URI, XSL, "use-attribute-sets");
125         _excludeResultPrefixes =
126             getQName(XSLT_URI, XSL, "exclude-result-prefixes");
127         _extensionElementPrefixes =
128             getQName(XSLT_URI, XSL, "extension-element-prefixes");
129     }
130 
setOutput(Output output)131     public void setOutput(Output output) {
132         if (_output != null) {
133             if (_output.getImportPrecedence() <= output.getImportPrecedence()) {
134                 output.mergeOutput(_output);
135                 _output.disable();
136                 _output = output;
137             }
138             else {
139                 output.disable();
140             }
141         }
142         else {
143             _output = output;
144         }
145     }
146 
getOutput()147     public Output getOutput() {
148         return _output;
149     }
150 
getOutputProperties()151     public Properties getOutputProperties() {
152         return getTopLevelStylesheet().getOutputProperties();
153     }
154 
addVariable(Variable var)155     public void addVariable(Variable var) {
156         addVariableOrParam(var);
157     }
158 
addParameter(Param param)159     public void addParameter(Param param) {
160         addVariableOrParam(param);
161     }
162 
addVariableOrParam(VariableBase var)163     private void addVariableOrParam(VariableBase var) {
164         Object existing = _variableScope.get(var.getName().getStringRep());
165         if (existing != null) {
166             if (existing instanceof Stack) {
167                 @SuppressWarnings("unchecked")
168                 Stack<VariableBase> stack = (Stack<VariableBase>)existing;
169                 stack.push(var);
170             }
171             else if (existing instanceof VariableBase) {
172                 Stack<VariableBase> stack = new Stack<>();
173                 stack.push((VariableBase)existing);
174                 stack.push(var);
175                 _variableScope.put(var.getName().getStringRep(), stack);
176             }
177         }
178         else {
179             _variableScope.put(var.getName().getStringRep(), var);
180         }
181     }
182 
removeVariable(QName name)183     public void removeVariable(QName name) {
184         Object existing = _variableScope.get(name.getStringRep());
185         if (existing instanceof Stack) {
186             @SuppressWarnings("unchecked")
187             Stack<VariableBase> stack = (Stack<VariableBase>)existing;
188             if (!stack.isEmpty()) stack.pop();
189             if (!stack.isEmpty()) return;
190         }
191         _variableScope.remove(name.getStringRep());
192     }
193 
lookupVariable(QName name)194     public VariableBase lookupVariable(QName name) {
195         Object existing = _variableScope.get(name.getStringRep());
196         if (existing instanceof VariableBase) {
197             return (VariableBase)existing;
198         }
199         else if (existing instanceof Stack) {
200             @SuppressWarnings("unchecked")
201             Stack<VariableBase> stack = (Stack<VariableBase>)existing;
202             return stack.peek();
203         }
204         return null;
205     }
206 
setXSLTC(XSLTC xsltc)207     public void setXSLTC(XSLTC xsltc) {
208         _xsltc = xsltc;
209     }
210 
getXSLTC()211     public XSLTC getXSLTC() {
212         return _xsltc;
213     }
214 
getCurrentImportPrecedence()215     public int getCurrentImportPrecedence() {
216         return _currentImportPrecedence;
217     }
218 
getNextImportPrecedence()219     public int getNextImportPrecedence() {
220         return ++_currentImportPrecedence;
221     }
222 
setCurrentStylesheet(Stylesheet stylesheet)223     public void setCurrentStylesheet(Stylesheet stylesheet) {
224         _currentStylesheet = stylesheet;
225     }
226 
getCurrentStylesheet()227     public Stylesheet getCurrentStylesheet() {
228         return _currentStylesheet;
229     }
230 
getTopLevelStylesheet()231     public Stylesheet getTopLevelStylesheet() {
232         return _xsltc.getStylesheet();
233     }
234 
getQNameSafe(final String stringRep)235     public QName getQNameSafe(final String stringRep) {
236         // parse and retrieve namespace
237         final int colon = stringRep.lastIndexOf(':');
238         if (colon != -1) {
239             final String prefix = stringRep.substring(0, colon);
240             final String localname = stringRep.substring(colon + 1);
241             String namespace = null;
242 
243             // Get the namespace uri from the symbol table
244             if (prefix.equals(XMLNS_PREFIX) == false) {
245                 namespace = _symbolTable.lookupNamespace(prefix);
246                 if (namespace == null) namespace = EMPTYSTRING;
247             }
248             return getQName(namespace, prefix, localname);
249         }
250         else {
251             final String uri = stringRep.equals(XMLNS_PREFIX) ? null
252                 : _symbolTable.lookupNamespace(EMPTYSTRING);
253             return getQName(uri, null, stringRep);
254         }
255     }
256 
getQName(final String stringRep)257     public QName getQName(final String stringRep) {
258         return getQName(stringRep, true, false);
259     }
260 
getQNameIgnoreDefaultNs(final String stringRep)261     public QName getQNameIgnoreDefaultNs(final String stringRep) {
262         return getQName(stringRep, true, true);
263     }
264 
getQName(final String stringRep, boolean reportError)265     public QName getQName(final String stringRep, boolean reportError) {
266         return getQName(stringRep, reportError, false);
267     }
268 
getQName(final String stringRep, boolean reportError, boolean ignoreDefaultNs)269     private QName getQName(final String stringRep, boolean reportError,
270         boolean ignoreDefaultNs)
271     {
272         // parse and retrieve namespace
273         final int colon = stringRep.lastIndexOf(':');
274         if (colon != -1) {
275             final String prefix = stringRep.substring(0, colon);
276             final String localname = stringRep.substring(colon + 1);
277             String namespace = null;
278 
279             // Get the namespace uri from the symbol table
280             if (prefix.equals(XMLNS_PREFIX) == false) {
281                 namespace = _symbolTable.lookupNamespace(prefix);
282                 if (namespace == null && reportError) {
283                     final int line = getLineNumber();
284                     ErrorMsg err = new ErrorMsg(ErrorMsg.NAMESPACE_UNDEF_ERR,
285                                                 line, prefix);
286                     reportError(ERROR, err);
287                 }
288             }
289             return getQName(namespace, prefix, localname);
290         }
291         else {
292             if (stringRep.equals(XMLNS_PREFIX)) {
293                 ignoreDefaultNs = true;
294             }
295             final String defURI = ignoreDefaultNs ? null
296                                   : _symbolTable.lookupNamespace(EMPTYSTRING);
297             return getQName(defURI, null, stringRep);
298         }
299     }
300 
getQName(String namespace, String prefix, String localname)301     public QName getQName(String namespace, String prefix, String localname) {
302         if (namespace == null || namespace.equals(EMPTYSTRING)) {
303             QName name = _qNames.get(localname);
304             if (name == null) {
305                 name = new QName(null, prefix, localname);
306                 _qNames.put(localname, name);
307             }
308             return name;
309         }
310         else {
311             Map<String, QName> space = _namespaces.get(namespace);
312             String lexicalQName =
313                        (prefix == null || prefix.length() == 0)
314                             ? localname
315                             : (prefix + ':' + localname);
316 
317             if (space == null) {
318                 final QName name = new QName(namespace, prefix, localname);
319                 _namespaces.put(namespace, space = new HashMap<>());
320                 space.put(lexicalQName, name);
321                 return name;
322             }
323             else {
324                 QName name = space.get(lexicalQName);
325                 if (name == null) {
326                     name = new QName(namespace, prefix, localname);
327                     space.put(lexicalQName, name);
328                 }
329                 return name;
330             }
331         }
332     }
333 
getQName(String scope, String name)334     public QName getQName(String scope, String name) {
335         return getQName(scope + name);
336     }
337 
getQName(QName scope, QName name)338     public QName getQName(QName scope, QName name) {
339         return getQName(scope.toString() + name.toString());
340     }
341 
getUseAttributeSets()342     public QName getUseAttributeSets() {
343         return _useAttributeSets;
344     }
345 
getExtensionElementPrefixes()346     public QName getExtensionElementPrefixes() {
347         return _extensionElementPrefixes;
348     }
349 
getExcludeResultPrefixes()350     public QName getExcludeResultPrefixes() {
351         return _excludeResultPrefixes;
352     }
353 
354     /**
355      * Create an instance of the <code>Stylesheet</code> class,
356      * and then parse, typecheck and compile the instance.
357      * Must be called after <code>parse()</code>.
358      */
makeStylesheet(SyntaxTreeNode element)359     public Stylesheet makeStylesheet(SyntaxTreeNode element)
360         throws CompilerException {
361         try {
362             Stylesheet stylesheet;
363 
364             if (element instanceof Stylesheet) {
365                 stylesheet = (Stylesheet)element;
366             }
367             else {
368                 stylesheet = new Stylesheet();
369                 stylesheet.setSimplified();
370                 stylesheet.addElement(element);
371                 stylesheet.setAttributes((AttributesImpl) element.getAttributes());
372 
373                 // Map the default NS if not already defined
374                 if (element.lookupNamespace(EMPTYSTRING) == null) {
375                     element.addPrefixMapping(EMPTYSTRING, EMPTYSTRING);
376                 }
377             }
378             stylesheet.setParser(this);
379             return stylesheet;
380         }
381         catch (ClassCastException e) {
382             ErrorMsg err = new ErrorMsg(ErrorMsg.NOT_STYLESHEET_ERR, element);
383             throw new CompilerException(err.toString());
384         }
385     }
386 
387     /**
388      * Instanciates a SAX2 parser and generate the AST from the input.
389      */
createAST(Stylesheet stylesheet)390     public void createAST(Stylesheet stylesheet) {
391         try {
392             if (stylesheet != null) {
393                 stylesheet.parseContents(this);
394                 final Iterator<SyntaxTreeNode> elements = stylesheet.elements();
395                 while (elements.hasNext()) {
396                     SyntaxTreeNode child = elements.next();
397                     if (child instanceof Text) {
398                         final int l = getLineNumber();
399                         ErrorMsg err =
400                             new ErrorMsg(ErrorMsg.ILLEGAL_TEXT_NODE_ERR,l,null);
401                         reportError(ERROR, err);
402                     }
403                 }
404                 if (!errorsFound()) {
405                     stylesheet.typeCheck(_symbolTable);
406                 }
407             }
408         }
409         catch (TypeCheckError e) {
410             reportError(ERROR, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e));
411         }
412     }
413 
414     /**
415      * Parses a stylesheet and builds the internal abstract syntax tree
416      * @param reader A SAX2 SAXReader (parser)
417      * @param input A SAX2 InputSource can be passed to a SAX reader
418      * @return The root of the abstract syntax tree
419      */
parse(XMLReader reader, InputSource input)420     public SyntaxTreeNode parse(XMLReader reader, InputSource input) {
421         try {
422             // Parse the input document and build the abstract syntax tree
423             reader.setContentHandler(this);
424             reader.parse(input);
425             // Find the start of the stylesheet within the tree
426             return (SyntaxTreeNode)getStylesheet(_root);
427         }
428         catch (IOException e) {
429             if (_xsltc.debug()) e.printStackTrace();
430             reportError(ERROR,new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e));
431         }
432         catch (SAXException e) {
433             Throwable ex = e.getException();
434             if (_xsltc.debug()) {
435                 e.printStackTrace();
436                 if (ex != null) ex.printStackTrace();
437             }
438             reportError(ERROR, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e));
439         }
440         catch (CompilerException e) {
441             if (_xsltc.debug()) e.printStackTrace();
442             reportError(ERROR, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e));
443         }
444         catch (Exception e) {
445             if (_xsltc.debug()) e.printStackTrace();
446             reportError(ERROR, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e));
447         }
448         return null;
449     }
450 
451     /**
452      * Parses a stylesheet and builds the internal abstract syntax tree
453      * @param input A SAX2 InputSource can be passed to a SAX reader
454      * @return The root of the abstract syntax tree
455      */
parse(InputSource input)456     public SyntaxTreeNode parse(InputSource input) {
457         final XMLReader reader = JdkXmlUtils.getXMLReader(_overrideDefaultParser,
458                 _xsltc.isSecureProcessing());
459 
460         JdkXmlUtils.setXMLReaderPropertyIfSupport(reader, XMLConstants.ACCESS_EXTERNAL_DTD,
461                 _xsltc.getProperty(XMLConstants.ACCESS_EXTERNAL_DTD), true);
462 
463         String lastProperty = "";
464         try {
465             XMLSecurityManager securityManager =
466                     (XMLSecurityManager) _xsltc.getProperty(XalanConstants.SECURITY_MANAGER);
467             for (XMLSecurityManager.Limit limit : XMLSecurityManager.Limit.values()) {
468                 lastProperty = limit.apiProperty();
469                 reader.setProperty(lastProperty, securityManager.getLimitValueAsString(limit));
470             }
471             if (securityManager.printEntityCountInfo()) {
472                 lastProperty = XalanConstants.JDK_ENTITY_COUNT_INFO;
473                 reader.setProperty(XalanConstants.JDK_ENTITY_COUNT_INFO, XalanConstants.JDK_YES);
474             }
475         } catch (SAXException se) {
476             XMLSecurityManager.printWarning(reader.getClass().getName(), lastProperty, se);
477         }
478 
479         return (parse(reader, input));
480     }
481 
getDocumentRoot()482     public SyntaxTreeNode getDocumentRoot() {
483         return _root;
484     }
485 
486     private String _PImedia = null;
487     private String _PItitle = null;
488     private String _PIcharset = null;
489 
490     /**
491      * Set the parameters to use to locate the correct <?xml-stylesheet ...?>
492      * processing instruction in the case where the input document is an
493      * XML document with one or more references to a stylesheet.
494      * @param media The media attribute to be matched. May be null, in which
495      * case the prefered templates will be used (i.e. alternate = no).
496      * @param title The value of the title attribute to match. May be null.
497      * @param charset The value of the charset attribute to match. May be null.
498      */
setPIParameters(String media, String title, String charset)499     protected void setPIParameters(String media, String title, String charset) {
500         _PImedia = media;
501         _PItitle = title;
502         _PIcharset = charset;
503     }
504 
505     /**
506      * Extracts the DOM for the stylesheet. In the case of an embedded
507      * stylesheet, it extracts the DOM subtree corresponding to the
508      * embedded stylesheet that has an 'id' attribute whose value is the
509      * same as the value declared in the <?xml-stylesheet...?> processing
510      * instruction (P.I.). In the xml-stylesheet P.I. the value is labeled
511      * as the 'href' data of the P.I. The extracted DOM representing the
512      * stylesheet is returned as an Element object.
513      */
getStylesheet(SyntaxTreeNode root)514     private SyntaxTreeNode getStylesheet(SyntaxTreeNode root)
515         throws CompilerException {
516 
517         // Assume that this is a pure XSL stylesheet if there is not
518         // <?xml-stylesheet ....?> processing instruction
519         if (_target == null) {
520             if (!_rootNamespaceDef) {
521                 ErrorMsg msg = new ErrorMsg(ErrorMsg.MISSING_XSLT_URI_ERR);
522                 throw new CompilerException(msg.toString());
523             }
524             return(root);
525         }
526 
527         // Find the xsl:stylesheet or xsl:transform with this reference
528         if (_target.charAt(0) == '#') {
529             SyntaxTreeNode element = findStylesheet(root, _target.substring(1));
530             if (element == null) {
531                 ErrorMsg msg = new ErrorMsg(ErrorMsg.MISSING_XSLT_TARGET_ERR,
532                                             _target, root);
533                 throw new CompilerException(msg.toString());
534             }
535             return(element);
536         }
537         else {
538             try {
539                 String path = _target;
540                 if (path.indexOf(":")==-1) {
541                     path = "file:" + path;
542                 }
543                 path = SystemIDResolver.getAbsoluteURI(path);
544                 String accessError = SecuritySupport.checkAccess(path,
545                         (String)_xsltc.getProperty(XMLConstants.ACCESS_EXTERNAL_STYLESHEET),
546                         XalanConstants.ACCESS_EXTERNAL_ALL);
547                 if (accessError != null) {
548                     ErrorMsg msg = new ErrorMsg(ErrorMsg.ACCESSING_XSLT_TARGET_ERR,
549                             SecuritySupport.sanitizePath(_target), accessError,
550                             root);
551                     throw new CompilerException(msg.toString());
552                 }
553             } catch (IOException ex) {
554                 throw new CompilerException(ex);
555             }
556 
557             return(loadExternalStylesheet(_target));
558         }
559     }
560 
561     /**
562      * Find a Stylesheet element with a specific ID attribute value.
563      * This method is used to find a Stylesheet node that is referred
564      * in a <?xml-stylesheet ... ?> processing instruction.
565      */
findStylesheet(SyntaxTreeNode root, String href)566     private SyntaxTreeNode findStylesheet(SyntaxTreeNode root, String href) {
567 
568         if (root == null) return null;
569 
570         if (root instanceof Stylesheet) {
571             String id = root.getAttribute("id");
572             if (id.equals(href)) return root;
573         }
574         List<SyntaxTreeNode> children = root.getContents();
575         if (children != null) {
576             final int count = children.size();
577             for (int i = 0; i < count; i++) {
578                 SyntaxTreeNode child = children.get(i);
579                 SyntaxTreeNode node = findStylesheet(child, href);
580                 if (node != null) return node;
581             }
582         }
583         return null;
584     }
585 
586     /**
587      * For embedded stylesheets: Load an external file with stylesheet
588      */
loadExternalStylesheet(String location)589     private SyntaxTreeNode loadExternalStylesheet(String location)
590         throws CompilerException {
591 
592         InputSource source;
593 
594         // Check if the location is URL or a local file
595         if ((new File(location)).exists())
596             source = new InputSource("file:"+location);
597         else
598             source = new InputSource(location);
599 
600         SyntaxTreeNode external = (SyntaxTreeNode)parse(source);
601         return(external);
602     }
603 
initAttrTable(String elementName, String[] attrs)604     private void initAttrTable(String elementName, String[] attrs) {
605         _instructionAttrs.put(getQName(XSLT_URI, XSL, elementName).getStringRep(),
606                                 attrs);
607     }
608 
initInstructionAttrs()609     private void initInstructionAttrs() {
610         initAttrTable("template",
611             new String[] {"match", "name", "priority", "mode"});
612         initAttrTable("stylesheet",
613             new String[] {"id", "version", "extension-element-prefixes",
614                 "exclude-result-prefixes"});
615         initAttrTable("transform",
616             new String[] {"id", "version", "extension-element-prefixes",
617                 "exclude-result-prefixes"});
618         initAttrTable("text", new String[] {"disable-output-escaping"});
619         initAttrTable("if", new String[] {"test"});
620         initAttrTable("choose", new String[] {});
621         initAttrTable("when", new String[] {"test"});
622         initAttrTable("otherwise", new String[] {});
623         initAttrTable("for-each", new String[] {"select"});
624         initAttrTable("message", new String[] {"terminate"});
625         initAttrTable("number",
626             new String[] {"level", "count", "from", "value", "format", "lang",
627                 "letter-value", "grouping-separator", "grouping-size"});
628                 initAttrTable("comment", new String[] {});
629         initAttrTable("copy", new String[] {"use-attribute-sets"});
630         initAttrTable("copy-of", new String[] {"select"});
631         initAttrTable("param", new String[] {"name", "select"});
632         initAttrTable("with-param", new String[] {"name", "select"});
633         initAttrTable("variable", new String[] {"name", "select"});
634         initAttrTable("output",
635             new String[] {"method", "version", "encoding",
636                 "omit-xml-declaration", "standalone", "doctype-public",
637                 "doctype-system", "cdata-section-elements", "indent",
638                 "media-type"});
639         initAttrTable("sort",
640            new String[] {"select", "order", "case-order", "lang", "data-type"});
641         initAttrTable("key", new String[] {"name", "match", "use"});
642         initAttrTable("fallback", new String[] {});
643         initAttrTable("attribute", new String[] {"name", "namespace"});
644         initAttrTable("attribute-set",
645             new String[] {"name", "use-attribute-sets"});
646         initAttrTable("value-of",
647             new String[] {"select", "disable-output-escaping"});
648         initAttrTable("element",
649             new String[] {"name", "namespace", "use-attribute-sets"});
650         initAttrTable("call-template", new String[] {"name"});
651         initAttrTable("apply-templates", new String[] {"select", "mode"});
652         initAttrTable("apply-imports", new String[] {});
653         initAttrTable("decimal-format",
654             new String[] {"name", "decimal-separator", "grouping-separator",
655                 "infinity", "minus-sign", "NaN", "percent", "per-mille",
656                 "zero-digit", "digit", "pattern-separator"});
657         initAttrTable("import", new String[] {"href"});
658         initAttrTable("include", new String[] {"href"});
659         initAttrTable("strip-space", new String[] {"elements"});
660         initAttrTable("preserve-space", new String[] {"elements"});
661         initAttrTable("processing-instruction", new String[] {"name"});
662         initAttrTable("namespace-alias",
663            new String[] {"stylesheet-prefix", "result-prefix"});
664     }
665 
666     /**
667      * Initialize the _instructionClasses map, which maps XSL element
668      * names to Java classes in this package.
669      */
initStdClasses()670     private void initStdClasses() {
671         initStdClass("template", "Template");
672         initStdClass("stylesheet", "Stylesheet");
673         initStdClass("transform", "Stylesheet");
674         initStdClass("text", "Text");
675         initStdClass("if", "If");
676         initStdClass("choose", "Choose");
677         initStdClass("when", "When");
678         initStdClass("otherwise", "Otherwise");
679         initStdClass("for-each", "ForEach");
680         initStdClass("message", "Message");
681         initStdClass("number", "Number");
682         initStdClass("comment", "Comment");
683         initStdClass("copy", "Copy");
684         initStdClass("copy-of", "CopyOf");
685         initStdClass("param", "Param");
686         initStdClass("with-param", "WithParam");
687         initStdClass("variable", "Variable");
688         initStdClass("output", "Output");
689         initStdClass("sort", "Sort");
690         initStdClass("key", "Key");
691         initStdClass("fallback", "Fallback");
692         initStdClass("attribute", "XslAttribute");
693         initStdClass("attribute-set", "AttributeSet");
694         initStdClass("value-of", "ValueOf");
695         initStdClass("element", "XslElement");
696         initStdClass("call-template", "CallTemplate");
697         initStdClass("apply-templates", "ApplyTemplates");
698         initStdClass("apply-imports", "ApplyImports");
699         initStdClass("decimal-format", "DecimalFormatting");
700         initStdClass("import", "Import");
701         initStdClass("include", "Include");
702         initStdClass("strip-space", "Whitespace");
703         initStdClass("preserve-space", "Whitespace");
704         initStdClass("processing-instruction", "ProcessingInstruction");
705         initStdClass("namespace-alias", "NamespaceAlias");
706     }
707 
initStdClass(String elementName, String className)708     private void initStdClass(String elementName, String className) {
709         _instructionClasses.put(getQName(XSLT_URI, XSL, elementName).getStringRep(),
710                                 COMPILER_PACKAGE + '.' + className);
711     }
712 
elementSupported(String namespace, String localName)713     public boolean elementSupported(String namespace, String localName) {
714         return(_instructionClasses.get(getQName(namespace, XSL, localName).getStringRep()) != null);
715     }
716 
functionSupported(String fname)717     public boolean functionSupported(String fname) {
718         return(_symbolTable.lookupPrimop(fname) != null);
719     }
720 
initExtClasses()721     private void initExtClasses() {
722         initExtClass("output", "TransletOutput");
723         initExtClass(REDIRECT_URI, "write", "TransletOutput");
724     }
725 
initExtClass(String elementName, String className)726     private void initExtClass(String elementName, String className) {
727         _instructionClasses.put(getQName(TRANSLET_URI, TRANSLET, elementName).getStringRep(),
728                                 COMPILER_PACKAGE + '.' + className);
729     }
730 
initExtClass(String namespace, String elementName, String className)731     private void initExtClass(String namespace, String elementName, String className) {
732         _instructionClasses.put(getQName(namespace, TRANSLET, elementName).getStringRep(),
733                                 COMPILER_PACKAGE + '.' + className);
734     }
735 
736     /**
737      * Add primops and base functions to the symbol table.
738      */
739     @SuppressWarnings("unused")
initSymbolTable()740     private void initSymbolTable() {
741         MethodType I_V  = new MethodType(Type.Int, Type.Void);
742         MethodType I_R  = new MethodType(Type.Int, Type.Real);
743         MethodType I_S  = new MethodType(Type.Int, Type.String);
744         MethodType I_D  = new MethodType(Type.Int, Type.NodeSet);
745         MethodType R_I  = new MethodType(Type.Real, Type.Int);
746         MethodType R_V  = new MethodType(Type.Real, Type.Void);
747         MethodType R_R  = new MethodType(Type.Real, Type.Real);
748         MethodType R_D  = new MethodType(Type.Real, Type.NodeSet);
749         MethodType R_O  = new MethodType(Type.Real, Type.Reference);
750         MethodType I_I  = new MethodType(Type.Int, Type.Int);
751         MethodType D_O  = new MethodType(Type.NodeSet, Type.Reference);
752         MethodType D_V  = new MethodType(Type.NodeSet, Type.Void);
753         MethodType D_S  = new MethodType(Type.NodeSet, Type.String);
754         MethodType D_D  = new MethodType(Type.NodeSet, Type.NodeSet);
755         MethodType A_V  = new MethodType(Type.Node, Type.Void);
756         MethodType S_V  = new MethodType(Type.String, Type.Void);
757         MethodType S_S  = new MethodType(Type.String, Type.String);
758         MethodType S_A  = new MethodType(Type.String, Type.Node);
759         MethodType S_D  = new MethodType(Type.String, Type.NodeSet);
760         MethodType S_O  = new MethodType(Type.String, Type.Reference);
761         MethodType B_O  = new MethodType(Type.Boolean, Type.Reference);
762         MethodType B_V  = new MethodType(Type.Boolean, Type.Void);
763         MethodType B_B  = new MethodType(Type.Boolean, Type.Boolean);
764         MethodType B_S  = new MethodType(Type.Boolean, Type.String);
765         MethodType D_X  = new MethodType(Type.NodeSet, Type.Object);
766         MethodType R_RR = new MethodType(Type.Real, Type.Real, Type.Real);
767         MethodType I_II = new MethodType(Type.Int, Type.Int, Type.Int);
768         MethodType B_RR = new MethodType(Type.Boolean, Type.Real, Type.Real);
769         MethodType B_II = new MethodType(Type.Boolean, Type.Int, Type.Int);
770         MethodType S_SS = new MethodType(Type.String, Type.String, Type.String);
771         MethodType S_DS = new MethodType(Type.String, Type.Real, Type.String);
772         MethodType S_SR = new MethodType(Type.String, Type.String, Type.Real);
773         MethodType O_SO = new MethodType(Type.Reference, Type.String, Type.Reference);
774 
775         MethodType D_SS =
776             new MethodType(Type.NodeSet, Type.String, Type.String);
777         MethodType D_SD =
778             new MethodType(Type.NodeSet, Type.String, Type.NodeSet);
779         MethodType B_BB =
780             new MethodType(Type.Boolean, Type.Boolean, Type.Boolean);
781         MethodType B_SS =
782             new MethodType(Type.Boolean, Type.String, Type.String);
783         MethodType S_SD =
784             new MethodType(Type.String, Type.String, Type.NodeSet);
785         MethodType S_DSS =
786             new MethodType(Type.String, Type.Real, Type.String, Type.String);
787         MethodType S_SRR =
788             new MethodType(Type.String, Type.String, Type.Real, Type.Real);
789         MethodType S_SSS =
790             new MethodType(Type.String, Type.String, Type.String, Type.String);
791 
792         /*
793          * Standard functions: implemented but not in this table concat().
794          * When adding a new function make sure to uncomment
795          * the corresponding line in <tt>FunctionAvailableCall</tt>.
796          */
797 
798         // The following functions are inlined
799 
800         _symbolTable.addPrimop("current", A_V);
801         _symbolTable.addPrimop("last", I_V);
802         _symbolTable.addPrimop("position", I_V);
803         _symbolTable.addPrimop("true", B_V);
804         _symbolTable.addPrimop("false", B_V);
805         _symbolTable.addPrimop("not", B_B);
806         _symbolTable.addPrimop("name", S_V);
807         _symbolTable.addPrimop("name", S_A);
808         _symbolTable.addPrimop("generate-id", S_V);
809         _symbolTable.addPrimop("generate-id", S_A);
810         _symbolTable.addPrimop("ceiling", R_R);
811         _symbolTable.addPrimop("floor", R_R);
812         _symbolTable.addPrimop("round", R_R);
813         _symbolTable.addPrimop("contains", B_SS);
814         _symbolTable.addPrimop("number", R_O);
815         _symbolTable.addPrimop("number", R_V);
816         _symbolTable.addPrimop("boolean", B_O);
817         _symbolTable.addPrimop("string", S_O);
818         _symbolTable.addPrimop("string", S_V);
819         _symbolTable.addPrimop("translate", S_SSS);
820         _symbolTable.addPrimop("string-length", I_V);
821         _symbolTable.addPrimop("string-length", I_S);
822         _symbolTable.addPrimop("starts-with", B_SS);
823         _symbolTable.addPrimop("format-number", S_DS);
824         _symbolTable.addPrimop("format-number", S_DSS);
825         _symbolTable.addPrimop("unparsed-entity-uri", S_S);
826         _symbolTable.addPrimop("key", D_SS);
827         _symbolTable.addPrimop("key", D_SD);
828         _symbolTable.addPrimop("id", D_S);
829         _symbolTable.addPrimop("id", D_D);
830         _symbolTable.addPrimop("namespace-uri", S_V);
831         _symbolTable.addPrimop("function-available", B_S);
832         _symbolTable.addPrimop("element-available", B_S);
833         _symbolTable.addPrimop("document", D_S);
834         _symbolTable.addPrimop("document", D_V);
835 
836         // The following functions are implemented in the basis library
837         _symbolTable.addPrimop("count", I_D);
838         _symbolTable.addPrimop("sum", R_D);
839         _symbolTable.addPrimop("local-name", S_V);
840         _symbolTable.addPrimop("local-name", S_D);
841         _symbolTable.addPrimop("namespace-uri", S_V);
842         _symbolTable.addPrimop("namespace-uri", S_D);
843         _symbolTable.addPrimop("substring", S_SR);
844         _symbolTable.addPrimop("substring", S_SRR);
845         _symbolTable.addPrimop("substring-after", S_SS);
846         _symbolTable.addPrimop("substring-before", S_SS);
847         _symbolTable.addPrimop("normalize-space", S_V);
848         _symbolTable.addPrimop("normalize-space", S_S);
849         _symbolTable.addPrimop("system-property", S_S);
850 
851         // Extensions
852         _symbolTable.addPrimop("nodeset", D_O);
853         _symbolTable.addPrimop("objectType", S_O);
854         _symbolTable.addPrimop("cast", O_SO);
855 
856         // Operators +, -, *, /, % defined on real types.
857         _symbolTable.addPrimop("+", R_RR);
858         _symbolTable.addPrimop("-", R_RR);
859         _symbolTable.addPrimop("*", R_RR);
860         _symbolTable.addPrimop("/", R_RR);
861         _symbolTable.addPrimop("%", R_RR);
862 
863         // Operators +, -, * defined on integer types.
864         // Operators / and % are not  defined on integers (may cause exception)
865         _symbolTable.addPrimop("+", I_II);
866         _symbolTable.addPrimop("-", I_II);
867         _symbolTable.addPrimop("*", I_II);
868 
869          // Operators <, <= >, >= defined on real types.
870         _symbolTable.addPrimop("<",  B_RR);
871         _symbolTable.addPrimop("<=", B_RR);
872         _symbolTable.addPrimop(">",  B_RR);
873         _symbolTable.addPrimop(">=", B_RR);
874 
875         // Operators <, <= >, >= defined on int types.
876         _symbolTable.addPrimop("<",  B_II);
877         _symbolTable.addPrimop("<=", B_II);
878         _symbolTable.addPrimop(">",  B_II);
879         _symbolTable.addPrimop(">=", B_II);
880 
881         // Operators <, <= >, >= defined on boolean types.
882         _symbolTable.addPrimop("<",  B_BB);
883         _symbolTable.addPrimop("<=", B_BB);
884         _symbolTable.addPrimop(">",  B_BB);
885         _symbolTable.addPrimop(">=", B_BB);
886 
887         // Operators 'and' and 'or'.
888         _symbolTable.addPrimop("or", B_BB);
889         _symbolTable.addPrimop("and", B_BB);
890 
891         // Unary minus.
892         _symbolTable.addPrimop("u-", R_R);
893         _symbolTable.addPrimop("u-", I_I);
894     }
895 
getSymbolTable()896     public SymbolTable getSymbolTable() {
897         return _symbolTable;
898     }
899 
getTemplate()900     public Template getTemplate() {
901         return _template;
902     }
903 
setTemplate(Template template)904     public void setTemplate(Template template) {
905         _template = template;
906     }
907 
908     private int _templateIndex = 0;
909 
getTemplateIndex()910     public int getTemplateIndex() {
911         return(_templateIndex++);
912     }
913 
914     /**
915      * Creates a new node in the abstract syntax tree. This node can be
916      *  o) a supported XSLT 1.0 element
917      *  o) an unsupported XSLT element (post 1.0)
918      *  o) a supported XSLT extension
919      *  o) an unsupported XSLT extension
920      *  o) a literal result element (not an XSLT element and not an extension)
921      * Unsupported elements do not directly generate an error. We have to wait
922      * until we have received all child elements of an unsupported element to
923      * see if any <xsl:fallback> elements exist.
924      */
925 
926     private boolean versionIsOne = true;
927 
makeInstance(String uri, String prefix, String local, Attributes attributes)928     public SyntaxTreeNode makeInstance(String uri, String prefix,
929         String local, Attributes attributes)
930     {
931         SyntaxTreeNode node = null;
932         QName qname = getQName(uri, prefix, local);
933         String className = _instructionClasses.get(qname.getStringRep());
934 
935         if (className != null) {
936             try {
937                 final Class<?> clazz = ObjectFactory.findProviderClass(className, true);
938                 node = (SyntaxTreeNode)clazz.newInstance();
939                 node.setQName(qname);
940                 node.setParser(this);
941                 if (_locator != null) {
942                     node.setLineNumber(getLineNumber());
943                 }
944                 if (node instanceof Stylesheet) {
945                     _xsltc.setStylesheet((Stylesheet)node);
946                 }
947                 checkForSuperfluousAttributes(node, attributes);
948             }
949             catch (ClassNotFoundException e) {
950                 ErrorMsg err = new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, node);
951                 reportError(ERROR, err);
952             }
953             catch (Exception e) {
954                 ErrorMsg err = new ErrorMsg(ErrorMsg.INTERNAL_ERR,
955                                             e.getMessage(), node);
956                 reportError(FATAL, err);
957             }
958         }
959         else {
960             if (uri != null) {
961                 // Check if the element belongs in our namespace
962                 if (uri.equals(XSLT_URI)) {
963                     node = new UnsupportedElement(uri, prefix, local, false);
964                     UnsupportedElement element = (UnsupportedElement)node;
965                     ErrorMsg msg = new ErrorMsg(ErrorMsg.UNSUPPORTED_XSL_ERR,
966                                                 getLineNumber(),local);
967                     element.setErrorMessage(msg);
968                     if (versionIsOne) {
969                         reportError(UNSUPPORTED,msg);
970                     }
971                 }
972                 // Check if this is an XSLTC extension element
973                 else if (uri.equals(TRANSLET_URI)) {
974                     node = new UnsupportedElement(uri, prefix, local, true);
975                     UnsupportedElement element = (UnsupportedElement)node;
976                     ErrorMsg msg = new ErrorMsg(ErrorMsg.UNSUPPORTED_EXT_ERR,
977                                                 getLineNumber(),local);
978                     element.setErrorMessage(msg);
979                 }
980                 // Check if this is an extension of some other XSLT processor
981                 else {
982                     Stylesheet sheet = _xsltc.getStylesheet();
983                     if ((sheet != null) && (sheet.isExtension(uri))) {
984                         if (sheet != _parentStack.peek()) {
985                             node = new UnsupportedElement(uri, prefix, local, true);
986                             UnsupportedElement elem = (UnsupportedElement)node;
987                             ErrorMsg msg =
988                                 new ErrorMsg(ErrorMsg.UNSUPPORTED_EXT_ERR,
989                                              getLineNumber(),
990                                              prefix+":"+local);
991                             elem.setErrorMessage(msg);
992                         }
993                     }
994                 }
995             }
996             if (node == null) {
997                 node = new LiteralElement();
998                 node.setLineNumber(getLineNumber());
999             }
1000         }
1001         if ((node != null) && (node instanceof LiteralElement)) {
1002             ((LiteralElement)node).setQName(qname);
1003         }
1004         return(node);
1005     }
1006 
1007     /**
1008      * checks the list of attributes against a list of allowed attributes
1009      * for a particular element node.
1010      */
checkForSuperfluousAttributes(SyntaxTreeNode node, Attributes attrs)1011     private void checkForSuperfluousAttributes(SyntaxTreeNode node,
1012         Attributes attrs)
1013     {
1014         QName qname = node.getQName();
1015         boolean isStylesheet = (node instanceof Stylesheet);
1016         String[] legal = _instructionAttrs.get(qname.getStringRep());
1017         if (versionIsOne && legal != null) {
1018             int j;
1019             final int n = attrs.getLength();
1020 
1021             for (int i = 0; i < n; i++) {
1022                 final String attrQName = attrs.getQName(i);
1023 
1024                 if (isStylesheet && attrQName.equals("version")) {
1025                     versionIsOne = attrs.getValue(i).equals("1.0");
1026                 }
1027 
1028                 // Ignore if special or if it has a prefix
1029                 if (attrQName.startsWith("xml") ||
1030                     attrQName.indexOf(':') > 0) continue;
1031 
1032                 for (j = 0; j < legal.length; j++) {
1033                     if (attrQName.equalsIgnoreCase(legal[j])) {
1034                         break;
1035                     }
1036                 }
1037                 if (j == legal.length) {
1038                     final ErrorMsg err =
1039                         new ErrorMsg(ErrorMsg.ILLEGAL_ATTRIBUTE_ERR,
1040                                 attrQName, node);
1041                     // Workaround for the TCK failure ErrorListener.errorTests.error001..
1042                     err.setWarningError(true);
1043                     reportError(WARNING, err);
1044                 }
1045             }
1046         }
1047     }
1048 
1049 
1050     /**
1051      * Parse an XPath expression:
1052      *  @param parent - XSL element where the expression occured
1053      *  @param exp    - textual representation of the expression
1054      */
parseExpression(SyntaxTreeNode parent, String exp)1055     public Expression parseExpression(SyntaxTreeNode parent, String exp) {
1056         return (Expression)parseTopLevel(parent, "<EXPRESSION>"+exp, null);
1057     }
1058 
1059     /**
1060      * Parse an XPath expression:
1061      *  @param parent - XSL element where the expression occured
1062      *  @param attr   - name of this element's attribute to get expression from
1063      *  @param def    - default expression (if the attribute was not found)
1064      */
parseExpression(SyntaxTreeNode parent, String attr, String def)1065     public Expression parseExpression(SyntaxTreeNode parent,
1066                                       String attr, String def) {
1067         // Get the textual representation of the expression (if any)
1068         String exp = parent.getAttribute(attr);
1069         // Use the default expression if none was found
1070         if ((exp.length() == 0) && (def != null)) exp = def;
1071         // Invoke the XPath parser
1072         return (Expression)parseTopLevel(parent, "<EXPRESSION>"+exp, exp);
1073     }
1074 
1075     /**
1076      * Parse an XPath pattern:
1077      *  @param parent  - XSL element where the pattern occured
1078      *  @param pattern - textual representation of the pattern
1079      */
parsePattern(SyntaxTreeNode parent, String pattern)1080     public Pattern parsePattern(SyntaxTreeNode parent, String pattern) {
1081         return (Pattern)parseTopLevel(parent, "<PATTERN>"+pattern, pattern);
1082     }
1083 
1084     /**
1085      * Parse an XPath pattern:
1086      *  @param parent - XSL element where the pattern occured
1087      *  @param attr   - name of this element's attribute to get pattern from
1088      *  @param def    - default pattern (if the attribute was not found)
1089      */
parsePattern(SyntaxTreeNode parent, String attr, String def)1090     public Pattern parsePattern(SyntaxTreeNode parent,
1091                                 String attr, String def) {
1092         // Get the textual representation of the pattern (if any)
1093         String pattern = parent.getAttribute(attr);
1094         // Use the default pattern if none was found
1095         if ((pattern.length() == 0) && (def != null)) pattern = def;
1096         // Invoke the XPath parser
1097         return (Pattern)parseTopLevel(parent, "<PATTERN>"+pattern, pattern);
1098     }
1099 
1100     /**
1101      * Parse an XPath expression or pattern using the generated XPathParser
1102      * The method will return a Dummy node if the XPath parser fails.
1103      */
parseTopLevel(SyntaxTreeNode parent, String text, String expression)1104     private SyntaxTreeNode parseTopLevel(SyntaxTreeNode parent, String text,
1105                                          String expression) {
1106         int line = getLineNumber();
1107 
1108         try {
1109             _xpathParser.setScanner(new XPathLexer(new StringReader(text)));
1110             Symbol result = _xpathParser.parse(expression, line);
1111             if (result != null) {
1112                 final SyntaxTreeNode node = (SyntaxTreeNode)result.value;
1113                 if (node != null) {
1114                     node.setParser(this);
1115                     node.setParent(parent);
1116                     node.setLineNumber(line);
1117                     return node;
1118                 }
1119             }
1120             reportError(ERROR, new ErrorMsg(ErrorMsg.XPATH_PARSER_ERR,
1121                                             expression, parent));
1122         }
1123         catch (Exception e) {
1124             if (_xsltc.debug()) e.printStackTrace();
1125             reportError(ERROR, new ErrorMsg(ErrorMsg.XPATH_PARSER_ERR,
1126                                             expression, parent));
1127         }
1128 
1129         // Return a dummy pattern (which is an expression)
1130         SyntaxTreeNode.Dummy.setParser(this);
1131         return SyntaxTreeNode.Dummy;
1132     }
1133 
1134     /************************ ERROR HANDLING SECTION ************************/
1135 
1136     /**
1137      * Returns true if there were any errors during compilation
1138      */
errorsFound()1139     public boolean errorsFound() {
1140         return _errors.size() > 0;
1141     }
1142 
1143     /**
1144      * Prints all compile-time errors
1145      */
printErrors()1146     public void printErrors() {
1147         final int size = _errors.size();
1148         if (size > 0) {
1149             System.err.println(new ErrorMsg(ErrorMsg.COMPILER_ERROR_KEY));
1150             for (int i = 0; i < size; i++) {
1151                 System.err.println("  " + _errors.get(i));
1152             }
1153         }
1154     }
1155 
1156     /**
1157      * Prints all compile-time warnings
1158      */
printWarnings()1159     public void printWarnings() {
1160         final int size = _warnings.size();
1161         if (size > 0) {
1162             System.err.println(new ErrorMsg(ErrorMsg.COMPILER_WARNING_KEY));
1163             for (int i = 0; i < size; i++) {
1164                 System.err.println("  " + _warnings.get(i));
1165             }
1166         }
1167     }
1168 
1169     /**
1170      * Common error/warning message handler
1171      */
reportError(final int category, final ErrorMsg error)1172     public void reportError(final int category, final ErrorMsg error) {
1173         switch (category) {
1174         case Constants.INTERNAL:
1175             // Unexpected internal errors, such as null-ptr exceptions, etc.
1176             // Immediately terminates compilation, no translet produced
1177             _errors.add(error);
1178             break;
1179         case Constants.UNSUPPORTED:
1180             // XSLT elements that are not implemented and unsupported ext.
1181             // Immediately terminates compilation, no translet produced
1182             _errors.add(error);
1183             break;
1184         case Constants.FATAL:
1185             // Fatal error in the stylesheet input (parsing or content)
1186             // Immediately terminates compilation, no translet produced
1187             _errors.add(error);
1188             break;
1189         case Constants.ERROR:
1190             // Other error in the stylesheet input (parsing or content)
1191             // Does not terminate compilation, no translet produced
1192             _errors.add(error);
1193             break;
1194         case Constants.WARNING:
1195             // Other error in the stylesheet input (content errors only)
1196             // Does not terminate compilation, a translet is produced
1197             _warnings.add(error);
1198             break;
1199         }
1200     }
1201 
getErrors()1202     public ArrayList<ErrorMsg> getErrors() {
1203         return _errors;
1204     }
1205 
getWarnings()1206     public ArrayList<ErrorMsg> getWarnings() {
1207         return _warnings;
1208     }
1209 
1210     /************************ SAX2 ContentHandler INTERFACE *****************/
1211 
1212     private Stack<SyntaxTreeNode> _parentStack = null;
1213     private Map<String, String> _prefixMapping = null;
1214 
1215     /**
1216      * SAX2: Receive notification of the beginning of a document.
1217      */
startDocument()1218     public void startDocument() {
1219         _root = null;
1220         _target = null;
1221         _prefixMapping = null;
1222         _parentStack = new Stack<>();
1223     }
1224 
1225     /**
1226      * SAX2: Receive notification of the end of a document.
1227      */
endDocument()1228     public void endDocument() { }
1229 
1230 
1231     /**
1232      * SAX2: Begin the scope of a prefix-URI Namespace mapping.
1233      *       This has to be passed on to the symbol table!
1234      */
startPrefixMapping(String prefix, String uri)1235     public void startPrefixMapping(String prefix, String uri) {
1236         if (_prefixMapping == null) {
1237             _prefixMapping = new HashMap<>();
1238         }
1239         _prefixMapping.put(prefix, uri);
1240     }
1241 
1242     /**
1243      * SAX2: End the scope of a prefix-URI Namespace mapping.
1244      *       This has to be passed on to the symbol table!
1245      */
endPrefixMapping(String prefix)1246     public void endPrefixMapping(String prefix) { }
1247 
1248     /**
1249      * SAX2: Receive notification of the beginning of an element.
1250      *       The parser may re-use the attribute list that we're passed so
1251      *       we clone the attributes in our own Attributes implementation
1252      */
startElement(String uri, String localname, String qname, Attributes attributes)1253     public void startElement(String uri, String localname,
1254                              String qname, Attributes attributes)
1255         throws SAXException {
1256         final int col = qname.lastIndexOf(':');
1257         final String prefix = (col == -1) ? null : qname.substring(0, col);
1258 
1259         SyntaxTreeNode element = makeInstance(uri, prefix,
1260                                         localname, attributes);
1261         if (element == null) {
1262             ErrorMsg err = new ErrorMsg(ErrorMsg.ELEMENT_PARSE_ERR,
1263                                         prefix+':'+localname);
1264             throw new SAXException(err.toString());
1265         }
1266 
1267         // If this is the root element of the XML document we need to make sure
1268         // that it contains a definition of the XSL namespace URI
1269         if (_root == null) {
1270             if ((_prefixMapping == null) ||
1271                 (_prefixMapping.containsValue(Constants.XSLT_URI) == false))
1272                 _rootNamespaceDef = false;
1273             else
1274                 _rootNamespaceDef = true;
1275             _root = element;
1276         }
1277         else {
1278             SyntaxTreeNode parent = _parentStack.peek();
1279             parent.addElement(element);
1280             element.setParent(parent);
1281         }
1282         element.setAttributes(new AttributesImpl(attributes));
1283         element.setPrefixMapping(_prefixMapping);
1284 
1285         if (element instanceof Stylesheet) {
1286             // Extension elements and excluded elements have to be
1287             // handled at this point in order to correctly generate
1288             // Fallback elements from <xsl:fallback>s.
1289             getSymbolTable().setCurrentNode(element);
1290             ((Stylesheet)element).declareExtensionPrefixes(this);
1291         }
1292 
1293         _prefixMapping = null;
1294         _parentStack.push(element);
1295     }
1296 
1297     /**
1298      * SAX2: Receive notification of the end of an element.
1299      */
endElement(String uri, String localname, String qname)1300     public void endElement(String uri, String localname, String qname) {
1301         _parentStack.pop();
1302     }
1303 
1304     /**
1305      * SAX2: Receive notification of character data.
1306      */
characters(char[] ch, int start, int length)1307     public void characters(char[] ch, int start, int length) {
1308         String string = new String(ch, start, length);
1309         SyntaxTreeNode parent = _parentStack.peek();
1310 
1311         if (string.length() == 0) return;
1312 
1313         // If this text occurs within an <xsl:text> element we append it
1314         // as-is to the existing text element
1315         if (parent instanceof Text) {
1316             ((Text)parent).setText(string);
1317             return;
1318         }
1319 
1320         // Ignore text nodes that occur directly under <xsl:stylesheet>
1321         if (parent instanceof Stylesheet) return;
1322 
1323         SyntaxTreeNode bro = parent.lastChild();
1324         if ((bro != null) && (bro instanceof Text)) {
1325             Text text = (Text)bro;
1326             if (!text.isTextElement()) {
1327                 if ((length > 1) || ( ((int)ch[0]) < 0x100)) {
1328                     text.setText(string);
1329                     return;
1330                 }
1331             }
1332         }
1333 
1334         // Add it as a regular text node otherwise
1335         parent.addElement(new Text(string));
1336     }
1337 
getTokenValue(String token)1338     private String getTokenValue(String token) {
1339         final int start = token.indexOf('"');
1340         final int stop = token.lastIndexOf('"');
1341         return token.substring(start+1, stop);
1342     }
1343 
1344     /**
1345      * SAX2: Receive notification of a processing instruction.
1346      *       These require special handling for stylesheet PIs.
1347      */
processingInstruction(String name, String value)1348     public void processingInstruction(String name, String value) {
1349         // We only handle the <?xml-stylesheet ...?> PI
1350         if ((_target == null) && (name.equals("xml-stylesheet"))) {
1351 
1352             String href = null;    // URI of stylesheet found
1353             String media = null;   // Media of stylesheet found
1354             String title = null;   // Title of stylesheet found
1355             String charset = null; // Charset of stylesheet found
1356 
1357             // Get the attributes from the processing instruction
1358             StringTokenizer tokens = new StringTokenizer(value);
1359             while (tokens.hasMoreElements()) {
1360                 String token = (String)tokens.nextElement();
1361                 if (token.startsWith("href"))
1362                     href = getTokenValue(token);
1363                 else if (token.startsWith("media"))
1364                     media = getTokenValue(token);
1365                 else if (token.startsWith("title"))
1366                     title = getTokenValue(token);
1367                 else if (token.startsWith("charset"))
1368                     charset = getTokenValue(token);
1369             }
1370 
1371             // Set the target to this PI's href if the parameters are
1372             // null or match the corresponding attributes of this PI.
1373             if ( ((_PImedia == null) || (_PImedia.equals(media))) &&
1374                  ((_PItitle == null) || (_PImedia.equals(title))) &&
1375                  ((_PIcharset == null) || (_PImedia.equals(charset))) ) {
1376                 _target = href;
1377             }
1378         }
1379     }
1380 
1381     /**
1382      * IGNORED - all ignorable whitespace is ignored
1383      */
ignorableWhitespace(char[] ch, int start, int length)1384     public void ignorableWhitespace(char[] ch, int start, int length) { }
1385 
1386     /**
1387      * IGNORED - we do not have to do anything with skipped entities
1388      */
skippedEntity(String name)1389     public void skippedEntity(String name) { }
1390 
1391     /**
1392      * Store the document locator to later retrieve line numbers of all
1393      * elements from the stylesheet
1394      */
setDocumentLocator(Locator locator)1395     public void setDocumentLocator(Locator locator) {
1396         _locator = locator;
1397     }
1398 
1399     /**
1400      * Get the line number, or zero
1401      * if there is no _locator.
1402      */
getLineNumber()1403     private int getLineNumber() {
1404         int line = 0;
1405         if (_locator != null)
1406                 line = _locator.getLineNumber();
1407         return line;
1408     }
1409 
1410 }
1411