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