1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 /* 19 * $Id: StylesheetHandler.java 468640 2006-10-28 06:53:53Z minchau $ 20 */ 21 package org.apache.xalan.processor; 22 23 import java.util.Stack; 24 25 import javax.xml.transform.ErrorListener; 26 import javax.xml.transform.Source; 27 import javax.xml.transform.SourceLocator; 28 import javax.xml.transform.Templates; 29 import javax.xml.transform.TransformerConfigurationException; 30 import javax.xml.transform.TransformerException; 31 import javax.xml.transform.sax.TemplatesHandler; 32 33 import org.apache.xalan.extensions.ExpressionVisitor; 34 import org.apache.xalan.res.XSLMessages; 35 import org.apache.xalan.res.XSLTErrorResources; 36 import org.apache.xalan.templates.Constants; 37 import org.apache.xalan.templates.ElemForEach; 38 import org.apache.xalan.templates.ElemTemplateElement; 39 import org.apache.xalan.templates.Stylesheet; 40 import org.apache.xalan.templates.StylesheetRoot; 41 import org.apache.xml.utils.BoolStack; 42 import org.apache.xml.utils.NamespaceSupport2; 43 import org.apache.xml.utils.NodeConsumer; 44 import org.apache.xml.utils.PrefixResolver; 45 import org.apache.xml.utils.SAXSourceLocator; 46 import org.apache.xml.utils.XMLCharacterRecognizer; 47 import org.apache.xpath.XPath; 48 import org.apache.xpath.compiler.FunctionTable; 49 import org.apache.xpath.functions.Function; 50 51 import org.w3c.dom.Node; 52 53 import org.xml.sax.Attributes; 54 import org.xml.sax.InputSource; 55 import org.xml.sax.Locator; 56 import org.xml.sax.helpers.DefaultHandler; 57 import org.xml.sax.helpers.NamespaceSupport; 58 59 /** 60 * Initializes and processes a stylesheet via SAX events. 61 * This class acts as essentially a state machine, maintaining 62 * a ContentHandler stack, and pushing appropriate content 63 * handlers as parse events occur. 64 * @xsl.usage advanced 65 */ 66 public class StylesheetHandler extends DefaultHandler 67 implements TemplatesHandler, PrefixResolver, NodeConsumer 68 { 69 70 71 /** 72 * The function table of XPath and XSLT; 73 */ 74 private FunctionTable m_funcTable = new FunctionTable(); 75 76 /** 77 * The flag for the setting of the optimize feature; 78 */ 79 private boolean m_optimize = true; 80 81 /** 82 * The flag for the setting of the incremental feature; 83 */ 84 private boolean m_incremental = false; 85 86 /** 87 * The flag for the setting of the source_location feature; 88 */ 89 private boolean m_source_location = false; 90 91 /** 92 * Create a StylesheetHandler object, creating a root stylesheet 93 * as the target. 94 * 95 * @param processor non-null reference to the transformer factory that owns this handler. 96 * 97 * @throws TransformerConfigurationException if a StylesheetRoot 98 * can not be constructed for some reason. 99 */ StylesheetHandler(TransformerFactoryImpl processor)100 public StylesheetHandler(TransformerFactoryImpl processor) 101 throws TransformerConfigurationException 102 { 103 Class func = org.apache.xalan.templates.FuncDocument.class; 104 m_funcTable.installFunction("document", func); 105 106 // func = new org.apache.xalan.templates.FuncKey(); 107 // FunctionTable.installFunction("key", func); 108 func = org.apache.xalan.templates.FuncFormatNumb.class; 109 110 m_funcTable.installFunction("format-number", func); 111 112 m_optimize =((Boolean) processor.getAttribute( 113 TransformerFactoryImpl.FEATURE_OPTIMIZE)).booleanValue(); 114 m_incremental = ((Boolean) processor.getAttribute( 115 TransformerFactoryImpl.FEATURE_INCREMENTAL)).booleanValue(); 116 m_source_location = ((Boolean) processor.getAttribute( 117 TransformerFactoryImpl.FEATURE_SOURCE_LOCATION)).booleanValue(); 118 // m_schema = new XSLTSchema(); 119 init(processor); 120 121 } 122 123 /** 124 * Do common initialization. 125 * 126 * @param processor non-null reference to the transformer factory that owns this handler. 127 */ init(TransformerFactoryImpl processor)128 void init(TransformerFactoryImpl processor) 129 { 130 m_stylesheetProcessor = processor; 131 132 // Set the initial content handler. 133 m_processors.push(m_schema.getElementProcessor()); 134 this.pushNewNamespaceSupport(); 135 136 // m_includeStack.push(SystemIDResolver.getAbsoluteURI(this.getBaseIdentifier(), null)); 137 // initXPath(processor, null); 138 } 139 140 /** 141 * Process an expression string into an XPath. 142 * Must be public for access by the AVT class. 143 * 144 * @param str A non-null reference to a valid or invalid XPath expression string. 145 * 146 * @return A non-null reference to an XPath object that represents the string argument. 147 * 148 * @throws javax.xml.transform.TransformerException if the expression can not be processed. 149 * @see <a href="http://www.w3.org/TR/xslt#section-Expressions">Section 4 Expressions in XSLT Specification</a> 150 */ createXPath(String str, ElemTemplateElement owningTemplate)151 public XPath createXPath(String str, ElemTemplateElement owningTemplate) 152 throws javax.xml.transform.TransformerException 153 { 154 ErrorListener handler = m_stylesheetProcessor.getErrorListener(); 155 XPath xpath = new XPath(str, owningTemplate, this, XPath.SELECT, handler, 156 m_funcTable); 157 // Visit the expression, registering namespaces for any extension functions it includes. 158 xpath.callVisitors(xpath, new ExpressionVisitor(getStylesheetRoot())); 159 return xpath; 160 } 161 162 /** 163 * Process an expression string into an XPath. 164 * 165 * @param str A non-null reference to a valid or invalid match pattern string. 166 * 167 * @return A non-null reference to an XPath object that represents the string argument. 168 * 169 * @throws javax.xml.transform.TransformerException if the pattern can not be processed. 170 * @see <a href="http://www.w3.org/TR/xslt#patterns">Section 5.2 Patterns in XSLT Specification</a> 171 */ createMatchPatternXPath(String str, ElemTemplateElement owningTemplate)172 XPath createMatchPatternXPath(String str, ElemTemplateElement owningTemplate) 173 throws javax.xml.transform.TransformerException 174 { 175 ErrorListener handler = m_stylesheetProcessor.getErrorListener(); 176 XPath xpath = new XPath(str, owningTemplate, this, XPath.MATCH, handler, 177 m_funcTable); 178 // Visit the expression, registering namespaces for any extension functions it includes. 179 xpath.callVisitors(xpath, new ExpressionVisitor(getStylesheetRoot())); 180 return xpath; 181 } 182 183 /** 184 * Given a namespace, get the corrisponding prefix from the current 185 * namespace support context. 186 * 187 * @param prefix The prefix to look up, which may be an empty string ("") for the default Namespace. 188 * 189 * @return The associated Namespace URI, or null if the prefix 190 * is undeclared in this context. 191 */ getNamespaceForPrefix(String prefix)192 public String getNamespaceForPrefix(String prefix) 193 { 194 return this.getNamespaceSupport().getURI(prefix); 195 } 196 197 /** 198 * Given a namespace, get the corrisponding prefix. This is here only 199 * to support the {@link org.apache.xml.utils.PrefixResolver} interface, 200 * and will throw an error if invoked on this object. 201 * 202 * @param prefix The prefix to look up, which may be an empty string ("") for the default Namespace. 203 * @param context The node context from which to look up the URI. 204 * 205 * @return The associated Namespace URI, or null if the prefix 206 * is undeclared in this context. 207 */ getNamespaceForPrefix(String prefix, org.w3c.dom.Node context)208 public String getNamespaceForPrefix(String prefix, org.w3c.dom.Node context) 209 { 210 211 // Don't need to support this here. Return the current URI for the prefix, 212 // ignoring the context. 213 assertion(true, "can't process a context node in StylesheetHandler!"); 214 215 return null; 216 } 217 218 /** 219 * Utility function to see if the stack contains the given URL. 220 * 221 * @param stack non-null reference to a Stack. 222 * @param url URL string on which an equality test will be performed. 223 * 224 * @return true if the stack contains the url argument. 225 */ stackContains(Stack stack, String url)226 private boolean stackContains(Stack stack, String url) 227 { 228 229 int n = stack.size(); 230 boolean contains = false; 231 232 for (int i = 0; i < n; i++) 233 { 234 String url2 = (String) stack.elementAt(i); 235 236 if (url2.equals(url)) 237 { 238 contains = true; 239 240 break; 241 } 242 } 243 244 return contains; 245 } 246 247 //////////////////////////////////////////////////////////////////// 248 // Implementation of the TRAX TemplatesBuilder interface. 249 //////////////////////////////////////////////////////////////////// 250 251 /** 252 * When this object is used as a ContentHandler or ContentHandler, it will 253 * create a Templates object, which the caller can get once 254 * the SAX events have been completed. 255 * @return The stylesheet object that was created during 256 * the SAX event process, or null if no stylesheet has 257 * been created. 258 * 259 * Author <a href="mailto:scott_boag@lotus.com">Scott Boag</a> 260 * 261 * 262 */ getTemplates()263 public Templates getTemplates() 264 { 265 return getStylesheetRoot(); 266 } 267 268 /** 269 * Set the base ID (URL or system ID) for the stylesheet 270 * created by this builder. This must be set in order to 271 * resolve relative URLs in the stylesheet. 272 * 273 * @param baseID Base URL for this stylesheet. 274 */ setSystemId(String baseID)275 public void setSystemId(String baseID) 276 { 277 pushBaseIndentifier(baseID); 278 } 279 280 /** 281 * Get the base ID (URI or system ID) from where relative 282 * URLs will be resolved. 283 * 284 * @return The systemID that was set with {@link #setSystemId}. 285 */ getSystemId()286 public String getSystemId() 287 { 288 return this.getBaseIdentifier(); 289 } 290 291 //////////////////////////////////////////////////////////////////// 292 // Implementation of the EntityResolver interface. 293 //////////////////////////////////////////////////////////////////// 294 295 /** 296 * Resolve an external entity. 297 * 298 * @param publicId The public identifer, or null if none is 299 * available. 300 * @param systemId The system identifier provided in the XML 301 * document. 302 * @return The new input source, or null to require the 303 * default behaviour. 304 * 305 * @throws org.xml.sax.SAXException if the entity can not be resolved. 306 */ resolveEntity(String publicId, String systemId)307 public InputSource resolveEntity(String publicId, String systemId) 308 throws org.xml.sax.SAXException 309 { 310 return getCurrentProcessor().resolveEntity(this, publicId, systemId); 311 } 312 313 //////////////////////////////////////////////////////////////////// 314 // Implementation of DTDHandler interface. 315 //////////////////////////////////////////////////////////////////// 316 317 /** 318 * Receive notification of a notation declaration. 319 * 320 * <p>By default, do nothing. Application writers may override this 321 * method in a subclass if they wish to keep track of the notations 322 * declared in a document.</p> 323 * 324 * @param name The notation name. 325 * @param publicId The notation public identifier, or null if not 326 * available. 327 * @param systemId The notation system identifier. 328 * @see org.xml.sax.DTDHandler#notationDecl 329 */ notationDecl(String name, String publicId, String systemId)330 public void notationDecl(String name, String publicId, String systemId) 331 { 332 getCurrentProcessor().notationDecl(this, name, publicId, systemId); 333 } 334 335 /** 336 * Receive notification of an unparsed entity declaration. 337 * 338 * @param name The entity name. 339 * @param publicId The entity public identifier, or null if not 340 * available. 341 * @param systemId The entity system identifier. 342 * @param notationName The name of the associated notation. 343 * @see org.xml.sax.DTDHandler#unparsedEntityDecl 344 */ unparsedEntityDecl(String name, String publicId, String systemId, String notationName)345 public void unparsedEntityDecl(String name, String publicId, 346 String systemId, String notationName) 347 { 348 getCurrentProcessor().unparsedEntityDecl(this, name, publicId, systemId, 349 notationName); 350 } 351 352 /** 353 * Given a namespace URI, and a local name or a node type, get the processor 354 * for the element, or return null if not allowed. 355 * 356 * @param uri The Namespace URI, or an empty string. 357 * @param localName The local name (without prefix), or empty string if not namespace processing. 358 * @param rawName The qualified name (with prefix). 359 * 360 * @return A non-null reference to a element processor. 361 * 362 * @throws org.xml.sax.SAXException if the element is not allowed in the 363 * found position in the stylesheet. 364 */ getProcessorFor( String uri, String localName, String rawName)365 XSLTElementProcessor getProcessorFor( 366 String uri, String localName, String rawName) 367 throws org.xml.sax.SAXException 368 { 369 370 XSLTElementProcessor currentProcessor = getCurrentProcessor(); 371 XSLTElementDef def = currentProcessor.getElemDef(); 372 XSLTElementProcessor elemProcessor = def.getProcessorFor(uri, localName); 373 374 if (null == elemProcessor 375 && !(currentProcessor instanceof ProcessorStylesheetDoc) 376 && ((null == getStylesheet() 377 || Double.valueOf(getStylesheet().getVersion()).doubleValue() 378 > Constants.XSLTVERSUPPORTED) 379 ||(!uri.equals(Constants.S_XSLNAMESPACEURL) && 380 currentProcessor instanceof ProcessorStylesheetElement) 381 || getElemVersion() > Constants.XSLTVERSUPPORTED 382 )) 383 { 384 elemProcessor = def.getProcessorForUnknown(uri, localName); 385 } 386 387 if (null == elemProcessor) 388 error(XSLMessages.createMessage(XSLTErrorResources.ER_NOT_ALLOWED_IN_POSITION, new Object[]{rawName}),null);//rawName + " is not allowed in this position in the stylesheet!", 389 390 391 return elemProcessor; 392 } 393 394 //////////////////////////////////////////////////////////////////// 395 // Implementation of ContentHandler interface. 396 //////////////////////////////////////////////////////////////////// 397 398 /** 399 * Receive a Locator object for document events. 400 * This is called by the parser to push a locator for the 401 * stylesheet being parsed. The stack needs to be popped 402 * after the stylesheet has been parsed. We pop in 403 * popStylesheet. 404 * 405 * @param locator A locator for all SAX document events. 406 * @see org.xml.sax.ContentHandler#setDocumentLocator 407 * @see org.xml.sax.Locator 408 */ setDocumentLocator(Locator locator)409 public void setDocumentLocator(Locator locator) 410 { 411 412 // System.out.println("pushing locator for: "+locator.getSystemId()); 413 m_stylesheetLocatorStack.push(new SAXSourceLocator(locator)); 414 } 415 416 /** 417 * The level of the stylesheet we are at. 418 */ 419 private int m_stylesheetLevel = -1; 420 421 /** 422 * Receive notification of the beginning of the document. 423 * 424 * @see org.xml.sax.ContentHandler#startDocument 425 * 426 * @throws org.xml.sax.SAXException Any SAX exception, possibly 427 * wrapping another exception. 428 */ startDocument()429 public void startDocument() throws org.xml.sax.SAXException 430 { 431 m_stylesheetLevel++; 432 pushSpaceHandling(false); 433 } 434 435 /** m_parsingComplete becomes true when the top-level stylesheet and all 436 * its included/imported stylesheets have been been fully parsed, as an 437 * indication that composition/optimization/compilation can begin. 438 * @see isStylesheetParsingComplete */ 439 private boolean m_parsingComplete = false; 440 441 /** 442 * Test whether the _last_ endDocument() has been processed. 443 * This is needed as guidance for stylesheet optimization 444 * and compilation engines, which generally don't want to start 445 * until all included and imported stylesheets have been fully 446 * parsed. 447 * 448 * @return true iff the complete stylesheet tree has been built. 449 */ isStylesheetParsingComplete()450 public boolean isStylesheetParsingComplete() 451 { 452 return m_parsingComplete; 453 } 454 455 /** 456 * Receive notification of the end of the document. 457 * 458 * @see org.xml.sax.ContentHandler#endDocument 459 * 460 * @throws org.xml.sax.SAXException Any SAX exception, possibly 461 * wrapping another exception. 462 */ endDocument()463 public void endDocument() throws org.xml.sax.SAXException 464 { 465 466 try 467 { 468 if (null != getStylesheetRoot()) 469 { 470 if (0 == m_stylesheetLevel) 471 getStylesheetRoot().recompose(); 472 } 473 else 474 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_STYLESHEETROOT, null)); //"Did not find the stylesheet root!"); 475 476 XSLTElementProcessor elemProcessor = getCurrentProcessor(); 477 478 if (null != elemProcessor) 479 elemProcessor.startNonText(this); 480 481 m_stylesheetLevel--; 482 483 popSpaceHandling(); 484 485 // WARNING: This test works only as long as stylesheets are parsed 486 // more or less recursively. If we switch to an iterative "work-list" 487 // model, this will become true prematurely. In that case, 488 // isStylesheetParsingComplete() will have to be adjusted to be aware 489 // of the worklist. 490 m_parsingComplete = (m_stylesheetLevel < 0); 491 } 492 catch (TransformerException te) 493 { 494 throw new org.xml.sax.SAXException(te); 495 } 496 } 497 498 private java.util.Vector m_prefixMappings = new java.util.Vector(); 499 500 /** 501 * Receive notification of the start of a Namespace mapping. 502 * 503 * <p>By default, do nothing. Application writers may override this 504 * method in a subclass to take specific actions at the start of 505 * each element (such as allocating a new tree node or writing 506 * output to a file).</p> 507 * 508 * @param prefix The Namespace prefix being declared. 509 * @param uri The Namespace URI mapped to the prefix. 510 * @see org.xml.sax.ContentHandler#startPrefixMapping 511 * 512 * @throws org.xml.sax.SAXException Any SAX exception, possibly 513 * wrapping another exception. 514 */ startPrefixMapping(String prefix, String uri)515 public void startPrefixMapping(String prefix, String uri) 516 throws org.xml.sax.SAXException 517 { 518 519 // m_nsSupport.pushContext(); 520 // this.getNamespaceSupport().declarePrefix(prefix, uri); 521 //m_prefixMappings.add(prefix); // JDK 1.2+ only -sc 522 //m_prefixMappings.add(uri); // JDK 1.2+ only -sc 523 m_prefixMappings.addElement(prefix); // JDK 1.1.x compat -sc 524 m_prefixMappings.addElement(uri); // JDK 1.1.x compat -sc 525 } 526 527 /** 528 * Receive notification of the end of a Namespace mapping. 529 * 530 * <p>By default, do nothing. Application writers may override this 531 * method in a subclass to take specific actions at the start of 532 * each element (such as allocating a new tree node or writing 533 * output to a file).</p> 534 * 535 * @param prefix The Namespace prefix being declared. 536 * @see org.xml.sax.ContentHandler#endPrefixMapping 537 * 538 * @throws org.xml.sax.SAXException Any SAX exception, possibly 539 * wrapping another exception. 540 */ endPrefixMapping(String prefix)541 public void endPrefixMapping(String prefix) throws org.xml.sax.SAXException 542 { 543 544 // m_nsSupport.popContext(); 545 } 546 547 /** 548 * Flush the characters buffer. 549 * 550 * @throws org.xml.sax.SAXException 551 */ flushCharacters()552 private void flushCharacters() throws org.xml.sax.SAXException 553 { 554 555 XSLTElementProcessor elemProcessor = getCurrentProcessor(); 556 557 if (null != elemProcessor) 558 elemProcessor.startNonText(this); 559 } 560 561 /** 562 * Receive notification of the start of an element. 563 * 564 * @param uri The Namespace URI, or an empty string. 565 * @param localName The local name (without prefix), or empty string if not namespace processing. 566 * @param rawName The qualified name (with prefix). 567 * @param attributes The specified or defaulted attributes. 568 * 569 * @throws org.xml.sax.SAXException 570 */ startElement( String uri, String localName, String rawName, Attributes attributes)571 public void startElement( 572 String uri, String localName, String rawName, Attributes attributes) 573 throws org.xml.sax.SAXException 574 { 575 NamespaceSupport nssupport = this.getNamespaceSupport(); 576 nssupport.pushContext(); 577 578 int n = m_prefixMappings.size(); 579 580 for (int i = 0; i < n; i++) 581 { 582 String prefix = (String)m_prefixMappings.elementAt(i++); 583 String nsURI = (String)m_prefixMappings.elementAt(i); 584 nssupport.declarePrefix(prefix, nsURI); 585 } 586 //m_prefixMappings.clear(); // JDK 1.2+ only -sc 587 m_prefixMappings.removeAllElements(); // JDK 1.1.x compat -sc 588 589 m_elementID++; 590 591 // This check is currently done for all elements. We should possibly consider 592 // limiting this check to xsl:stylesheet elements only since that is all it really 593 // applies to. Also, it could be bypassed if m_shouldProcess is already true. 594 // In other words, the next two statements could instead look something like this: 595 // if (!m_shouldProcess) 596 // { 597 // if (localName.equals(Constants.ELEMNAME_STYLESHEET_STRING) && 598 // url.equals(Constants.S_XSLNAMESPACEURL)) 599 // { 600 // checkForFragmentID(attributes); 601 // if (!m_shouldProcess) 602 // return; 603 // } 604 // else 605 // return; 606 // } 607 // I didn't include this code statement at this time because in practice 608 // it is a small performance hit and I was waiting to see if its absence 609 // caused a problem. - GLP 610 611 checkForFragmentID(attributes); 612 613 if (!m_shouldProcess) 614 return; 615 616 flushCharacters(); 617 618 pushSpaceHandling(attributes); 619 620 XSLTElementProcessor elemProcessor = getProcessorFor(uri, localName, 621 rawName); 622 623 if(null != elemProcessor) // defensive, for better multiple error reporting. -sb 624 { 625 this.pushProcessor(elemProcessor); 626 elemProcessor.startElement(this, uri, localName, rawName, attributes); 627 } 628 else 629 { 630 m_shouldProcess = false; 631 popSpaceHandling(); 632 } 633 634 } 635 636 /** 637 * Receive notification of the end of an element. 638 * 639 * @param uri The Namespace URI, or an empty string. 640 * @param localName The local name (without prefix), or empty string if not namespace processing. 641 * @param rawName The qualified name (with prefix). 642 * @see org.xml.sax.ContentHandler#endElement 643 * 644 * @throws org.xml.sax.SAXException Any SAX exception, possibly 645 * wrapping another exception. 646 */ endElement(String uri, String localName, String rawName)647 public void endElement(String uri, String localName, String rawName) 648 throws org.xml.sax.SAXException 649 { 650 651 m_elementID--; 652 653 if (!m_shouldProcess) 654 return; 655 656 if ((m_elementID + 1) == m_fragmentID) 657 m_shouldProcess = false; 658 659 flushCharacters(); 660 661 popSpaceHandling(); 662 663 XSLTElementProcessor p = getCurrentProcessor(); 664 665 p.endElement(this, uri, localName, rawName); 666 this.popProcessor(); 667 this.getNamespaceSupport().popContext(); 668 } 669 670 /** 671 * Receive notification of character data inside an element. 672 * 673 * @param ch The characters. 674 * @param start The start position in the character array. 675 * @param length The number of characters to use from the 676 * character array. 677 * @see org.xml.sax.ContentHandler#characters 678 * 679 * @throws org.xml.sax.SAXException Any SAX exception, possibly 680 * wrapping another exception. 681 */ characters(char ch[], int start, int length)682 public void characters(char ch[], int start, int length) 683 throws org.xml.sax.SAXException 684 { 685 686 if (!m_shouldProcess) 687 return; 688 689 XSLTElementProcessor elemProcessor = getCurrentProcessor(); 690 XSLTElementDef def = elemProcessor.getElemDef(); 691 692 if (def.getType() != XSLTElementDef.T_PCDATA) 693 elemProcessor = def.getProcessorFor(null, "text()"); 694 695 if (null == elemProcessor) 696 { 697 698 // If it's whitespace, just ignore it, otherwise flag an error. 699 if (!XMLCharacterRecognizer.isWhiteSpace(ch, start, length)) 700 error( 701 XSLMessages.createMessage(XSLTErrorResources.ER_NONWHITESPACE_NOT_ALLOWED_IN_POSITION, null),null);//"Non-whitespace text is not allowed in this position in the stylesheet!", 702 703 } 704 else 705 elemProcessor.characters(this, ch, start, length); 706 } 707 708 /** 709 * Receive notification of ignorable whitespace in element content. 710 * 711 * @param ch The whitespace characters. 712 * @param start The start position in the character array. 713 * @param length The number of characters to use from the 714 * character array. 715 * @see org.xml.sax.ContentHandler#ignorableWhitespace 716 * 717 * @throws org.xml.sax.SAXException Any SAX exception, possibly 718 * wrapping another exception. 719 */ ignorableWhitespace(char ch[], int start, int length)720 public void ignorableWhitespace(char ch[], int start, int length) 721 throws org.xml.sax.SAXException 722 { 723 724 if (!m_shouldProcess) 725 return; 726 727 getCurrentProcessor().ignorableWhitespace(this, ch, start, length); 728 } 729 730 /** 731 * Receive notification of a processing instruction. 732 * 733 * <p>The Parser will invoke this method once for each processing 734 * instruction found: note that processing instructions may occur 735 * before or after the main document element.</p> 736 * 737 * <p>A SAX parser should never report an XML declaration (XML 1.0, 738 * section 2.8) or a text declaration (XML 1.0, section 4.3.1) 739 * using this method.</p> 740 * 741 * <p>By default, do nothing. Application writers may override this 742 * method in a subclass to take specific actions for each 743 * processing instruction, such as setting status variables or 744 * invoking other methods.</p> 745 * 746 * @param target The processing instruction target. 747 * @param data The processing instruction data, or null if 748 * none is supplied. 749 * @see org.xml.sax.ContentHandler#processingInstruction 750 * 751 * @throws org.xml.sax.SAXException Any SAX exception, possibly 752 * wrapping another exception. 753 */ processingInstruction(String target, String data)754 public void processingInstruction(String target, String data) 755 throws org.xml.sax.SAXException 756 { 757 if (!m_shouldProcess) 758 return; 759 760 // Recreating Scott's kluge: 761 // A xsl:for-each or xsl:apply-templates may have a special 762 // PI that tells us not to cache the document. This PI 763 // should really be namespaced. 764 // String localName = getLocalName(target); 765 // String ns = m_stylesheet.getNamespaceFromStack(target); 766 // 767 // %REVIEW%: We need a better PI architecture 768 769 String prefix="",ns="", localName=target; 770 int colon=target.indexOf(':'); 771 if(colon>=0) 772 { 773 ns=getNamespaceForPrefix(prefix=target.substring(0,colon)); 774 localName=target.substring(colon+1); 775 } 776 777 try 778 { 779 // A xsl:for-each or xsl:apply-templates may have a special 780 // PI that tells us not to cache the document. This PI 781 // should really be namespaced... but since the XML Namespaces 782 // spec never defined namespaces as applying to PI's, and since 783 // the testcase we're trying to support is inconsistant in whether 784 // it binds the prefix, I'm going to make this sloppy for 785 // testing purposes. 786 if( 787 "xalan-doc-cache-off".equals(target) || 788 "xalan:doc-cache-off".equals(target) || 789 ("doc-cache-off".equals(localName) && 790 ns.equals("org.apache.xalan.xslt.extensions.Redirect") ) 791 ) 792 { 793 if(!(m_elems.peek() instanceof ElemForEach)) 794 throw new TransformerException 795 ("xalan:doc-cache-off not allowed here!", 796 getLocator()); 797 ElemForEach elem = (ElemForEach)m_elems.peek(); 798 799 elem.m_doc_cache_off = true; 800 801 //System.out.println("JJK***** Recognized <? {"+ns+"}"+prefix+":"+localName+" "+data+"?>"); 802 } 803 } 804 catch(Exception e) 805 { 806 // JJK: Officially, unknown PIs can just be ignored. 807 // Do we want to issue a warning? 808 } 809 810 811 flushCharacters(); 812 getCurrentProcessor().processingInstruction(this, target, data); 813 } 814 815 /** 816 * Receive notification of a skipped entity. 817 * 818 * <p>By default, do nothing. Application writers may override this 819 * method in a subclass to take specific actions for each 820 * processing instruction, such as setting status variables or 821 * invoking other methods.</p> 822 * 823 * @param name The name of the skipped entity. 824 * @see org.xml.sax.ContentHandler#processingInstruction 825 * 826 * @throws org.xml.sax.SAXException Any SAX exception, possibly 827 * wrapping another exception. 828 */ skippedEntity(String name)829 public void skippedEntity(String name) throws org.xml.sax.SAXException 830 { 831 832 if (!m_shouldProcess) 833 return; 834 835 getCurrentProcessor().skippedEntity(this, name); 836 } 837 838 /** 839 * Warn the user of an problem. 840 * 841 * @param msg An key into the {@link org.apache.xalan.res.XSLTErrorResources} 842 * table, that is one of the WG_ prefixed definitions. 843 * @param args An array of arguments for the given warning. 844 * 845 * @throws org.xml.sax.SAXException that wraps a 846 * {@link javax.xml.transform.TransformerException} if the current 847 * {@link javax.xml.transform.ErrorListener#warning} 848 * method chooses to flag this condition as an error. 849 * @xsl.usage internal 850 */ warn(String msg, Object args[])851 public void warn(String msg, Object args[]) throws org.xml.sax.SAXException 852 { 853 854 String formattedMsg = XSLMessages.createWarning(msg, args); 855 SAXSourceLocator locator = getLocator(); 856 ErrorListener handler = m_stylesheetProcessor.getErrorListener(); 857 858 try 859 { 860 if (null != handler) 861 handler.warning(new TransformerException(formattedMsg, locator)); 862 } 863 catch (TransformerException te) 864 { 865 throw new org.xml.sax.SAXException(te); 866 } 867 } 868 869 /** 870 * Assert that a condition is true. If it is not true, throw an error. 871 * 872 * @param condition false if an error should not be thrown, otherwise true. 873 * @param msg Error message to be passed to the RuntimeException as an 874 * argument. 875 * @throws RuntimeException if the condition is not true. 876 * @xsl.usage internal 877 */ assertion(boolean condition, String msg)878 private void assertion(boolean condition, String msg) throws RuntimeException 879 { 880 if (!condition) 881 throw new RuntimeException(msg); 882 } 883 884 /** 885 * Tell the user of an error, and probably throw an 886 * exception. 887 * 888 * @param msg An error message. 889 * @param e An error which the SAXException should wrap. 890 * 891 * @throws org.xml.sax.SAXException that wraps a 892 * {@link javax.xml.transform.TransformerException} if the current 893 * {@link javax.xml.transform.ErrorListener#error} 894 * method chooses to flag this condition as an error. 895 * @xsl.usage internal 896 */ error(String msg, Exception e)897 protected void error(String msg, Exception e) 898 throws org.xml.sax.SAXException 899 { 900 901 SAXSourceLocator locator = getLocator(); 902 ErrorListener handler = m_stylesheetProcessor.getErrorListener(); 903 TransformerException pe; 904 905 if (!(e instanceof TransformerException)) 906 { 907 pe = (null == e) 908 ? new TransformerException(msg, locator) 909 : new TransformerException(msg, locator, e); 910 } 911 else 912 pe = (TransformerException) e; 913 914 if (null != handler) 915 { 916 try 917 { 918 handler.error(pe); 919 } 920 catch (TransformerException te) 921 { 922 throw new org.xml.sax.SAXException(te); 923 } 924 } 925 else 926 throw new org.xml.sax.SAXException(pe); 927 } 928 929 /** 930 * Tell the user of an error, and probably throw an 931 * exception. 932 * 933 * @param msg A key into the {@link org.apache.xalan.res.XSLTErrorResources} 934 * table, that is one of the WG_ prefixed definitions. 935 * @param args An array of arguments for the given warning. 936 * @param e An error which the SAXException should wrap. 937 * 938 * @throws org.xml.sax.SAXException that wraps a 939 * {@link javax.xml.transform.TransformerException} if the current 940 * {@link javax.xml.transform.ErrorListener#error} 941 * method chooses to flag this condition as an error. 942 * @xsl.usage internal 943 */ error(String msg, Object args[], Exception e)944 protected void error(String msg, Object args[], Exception e) 945 throws org.xml.sax.SAXException 946 { 947 948 String formattedMsg = XSLMessages.createMessage(msg, args); 949 950 error(formattedMsg, e); 951 } 952 953 /** 954 * Receive notification of a XSLT processing warning. 955 * 956 * @param e The warning information encoded as an exception. 957 * 958 * @throws org.xml.sax.SAXException that wraps a 959 * {@link javax.xml.transform.TransformerException} if the current 960 * {@link javax.xml.transform.ErrorListener#warning} 961 * method chooses to flag this condition as an error. 962 */ warning(org.xml.sax.SAXParseException e)963 public void warning(org.xml.sax.SAXParseException e) 964 throws org.xml.sax.SAXException 965 { 966 967 String formattedMsg = e.getMessage(); 968 SAXSourceLocator locator = getLocator(); 969 ErrorListener handler = m_stylesheetProcessor.getErrorListener(); 970 971 try 972 { 973 handler.warning(new TransformerException(formattedMsg, locator)); 974 } 975 catch (TransformerException te) 976 { 977 throw new org.xml.sax.SAXException(te); 978 } 979 } 980 981 /** 982 * Receive notification of a recoverable XSLT processing error. 983 * 984 * @param e The error information encoded as an exception. 985 * 986 * @throws org.xml.sax.SAXException that wraps a 987 * {@link javax.xml.transform.TransformerException} if the current 988 * {@link javax.xml.transform.ErrorListener#error} 989 * method chooses to flag this condition as an error. 990 */ error(org.xml.sax.SAXParseException e)991 public void error(org.xml.sax.SAXParseException e) 992 throws org.xml.sax.SAXException 993 { 994 995 String formattedMsg = e.getMessage(); 996 SAXSourceLocator locator = getLocator(); 997 ErrorListener handler = m_stylesheetProcessor.getErrorListener(); 998 999 try 1000 { 1001 handler.error(new TransformerException(formattedMsg, locator)); 1002 } 1003 catch (TransformerException te) 1004 { 1005 throw new org.xml.sax.SAXException(te); 1006 } 1007 } 1008 1009 /** 1010 * Report a fatal XSLT processing error. 1011 * 1012 * @param e The error information encoded as an exception. 1013 * 1014 * @throws org.xml.sax.SAXException that wraps a 1015 * {@link javax.xml.transform.TransformerException} if the current 1016 * {@link javax.xml.transform.ErrorListener#fatalError} 1017 * method chooses to flag this condition as an error. 1018 */ fatalError(org.xml.sax.SAXParseException e)1019 public void fatalError(org.xml.sax.SAXParseException e) 1020 throws org.xml.sax.SAXException 1021 { 1022 1023 String formattedMsg = e.getMessage(); 1024 SAXSourceLocator locator = getLocator(); 1025 ErrorListener handler = m_stylesheetProcessor.getErrorListener(); 1026 1027 try 1028 { 1029 handler.fatalError(new TransformerException(formattedMsg, locator)); 1030 } 1031 catch (TransformerException te) 1032 { 1033 throw new org.xml.sax.SAXException(te); 1034 } 1035 } 1036 1037 /** 1038 * If we have a URL to a XML fragment, this is set 1039 * to false until the ID is found. 1040 * (warning: I worry that this should be in a stack). 1041 */ 1042 private boolean m_shouldProcess = true; 1043 1044 /** 1045 * If we have a URL to a XML fragment, the value is stored 1046 * in this string, and the m_shouldProcess flag is set to 1047 * false until we match an ID with this string. 1048 * (warning: I worry that this should be in a stack). 1049 */ 1050 private String m_fragmentIDString; 1051 1052 /** 1053 * Keep track of the elementID, so we can tell when 1054 * is has completed. This isn't a real ID, but rather 1055 * a nesting level. However, it's good enough for 1056 * our purposes. 1057 * (warning: I worry that this should be in a stack). 1058 */ 1059 private int m_elementID = 0; 1060 1061 /** 1062 * The ID of the fragment that has been found 1063 * (warning: I worry that this should be in a stack). 1064 */ 1065 private int m_fragmentID = 0; 1066 1067 /** 1068 * Check to see if an ID attribute matched the #id, called 1069 * from startElement. 1070 * 1071 * @param attributes The specified or defaulted attributes. 1072 */ checkForFragmentID(Attributes attributes)1073 private void checkForFragmentID(Attributes attributes) 1074 { 1075 1076 if (!m_shouldProcess) 1077 { 1078 if ((null != attributes) && (null != m_fragmentIDString)) 1079 { 1080 int n = attributes.getLength(); 1081 1082 for (int i = 0; i < n; i++) 1083 { 1084 String name = attributes.getQName(i); 1085 1086 if (name.equals(Constants.ATTRNAME_ID)) 1087 { 1088 String val = attributes.getValue(i); 1089 1090 if (val.equalsIgnoreCase(m_fragmentIDString)) 1091 { 1092 m_shouldProcess = true; 1093 m_fragmentID = m_elementID; 1094 } 1095 } 1096 } 1097 } 1098 } 1099 } 1100 1101 /** 1102 * The XSLT TransformerFactory for needed services. 1103 */ 1104 private TransformerFactoryImpl m_stylesheetProcessor; 1105 1106 /** 1107 * Get the XSLT TransformerFactoryImpl for needed services. 1108 * TODO: This method should be renamed. 1109 * 1110 * @return The TransformerFactoryImpl that owns this handler. 1111 */ getStylesheetProcessor()1112 public TransformerFactoryImpl getStylesheetProcessor() 1113 { 1114 return m_stylesheetProcessor; 1115 } 1116 1117 /** 1118 * If getStylesheetType returns this value, the current stylesheet 1119 * is a root stylesheet. 1120 * @xsl.usage internal 1121 */ 1122 public static final int STYPE_ROOT = 1; 1123 1124 /** 1125 * If getStylesheetType returns this value, the current stylesheet 1126 * is an included stylesheet. 1127 * @xsl.usage internal 1128 */ 1129 public static final int STYPE_INCLUDE = 2; 1130 1131 /** 1132 * If getStylesheetType returns this value, the current stylesheet 1133 * is an imported stylesheet. 1134 * @xsl.usage internal 1135 */ 1136 public static final int STYPE_IMPORT = 3; 1137 1138 /** The current stylesheet type. */ 1139 private int m_stylesheetType = STYPE_ROOT; 1140 1141 /** 1142 * Get the type of stylesheet that should be built 1143 * or is being processed. 1144 * 1145 * @return one of STYPE_ROOT, STYPE_INCLUDE, or STYPE_IMPORT. 1146 */ getStylesheetType()1147 int getStylesheetType() 1148 { 1149 return m_stylesheetType; 1150 } 1151 1152 /** 1153 * Set the type of stylesheet that should be built 1154 * or is being processed. 1155 * 1156 * @param type Must be one of STYPE_ROOT, STYPE_INCLUDE, or STYPE_IMPORT. 1157 */ setStylesheetType(int type)1158 void setStylesheetType(int type) 1159 { 1160 m_stylesheetType = type; 1161 } 1162 1163 /** 1164 * The stack of stylesheets being processed. 1165 */ 1166 private Stack m_stylesheets = new Stack(); 1167 1168 /** 1169 * Return the stylesheet that this handler is constructing. 1170 * 1171 * @return The current stylesheet that is on top of the stylesheets stack, 1172 * or null if no stylesheet is on the stylesheets stack. 1173 */ getStylesheet()1174 Stylesheet getStylesheet() 1175 { 1176 return (m_stylesheets.size() == 0) 1177 ? null : (Stylesheet) m_stylesheets.peek(); 1178 } 1179 1180 /** 1181 * Return the last stylesheet that was popped off the stylesheets stack. 1182 * 1183 * @return The last popped stylesheet, or null. 1184 */ getLastPoppedStylesheet()1185 Stylesheet getLastPoppedStylesheet() 1186 { 1187 return m_lastPoppedStylesheet; 1188 } 1189 1190 /** 1191 * Return the stylesheet root that this handler is constructing. 1192 * 1193 * @return The root stylesheet of the stylesheets tree. 1194 */ getStylesheetRoot()1195 public StylesheetRoot getStylesheetRoot() 1196 { 1197 if (m_stylesheetRoot != null){ 1198 m_stylesheetRoot.setOptimizer(m_optimize); 1199 m_stylesheetRoot.setIncremental(m_incremental); 1200 m_stylesheetRoot.setSource_location(m_source_location); 1201 } 1202 return m_stylesheetRoot; 1203 } 1204 1205 /** The root stylesheet of the stylesheets tree. */ 1206 StylesheetRoot m_stylesheetRoot; 1207 1208 /** The last stylesheet that was popped off the stylesheets stack. */ 1209 Stylesheet m_lastPoppedStylesheet; 1210 1211 /** 1212 * Push the current stylesheet being constructed. If no other stylesheets 1213 * have been pushed onto the stack, assume the argument is a stylesheet 1214 * root, and also set the stylesheet root member. 1215 * 1216 * @param s non-null reference to a stylesheet. 1217 */ pushStylesheet(Stylesheet s)1218 public void pushStylesheet(Stylesheet s) 1219 { 1220 1221 if (m_stylesheets.size() == 0) 1222 m_stylesheetRoot = (StylesheetRoot) s; 1223 1224 m_stylesheets.push(s); 1225 } 1226 1227 /** 1228 * Pop the last stylesheet pushed, and return the stylesheet that this 1229 * handler is constructing, and set the last popped stylesheet member. 1230 * Also pop the stylesheet locator stack. 1231 * 1232 * @return The stylesheet popped off the stack, or the last popped stylesheet. 1233 */ popStylesheet()1234 Stylesheet popStylesheet() 1235 { 1236 1237 // The stylesheetLocatorStack needs to be popped because 1238 // a locator was pushed in for this stylesheet by the SAXparser by calling 1239 // setDocumentLocator(). 1240 if (!m_stylesheetLocatorStack.isEmpty()) 1241 m_stylesheetLocatorStack.pop(); 1242 1243 if (!m_stylesheets.isEmpty()) 1244 m_lastPoppedStylesheet = (Stylesheet) m_stylesheets.pop(); 1245 1246 // Shouldn't this be null if stylesheets is empty? -sb 1247 return m_lastPoppedStylesheet; 1248 } 1249 1250 /** 1251 * The stack of current processors. 1252 */ 1253 private Stack m_processors = new Stack(); 1254 1255 /** 1256 * Get the current XSLTElementProcessor at the top of the stack. 1257 * 1258 * @return Valid XSLTElementProcessor, which should never be null. 1259 */ getCurrentProcessor()1260 XSLTElementProcessor getCurrentProcessor() 1261 { 1262 return (XSLTElementProcessor) m_processors.peek(); 1263 } 1264 1265 /** 1266 * Push the current XSLTElementProcessor onto the top of the stack. 1267 * 1268 * @param processor non-null reference to the current element processor. 1269 */ pushProcessor(XSLTElementProcessor processor)1270 void pushProcessor(XSLTElementProcessor processor) 1271 { 1272 m_processors.push(processor); 1273 } 1274 1275 /** 1276 * Pop the current XSLTElementProcessor from the top of the stack. 1277 * @return the XSLTElementProcessor which was popped. 1278 */ popProcessor()1279 XSLTElementProcessor popProcessor() 1280 { 1281 return (XSLTElementProcessor) m_processors.pop(); 1282 } 1283 1284 /** 1285 * The root of the XSLT Schema, which tells us how to 1286 * transition content handlers, create elements, etc. 1287 * For the moment at least, this can't be static, since 1288 * the processors store state. 1289 */ 1290 private XSLTSchema m_schema = new XSLTSchema(); 1291 1292 /** 1293 * Get the root of the XSLT Schema, which tells us how to 1294 * transition content handlers, create elements, etc. 1295 * 1296 * @return The root XSLT Schema, which should never be null. 1297 * @xsl.usage internal 1298 */ getSchema()1299 public XSLTSchema getSchema() 1300 { 1301 return m_schema; 1302 } 1303 1304 /** 1305 * The stack of elements, pushed and popped as events occur. 1306 */ 1307 private Stack m_elems = new Stack(); 1308 1309 /** 1310 * Get the current ElemTemplateElement at the top of the stack. 1311 * @return Valid ElemTemplateElement, which may be null. 1312 */ getElemTemplateElement()1313 ElemTemplateElement getElemTemplateElement() 1314 { 1315 1316 try 1317 { 1318 return (ElemTemplateElement) m_elems.peek(); 1319 } 1320 catch (java.util.EmptyStackException ese) 1321 { 1322 return null; 1323 } 1324 } 1325 1326 /** An increasing number that is used to indicate the order in which this element 1327 * was encountered during the parse of the XSLT tree. 1328 */ 1329 private int m_docOrderCount = 0; 1330 1331 /** 1332 * Returns the next m_docOrderCount number and increments the number for future use. 1333 */ nextUid()1334 int nextUid() 1335 { 1336 return m_docOrderCount++; 1337 } 1338 1339 /** 1340 * Push the current XSLTElementProcessor to the top of the stack. As a 1341 * side-effect, set the document order index (simply because this is a 1342 * convenient place to set it). 1343 * 1344 * @param elem Should be a non-null reference to the intended current 1345 * template element. 1346 */ pushElemTemplateElement(ElemTemplateElement elem)1347 void pushElemTemplateElement(ElemTemplateElement elem) 1348 { 1349 1350 if (elem.getUid() == -1) 1351 elem.setUid(nextUid()); 1352 1353 m_elems.push(elem); 1354 } 1355 1356 /** 1357 * Get the current XSLTElementProcessor from the top of the stack. 1358 * @return the ElemTemplateElement which was popped. 1359 */ popElemTemplateElement()1360 ElemTemplateElement popElemTemplateElement() 1361 { 1362 return (ElemTemplateElement) m_elems.pop(); 1363 } 1364 1365 /** 1366 * This will act as a stack to keep track of the 1367 * current include base. 1368 */ 1369 Stack m_baseIdentifiers = new Stack(); 1370 1371 /** 1372 * Push a base identifier onto the base URI stack. 1373 * 1374 * @param baseID The current base identifier for this position in the 1375 * stylesheet, which may be a fragment identifier, or which may be null. 1376 * @see <a href="http://www.w3.org/TR/xslt#base-uri"> 1377 * Section 3.2 Base URI of XSLT specification.</a> 1378 */ pushBaseIndentifier(String baseID)1379 void pushBaseIndentifier(String baseID) 1380 { 1381 1382 if (null != baseID) 1383 { 1384 int posOfHash = baseID.indexOf('#'); 1385 1386 if (posOfHash > -1) 1387 { 1388 m_fragmentIDString = baseID.substring(posOfHash + 1); 1389 m_shouldProcess = false; 1390 } 1391 else 1392 m_shouldProcess = true; 1393 } 1394 else 1395 m_shouldProcess = true; 1396 1397 m_baseIdentifiers.push(baseID); 1398 } 1399 1400 /** 1401 * Pop a base URI from the stack. 1402 * @return baseIdentifier. 1403 */ popBaseIndentifier()1404 String popBaseIndentifier() 1405 { 1406 return (String) m_baseIdentifiers.pop(); 1407 } 1408 1409 /** 1410 * Return the base identifier. 1411 * 1412 * @return The base identifier of the current stylesheet. 1413 */ getBaseIdentifier()1414 public String getBaseIdentifier() 1415 { 1416 1417 // Try to get the baseIdentifier from the baseIdentifier's stack, 1418 // which may not be the same thing as the value found in the 1419 // SourceLocators stack. 1420 String base = (String) (m_baseIdentifiers.isEmpty() 1421 ? null : m_baseIdentifiers.peek()); 1422 1423 // Otherwise try the stylesheet. 1424 if (null == base) 1425 { 1426 SourceLocator locator = getLocator(); 1427 1428 base = (null == locator) ? "" : locator.getSystemId(); 1429 } 1430 1431 return base; 1432 } 1433 1434 /** 1435 * The top of this stack should contain the currently processed 1436 * stylesheet SAX locator object. 1437 */ 1438 private Stack m_stylesheetLocatorStack = new Stack(); 1439 1440 /** 1441 * Get the current stylesheet Locator object. 1442 * 1443 * @return non-null reference to the current locator object. 1444 */ getLocator()1445 public SAXSourceLocator getLocator() 1446 { 1447 1448 if (m_stylesheetLocatorStack.isEmpty()) 1449 { 1450 SAXSourceLocator locator = new SAXSourceLocator(); 1451 1452 locator.setSystemId(this.getStylesheetProcessor().getDOMsystemID()); 1453 1454 return locator; 1455 1456 // m_stylesheetLocatorStack.push(locator); 1457 } 1458 1459 return ((SAXSourceLocator) m_stylesheetLocatorStack.peek()); 1460 } 1461 1462 /** 1463 * A stack of URL hrefs for imported stylesheets. This is 1464 * used to diagnose circular imports. 1465 */ 1466 private Stack m_importStack = new Stack(); 1467 1468 /** 1469 * A stack of Source objects obtained from a URIResolver, 1470 * for each element in this stack there is a 1-1 correspondence 1471 * with an element in the m_importStack. 1472 */ 1473 private Stack m_importSourceStack = new Stack(); 1474 1475 /** 1476 * Push an import href onto the stylesheet stack. 1477 * 1478 * @param hrefUrl non-null reference to the URL for the current imported 1479 * stylesheet. 1480 */ pushImportURL(String hrefUrl)1481 void pushImportURL(String hrefUrl) 1482 { 1483 m_importStack.push(hrefUrl); 1484 } 1485 1486 /** 1487 * Push the Source of an import href onto the stylesheet stack, 1488 * obtained from a URIResolver, null if there is no URIResolver, 1489 * or if that resolver returned null. 1490 */ pushImportSource(Source sourceFromURIResolver)1491 void pushImportSource(Source sourceFromURIResolver) 1492 { 1493 m_importSourceStack.push(sourceFromURIResolver); 1494 } 1495 1496 /** 1497 * See if the imported stylesheet stack already contains 1498 * the given URL. Used to test for recursive imports. 1499 * 1500 * @param hrefUrl non-null reference to a URL string. 1501 * 1502 * @return true if the URL is on the import stack. 1503 */ importStackContains(String hrefUrl)1504 boolean importStackContains(String hrefUrl) 1505 { 1506 return stackContains(m_importStack, hrefUrl); 1507 } 1508 1509 /** 1510 * Pop an import href from the stylesheet stack. 1511 * 1512 * @return non-null reference to the import URL that was popped. 1513 */ popImportURL()1514 String popImportURL() 1515 { 1516 return (String) m_importStack.pop(); 1517 } 1518 peekImportURL()1519 String peekImportURL() 1520 { 1521 return (String) m_importStack.peek(); 1522 } 1523 peekSourceFromURIResolver()1524 Source peekSourceFromURIResolver() 1525 { 1526 return (Source) m_importSourceStack.peek(); 1527 } 1528 1529 /** 1530 * Pop a Source from a user provided URIResolver, corresponding 1531 * to the URL popped from the m_importStack. 1532 */ popImportSource()1533 Source popImportSource() 1534 { 1535 return (Source) m_importSourceStack.pop(); 1536 } 1537 1538 /** 1539 * If this is set to true, we've already warned about using the 1540 * older XSLT namespace URL. 1541 */ 1542 private boolean warnedAboutOldXSLTNamespace = false; 1543 1544 /** Stack of NamespaceSupport objects. */ 1545 Stack m_nsSupportStack = new Stack(); 1546 1547 /** 1548 * Push a new NamespaceSupport instance. 1549 */ pushNewNamespaceSupport()1550 void pushNewNamespaceSupport() 1551 { 1552 m_nsSupportStack.push(new NamespaceSupport2()); 1553 } 1554 1555 /** 1556 * Pop the current NamespaceSupport object. 1557 * 1558 */ popNamespaceSupport()1559 void popNamespaceSupport() 1560 { 1561 m_nsSupportStack.pop(); 1562 } 1563 1564 /** 1565 * Get the current NamespaceSupport object. 1566 * 1567 * @return a non-null reference to the current NamespaceSupport object, 1568 * which is the top of the namespace support stack. 1569 */ getNamespaceSupport()1570 NamespaceSupport getNamespaceSupport() 1571 { 1572 return (NamespaceSupport) m_nsSupportStack.peek(); 1573 } 1574 1575 /** 1576 * The originating node if the current stylesheet is being created 1577 * from a DOM. 1578 * @see org.apache.xml.utils.NodeConsumer 1579 */ 1580 private Node m_originatingNode; 1581 1582 /** 1583 * Set the node that is originating the SAX event. 1584 * 1585 * @param n Reference to node that originated the current event. 1586 * @see org.apache.xml.utils.NodeConsumer 1587 */ setOriginatingNode(Node n)1588 public void setOriginatingNode(Node n) 1589 { 1590 m_originatingNode = n; 1591 } 1592 1593 /** 1594 * Set the node that is originating the SAX event. 1595 * 1596 * @return Reference to node that originated the current event. 1597 * @see org.apache.xml.utils.NodeConsumer 1598 */ getOriginatingNode()1599 public Node getOriginatingNode() 1600 { 1601 return m_originatingNode; 1602 } 1603 1604 /** 1605 * Stack of booleans that are pushed and popped in start/endElement depending 1606 * on the value of xml:space=default/preserve. 1607 */ 1608 private BoolStack m_spacePreserveStack = new BoolStack(); 1609 1610 /** 1611 * Return boolean value from the spacePreserve stack depending on the value 1612 * of xml:space=default/preserve. 1613 * 1614 * @return true if space should be preserved, false otherwise. 1615 */ isSpacePreserve()1616 boolean isSpacePreserve() 1617 { 1618 return m_spacePreserveStack.peek(); 1619 } 1620 1621 /** 1622 * Pop boolean value from the spacePreserve stack. 1623 */ popSpaceHandling()1624 void popSpaceHandling() 1625 { 1626 m_spacePreserveStack.pop(); 1627 } 1628 1629 /** 1630 * Push boolean value on to the spacePreserve stack. 1631 * 1632 * @param b true if space should be preserved, false otherwise. 1633 */ pushSpaceHandling(boolean b)1634 void pushSpaceHandling(boolean b) 1635 throws org.xml.sax.SAXParseException 1636 { 1637 m_spacePreserveStack.push(b); 1638 } 1639 1640 /** 1641 * Push boolean value on to the spacePreserve stack depending on the value 1642 * of xml:space=default/preserve. 1643 * 1644 * @param attrs list of attributes that were passed to startElement. 1645 */ pushSpaceHandling(Attributes attrs)1646 void pushSpaceHandling(Attributes attrs) 1647 throws org.xml.sax.SAXParseException 1648 { 1649 String value = attrs.getValue("xml:space"); 1650 if(null == value) 1651 { 1652 m_spacePreserveStack.push(m_spacePreserveStack.peekOrFalse()); 1653 } 1654 else if(value.equals("preserve")) 1655 { 1656 m_spacePreserveStack.push(true); 1657 } 1658 else if(value.equals("default")) 1659 { 1660 m_spacePreserveStack.push(false); 1661 } 1662 else 1663 { 1664 SAXSourceLocator locator = getLocator(); 1665 ErrorListener handler = m_stylesheetProcessor.getErrorListener(); 1666 1667 try 1668 { 1669 handler.error(new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_ILLEGAL_XMLSPACE_VALUE, null), locator)); //"Illegal value for xml:space", locator)); 1670 } 1671 catch (TransformerException te) 1672 { 1673 throw new org.xml.sax.SAXParseException(te.getMessage(), locator, te); 1674 } 1675 m_spacePreserveStack.push(m_spacePreserveStack.peek()); 1676 } 1677 } 1678 getElemVersion()1679 private double getElemVersion() 1680 { 1681 ElemTemplateElement elem = getElemTemplateElement(); 1682 double version = -1; 1683 while ((version == -1 || version == Constants.XSLTVERSUPPORTED) && elem != null) 1684 { 1685 try{ 1686 version = Double.valueOf(elem.getXmlVersion()).doubleValue(); 1687 } 1688 catch (Exception ex) 1689 { 1690 version = -1; 1691 } 1692 elem = elem.getParentElem(); 1693 } 1694 return (version == -1)? Constants.XSLTVERSUPPORTED : version; 1695 } 1696 /** 1697 * @see PrefixResolver#handlesNullPrefixes() 1698 */ handlesNullPrefixes()1699 public boolean handlesNullPrefixes() { 1700 return false; 1701 } 1702 1703 /** 1704 * @return Optimization flag 1705 */ getOptimize()1706 public boolean getOptimize() { 1707 return m_optimize; 1708 } 1709 1710 /** 1711 * @return Incremental flag 1712 */ getIncremental()1713 public boolean getIncremental() { 1714 return m_incremental; 1715 } 1716 1717 /** 1718 * @return Source Location flag 1719 */ getSource_location()1720 public boolean getSource_location() { 1721 return m_source_location; 1722 } 1723 1724 } 1725 1726 1727 1728