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 }