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