Parser.java revision 963:0ea62da60f01
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/*
21 * $Id: Parser.java,v 1.2.4.1 2005/09/13 12:14:32 pvedula Exp $
22 */
23
24package com.sun.org.apache.xalan.internal.xsltc.compiler;
25
26import com.sun.java_cup.internal.runtime.Symbol;
27import com.sun.org.apache.xalan.internal.XalanConstants;
28import com.sun.org.apache.xalan.internal.utils.FactoryImpl;
29import com.sun.org.apache.xalan.internal.utils.ObjectFactory;
30import com.sun.org.apache.xalan.internal.utils.SecuritySupport;
31import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager;
32import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
33import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodType;
34import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
35import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
36import com.sun.org.apache.xml.internal.serializer.utils.SystemIDResolver;
37import java.io.File;
38import java.io.IOException;
39import java.io.StringReader;
40import java.util.ArrayList;
41import java.util.HashMap;
42import java.util.Iterator;
43import java.util.List;
44import java.util.Map;
45import java.util.Properties;
46import java.util.Stack;
47import java.util.StringTokenizer;
48import javax.xml.XMLConstants;
49import javax.xml.catalog.CatalogFeatures;
50import javax.xml.parsers.ParserConfigurationException;
51import javax.xml.parsers.SAXParser;
52import javax.xml.parsers.SAXParserFactory;
53import jdk.xml.internal.JdkXmlFeatures;
54import jdk.xml.internal.JdkXmlUtils;
55import org.xml.sax.Attributes;
56import org.xml.sax.ContentHandler;
57import org.xml.sax.InputSource;
58import org.xml.sax.Locator;
59import org.xml.sax.SAXException;
60import org.xml.sax.SAXNotRecognizedException;
61import org.xml.sax.SAXNotSupportedException;
62import org.xml.sax.SAXParseException;
63import org.xml.sax.XMLReader;
64import org.xml.sax.helpers.AttributesImpl;
65
66/**
67 * @author Jacek Ambroziak
68 * @author Santiago Pericas-Geertsen
69 * @author G. Todd Miller
70 * @author Morten Jorgensen
71 * @author Erwin Bolwidt <ejb@klomp.org>
72 */
73public class Parser implements Constants, ContentHandler {
74
75    private static final String XSL = "xsl";            // standard prefix
76    private static final String TRANSLET = "translet"; // extension prefix
77
78    private Locator _locator = null;
79
80    private XSLTC _xsltc;             // Reference to the compiler object.
81    private XPathParser _xpathParser; // Reference to the XPath parser.
82    private ArrayList<ErrorMsg> _errors;           // Contains all compilation errors
83    private ArrayList<ErrorMsg> _warnings;         // Contains all compilation errors
84
85    private Map<String, String>   _instructionClasses; // Maps instructions to classes
86    private Map<String, String[]> _instructionAttrs;  // reqd and opt attrs
87    private Map<String, QName>   _qNames;
88    private Map<String, Map>     _namespaces;
89    private QName       _useAttributeSets;
90    private QName       _excludeResultPrefixes;
91    private QName       _extensionElementPrefixes;
92    private Map<String, Object>   _variableScope;
93    private Stylesheet  _currentStylesheet;
94    private SymbolTable _symbolTable; // Maps QNames to syntax-tree nodes
95    private Output      _output;
96    private Template    _template;    // Reference to the template being parsed.
97
98    private boolean     _rootNamespaceDef; // Used for validity check
99
100    private SyntaxTreeNode _root;
101
102    private String _target;
103
104    private int _currentImportPrecedence;
105
106    private boolean _useServicesMechanism = true;
107
108    public Parser(XSLTC xsltc, boolean useServicesMechanism) {
109        _xsltc = xsltc;
110        _useServicesMechanism = useServicesMechanism;
111    }
112
113    public void init() {
114        _qNames              = new HashMap<>(512);
115        _namespaces          = new HashMap<>();
116        _instructionClasses  = new HashMap<>();
117        _instructionAttrs    = new HashMap<>();
118        _variableScope       = new HashMap<>();
119        _template            = null;
120        _errors              = new ArrayList<>();
121        _warnings            = new ArrayList<>();
122        _symbolTable         = new SymbolTable();
123        _xpathParser         = new XPathParser(this);
124        _currentStylesheet   = null;
125        _output              = null;
126        _root                = null;
127        _rootNamespaceDef    = false;
128        _currentImportPrecedence = 1;
129
130        initStdClasses();
131        initInstructionAttrs();
132        initExtClasses();
133        initSymbolTable();
134
135        _useAttributeSets =
136            getQName(XSLT_URI, XSL, "use-attribute-sets");
137        _excludeResultPrefixes =
138            getQName(XSLT_URI, XSL, "exclude-result-prefixes");
139        _extensionElementPrefixes =
140            getQName(XSLT_URI, XSL, "extension-element-prefixes");
141    }
142
143    public void setOutput(Output output) {
144        if (_output != null) {
145            if (_output.getImportPrecedence() <= output.getImportPrecedence()) {
146                String cdata = _output.getCdata();
147                output.mergeOutput(_output);
148                _output.disable();
149                _output = output;
150            }
151            else {
152                output.disable();
153            }
154        }
155        else {
156            _output = output;
157        }
158    }
159
160    public Output getOutput() {
161        return _output;
162    }
163
164    public Properties getOutputProperties() {
165        return getTopLevelStylesheet().getOutputProperties();
166    }
167
168    public void addVariable(Variable var) {
169        addVariableOrParam(var);
170    }
171
172    public void addParameter(Param param) {
173        addVariableOrParam(param);
174    }
175
176    private void addVariableOrParam(VariableBase var) {
177        Object existing = _variableScope.get(var.getName().getStringRep());
178        if (existing != null) {
179            if (existing instanceof Stack) {
180                Stack stack = (Stack)existing;
181                stack.push(var);
182            }
183            else if (existing instanceof VariableBase) {
184                Stack stack = new Stack();
185                stack.push(existing);
186                stack.push(var);
187                _variableScope.put(var.getName().getStringRep(), stack);
188            }
189        }
190        else {
191            _variableScope.put(var.getName().getStringRep(), var);
192        }
193    }
194
195    public void removeVariable(QName name) {
196        Object existing = _variableScope.get(name.getStringRep());
197        if (existing instanceof Stack) {
198            Stack stack = (Stack)existing;
199            if (!stack.isEmpty()) stack.pop();
200            if (!stack.isEmpty()) return;
201        }
202        _variableScope.remove(name.getStringRep());
203    }
204
205    public VariableBase lookupVariable(QName name) {
206        Object existing = _variableScope.get(name.getStringRep());
207        if (existing instanceof VariableBase) {
208            return((VariableBase)existing);
209        }
210        else if (existing instanceof Stack) {
211            Stack stack = (Stack)existing;
212            return((VariableBase)stack.peek());
213        }
214        return(null);
215    }
216
217    public void setXSLTC(XSLTC xsltc) {
218        _xsltc = xsltc;
219    }
220
221    public XSLTC getXSLTC() {
222        return _xsltc;
223    }
224
225    public int getCurrentImportPrecedence() {
226        return _currentImportPrecedence;
227    }
228
229    public int getNextImportPrecedence() {
230        return ++_currentImportPrecedence;
231    }
232
233    public void setCurrentStylesheet(Stylesheet stylesheet) {
234        _currentStylesheet = stylesheet;
235    }
236
237    public Stylesheet getCurrentStylesheet() {
238        return _currentStylesheet;
239    }
240
241    public Stylesheet getTopLevelStylesheet() {
242        return _xsltc.getStylesheet();
243    }
244
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
267    public QName getQName(final String stringRep) {
268        return getQName(stringRep, true, false);
269    }
270
271    public QName getQNameIgnoreDefaultNs(final String stringRep) {
272        return getQName(stringRep, true, true);
273    }
274
275    public QName getQName(final String stringRep, boolean reportError) {
276        return getQName(stringRep, reportError, false);
277    }
278
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
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
344    public QName getQName(String scope, String name) {
345        return getQName(scope + name);
346    }
347
348    public QName getQName(QName scope, QName name) {
349        return getQName(scope.toString() + name.toString());
350    }
351
352    public QName getUseAttributeSets() {
353        return _useAttributeSets;
354    }
355
356    public QName getExtensionElementPrefixes() {
357        return _extensionElementPrefixes;
358    }
359
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     */
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     */
400    public void createAST(Stylesheet stylesheet) {
401        try {
402            if (stylesheet != null) {
403                stylesheet.parseContents(this);
404                final int precedence = stylesheet.getImportPrecedence();
405                final Iterator<SyntaxTreeNode> elements = stylesheet.elements();
406                while (elements.hasNext()) {
407                    Object child = elements.next();
408                    if (child instanceof Text) {
409                        final int l = getLineNumber();
410                        ErrorMsg err =
411                            new ErrorMsg(ErrorMsg.ILLEGAL_TEXT_NODE_ERR,l,null);
412                        reportError(ERROR, err);
413                    }
414                }
415                if (!errorsFound()) {
416                    stylesheet.typeCheck(_symbolTable);
417                }
418            }
419        }
420        catch (TypeCheckError e) {
421            reportError(ERROR, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e));
422        }
423    }
424
425    /**
426     * Parses a stylesheet and builds the internal abstract syntax tree
427     * @param reader A SAX2 SAXReader (parser)
428     * @param input A SAX2 InputSource can be passed to a SAX reader
429     * @return The root of the abstract syntax tree
430     */
431    public SyntaxTreeNode parse(XMLReader reader, InputSource input) {
432        try {
433            // Parse the input document and build the abstract syntax tree
434            reader.setContentHandler(this);
435            reader.parse(input);
436            // Find the start of the stylesheet within the tree
437            return (SyntaxTreeNode)getStylesheet(_root);
438        }
439        catch (IOException e) {
440            if (_xsltc.debug()) e.printStackTrace();
441            reportError(ERROR,new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e));
442        }
443        catch (SAXException e) {
444            Throwable ex = e.getException();
445            if (_xsltc.debug()) {
446                e.printStackTrace();
447                if (ex != null) ex.printStackTrace();
448            }
449            reportError(ERROR, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e));
450        }
451        catch (CompilerException e) {
452            if (_xsltc.debug()) e.printStackTrace();
453            reportError(ERROR, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e));
454        }
455        catch (Exception e) {
456            if (_xsltc.debug()) e.printStackTrace();
457            reportError(ERROR, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e));
458        }
459        return null;
460    }
461
462    /**
463     * Parses a stylesheet and builds the internal abstract syntax tree
464     * @param input A SAX2 InputSource can be passed to a SAX reader
465     * @return The root of the abstract syntax tree
466     */
467    public SyntaxTreeNode parse(InputSource input) {
468        try {
469            // Create a SAX parser and get the XMLReader object it uses
470            final SAXParserFactory factory = FactoryImpl.getSAXFactory(_useServicesMechanism);
471
472            if (_xsltc.isSecureProcessing()) {
473                try {
474                    factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
475                }
476                catch (SAXException e) {}
477            }
478
479            try {
480                factory.setFeature(Constants.NAMESPACE_FEATURE,true);
481            }
482            catch (ParserConfigurationException | SAXNotRecognizedException | SAXNotSupportedException e) {
483                factory.setNamespaceAware(true);
484            }
485
486            final SAXParser parser = factory.newSAXParser();
487            try {
488                parser.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD,
489                        _xsltc.getProperty(XMLConstants.ACCESS_EXTERNAL_DTD));
490            } catch (SAXNotRecognizedException e) {
491                ErrorMsg err = new ErrorMsg(ErrorMsg.WARNING_MSG,
492                        parser.getClass().getName() + ": " + e.getMessage());
493                reportError(WARNING, err);
494            }
495
496            boolean supportCatalog = true;
497            boolean useCatalog = _xsltc.getFeature(JdkXmlFeatures.XmlFeature.USE_CATALOG);
498            try {
499                factory.setFeature(JdkXmlUtils.USE_CATALOG,useCatalog);
500            }
501            catch (ParserConfigurationException | SAXNotRecognizedException | SAXNotSupportedException e) {
502                supportCatalog = false;
503            }
504
505            if (supportCatalog && useCatalog) {
506                try {
507                    CatalogFeatures cf = (CatalogFeatures)_xsltc.getProperty(JdkXmlFeatures.CATALOG_FEATURES);
508                    if (cf != null) {
509                        for (CatalogFeatures.Feature f : CatalogFeatures.Feature.values()) {
510                            parser.setProperty(f.getPropertyName(), cf.get(f));
511                        }
512                    }
513                } catch (SAXNotRecognizedException e) {
514                    //shall not happen for internal settings
515                }
516            }
517
518            final XMLReader reader = parser.getXMLReader();
519            String lastProperty = "";
520            try {
521                XMLSecurityManager securityManager =
522                        (XMLSecurityManager)_xsltc.getProperty(XalanConstants.SECURITY_MANAGER);
523                for (XMLSecurityManager.Limit limit : XMLSecurityManager.Limit.values()) {
524                    lastProperty = limit.apiProperty();
525                    reader.setProperty(lastProperty, securityManager.getLimitValueAsString(limit));
526                }
527                if (securityManager.printEntityCountInfo()) {
528                    lastProperty = XalanConstants.JDK_ENTITY_COUNT_INFO;
529                    parser.setProperty(XalanConstants.JDK_ENTITY_COUNT_INFO, XalanConstants.JDK_YES);
530                }
531            } catch (SAXException se) {
532                XMLSecurityManager.printWarning(reader.getClass().getName(), lastProperty, se);
533            }
534
535            return(parse(reader, input));
536        }
537        catch (ParserConfigurationException e) {
538            ErrorMsg err = new ErrorMsg(ErrorMsg.SAX_PARSER_CONFIG_ERR);
539            reportError(ERROR, err);
540        }
541        catch (SAXParseException e){
542            reportError(ERROR, new ErrorMsg(e.getMessage(),e.getLineNumber()));
543        }
544        catch (SAXException e) {
545            reportError(ERROR, new ErrorMsg(e.getMessage()));
546        }
547        return null;
548    }
549
550    public SyntaxTreeNode getDocumentRoot() {
551        return _root;
552    }
553
554    private String _PImedia = null;
555    private String _PItitle = null;
556    private String _PIcharset = null;
557
558    /**
559     * Set the parameters to use to locate the correct <?xml-stylesheet ...?>
560     * processing instruction in the case where the input document is an
561     * XML document with one or more references to a stylesheet.
562     * @param media The media attribute to be matched. May be null, in which
563     * case the prefered templates will be used (i.e. alternate = no).
564     * @param title The value of the title attribute to match. May be null.
565     * @param charset The value of the charset attribute to match. May be null.
566     */
567    protected void setPIParameters(String media, String title, String charset) {
568        _PImedia = media;
569        _PItitle = title;
570        _PIcharset = charset;
571    }
572
573    /**
574     * Extracts the DOM for the stylesheet. In the case of an embedded
575     * stylesheet, it extracts the DOM subtree corresponding to the
576     * embedded stylesheet that has an 'id' attribute whose value is the
577     * same as the value declared in the <?xml-stylesheet...?> processing
578     * instruction (P.I.). In the xml-stylesheet P.I. the value is labeled
579     * as the 'href' data of the P.I. The extracted DOM representing the
580     * stylesheet is returned as an Element object.
581     */
582    private SyntaxTreeNode getStylesheet(SyntaxTreeNode root)
583        throws CompilerException {
584
585        // Assume that this is a pure XSL stylesheet if there is not
586        // <?xml-stylesheet ....?> processing instruction
587        if (_target == null) {
588            if (!_rootNamespaceDef) {
589                ErrorMsg msg = new ErrorMsg(ErrorMsg.MISSING_XSLT_URI_ERR);
590                throw new CompilerException(msg.toString());
591            }
592            return(root);
593        }
594
595        // Find the xsl:stylesheet or xsl:transform with this reference
596        if (_target.charAt(0) == '#') {
597            SyntaxTreeNode element = findStylesheet(root, _target.substring(1));
598            if (element == null) {
599                ErrorMsg msg = new ErrorMsg(ErrorMsg.MISSING_XSLT_TARGET_ERR,
600                                            _target, root);
601                throw new CompilerException(msg.toString());
602            }
603            return(element);
604        }
605        else {
606            try {
607                String path = _target;
608                if (path.indexOf(":")==-1) {
609                    path = "file:" + path;
610                }
611                path = SystemIDResolver.getAbsoluteURI(path);
612                String accessError = SecuritySupport.checkAccess(path,
613                        (String)_xsltc.getProperty(XMLConstants.ACCESS_EXTERNAL_STYLESHEET),
614                        XalanConstants.ACCESS_EXTERNAL_ALL);
615                if (accessError != null) {
616                    ErrorMsg msg = new ErrorMsg(ErrorMsg.ACCESSING_XSLT_TARGET_ERR,
617                            SecuritySupport.sanitizePath(_target), accessError,
618                            root);
619                    throw new CompilerException(msg.toString());
620                }
621            } catch (IOException ex) {
622                throw new CompilerException(ex);
623            }
624
625            return(loadExternalStylesheet(_target));
626        }
627    }
628
629    /**
630     * Find a Stylesheet element with a specific ID attribute value.
631     * This method is used to find a Stylesheet node that is referred
632     * in a <?xml-stylesheet ... ?> processing instruction.
633     */
634    private SyntaxTreeNode findStylesheet(SyntaxTreeNode root, String href) {
635
636        if (root == null) return null;
637
638        if (root instanceof Stylesheet) {
639            String id = root.getAttribute("id");
640            if (id.equals(href)) return root;
641        }
642        List<SyntaxTreeNode> children = root.getContents();
643        if (children != null) {
644            final int count = children.size();
645            for (int i = 0; i < count; i++) {
646                SyntaxTreeNode child = children.get(i);
647                SyntaxTreeNode node = findStylesheet(child, href);
648                if (node != null) return node;
649            }
650        }
651        return null;
652    }
653
654    /**
655     * For embedded stylesheets: Load an external file with stylesheet
656     */
657    private SyntaxTreeNode loadExternalStylesheet(String location)
658        throws CompilerException {
659
660        InputSource source;
661
662        // Check if the location is URL or a local file
663        if ((new File(location)).exists())
664            source = new InputSource("file:"+location);
665        else
666            source = new InputSource(location);
667
668        SyntaxTreeNode external = (SyntaxTreeNode)parse(source);
669        return(external);
670    }
671
672    private void initAttrTable(String elementName, String[] attrs) {
673        _instructionAttrs.put(getQName(XSLT_URI, XSL, elementName).getStringRep(),
674                                attrs);
675    }
676
677    private void initInstructionAttrs() {
678        initAttrTable("template",
679            new String[] {"match", "name", "priority", "mode"});
680        initAttrTable("stylesheet",
681            new String[] {"id", "version", "extension-element-prefixes",
682                "exclude-result-prefixes"});
683        initAttrTable("transform",
684            new String[] {"id", "version", "extension-element-prefixes",
685                "exclude-result-prefixes"});
686        initAttrTable("text", new String[] {"disable-output-escaping"});
687        initAttrTable("if", new String[] {"test"});
688        initAttrTable("choose", new String[] {});
689        initAttrTable("when", new String[] {"test"});
690        initAttrTable("otherwise", new String[] {});
691        initAttrTable("for-each", new String[] {"select"});
692        initAttrTable("message", new String[] {"terminate"});
693        initAttrTable("number",
694            new String[] {"level", "count", "from", "value", "format", "lang",
695                "letter-value", "grouping-separator", "grouping-size"});
696                initAttrTable("comment", new String[] {});
697        initAttrTable("copy", new String[] {"use-attribute-sets"});
698        initAttrTable("copy-of", new String[] {"select"});
699        initAttrTable("param", new String[] {"name", "select"});
700        initAttrTable("with-param", new String[] {"name", "select"});
701        initAttrTable("variable", new String[] {"name", "select"});
702        initAttrTable("output",
703            new String[] {"method", "version", "encoding",
704                "omit-xml-declaration", "standalone", "doctype-public",
705                "doctype-system", "cdata-section-elements", "indent",
706                "media-type"});
707        initAttrTable("sort",
708           new String[] {"select", "order", "case-order", "lang", "data-type"});
709        initAttrTable("key", new String[] {"name", "match", "use"});
710        initAttrTable("fallback", new String[] {});
711        initAttrTable("attribute", new String[] {"name", "namespace"});
712        initAttrTable("attribute-set",
713            new String[] {"name", "use-attribute-sets"});
714        initAttrTable("value-of",
715            new String[] {"select", "disable-output-escaping"});
716        initAttrTable("element",
717            new String[] {"name", "namespace", "use-attribute-sets"});
718        initAttrTable("call-template", new String[] {"name"});
719        initAttrTable("apply-templates", new String[] {"select", "mode"});
720        initAttrTable("apply-imports", new String[] {});
721        initAttrTable("decimal-format",
722            new String[] {"name", "decimal-separator", "grouping-separator",
723                "infinity", "minus-sign", "NaN", "percent", "per-mille",
724                "zero-digit", "digit", "pattern-separator"});
725        initAttrTable("import", new String[] {"href"});
726        initAttrTable("include", new String[] {"href"});
727        initAttrTable("strip-space", new String[] {"elements"});
728        initAttrTable("preserve-space", new String[] {"elements"});
729        initAttrTable("processing-instruction", new String[] {"name"});
730        initAttrTable("namespace-alias",
731           new String[] {"stylesheet-prefix", "result-prefix"});
732    }
733
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    private void initSymbolTable() {
810        MethodType I_V  = new MethodType(Type.Int, Type.Void);
811        MethodType I_R  = new MethodType(Type.Int, Type.Real);
812        MethodType I_S  = new MethodType(Type.Int, Type.String);
813        MethodType I_D  = new MethodType(Type.Int, Type.NodeSet);
814        MethodType R_I  = new MethodType(Type.Real, Type.Int);
815        MethodType R_V  = new MethodType(Type.Real, Type.Void);
816        MethodType R_R  = new MethodType(Type.Real, Type.Real);
817        MethodType R_D  = new MethodType(Type.Real, Type.NodeSet);
818        MethodType R_O  = new MethodType(Type.Real, Type.Reference);
819        MethodType I_I  = new MethodType(Type.Int, Type.Int);
820        MethodType D_O  = new MethodType(Type.NodeSet, Type.Reference);
821        MethodType D_V  = new MethodType(Type.NodeSet, Type.Void);
822        MethodType D_S  = new MethodType(Type.NodeSet, Type.String);
823        MethodType D_D  = new MethodType(Type.NodeSet, Type.NodeSet);
824        MethodType A_V  = new MethodType(Type.Node, Type.Void);
825        MethodType S_V  = new MethodType(Type.String, Type.Void);
826        MethodType S_S  = new MethodType(Type.String, Type.String);
827        MethodType S_A  = new MethodType(Type.String, Type.Node);
828        MethodType S_D  = new MethodType(Type.String, Type.NodeSet);
829        MethodType S_O  = new MethodType(Type.String, Type.Reference);
830        MethodType B_O  = new MethodType(Type.Boolean, Type.Reference);
831        MethodType B_V  = new MethodType(Type.Boolean, Type.Void);
832        MethodType B_B  = new MethodType(Type.Boolean, Type.Boolean);
833        MethodType B_S  = new MethodType(Type.Boolean, Type.String);
834        MethodType D_X  = new MethodType(Type.NodeSet, Type.Object);
835        MethodType R_RR = new MethodType(Type.Real, Type.Real, Type.Real);
836        MethodType I_II = new MethodType(Type.Int, Type.Int, Type.Int);
837        MethodType B_RR = new MethodType(Type.Boolean, Type.Real, Type.Real);
838        MethodType B_II = new MethodType(Type.Boolean, Type.Int, Type.Int);
839        MethodType S_SS = new MethodType(Type.String, Type.String, Type.String);
840        MethodType S_DS = new MethodType(Type.String, Type.Real, Type.String);
841        MethodType S_SR = new MethodType(Type.String, Type.String, Type.Real);
842        MethodType O_SO = new MethodType(Type.Reference, Type.String, Type.Reference);
843
844        MethodType D_SS =
845            new MethodType(Type.NodeSet, Type.String, Type.String);
846        MethodType D_SD =
847            new MethodType(Type.NodeSet, Type.String, Type.NodeSet);
848        MethodType B_BB =
849            new MethodType(Type.Boolean, Type.Boolean, Type.Boolean);
850        MethodType B_SS =
851            new MethodType(Type.Boolean, Type.String, Type.String);
852        MethodType S_SD =
853            new MethodType(Type.String, Type.String, Type.NodeSet);
854        MethodType S_DSS =
855            new MethodType(Type.String, Type.Real, Type.String, Type.String);
856        MethodType S_SRR =
857            new MethodType(Type.String, Type.String, Type.Real, Type.Real);
858        MethodType S_SSS =
859            new MethodType(Type.String, Type.String, Type.String, Type.String);
860
861        /*
862         * Standard functions: implemented but not in this table concat().
863         * When adding a new function make sure to uncomment
864         * the corresponding line in <tt>FunctionAvailableCall</tt>.
865         */
866
867        // The following functions are inlined
868
869        _symbolTable.addPrimop("current", A_V);
870        _symbolTable.addPrimop("last", I_V);
871        _symbolTable.addPrimop("position", I_V);
872        _symbolTable.addPrimop("true", B_V);
873        _symbolTable.addPrimop("false", B_V);
874        _symbolTable.addPrimop("not", B_B);
875        _symbolTable.addPrimop("name", S_V);
876        _symbolTable.addPrimop("name", S_A);
877        _symbolTable.addPrimop("generate-id", S_V);
878        _symbolTable.addPrimop("generate-id", S_A);
879        _symbolTable.addPrimop("ceiling", R_R);
880        _symbolTable.addPrimop("floor", R_R);
881        _symbolTable.addPrimop("round", R_R);
882        _symbolTable.addPrimop("contains", B_SS);
883        _symbolTable.addPrimop("number", R_O);
884        _symbolTable.addPrimop("number", R_V);
885        _symbolTable.addPrimop("boolean", B_O);
886        _symbolTable.addPrimop("string", S_O);
887        _symbolTable.addPrimop("string", S_V);
888        _symbolTable.addPrimop("translate", S_SSS);
889        _symbolTable.addPrimop("string-length", I_V);
890        _symbolTable.addPrimop("string-length", I_S);
891        _symbolTable.addPrimop("starts-with", B_SS);
892        _symbolTable.addPrimop("format-number", S_DS);
893        _symbolTable.addPrimop("format-number", S_DSS);
894        _symbolTable.addPrimop("unparsed-entity-uri", S_S);
895        _symbolTable.addPrimop("key", D_SS);
896        _symbolTable.addPrimop("key", D_SD);
897        _symbolTable.addPrimop("id", D_S);
898        _symbolTable.addPrimop("id", D_D);
899        _symbolTable.addPrimop("namespace-uri", S_V);
900        _symbolTable.addPrimop("function-available", B_S);
901        _symbolTable.addPrimop("element-available", B_S);
902        _symbolTable.addPrimop("document", D_S);
903        _symbolTable.addPrimop("document", D_V);
904
905        // The following functions are implemented in the basis library
906        _symbolTable.addPrimop("count", I_D);
907        _symbolTable.addPrimop("sum", R_D);
908        _symbolTable.addPrimop("local-name", S_V);
909        _symbolTable.addPrimop("local-name", S_D);
910        _symbolTable.addPrimop("namespace-uri", S_V);
911        _symbolTable.addPrimop("namespace-uri", S_D);
912        _symbolTable.addPrimop("substring", S_SR);
913        _symbolTable.addPrimop("substring", S_SRR);
914        _symbolTable.addPrimop("substring-after", S_SS);
915        _symbolTable.addPrimop("substring-before", S_SS);
916        _symbolTable.addPrimop("normalize-space", S_V);
917        _symbolTable.addPrimop("normalize-space", S_S);
918        _symbolTable.addPrimop("system-property", S_S);
919
920        // Extensions
921        _symbolTable.addPrimop("nodeset", D_O);
922        _symbolTable.addPrimop("objectType", S_O);
923        _symbolTable.addPrimop("cast", O_SO);
924
925        // Operators +, -, *, /, % defined on real types.
926        _symbolTable.addPrimop("+", R_RR);
927        _symbolTable.addPrimop("-", R_RR);
928        _symbolTable.addPrimop("*", R_RR);
929        _symbolTable.addPrimop("/", R_RR);
930        _symbolTable.addPrimop("%", R_RR);
931
932        // Operators +, -, * defined on integer types.
933        // Operators / and % are not  defined on integers (may cause exception)
934        _symbolTable.addPrimop("+", I_II);
935        _symbolTable.addPrimop("-", I_II);
936        _symbolTable.addPrimop("*", I_II);
937
938         // Operators <, <= >, >= defined on real types.
939        _symbolTable.addPrimop("<",  B_RR);
940        _symbolTable.addPrimop("<=", B_RR);
941        _symbolTable.addPrimop(">",  B_RR);
942        _symbolTable.addPrimop(">=", B_RR);
943
944        // Operators <, <= >, >= defined on int types.
945        _symbolTable.addPrimop("<",  B_II);
946        _symbolTable.addPrimop("<=", B_II);
947        _symbolTable.addPrimop(">",  B_II);
948        _symbolTable.addPrimop(">=", B_II);
949
950        // Operators <, <= >, >= defined on boolean types.
951        _symbolTable.addPrimop("<",  B_BB);
952        _symbolTable.addPrimop("<=", B_BB);
953        _symbolTable.addPrimop(">",  B_BB);
954        _symbolTable.addPrimop(">=", B_BB);
955
956        // Operators 'and' and 'or'.
957        _symbolTable.addPrimop("or", B_BB);
958        _symbolTable.addPrimop("and", B_BB);
959
960        // Unary minus.
961        _symbolTable.addPrimop("u-", R_R);
962        _symbolTable.addPrimop("u-", I_I);
963    }
964
965    public SymbolTable getSymbolTable() {
966        return _symbolTable;
967    }
968
969    public Template getTemplate() {
970        return _template;
971    }
972
973    public void setTemplate(Template template) {
974        _template = template;
975    }
976
977    private int _templateIndex = 0;
978
979    public int getTemplateIndex() {
980        return(_templateIndex++);
981    }
982
983    /**
984     * Creates a new node in the abstract syntax tree. This node can be
985     *  o) a supported XSLT 1.0 element
986     *  o) an unsupported XSLT element (post 1.0)
987     *  o) a supported XSLT extension
988     *  o) an unsupported XSLT extension
989     *  o) a literal result element (not an XSLT element and not an extension)
990     * Unsupported elements do not directly generate an error. We have to wait
991     * until we have received all child elements of an unsupported element to
992     * see if any <xsl:fallback> elements exist.
993     */
994
995    private boolean versionIsOne = true;
996
997    public SyntaxTreeNode makeInstance(String uri, String prefix,
998        String local, Attributes attributes)
999    {
1000        SyntaxTreeNode node = null;
1001        QName  qname = getQName(uri, prefix, local);
1002        String className = _instructionClasses.get(qname.getStringRep());
1003
1004        if (className != null) {
1005            try {
1006                final Class clazz = ObjectFactory.findProviderClass(className, true);
1007                node = (SyntaxTreeNode)clazz.newInstance();
1008                node.setQName(qname);
1009                node.setParser(this);
1010                if (_locator != null) {
1011                    node.setLineNumber(getLineNumber());
1012                }
1013                if (node instanceof Stylesheet) {
1014                    _xsltc.setStylesheet((Stylesheet)node);
1015                }
1016                checkForSuperfluousAttributes(node, attributes);
1017            }
1018            catch (ClassNotFoundException e) {
1019                ErrorMsg err = new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, node);
1020                reportError(ERROR, err);
1021            }
1022            catch (Exception e) {
1023                ErrorMsg err = new ErrorMsg(ErrorMsg.INTERNAL_ERR,
1024                                            e.getMessage(), node);
1025                reportError(FATAL, err);
1026            }
1027        }
1028        else {
1029            if (uri != null) {
1030                // Check if the element belongs in our namespace
1031                if (uri.equals(XSLT_URI)) {
1032                    node = new UnsupportedElement(uri, prefix, local, false);
1033                    UnsupportedElement element = (UnsupportedElement)node;
1034                    ErrorMsg msg = new ErrorMsg(ErrorMsg.UNSUPPORTED_XSL_ERR,
1035                                                getLineNumber(),local);
1036                    element.setErrorMessage(msg);
1037                    if (versionIsOne) {
1038                        reportError(UNSUPPORTED,msg);
1039                    }
1040                }
1041                // Check if this is an XSLTC extension element
1042                else if (uri.equals(TRANSLET_URI)) {
1043                    node = new UnsupportedElement(uri, prefix, local, true);
1044                    UnsupportedElement element = (UnsupportedElement)node;
1045                    ErrorMsg msg = new ErrorMsg(ErrorMsg.UNSUPPORTED_EXT_ERR,
1046                                                getLineNumber(),local);
1047                    element.setErrorMessage(msg);
1048                }
1049                // Check if this is an extension of some other XSLT processor
1050                else {
1051                    Stylesheet sheet = _xsltc.getStylesheet();
1052                    if ((sheet != null) && (sheet.isExtension(uri))) {
1053                        if (sheet != (SyntaxTreeNode)_parentStack.peek()) {
1054                            node = new UnsupportedElement(uri, prefix, local, true);
1055                            UnsupportedElement elem = (UnsupportedElement)node;
1056                            ErrorMsg msg =
1057                                new ErrorMsg(ErrorMsg.UNSUPPORTED_EXT_ERR,
1058                                             getLineNumber(),
1059                                             prefix+":"+local);
1060                            elem.setErrorMessage(msg);
1061                        }
1062                    }
1063                }
1064            }
1065            if (node == null) {
1066                node = new LiteralElement();
1067                node.setLineNumber(getLineNumber());
1068            }
1069        }
1070        if ((node != null) && (node instanceof LiteralElement)) {
1071            ((LiteralElement)node).setQName(qname);
1072        }
1073        return(node);
1074    }
1075
1076    /**
1077     * checks the list of attributes against a list of allowed attributes
1078     * for a particular element node.
1079     */
1080    private void checkForSuperfluousAttributes(SyntaxTreeNode node,
1081        Attributes attrs)
1082    {
1083        QName qname = node.getQName();
1084        boolean isStylesheet = (node instanceof Stylesheet);
1085        String[] legal = _instructionAttrs.get(qname.getStringRep());
1086        if (versionIsOne && legal != null) {
1087            int j;
1088            final int n = attrs.getLength();
1089
1090            for (int i = 0; i < n; i++) {
1091                final String attrQName = attrs.getQName(i);
1092
1093                if (isStylesheet && attrQName.equals("version")) {
1094                    versionIsOne = attrs.getValue(i).equals("1.0");
1095                }
1096
1097                // Ignore if special or if it has a prefix
1098                if (attrQName.startsWith("xml") ||
1099                    attrQName.indexOf(':') > 0) continue;
1100
1101                for (j = 0; j < legal.length; j++) {
1102                    if (attrQName.equalsIgnoreCase(legal[j])) {
1103                        break;
1104                    }
1105                }
1106                if (j == legal.length) {
1107                    final ErrorMsg err =
1108                        new ErrorMsg(ErrorMsg.ILLEGAL_ATTRIBUTE_ERR,
1109                                attrQName, node);
1110                    // Workaround for the TCK failure ErrorListener.errorTests.error001..
1111                    err.setWarningError(true);
1112                    reportError(WARNING, err);
1113                }
1114            }
1115        }
1116    }
1117
1118
1119    /**
1120     * Parse an XPath expression:
1121     *  @param parent - XSL element where the expression occured
1122     *  @param exp    - textual representation of the expression
1123     */
1124    public Expression parseExpression(SyntaxTreeNode parent, String exp) {
1125        return (Expression)parseTopLevel(parent, "<EXPRESSION>"+exp, null);
1126    }
1127
1128    /**
1129     * Parse an XPath expression:
1130     *  @param parent - XSL element where the expression occured
1131     *  @param attr   - name of this element's attribute to get expression from
1132     *  @param def    - default expression (if the attribute was not found)
1133     */
1134    public Expression parseExpression(SyntaxTreeNode parent,
1135                                      String attr, String def) {
1136        // Get the textual representation of the expression (if any)
1137        String exp = parent.getAttribute(attr);
1138        // Use the default expression if none was found
1139        if ((exp.length() == 0) && (def != null)) exp = def;
1140        // Invoke the XPath parser
1141        return (Expression)parseTopLevel(parent, "<EXPRESSION>"+exp, exp);
1142    }
1143
1144    /**
1145     * Parse an XPath pattern:
1146     *  @param parent  - XSL element where the pattern occured
1147     *  @param pattern - textual representation of the pattern
1148     */
1149    public Pattern parsePattern(SyntaxTreeNode parent, String pattern) {
1150        return (Pattern)parseTopLevel(parent, "<PATTERN>"+pattern, pattern);
1151    }
1152
1153    /**
1154     * Parse an XPath pattern:
1155     *  @param parent - XSL element where the pattern occured
1156     *  @param attr   - name of this element's attribute to get pattern from
1157     *  @param def    - default pattern (if the attribute was not found)
1158     */
1159    public Pattern parsePattern(SyntaxTreeNode parent,
1160                                String attr, String def) {
1161        // Get the textual representation of the pattern (if any)
1162        String pattern = parent.getAttribute(attr);
1163        // Use the default pattern if none was found
1164        if ((pattern.length() == 0) && (def != null)) pattern = def;
1165        // Invoke the XPath parser
1166        return (Pattern)parseTopLevel(parent, "<PATTERN>"+pattern, pattern);
1167    }
1168
1169    /**
1170     * Parse an XPath expression or pattern using the generated XPathParser
1171     * The method will return a Dummy node if the XPath parser fails.
1172     */
1173    private SyntaxTreeNode parseTopLevel(SyntaxTreeNode parent, String text,
1174                                         String expression) {
1175        int line = getLineNumber();
1176
1177        try {
1178            _xpathParser.setScanner(new XPathLexer(new StringReader(text)));
1179            Symbol result = _xpathParser.parse(expression, line);
1180            if (result != null) {
1181                final SyntaxTreeNode node = (SyntaxTreeNode)result.value;
1182                if (node != null) {
1183                    node.setParser(this);
1184                    node.setParent(parent);
1185                    node.setLineNumber(line);
1186// System.out.println("e = " + text + " " + node);
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 _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 = (SyntaxTreeNode)_parentStack.peek();
1349            parent.addElement(element);
1350            element.setParent(parent);
1351        }
1352        element.setAttributes(new AttributesImpl(attributes));
1353        element.setPrefixMapping(_prefixMapping);
1354
1355        if (element instanceof Stylesheet) {
1356            // Extension elements and excluded elements have to be
1357            // handled at this point in order to correctly generate
1358            // Fallback elements from <xsl:fallback>s.
1359            getSymbolTable().setCurrentNode(element);
1360            ((Stylesheet)element).declareExtensionPrefixes(this);
1361        }
1362
1363        _prefixMapping = null;
1364        _parentStack.push(element);
1365    }
1366
1367    /**
1368     * SAX2: Receive notification of the end of an element.
1369     */
1370    public void endElement(String uri, String localname, String qname) {
1371        _parentStack.pop();
1372    }
1373
1374    /**
1375     * SAX2: Receive notification of character data.
1376     */
1377    public void characters(char[] ch, int start, int length) {
1378        String string = new String(ch, start, length);
1379        SyntaxTreeNode parent = (SyntaxTreeNode)_parentStack.peek();
1380
1381        if (string.length() == 0) return;
1382
1383        // If this text occurs within an <xsl:text> element we append it
1384        // as-is to the existing text element
1385        if (parent instanceof Text) {
1386            ((Text)parent).setText(string);
1387            return;
1388        }
1389
1390        // Ignore text nodes that occur directly under <xsl:stylesheet>
1391        if (parent instanceof Stylesheet) return;
1392
1393        SyntaxTreeNode bro = parent.lastChild();
1394        if ((bro != null) && (bro instanceof Text)) {
1395            Text text = (Text)bro;
1396            if (!text.isTextElement()) {
1397                if ((length > 1) || ( ((int)ch[0]) < 0x100)) {
1398                    text.setText(string);
1399                    return;
1400                }
1401            }
1402        }
1403
1404        // Add it as a regular text node otherwise
1405        parent.addElement(new Text(string));
1406    }
1407
1408    private String getTokenValue(String token) {
1409        final int start = token.indexOf('"');
1410        final int stop = token.lastIndexOf('"');
1411        return token.substring(start+1, stop);
1412    }
1413
1414    /**
1415     * SAX2: Receive notification of a processing instruction.
1416     *       These require special handling for stylesheet PIs.
1417     */
1418    public void processingInstruction(String name, String value) {
1419        // We only handle the <?xml-stylesheet ...?> PI
1420        if ((_target == null) && (name.equals("xml-stylesheet"))) {
1421
1422            String href = null;    // URI of stylesheet found
1423            String media = null;   // Media of stylesheet found
1424            String title = null;   // Title of stylesheet found
1425            String charset = null; // Charset of stylesheet found
1426
1427            // Get the attributes from the processing instruction
1428            StringTokenizer tokens = new StringTokenizer(value);
1429            while (tokens.hasMoreElements()) {
1430                String token = (String)tokens.nextElement();
1431                if (token.startsWith("href"))
1432                    href = getTokenValue(token);
1433                else if (token.startsWith("media"))
1434                    media = getTokenValue(token);
1435                else if (token.startsWith("title"))
1436                    title = getTokenValue(token);
1437                else if (token.startsWith("charset"))
1438                    charset = getTokenValue(token);
1439            }
1440
1441            // Set the target to this PI's href if the parameters are
1442            // null or match the corresponding attributes of this PI.
1443            if ( ((_PImedia == null) || (_PImedia.equals(media))) &&
1444                 ((_PItitle == null) || (_PImedia.equals(title))) &&
1445                 ((_PIcharset == null) || (_PImedia.equals(charset))) ) {
1446                _target = href;
1447            }
1448        }
1449    }
1450
1451    /**
1452     * IGNORED - all ignorable whitespace is ignored
1453     */
1454    public void ignorableWhitespace(char[] ch, int start, int length) { }
1455
1456    /**
1457     * IGNORED - we do not have to do anything with skipped entities
1458     */
1459    public void skippedEntity(String name) { }
1460
1461    /**
1462     * Store the document locator to later retrieve line numbers of all
1463     * elements from the stylesheet
1464     */
1465    public void setDocumentLocator(Locator locator) {
1466        _locator = locator;
1467    }
1468
1469    /**
1470     * Get the line number, or zero
1471     * if there is no _locator.
1472     */
1473    private int getLineNumber() {
1474        int line = 0;
1475        if (_locator != null)
1476                line = _locator.getLineNumber();
1477        return line;
1478    }
1479
1480}
1481