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