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 package com.sun.org.apache.xalan.internal.xsltc.compiler;
  22 
  23 import com.sun.java_cup.internal.runtime.Symbol;
  24 import com.sun.org.apache.xalan.internal.XalanConstants;
  25 import com.sun.org.apache.xalan.internal.utils.FactoryImpl;
  26 import com.sun.org.apache.xalan.internal.utils.ObjectFactory;
  27 import com.sun.org.apache.xalan.internal.utils.SecuritySupport;
  28 import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager;
  29 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
  30 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodType;
  31 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
  32 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
  33 import com.sun.org.apache.xml.internal.serializer.utils.SystemIDResolver;
  34 import java.io.File;
  35 import java.io.IOException;
  36 import java.io.StringReader;
  37 import java.util.ArrayList;
  38 import java.util.HashMap;
  39 import java.util.Iterator;
  40 import java.util.List;
  41 import java.util.Map;
  42 import java.util.Properties;
  43 import java.util.Stack;
  44 import java.util.StringTokenizer;
  45 import javax.xml.XMLConstants;
  46 import javax.xml.catalog.CatalogFeatures;
  47 import javax.xml.parsers.ParserConfigurationException;
  48 import javax.xml.parsers.SAXParser;
  49 import javax.xml.parsers.SAXParserFactory;
  50 import jdk.xml.internal.JdkXmlFeatures;
  51 import jdk.xml.internal.JdkXmlUtils;
  52 import org.xml.sax.Attributes;
  53 import org.xml.sax.ContentHandler;
  54 import org.xml.sax.InputSource;
  55 import org.xml.sax.Locator;
  56 import org.xml.sax.SAXException;
  57 import org.xml.sax.SAXNotRecognizedException;
  58 import org.xml.sax.SAXNotSupportedException;
  59 import org.xml.sax.SAXParseException;
  60 import org.xml.sax.XMLReader;
  61 import 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  */
  70 public 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             return(parse(reader, input));
 534         }
 535         catch (ParserConfigurationException e) {
 536             ErrorMsg err = new ErrorMsg(ErrorMsg.SAX_PARSER_CONFIG_ERR);
 537             reportError(ERROR, err);
 538         }
 539         catch (SAXParseException e){
 540             reportError(ERROR, new ErrorMsg(e.getMessage(),e.getLineNumber()));
 541         }
 542         catch (SAXException e) {
 543             reportError(ERROR, new ErrorMsg(e.getMessage()));
 544         }
 545         return null;
 546     }
 547 
 548     public SyntaxTreeNode getDocumentRoot() {
 549         return _root;
 550     }
 551 
 552     private String _PImedia = null;
 553     private String _PItitle = null;
 554     private String _PIcharset = null;
 555 
 556     /**
 557      * Set the parameters to use to locate the correct <?xml-stylesheet ...?>
 558      * processing instruction in the case where the input document is an
 559      * XML document with one or more references to a stylesheet.
 560      * @param media The media attribute to be matched. May be null, in which
 561      * case the prefered templates will be used (i.e. alternate = no).
 562      * @param title The value of the title attribute to match. May be null.
 563      * @param charset The value of the charset attribute to match. May be null.
 564      */
 565     protected void setPIParameters(String media, String title, String charset) {
 566         _PImedia = media;
 567         _PItitle = title;
 568         _PIcharset = charset;
 569     }
 570 
 571     /**
 572      * Extracts the DOM for the stylesheet. In the case of an embedded
 573      * stylesheet, it extracts the DOM subtree corresponding to the
 574      * embedded stylesheet that has an 'id' attribute whose value is the
 575      * same as the value declared in the <?xml-stylesheet...?> processing
 576      * instruction (P.I.). In the xml-stylesheet P.I. the value is labeled
 577      * as the 'href' data of the P.I. The extracted DOM representing the
 578      * stylesheet is returned as an Element object.
 579      */
 580     private SyntaxTreeNode getStylesheet(SyntaxTreeNode root)
 581         throws CompilerException {
 582 
 583         // Assume that this is a pure XSL stylesheet if there is not
 584         // <?xml-stylesheet ....?> processing instruction
 585         if (_target == null) {
 586             if (!_rootNamespaceDef) {
 587                 ErrorMsg msg = new ErrorMsg(ErrorMsg.MISSING_XSLT_URI_ERR);
 588                 throw new CompilerException(msg.toString());
 589             }
 590             return(root);
 591         }
 592 
 593         // Find the xsl:stylesheet or xsl:transform with this reference
 594         if (_target.charAt(0) == '#') {
 595             SyntaxTreeNode element = findStylesheet(root, _target.substring(1));
 596             if (element == null) {
 597                 ErrorMsg msg = new ErrorMsg(ErrorMsg.MISSING_XSLT_TARGET_ERR,
 598                                             _target, root);
 599                 throw new CompilerException(msg.toString());
 600             }
 601             return(element);
 602         }
 603         else {
 604             try {
 605                 String path = _target;
 606                 if (path.indexOf(":")==-1) {
 607                     path = "file:" + path;
 608                 }
 609                 path = SystemIDResolver.getAbsoluteURI(path);
 610                 String accessError = SecuritySupport.checkAccess(path,
 611                         (String)_xsltc.getProperty(XMLConstants.ACCESS_EXTERNAL_STYLESHEET),
 612                         XalanConstants.ACCESS_EXTERNAL_ALL);
 613                 if (accessError != null) {
 614                     ErrorMsg msg = new ErrorMsg(ErrorMsg.ACCESSING_XSLT_TARGET_ERR,
 615                             SecuritySupport.sanitizePath(_target), accessError,
 616                             root);
 617                     throw new CompilerException(msg.toString());
 618                 }
 619             } catch (IOException ex) {
 620                 throw new CompilerException(ex);
 621             }
 622 
 623             return(loadExternalStylesheet(_target));
 624         }
 625     }
 626 
 627     /**
 628      * Find a Stylesheet element with a specific ID attribute value.
 629      * This method is used to find a Stylesheet node that is referred
 630      * in a <?xml-stylesheet ... ?> processing instruction.
 631      */
 632     private SyntaxTreeNode findStylesheet(SyntaxTreeNode root, String href) {
 633 
 634         if (root == null) return null;
 635 
 636         if (root instanceof Stylesheet) {
 637             String id = root.getAttribute("id");
 638             if (id.equals(href)) return root;
 639         }
 640         List<SyntaxTreeNode> children = root.getContents();
 641         if (children != null) {
 642             final int count = children.size();
 643             for (int i = 0; i < count; i++) {
 644                 SyntaxTreeNode child = children.get(i);
 645                 SyntaxTreeNode node = findStylesheet(child, href);
 646                 if (node != null) return node;
 647             }
 648         }
 649         return null;
 650     }
 651 
 652     /**
 653      * For embedded stylesheets: Load an external file with stylesheet
 654      */
 655     private SyntaxTreeNode loadExternalStylesheet(String location)
 656         throws CompilerException {
 657 
 658         InputSource source;
 659 
 660         // Check if the location is URL or a local file
 661         if ((new File(location)).exists())
 662             source = new InputSource("file:"+location);
 663         else
 664             source = new InputSource(location);
 665 
 666         SyntaxTreeNode external = (SyntaxTreeNode)parse(source);
 667         return(external);
 668     }
 669 
 670     private void initAttrTable(String elementName, String[] attrs) {
 671         _instructionAttrs.put(getQName(XSLT_URI, XSL, elementName).getStringRep(),
 672                                 attrs);
 673     }
 674 
 675     private void initInstructionAttrs() {
 676         initAttrTable("template",
 677             new String[] {"match", "name", "priority", "mode"});
 678         initAttrTable("stylesheet",
 679             new String[] {"id", "version", "extension-element-prefixes",
 680                 "exclude-result-prefixes"});
 681         initAttrTable("transform",
 682             new String[] {"id", "version", "extension-element-prefixes",
 683                 "exclude-result-prefixes"});
 684         initAttrTable("text", new String[] {"disable-output-escaping"});
 685         initAttrTable("if", new String[] {"test"});
 686         initAttrTable("choose", new String[] {});
 687         initAttrTable("when", new String[] {"test"});
 688         initAttrTable("otherwise", new String[] {});
 689         initAttrTable("for-each", new String[] {"select"});
 690         initAttrTable("message", new String[] {"terminate"});
 691         initAttrTable("number",
 692             new String[] {"level", "count", "from", "value", "format", "lang",
 693                 "letter-value", "grouping-separator", "grouping-size"});
 694                 initAttrTable("comment", new String[] {});
 695         initAttrTable("copy", new String[] {"use-attribute-sets"});
 696         initAttrTable("copy-of", new String[] {"select"});
 697         initAttrTable("param", new String[] {"name", "select"});
 698         initAttrTable("with-param", new String[] {"name", "select"});
 699         initAttrTable("variable", new String[] {"name", "select"});
 700         initAttrTable("output",
 701             new String[] {"method", "version", "encoding",
 702                 "omit-xml-declaration", "standalone", "doctype-public",
 703                 "doctype-system", "cdata-section-elements", "indent",
 704                 "media-type"});
 705         initAttrTable("sort",
 706            new String[] {"select", "order", "case-order", "lang", "data-type"});
 707         initAttrTable("key", new String[] {"name", "match", "use"});
 708         initAttrTable("fallback", new String[] {});
 709         initAttrTable("attribute", new String[] {"name", "namespace"});
 710         initAttrTable("attribute-set",
 711             new String[] {"name", "use-attribute-sets"});
 712         initAttrTable("value-of",
 713             new String[] {"select", "disable-output-escaping"});
 714         initAttrTable("element",
 715             new String[] {"name", "namespace", "use-attribute-sets"});
 716         initAttrTable("call-template", new String[] {"name"});
 717         initAttrTable("apply-templates", new String[] {"select", "mode"});
 718         initAttrTable("apply-imports", new String[] {});
 719         initAttrTable("decimal-format",
 720             new String[] {"name", "decimal-separator", "grouping-separator",
 721                 "infinity", "minus-sign", "NaN", "percent", "per-mille",
 722                 "zero-digit", "digit", "pattern-separator"});
 723         initAttrTable("import", new String[] {"href"});
 724         initAttrTable("include", new String[] {"href"});
 725         initAttrTable("strip-space", new String[] {"elements"});
 726         initAttrTable("preserve-space", new String[] {"elements"});
 727         initAttrTable("processing-instruction", new String[] {"name"});
 728         initAttrTable("namespace-alias",
 729            new String[] {"stylesheet-prefix", "result-prefix"});
 730     }
 731 
 732     /**
 733      * Initialize the _instructionClasses map, which maps XSL element
 734      * names to Java classes in this package.
 735      */
 736     private void initStdClasses() {
 737         initStdClass("template", "Template");
 738         initStdClass("stylesheet", "Stylesheet");
 739         initStdClass("transform", "Stylesheet");
 740         initStdClass("text", "Text");
 741         initStdClass("if", "If");
 742         initStdClass("choose", "Choose");
 743         initStdClass("when", "When");
 744         initStdClass("otherwise", "Otherwise");
 745         initStdClass("for-each", "ForEach");
 746         initStdClass("message", "Message");
 747         initStdClass("number", "Number");
 748         initStdClass("comment", "Comment");
 749         initStdClass("copy", "Copy");
 750         initStdClass("copy-of", "CopyOf");
 751         initStdClass("param", "Param");
 752         initStdClass("with-param", "WithParam");
 753         initStdClass("variable", "Variable");
 754         initStdClass("output", "Output");
 755         initStdClass("sort", "Sort");
 756         initStdClass("key", "Key");
 757         initStdClass("fallback", "Fallback");
 758         initStdClass("attribute", "XslAttribute");
 759         initStdClass("attribute-set", "AttributeSet");
 760         initStdClass("value-of", "ValueOf");
 761         initStdClass("element", "XslElement");
 762         initStdClass("call-template", "CallTemplate");
 763         initStdClass("apply-templates", "ApplyTemplates");
 764         initStdClass("apply-imports", "ApplyImports");
 765         initStdClass("decimal-format", "DecimalFormatting");
 766         initStdClass("import", "Import");
 767         initStdClass("include", "Include");
 768         initStdClass("strip-space", "Whitespace");
 769         initStdClass("preserve-space", "Whitespace");
 770         initStdClass("processing-instruction", "ProcessingInstruction");
 771         initStdClass("namespace-alias", "NamespaceAlias");
 772     }
 773 
 774     private void initStdClass(String elementName, String className) {
 775         _instructionClasses.put(getQName(XSLT_URI, XSL, elementName).getStringRep(),
 776                                 COMPILER_PACKAGE + '.' + className);
 777     }
 778 
 779     public boolean elementSupported(String namespace, String localName) {
 780         return(_instructionClasses.get(getQName(namespace, XSL, localName).getStringRep()) != null);
 781     }
 782 
 783     public boolean functionSupported(String fname) {
 784         return(_symbolTable.lookupPrimop(fname) != null);
 785     }
 786 
 787     private void initExtClasses() {
 788         initExtClass("output", "TransletOutput");
 789         initExtClass(REDIRECT_URI, "write", "TransletOutput");
 790     }
 791 
 792     private void initExtClass(String elementName, String className) {
 793         _instructionClasses.put(getQName(TRANSLET_URI, TRANSLET, elementName).getStringRep(),
 794                                 COMPILER_PACKAGE + '.' + className);
 795     }
 796 
 797     private void initExtClass(String namespace, String elementName, String className) {
 798         _instructionClasses.put(getQName(namespace, TRANSLET, elementName).getStringRep(),
 799                                 COMPILER_PACKAGE + '.' + className);
 800     }
 801 
 802     /**
 803      * Add primops and base functions to the symbol table.
 804      */
 805     @SuppressWarnings("unused")
 806     private void initSymbolTable() {
 807         MethodType I_V  = new MethodType(Type.Int, Type.Void);
 808         MethodType I_R  = new MethodType(Type.Int, Type.Real);
 809         MethodType I_S  = new MethodType(Type.Int, Type.String);
 810         MethodType I_D  = new MethodType(Type.Int, Type.NodeSet);
 811         MethodType R_I  = new MethodType(Type.Real, Type.Int);
 812         MethodType R_V  = new MethodType(Type.Real, Type.Void);
 813         MethodType R_R  = new MethodType(Type.Real, Type.Real);
 814         MethodType R_D  = new MethodType(Type.Real, Type.NodeSet);
 815         MethodType R_O  = new MethodType(Type.Real, Type.Reference);
 816         MethodType I_I  = new MethodType(Type.Int, Type.Int);
 817         MethodType D_O  = new MethodType(Type.NodeSet, Type.Reference);
 818         MethodType D_V  = new MethodType(Type.NodeSet, Type.Void);
 819         MethodType D_S  = new MethodType(Type.NodeSet, Type.String);
 820         MethodType D_D  = new MethodType(Type.NodeSet, Type.NodeSet);
 821         MethodType A_V  = new MethodType(Type.Node, Type.Void);
 822         MethodType S_V  = new MethodType(Type.String, Type.Void);
 823         MethodType S_S  = new MethodType(Type.String, Type.String);
 824         MethodType S_A  = new MethodType(Type.String, Type.Node);
 825         MethodType S_D  = new MethodType(Type.String, Type.NodeSet);
 826         MethodType S_O  = new MethodType(Type.String, Type.Reference);
 827         MethodType B_O  = new MethodType(Type.Boolean, Type.Reference);
 828         MethodType B_V  = new MethodType(Type.Boolean, Type.Void);
 829         MethodType B_B  = new MethodType(Type.Boolean, Type.Boolean);
 830         MethodType B_S  = new MethodType(Type.Boolean, Type.String);
 831         MethodType D_X  = new MethodType(Type.NodeSet, Type.Object);
 832         MethodType R_RR = new MethodType(Type.Real, Type.Real, Type.Real);
 833         MethodType I_II = new MethodType(Type.Int, Type.Int, Type.Int);
 834         MethodType B_RR = new MethodType(Type.Boolean, Type.Real, Type.Real);
 835         MethodType B_II = new MethodType(Type.Boolean, Type.Int, Type.Int);
 836         MethodType S_SS = new MethodType(Type.String, Type.String, Type.String);
 837         MethodType S_DS = new MethodType(Type.String, Type.Real, Type.String);
 838         MethodType S_SR = new MethodType(Type.String, Type.String, Type.Real);
 839         MethodType O_SO = new MethodType(Type.Reference, Type.String, Type.Reference);
 840 
 841         MethodType D_SS =
 842             new MethodType(Type.NodeSet, Type.String, Type.String);
 843         MethodType D_SD =
 844             new MethodType(Type.NodeSet, Type.String, Type.NodeSet);
 845         MethodType B_BB =
 846             new MethodType(Type.Boolean, Type.Boolean, Type.Boolean);
 847         MethodType B_SS =
 848             new MethodType(Type.Boolean, Type.String, Type.String);
 849         MethodType S_SD =
 850             new MethodType(Type.String, Type.String, Type.NodeSet);
 851         MethodType S_DSS =
 852             new MethodType(Type.String, Type.Real, Type.String, Type.String);
 853         MethodType S_SRR =
 854             new MethodType(Type.String, Type.String, Type.Real, Type.Real);
 855         MethodType S_SSS =
 856             new MethodType(Type.String, Type.String, Type.String, Type.String);
 857 
 858         /*
 859          * Standard functions: implemented but not in this table concat().
 860          * When adding a new function make sure to uncomment
 861          * the corresponding line in <tt>FunctionAvailableCall</tt>.
 862          */
 863 
 864         // The following functions are inlined
 865 
 866         _symbolTable.addPrimop("current", A_V);
 867         _symbolTable.addPrimop("last", I_V);
 868         _symbolTable.addPrimop("position", I_V);
 869         _symbolTable.addPrimop("true", B_V);
 870         _symbolTable.addPrimop("false", B_V);
 871         _symbolTable.addPrimop("not", B_B);
 872         _symbolTable.addPrimop("name", S_V);
 873         _symbolTable.addPrimop("name", S_A);
 874         _symbolTable.addPrimop("generate-id", S_V);
 875         _symbolTable.addPrimop("generate-id", S_A);
 876         _symbolTable.addPrimop("ceiling", R_R);
 877         _symbolTable.addPrimop("floor", R_R);
 878         _symbolTable.addPrimop("round", R_R);
 879         _symbolTable.addPrimop("contains", B_SS);
 880         _symbolTable.addPrimop("number", R_O);
 881         _symbolTable.addPrimop("number", R_V);
 882         _symbolTable.addPrimop("boolean", B_O);
 883         _symbolTable.addPrimop("string", S_O);
 884         _symbolTable.addPrimop("string", S_V);
 885         _symbolTable.addPrimop("translate", S_SSS);
 886         _symbolTable.addPrimop("string-length", I_V);
 887         _symbolTable.addPrimop("string-length", I_S);
 888         _symbolTable.addPrimop("starts-with", B_SS);
 889         _symbolTable.addPrimop("format-number", S_DS);
 890         _symbolTable.addPrimop("format-number", S_DSS);
 891         _symbolTable.addPrimop("unparsed-entity-uri", S_S);
 892         _symbolTable.addPrimop("key", D_SS);
 893         _symbolTable.addPrimop("key", D_SD);
 894         _symbolTable.addPrimop("id", D_S);
 895         _symbolTable.addPrimop("id", D_D);
 896         _symbolTable.addPrimop("namespace-uri", S_V);
 897         _symbolTable.addPrimop("function-available", B_S);
 898         _symbolTable.addPrimop("element-available", B_S);
 899         _symbolTable.addPrimop("document", D_S);
 900         _symbolTable.addPrimop("document", D_V);
 901 
 902         // The following functions are implemented in the basis library
 903         _symbolTable.addPrimop("count", I_D);
 904         _symbolTable.addPrimop("sum", R_D);
 905         _symbolTable.addPrimop("local-name", S_V);
 906         _symbolTable.addPrimop("local-name", S_D);
 907         _symbolTable.addPrimop("namespace-uri", S_V);
 908         _symbolTable.addPrimop("namespace-uri", S_D);
 909         _symbolTable.addPrimop("substring", S_SR);
 910         _symbolTable.addPrimop("substring", S_SRR);
 911         _symbolTable.addPrimop("substring-after", S_SS);
 912         _symbolTable.addPrimop("substring-before", S_SS);
 913         _symbolTable.addPrimop("normalize-space", S_V);
 914         _symbolTable.addPrimop("normalize-space", S_S);
 915         _symbolTable.addPrimop("system-property", S_S);
 916 
 917         // Extensions
 918         _symbolTable.addPrimop("nodeset", D_O);
 919         _symbolTable.addPrimop("objectType", S_O);
 920         _symbolTable.addPrimop("cast", O_SO);
 921 
 922         // Operators +, -, *, /, % defined on real types.
 923         _symbolTable.addPrimop("+", R_RR);
 924         _symbolTable.addPrimop("-", R_RR);
 925         _symbolTable.addPrimop("*", R_RR);
 926         _symbolTable.addPrimop("/", R_RR);
 927         _symbolTable.addPrimop("%", R_RR);
 928 
 929         // Operators +, -, * defined on integer types.
 930         // Operators / and % are not  defined on integers (may cause exception)
 931         _symbolTable.addPrimop("+", I_II);
 932         _symbolTable.addPrimop("-", I_II);
 933         _symbolTable.addPrimop("*", I_II);
 934 
 935          // Operators <, <= >, >= defined on real types.
 936         _symbolTable.addPrimop("<",  B_RR);
 937         _symbolTable.addPrimop("<=", B_RR);
 938         _symbolTable.addPrimop(">",  B_RR);
 939         _symbolTable.addPrimop(">=", B_RR);
 940 
 941         // Operators <, <= >, >= defined on int types.
 942         _symbolTable.addPrimop("<",  B_II);
 943         _symbolTable.addPrimop("<=", B_II);
 944         _symbolTable.addPrimop(">",  B_II);
 945         _symbolTable.addPrimop(">=", B_II);
 946 
 947         // Operators <, <= >, >= defined on boolean types.
 948         _symbolTable.addPrimop("<",  B_BB);
 949         _symbolTable.addPrimop("<=", B_BB);
 950         _symbolTable.addPrimop(">",  B_BB);
 951         _symbolTable.addPrimop(">=", B_BB);
 952 
 953         // Operators 'and' and 'or'.
 954         _symbolTable.addPrimop("or", B_BB);
 955         _symbolTable.addPrimop("and", B_BB);
 956 
 957         // Unary minus.
 958         _symbolTable.addPrimop("u-", R_R);
 959         _symbolTable.addPrimop("u-", I_I);
 960     }
 961 
 962     public SymbolTable getSymbolTable() {
 963         return _symbolTable;
 964     }
 965 
 966     public Template getTemplate() {
 967         return _template;
 968     }
 969 
 970     public void setTemplate(Template template) {
 971         _template = template;
 972     }
 973 
 974     private int _templateIndex = 0;
 975 
 976     public int getTemplateIndex() {
 977         return(_templateIndex++);
 978     }
 979 
 980     /**
 981      * Creates a new node in the abstract syntax tree. This node can be
 982      *  o) a supported XSLT 1.0 element
 983      *  o) an unsupported XSLT element (post 1.0)
 984      *  o) a supported XSLT extension
 985      *  o) an unsupported XSLT extension
 986      *  o) a literal result element (not an XSLT element and not an extension)
 987      * Unsupported elements do not directly generate an error. We have to wait
 988      * until we have received all child elements of an unsupported element to
 989      * see if any <xsl:fallback> elements exist.
 990      */
 991 
 992     private boolean versionIsOne = true;
 993 
 994     public SyntaxTreeNode makeInstance(String uri, String prefix,
 995         String local, Attributes attributes)
 996     {
 997         SyntaxTreeNode node = null;
 998         QName qname = getQName(uri, prefix, local);
 999         String className = _instructionClasses.get(qname.getStringRep());
1000 
1001         if (className != null) {
1002             try {
1003                 final Class<?> clazz = ObjectFactory.findProviderClass(className, true);
1004                 node = (SyntaxTreeNode)clazz.newInstance();
1005                 node.setQName(qname);
1006                 node.setParser(this);
1007                 if (_locator != null) {
1008                     node.setLineNumber(getLineNumber());
1009                 }
1010                 if (node instanceof Stylesheet) {
1011                     _xsltc.setStylesheet((Stylesheet)node);
1012                 }
1013                 checkForSuperfluousAttributes(node, attributes);
1014             }
1015             catch (ClassNotFoundException e) {
1016                 ErrorMsg err = new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, node);
1017                 reportError(ERROR, err);
1018             }
1019             catch (Exception e) {
1020                 ErrorMsg err = new ErrorMsg(ErrorMsg.INTERNAL_ERR,
1021                                             e.getMessage(), node);
1022                 reportError(FATAL, err);
1023             }
1024         }
1025         else {
1026             if (uri != null) {
1027                 // Check if the element belongs in our namespace
1028                 if (uri.equals(XSLT_URI)) {
1029                     node = new UnsupportedElement(uri, prefix, local, false);
1030                     UnsupportedElement element = (UnsupportedElement)node;
1031                     ErrorMsg msg = new ErrorMsg(ErrorMsg.UNSUPPORTED_XSL_ERR,
1032                                                 getLineNumber(),local);
1033                     element.setErrorMessage(msg);
1034                     if (versionIsOne) {
1035                         reportError(UNSUPPORTED,msg);
1036                     }
1037                 }
1038                 // Check if this is an XSLTC extension element
1039                 else if (uri.equals(TRANSLET_URI)) {
1040                     node = new UnsupportedElement(uri, prefix, local, true);
1041                     UnsupportedElement element = (UnsupportedElement)node;
1042                     ErrorMsg msg = new ErrorMsg(ErrorMsg.UNSUPPORTED_EXT_ERR,
1043                                                 getLineNumber(),local);
1044                     element.setErrorMessage(msg);
1045                 }
1046                 // Check if this is an extension of some other XSLT processor
1047                 else {
1048                     Stylesheet sheet = _xsltc.getStylesheet();
1049                     if ((sheet != null) && (sheet.isExtension(uri))) {
1050                         if (sheet != _parentStack.peek()) {
1051                             node = new UnsupportedElement(uri, prefix, local, true);
1052                             UnsupportedElement elem = (UnsupportedElement)node;
1053                             ErrorMsg msg =
1054                                 new ErrorMsg(ErrorMsg.UNSUPPORTED_EXT_ERR,
1055                                              getLineNumber(),
1056                                              prefix+":"+local);
1057                             elem.setErrorMessage(msg);
1058                         }
1059                     }
1060                 }
1061             }
1062             if (node == null) {
1063                 node = new LiteralElement();
1064                 node.setLineNumber(getLineNumber());
1065             }
1066         }
1067         if ((node != null) && (node instanceof LiteralElement)) {
1068             ((LiteralElement)node).setQName(qname);
1069         }
1070         return(node);
1071     }
1072 
1073     /**
1074      * checks the list of attributes against a list of allowed attributes
1075      * for a particular element node.
1076      */
1077     private void checkForSuperfluousAttributes(SyntaxTreeNode node,
1078         Attributes attrs)
1079     {
1080         QName qname = node.getQName();
1081         boolean isStylesheet = (node instanceof Stylesheet);
1082         String[] legal = _instructionAttrs.get(qname.getStringRep());
1083         if (versionIsOne && legal != null) {
1084             int j;
1085             final int n = attrs.getLength();
1086 
1087             for (int i = 0; i < n; i++) {
1088                 final String attrQName = attrs.getQName(i);
1089 
1090                 if (isStylesheet && attrQName.equals("version")) {
1091                     versionIsOne = attrs.getValue(i).equals("1.0");
1092                 }
1093 
1094                 // Ignore if special or if it has a prefix
1095                 if (attrQName.startsWith("xml") ||
1096                     attrQName.indexOf(':') > 0) continue;
1097 
1098                 for (j = 0; j < legal.length; j++) {
1099                     if (attrQName.equalsIgnoreCase(legal[j])) {
1100                         break;
1101                     }
1102                 }
1103                 if (j == legal.length) {
1104                     final ErrorMsg err =
1105                         new ErrorMsg(ErrorMsg.ILLEGAL_ATTRIBUTE_ERR,
1106                                 attrQName, node);
1107                     // Workaround for the TCK failure ErrorListener.errorTests.error001..
1108                     err.setWarningError(true);
1109                     reportError(WARNING, err);
1110                 }
1111             }
1112         }
1113     }
1114 
1115 
1116     /**
1117      * Parse an XPath expression:
1118      *  @param parent - XSL element where the expression occured
1119      *  @param exp    - textual representation of the expression
1120      */
1121     public Expression parseExpression(SyntaxTreeNode parent, String exp) {
1122         return (Expression)parseTopLevel(parent, "<EXPRESSION>"+exp, null);
1123     }
1124 
1125     /**
1126      * Parse an XPath expression:
1127      *  @param parent - XSL element where the expression occured
1128      *  @param attr   - name of this element's attribute to get expression from
1129      *  @param def    - default expression (if the attribute was not found)
1130      */
1131     public Expression parseExpression(SyntaxTreeNode parent,
1132                                       String attr, String def) {
1133         // Get the textual representation of the expression (if any)
1134         String exp = parent.getAttribute(attr);
1135         // Use the default expression if none was found
1136         if ((exp.length() == 0) && (def != null)) exp = def;
1137         // Invoke the XPath parser
1138         return (Expression)parseTopLevel(parent, "<EXPRESSION>"+exp, exp);
1139     }
1140 
1141     /**
1142      * Parse an XPath pattern:
1143      *  @param parent  - XSL element where the pattern occured
1144      *  @param pattern - textual representation of the pattern
1145      */
1146     public Pattern parsePattern(SyntaxTreeNode parent, String pattern) {
1147         return (Pattern)parseTopLevel(parent, "<PATTERN>"+pattern, pattern);
1148     }
1149 
1150     /**
1151      * Parse an XPath pattern:
1152      *  @param parent - XSL element where the pattern occured
1153      *  @param attr   - name of this element's attribute to get pattern from
1154      *  @param def    - default pattern (if the attribute was not found)
1155      */
1156     public Pattern parsePattern(SyntaxTreeNode parent,
1157                                 String attr, String def) {
1158         // Get the textual representation of the pattern (if any)
1159         String pattern = parent.getAttribute(attr);
1160         // Use the default pattern if none was found
1161         if ((pattern.length() == 0) && (def != null)) pattern = def;
1162         // Invoke the XPath parser
1163         return (Pattern)parseTopLevel(parent, "<PATTERN>"+pattern, pattern);
1164     }
1165 
1166     /**
1167      * Parse an XPath expression or pattern using the generated XPathParser
1168      * The method will return a Dummy node if the XPath parser fails.
1169      */
1170     private SyntaxTreeNode parseTopLevel(SyntaxTreeNode parent, String text,
1171                                          String expression) {
1172         int line = getLineNumber();
1173 
1174         try {
1175             _xpathParser.setScanner(new XPathLexer(new StringReader(text)));
1176             Symbol result = _xpathParser.parse(expression, line);
1177             if (result != null) {
1178                 final SyntaxTreeNode node = (SyntaxTreeNode)result.value;
1179                 if (node != null) {
1180                     node.setParser(this);
1181                     node.setParent(parent);
1182                     node.setLineNumber(line);
1183                     return node;
1184                 }
1185             }
1186             reportError(ERROR, new ErrorMsg(ErrorMsg.XPATH_PARSER_ERR,
1187                                             expression, parent));
1188         }
1189         catch (Exception e) {
1190             if (_xsltc.debug()) e.printStackTrace();
1191             reportError(ERROR, new ErrorMsg(ErrorMsg.XPATH_PARSER_ERR,
1192                                             expression, parent));
1193         }
1194 
1195         // Return a dummy pattern (which is an expression)
1196         SyntaxTreeNode.Dummy.setParser(this);
1197         return SyntaxTreeNode.Dummy;
1198     }
1199 
1200     /************************ ERROR HANDLING SECTION ************************/
1201 
1202     /**
1203      * Returns true if there were any errors during compilation
1204      */
1205     public boolean errorsFound() {
1206         return _errors.size() > 0;
1207     }
1208 
1209     /**
1210      * Prints all compile-time errors
1211      */
1212     public void printErrors() {
1213         final int size = _errors.size();
1214         if (size > 0) {
1215             System.err.println(new ErrorMsg(ErrorMsg.COMPILER_ERROR_KEY));
1216             for (int i = 0; i < size; i++) {
1217                 System.err.println("  " + _errors.get(i));
1218             }
1219         }
1220     }
1221 
1222     /**
1223      * Prints all compile-time warnings
1224      */
1225     public void printWarnings() {
1226         final int size = _warnings.size();
1227         if (size > 0) {
1228             System.err.println(new ErrorMsg(ErrorMsg.COMPILER_WARNING_KEY));
1229             for (int i = 0; i < size; i++) {
1230                 System.err.println("  " + _warnings.get(i));
1231             }
1232         }
1233     }
1234 
1235     /**
1236      * Common error/warning message handler
1237      */
1238     public void reportError(final int category, final ErrorMsg error) {
1239         switch (category) {
1240         case Constants.INTERNAL:
1241             // Unexpected internal errors, such as null-ptr exceptions, etc.
1242             // Immediately terminates compilation, no translet produced
1243             _errors.add(error);
1244             break;
1245         case Constants.UNSUPPORTED:
1246             // XSLT elements that are not implemented and unsupported ext.
1247             // Immediately terminates compilation, no translet produced
1248             _errors.add(error);
1249             break;
1250         case Constants.FATAL:
1251             // Fatal error in the stylesheet input (parsing or content)
1252             // Immediately terminates compilation, no translet produced
1253             _errors.add(error);
1254             break;
1255         case Constants.ERROR:
1256             // Other error in the stylesheet input (parsing or content)
1257             // Does not terminate compilation, no translet produced
1258             _errors.add(error);
1259             break;
1260         case Constants.WARNING:
1261             // Other error in the stylesheet input (content errors only)
1262             // Does not terminate compilation, a translet is produced
1263             _warnings.add(error);
1264             break;
1265         }
1266     }
1267 
1268     public ArrayList<ErrorMsg> getErrors() {
1269         return _errors;
1270     }
1271 
1272     public ArrayList<ErrorMsg> getWarnings() {
1273         return _warnings;
1274     }
1275 
1276     /************************ SAX2 ContentHandler INTERFACE *****************/
1277 
1278     private Stack<SyntaxTreeNode> _parentStack = null;
1279     private Map<String, String> _prefixMapping = null;
1280 
1281     /**
1282      * SAX2: Receive notification of the beginning of a document.
1283      */
1284     public void startDocument() {
1285         _root = null;
1286         _target = null;
1287         _prefixMapping = null;
1288         _parentStack = new Stack<>();
1289     }
1290 
1291     /**
1292      * SAX2: Receive notification of the end of a document.
1293      */
1294     public void endDocument() { }
1295 
1296 
1297     /**
1298      * SAX2: Begin the scope of a prefix-URI Namespace mapping.
1299      *       This has to be passed on to the symbol table!
1300      */
1301     public void startPrefixMapping(String prefix, String uri) {
1302         if (_prefixMapping == null) {
1303             _prefixMapping = new HashMap<>();
1304         }
1305         _prefixMapping.put(prefix, uri);
1306     }
1307 
1308     /**
1309      * SAX2: End the scope of a prefix-URI Namespace mapping.
1310      *       This has to be passed on to the symbol table!
1311      */
1312     public void endPrefixMapping(String prefix) { }
1313 
1314     /**
1315      * SAX2: Receive notification of the beginning of an element.
1316      *       The parser may re-use the attribute list that we're passed so
1317      *       we clone the attributes in our own Attributes implementation
1318      */
1319     public void startElement(String uri, String localname,
1320                              String qname, Attributes attributes)
1321         throws SAXException {
1322         final int col = qname.lastIndexOf(':');
1323         final String prefix = (col == -1) ? null : qname.substring(0, col);
1324 
1325         SyntaxTreeNode element = makeInstance(uri, prefix,
1326                                         localname, attributes);
1327         if (element == null) {
1328             ErrorMsg err = new ErrorMsg(ErrorMsg.ELEMENT_PARSE_ERR,
1329                                         prefix+':'+localname);
1330             throw new SAXException(err.toString());
1331         }
1332 
1333         // If this is the root element of the XML document we need to make sure
1334         // that it contains a definition of the XSL namespace URI
1335         if (_root == null) {
1336             if ((_prefixMapping == null) ||
1337                 (_prefixMapping.containsValue(Constants.XSLT_URI) == false))
1338                 _rootNamespaceDef = false;
1339             else
1340                 _rootNamespaceDef = true;
1341             _root = element;
1342         }
1343         else {
1344             SyntaxTreeNode parent = _parentStack.peek();
1345             parent.addElement(element);
1346             element.setParent(parent);
1347         }
1348         element.setAttributes(new AttributesImpl(attributes));
1349         element.setPrefixMapping(_prefixMapping);
1350 
1351         if (element instanceof Stylesheet) {
1352             // Extension elements and excluded elements have to be
1353             // handled at this point in order to correctly generate
1354             // Fallback elements from <xsl:fallback>s.
1355             getSymbolTable().setCurrentNode(element);
1356             ((Stylesheet)element).declareExtensionPrefixes(this);
1357         }
1358 
1359         _prefixMapping = null;
1360         _parentStack.push(element);
1361     }
1362 
1363     /**
1364      * SAX2: Receive notification of the end of an element.
1365      */
1366     public void endElement(String uri, String localname, String qname) {
1367         _parentStack.pop();
1368     }
1369 
1370     /**
1371      * SAX2: Receive notification of character data.
1372      */
1373     public void characters(char[] ch, int start, int length) {
1374         String string = new String(ch, start, length);
1375         SyntaxTreeNode parent = _parentStack.peek();
1376 
1377         if (string.length() == 0) return;
1378 
1379         // If this text occurs within an <xsl:text> element we append it
1380         // as-is to the existing text element
1381         if (parent instanceof Text) {
1382             ((Text)parent).setText(string);
1383             return;
1384         }
1385 
1386         // Ignore text nodes that occur directly under <xsl:stylesheet>
1387         if (parent instanceof Stylesheet) return;
1388 
1389         SyntaxTreeNode bro = parent.lastChild();
1390         if ((bro != null) && (bro instanceof Text)) {
1391             Text text = (Text)bro;
1392             if (!text.isTextElement()) {
1393                 if ((length > 1) || ( ((int)ch[0]) < 0x100)) {
1394                     text.setText(string);
1395                     return;
1396                 }
1397             }
1398         }
1399 
1400         // Add it as a regular text node otherwise
1401         parent.addElement(new Text(string));
1402     }
1403 
1404     private String getTokenValue(String token) {
1405         final int start = token.indexOf('"');
1406         final int stop = token.lastIndexOf('"');
1407         return token.substring(start+1, stop);
1408     }
1409 
1410     /**
1411      * SAX2: Receive notification of a processing instruction.
1412      *       These require special handling for stylesheet PIs.
1413      */
1414     public void processingInstruction(String name, String value) {
1415         // We only handle the <?xml-stylesheet ...?> PI
1416         if ((_target == null) && (name.equals("xml-stylesheet"))) {
1417 
1418             String href = null;    // URI of stylesheet found
1419             String media = null;   // Media of stylesheet found
1420             String title = null;   // Title of stylesheet found
1421             String charset = null; // Charset of stylesheet found
1422 
1423             // Get the attributes from the processing instruction
1424             StringTokenizer tokens = new StringTokenizer(value);
1425             while (tokens.hasMoreElements()) {
1426                 String token = (String)tokens.nextElement();
1427                 if (token.startsWith("href"))
1428                     href = getTokenValue(token);
1429                 else if (token.startsWith("media"))
1430                     media = getTokenValue(token);
1431                 else if (token.startsWith("title"))
1432                     title = getTokenValue(token);
1433                 else if (token.startsWith("charset"))
1434                     charset = getTokenValue(token);
1435             }
1436 
1437             // Set the target to this PI's href if the parameters are
1438             // null or match the corresponding attributes of this PI.
1439             if ( ((_PImedia == null) || (_PImedia.equals(media))) &&
1440                  ((_PItitle == null) || (_PImedia.equals(title))) &&
1441                  ((_PIcharset == null) || (_PImedia.equals(charset))) ) {
1442                 _target = href;
1443             }
1444         }
1445     }
1446 
1447     /**
1448      * IGNORED - all ignorable whitespace is ignored
1449      */
1450     public void ignorableWhitespace(char[] ch, int start, int length) { }
1451 
1452     /**
1453      * IGNORED - we do not have to do anything with skipped entities
1454      */
1455     public void skippedEntity(String name) { }
1456 
1457     /**
1458      * Store the document locator to later retrieve line numbers of all
1459      * elements from the stylesheet
1460      */
1461     public void setDocumentLocator(Locator locator) {
1462         _locator = locator;
1463     }
1464 
1465     /**
1466      * Get the line number, or zero
1467      * if there is no _locator.
1468      */
1469     private int getLineNumber() {
1470         int line = 0;
1471         if (_locator != null)
1472                 line = _locator.getLineNumber();
1473         return line;
1474     }
1475 
1476 }