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