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