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