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 }