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