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