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