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