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: ElemTemplateElement.java 475981 2006-11-16 23:35:53Z minchau $ 20 */ 21 package org.apache.xalan.templates; 22 23 import java.io.Serializable; 24 import java.util.ArrayList; 25 import java.util.Enumeration; 26 import java.util.List; 27 28 import javax.xml.transform.SourceLocator; 29 import javax.xml.transform.TransformerException; 30 31 import org.apache.xalan.res.XSLMessages; 32 import org.apache.xalan.res.XSLTErrorResources; 33 import org.apache.xalan.transformer.TransformerImpl; 34 import org.apache.xml.serializer.SerializationHandler; 35 import org.apache.xml.utils.PrefixResolver; 36 import org.apache.xml.utils.UnImplNode; 37 import org.apache.xpath.ExpressionNode; 38 import org.apache.xpath.WhitespaceStrippingElementMatcher; 39 40 import org.w3c.dom.DOMException; 41 import org.w3c.dom.Document; 42 import org.w3c.dom.Node; 43 import org.w3c.dom.NodeList; 44 45 import org.xml.sax.helpers.NamespaceSupport; 46 47 /** 48 * An instance of this class represents an element inside 49 * an xsl:template class. It has a single "execute" method 50 * which is expected to perform the given action on the 51 * result tree. 52 * This class acts like a Element node, and implements the 53 * Element interface, but is not a full implementation 54 * of that interface... it only implements enough for 55 * basic traversal of the tree. 56 * 57 * @see Stylesheet 58 * @xsl.usage advanced 59 */ 60 public class ElemTemplateElement extends UnImplNode 61 implements PrefixResolver, Serializable, ExpressionNode, 62 WhitespaceStrippingElementMatcher, XSLTVisitable 63 { 64 static final long serialVersionUID = 4440018597841834447L; 65 66 /** 67 * Construct a template element instance. 68 * 69 */ 70 public ElemTemplateElement(){} 71 72 /** 73 * Tell if this template is a compiled template. 74 * 75 * @return Boolean flag indicating whether this is a compiled template 76 */ 77 public boolean isCompiledTemplate() 78 { 79 return false; 80 } 81 82 /** 83 * Get an integer representation of the element type. 84 * 85 * @return An integer representation of the element, defined in the 86 * Constants class. 87 * @see org.apache.xalan.templates.Constants 88 */ 89 public int getXSLToken() 90 { 91 return Constants.ELEMNAME_UNDEFINED; 92 } 93 94 /** 95 * Return the node name. 96 * 97 * @return An invalid node name 98 */ 99 public String getNodeName() 100 { 101 return "Unknown XSLT Element"; 102 } 103 104 /** 105 * For now, just return the result of getNodeName(), which 106 * the local name. 107 * 108 * @return The result of getNodeName(). 109 */ 110 public String getLocalName() 111 { 112 113 return getNodeName(); 114 } 115 116 117 /** 118 * This function will be called on top-level elements 119 * only, just before the transform begins. 120 * 121 * @param transformer The XSLT TransformerFactory. 122 * 123 * @throws TransformerException 124 */ 125 public void runtimeInit(TransformerImpl transformer) throws TransformerException{} 126 127 /** 128 * Execute the element's primary function. Subclasses of this 129 * function may recursivly execute down the element tree. 130 * 131 * @param transformer The XSLT TransformerFactory. 132 * 133 * @throws TransformerException if any checked exception occurs. 134 */ 135 public void execute( 136 TransformerImpl transformer) 137 throws TransformerException{} 138 139 /** 140 * Get the owning "composed" stylesheet. This looks up the 141 * inheritance chain until it calls getStylesheetComposed 142 * on a Stylesheet object, which will Get the owning 143 * aggregated stylesheet, or that stylesheet if it is aggregated. 144 * 145 * @return the owning "composed" stylesheet. 146 */ 147 public StylesheetComposed getStylesheetComposed() 148 { 149 return m_parentNode.getStylesheetComposed(); 150 } 151 152 /** 153 * Get the owning stylesheet. This looks up the 154 * inheritance chain until it calls getStylesheet 155 * on a Stylesheet object, which will return itself. 156 * 157 * @return the owning stylesheet 158 */ 159 public Stylesheet getStylesheet() 160 { 161 return (null==m_parentNode) ? null : m_parentNode.getStylesheet(); 162 } 163 164 /** 165 * Get the owning root stylesheet. This looks up the 166 * inheritance chain until it calls StylesheetRoot 167 * on a Stylesheet object, which will return a reference 168 * to the root stylesheet. 169 * 170 * @return the owning root stylesheet 171 */ 172 public StylesheetRoot getStylesheetRoot() 173 { 174 return m_parentNode.getStylesheetRoot(); 175 } 176 177 /** 178 * This function is called during recomposition to 179 * control how this element is composed. 180 */ 181 public void recompose(StylesheetRoot root) throws TransformerException 182 { 183 } 184 185 /** 186 * This function is called after everything else has been 187 * recomposed, and allows the template to set remaining 188 * values that may be based on some other property that 189 * depends on recomposition. 190 */ 191 public void compose(StylesheetRoot sroot) throws TransformerException 192 { 193 resolvePrefixTables(); 194 ElemTemplateElement t = getFirstChildElem(); 195 m_hasTextLitOnly = ((t != null) 196 && (t.getXSLToken() == Constants.ELEMNAME_TEXTLITERALRESULT) 197 && (t.getNextSiblingElem() == null)); 198 199 StylesheetRoot.ComposeState cstate = sroot.getComposeState(); 200 cstate.pushStackMark(); 201 } 202 203 /** 204 * This after the template's children have been composed. 205 */ 206 public void endCompose(StylesheetRoot sroot) throws TransformerException 207 { 208 StylesheetRoot.ComposeState cstate = sroot.getComposeState(); 209 cstate.popStackMark(); 210 } 211 212 /** 213 * Throw a template element runtime error. (Note: should we throw a TransformerException instead?) 214 * 215 * @param msg key of the error that occured. 216 * @param args Arguments to be used in the message 217 */ 218 public void error(String msg, Object[] args) 219 { 220 221 String themsg = XSLMessages.createMessage(msg, args); 222 223 throw new RuntimeException(XSLMessages.createMessage( 224 XSLTErrorResources.ER_ELEMTEMPLATEELEM_ERR, 225 new Object[]{ themsg })); 226 } 227 228 /* 229 * Throw an error. 230 * 231 * @param msg Message key for the error 232 * 233 */ 234 public void error(String msg) 235 { 236 error(msg, null); 237 } 238 239 240 // Implemented DOM Element methods. 241 /** 242 * Add a child to the child list. 243 * NOTE: This presumes the child did not previously have a parent. 244 * Making that assumption makes this a less expensive operation -- but 245 * requires that if you *do* want to reparent a node, you use removeChild() 246 * first to remove it from its previous context. Failing to do so will 247 * damage the tree. 248 * 249 * @param newChild Child to be added to child list 250 * 251 * @return Child just added to the child list 252 * @throws DOMException 253 */ 254 public Node appendChild(Node newChild) throws DOMException 255 { 256 257 if (null == newChild) 258 { 259 error(XSLTErrorResources.ER_NULL_CHILD, null); //"Trying to add a null child!"); 260 } 261 262 ElemTemplateElement elem = (ElemTemplateElement) newChild; 263 264 if (null == m_firstChild) 265 { 266 m_firstChild = elem; 267 } 268 else 269 { 270 ElemTemplateElement last = (ElemTemplateElement) getLastChild(); 271 272 last.m_nextSibling = elem; 273 } 274 275 elem.m_parentNode = this; 276 277 return newChild; 278 } 279 280 /** 281 * Add a child to the child list. 282 * NOTE: This presumes the child did not previously have a parent. 283 * Making that assumption makes this a less expensive operation -- but 284 * requires that if you *do* want to reparent a node, you use removeChild() 285 * first to remove it from its previous context. Failing to do so will 286 * damage the tree. 287 * 288 * @param elem Child to be added to child list 289 * 290 * @return Child just added to the child list 291 */ 292 public ElemTemplateElement appendChild(ElemTemplateElement elem) 293 { 294 295 if (null == elem) 296 { 297 error(XSLTErrorResources.ER_NULL_CHILD, null); //"Trying to add a null child!"); 298 } 299 300 if (null == m_firstChild) 301 { 302 m_firstChild = elem; 303 } 304 else 305 { 306 ElemTemplateElement last = getLastChildElem(); 307 308 last.m_nextSibling = elem; 309 } 310 311 elem.setParentElem(this); 312 313 return elem; 314 } 315 316 317 /** 318 * Tell if there are child nodes. 319 * 320 * @return True if there are child nodes 321 */ 322 public boolean hasChildNodes() 323 { 324 return (null != m_firstChild); 325 } 326 327 /** 328 * Get the type of the node. 329 * 330 * @return Constant for this node type 331 */ 332 public short getNodeType() 333 { 334 return org.w3c.dom.Node.ELEMENT_NODE; 335 } 336 337 /** 338 * Return the nodelist (same reference). 339 * 340 * @return The nodelist containing the child nodes (this) 341 */ 342 public NodeList getChildNodes() 343 { 344 return this; 345 } 346 347 /** 348 * Remove a child. 349 * ADDED 9/8/200 to support compilation. 350 * TODO: ***** Alternative is "removeMe() from my parent if any" 351 * ... which is less well checked, but more convenient in some cases. 352 * Given that we assume only experts are calling this class, it might 353 * be preferable. It's less DOMish, though. 354 * 355 * @param childETE The child to remove. This operation is a no-op 356 * if oldChild is not a child of this node. 357 * 358 * @return the removed child, or null if the specified 359 * node was not a child of this element. 360 */ 361 public ElemTemplateElement removeChild(ElemTemplateElement childETE) 362 { 363 364 if (childETE == null || childETE.m_parentNode != this) 365 return null; 366 367 // Pointers to the child 368 if (childETE == m_firstChild) 369 m_firstChild = childETE.m_nextSibling; 370 else 371 { 372 ElemTemplateElement prev = childETE.getPreviousSiblingElem(); 373 374 prev.m_nextSibling = childETE.m_nextSibling; 375 } 376 377 // Pointers from the child 378 childETE.m_parentNode = null; 379 childETE.m_nextSibling = null; 380 381 return childETE; 382 } 383 384 /** 385 * Replace the old child with a new child. 386 * 387 * @param newChild New child to replace with 388 * @param oldChild Old child to be replaced 389 * 390 * @return The new child 391 * 392 * @throws DOMException 393 */ 394 public Node replaceChild(Node newChild, Node oldChild) throws DOMException 395 { 396 397 if (oldChild == null || oldChild.getParentNode() != this) 398 return null; 399 400 ElemTemplateElement newChildElem = ((ElemTemplateElement) newChild); 401 ElemTemplateElement oldChildElem = ((ElemTemplateElement) oldChild); 402 403 // Fix up previous sibling. 404 ElemTemplateElement prev = 405 (ElemTemplateElement) oldChildElem.getPreviousSibling(); 406 407 if (null != prev) 408 prev.m_nextSibling = newChildElem; 409 410 // Fix up parent (this) 411 if (m_firstChild == oldChildElem) 412 m_firstChild = newChildElem; 413 414 newChildElem.m_parentNode = this; 415 oldChildElem.m_parentNode = null; 416 newChildElem.m_nextSibling = oldChildElem.m_nextSibling; 417 oldChildElem.m_nextSibling = null; 418 419 // newChildElem.m_stylesheet = oldChildElem.m_stylesheet; 420 // oldChildElem.m_stylesheet = null; 421 return newChildElem; 422 } 423 424 /** 425 * Unimplemented. See org.w3c.dom.Node 426 * 427 * @param newChild New child node to insert 428 * @param refChild Insert in front of this child 429 * 430 * @return null 431 * 432 * @throws DOMException 433 */ 434 public Node insertBefore(Node newChild, Node refChild) throws DOMException 435 { 436 if(null == refChild) 437 { 438 appendChild(newChild); 439 return newChild; 440 } 441 442 if(newChild == refChild) 443 { 444 // hmm... 445 return newChild; 446 } 447 448 Node node = m_firstChild; 449 Node prev = null; 450 boolean foundit = false; 451 452 while (null != node) 453 { 454 // If the newChild is already in the tree, it is first removed. 455 if(newChild == node) 456 { 457 if(null != prev) 458 ((ElemTemplateElement)prev).m_nextSibling = 459 (ElemTemplateElement)node.getNextSibling(); 460 else 461 m_firstChild = (ElemTemplateElement)node.getNextSibling(); 462 node = node.getNextSibling(); 463 continue; // prev remains the same. 464 } 465 if(refChild == node) 466 { 467 if(null != prev) 468 { 469 ((ElemTemplateElement)prev).m_nextSibling = (ElemTemplateElement)newChild; 470 } 471 else 472 { 473 m_firstChild = (ElemTemplateElement)newChild; 474 } 475 ((ElemTemplateElement)newChild).m_nextSibling = (ElemTemplateElement)refChild; 476 ((ElemTemplateElement)newChild).setParentElem(this); 477 prev = newChild; 478 node = node.getNextSibling(); 479 foundit = true; 480 continue; 481 } 482 prev = node; 483 node = node.getNextSibling(); 484 } 485 486 if(!foundit) 487 throw new DOMException(DOMException.NOT_FOUND_ERR, 488 "refChild was not found in insertBefore method!"); 489 else 490 return newChild; 491 } 492 493 494 /** 495 * Replace the old child with a new child. 496 * 497 * @param newChildElem New child to replace with 498 * @param oldChildElem Old child to be replaced 499 * 500 * @return The new child 501 * 502 * @throws DOMException 503 */ 504 public ElemTemplateElement replaceChild(ElemTemplateElement newChildElem, 505 ElemTemplateElement oldChildElem) 506 { 507 508 if (oldChildElem == null || oldChildElem.getParentElem() != this) 509 return null; 510 511 // Fix up previous sibling. 512 ElemTemplateElement prev = 513 oldChildElem.getPreviousSiblingElem(); 514 515 if (null != prev) 516 prev.m_nextSibling = newChildElem; 517 518 // Fix up parent (this) 519 if (m_firstChild == oldChildElem) 520 m_firstChild = newChildElem; 521 522 newChildElem.m_parentNode = this; 523 oldChildElem.m_parentNode = null; 524 newChildElem.m_nextSibling = oldChildElem.m_nextSibling; 525 oldChildElem.m_nextSibling = null; 526 527 // newChildElem.m_stylesheet = oldChildElem.m_stylesheet; 528 // oldChildElem.m_stylesheet = null; 529 return newChildElem; 530 } 531 532 /** 533 * NodeList method: Count the immediate children of this node 534 * 535 * @return The count of children of this node 536 */ 537 public int getLength() 538 { 539 540 // It is assumed that the getChildNodes call synchronized 541 // the children. Therefore, we can access the first child 542 // reference directly. 543 int count = 0; 544 545 for (ElemTemplateElement node = m_firstChild; node != null; 546 node = node.m_nextSibling) 547 { 548 count++; 549 } 550 551 return count; 552 } // getLength():int 553 554 /** 555 * NodeList method: Return the Nth immediate child of this node, or 556 * null if the index is out of bounds. 557 * 558 * @param index Index of child to find 559 * @return org.w3c.dom.Node: the child node at given index 560 */ 561 public Node item(int index) 562 { 563 564 // It is assumed that the getChildNodes call synchronized 565 // the children. Therefore, we can access the first child 566 // reference directly. 567 ElemTemplateElement node = m_firstChild; 568 569 for (int i = 0; i < index && node != null; i++) 570 { 571 node = node.m_nextSibling; 572 } 573 574 return node; 575 } // item(int):Node 576 577 /** 578 * Get the stylesheet owner. 579 * 580 * @return The stylesheet owner 581 */ 582 public Document getOwnerDocument() 583 { 584 return getStylesheet(); 585 } 586 587 /** 588 * Get the owning xsl:template element. 589 * 590 * @return The owning xsl:template element, this element if it is a xsl:template, or null if not found. 591 */ 592 public ElemTemplate getOwnerXSLTemplate() 593 { 594 ElemTemplateElement el = this; 595 int type = el.getXSLToken(); 596 while((null != el) && (type != Constants.ELEMNAME_TEMPLATE)) 597 { 598 el = el.getParentElem(); 599 if(null != el) 600 type = el.getXSLToken(); 601 } 602 return (ElemTemplate)el; 603 } 604 605 606 /** 607 * Return the element name. 608 * 609 * @return The element name 610 */ 611 public String getTagName() 612 { 613 return getNodeName(); 614 } 615 616 /** 617 * Tell if this element only has one text child, for optimization purposes. 618 * @return true of this element only has one text literal child. 619 */ 620 public boolean hasTextLitOnly() 621 { 622 return m_hasTextLitOnly; 623 } 624 625 /** 626 * Return the base identifier. 627 * 628 * @return The base identifier 629 */ 630 public String getBaseIdentifier() 631 { 632 633 // Should this always be absolute? 634 return this.getSystemId(); 635 } 636 637 /** line number where the current document event ends. 638 * @serial */ 639 private int m_lineNumber; 640 641 /** line number where the current document event ends. 642 * @serial */ 643 private int m_endLineNumber; 644 645 /** 646 * Return the line number where the current document event ends. 647 * Note that this is the line position of the first character 648 * after the text associated with the document event. 649 * @return The line number, or -1 if none is available. 650 * @see #getColumnNumber 651 */ 652 public int getEndLineNumber() 653 { 654 return m_endLineNumber; 655 } 656 657 /** 658 * Return the line number where the current document event ends. 659 * Note that this is the line position of the first character 660 * after the text associated with the document event. 661 * @return The line number, or -1 if none is available. 662 * @see #getColumnNumber 663 */ 664 public int getLineNumber() 665 { 666 return m_lineNumber; 667 } 668 669 /** the column number where the current document event ends. 670 * @serial */ 671 private int m_columnNumber; 672 673 /** the column number where the current document event ends. 674 * @serial */ 675 private int m_endColumnNumber; 676 677 /** 678 * Return the column number where the current document event ends. 679 * Note that this is the column number of the first 680 * character after the text associated with the document 681 * event. The first column in a line is position 1. 682 * @return The column number, or -1 if none is available. 683 * @see #getLineNumber 684 */ 685 public int getEndColumnNumber() 686 { 687 return m_endColumnNumber; 688 } 689 690 /** 691 * Return the column number where the current document event ends. 692 * Note that this is the column number of the first 693 * character after the text associated with the document 694 * event. The first column in a line is position 1. 695 * @return The column number, or -1 if none is available. 696 * @see #getLineNumber 697 */ 698 public int getColumnNumber() 699 { 700 return m_columnNumber; 701 } 702 703 /** 704 * Return the public identifier for the current document event. 705 * <p>This will be the public identifier 706 * @return A string containing the public identifier, or 707 * null if none is available. 708 * @see #getSystemId 709 */ 710 public String getPublicId() 711 { 712 return (null != m_parentNode) ? m_parentNode.getPublicId() : null; 713 } 714 715 /** 716 * Return the system identifier for the current document event. 717 * 718 * <p>If the system identifier is a URL, the parser must resolve it 719 * fully before passing it to the application.</p> 720 * 721 * @return A string containing the system identifier, or null 722 * if none is available. 723 * @see #getPublicId 724 */ 725 public String getSystemId() 726 { 727 Stylesheet sheet=getStylesheet(); 728 return (sheet==null) ? null : sheet.getHref(); 729 } 730 731 /** 732 * Set the location information for this element. 733 * 734 * @param locator Source Locator with location information for this element 735 */ 736 public void setLocaterInfo(SourceLocator locator) 737 { 738 m_lineNumber = locator.getLineNumber(); 739 m_columnNumber = locator.getColumnNumber(); 740 } 741 742 /** 743 * Set the end location information for this element. 744 * 745 * @param locator Source Locator with location information for this element 746 */ 747 public void setEndLocaterInfo(SourceLocator locator) 748 { 749 m_endLineNumber = locator.getLineNumber(); 750 m_endColumnNumber = locator.getColumnNumber(); 751 } 752 753 /** 754 * Tell if this element has the default space handling 755 * turned off or on according to the xml:space attribute. 756 * @serial 757 */ 758 private boolean m_defaultSpace = true; 759 760 /** 761 * Tell if this element only has one text child, for optimization purposes. 762 * @serial 763 */ 764 private boolean m_hasTextLitOnly = false; 765 766 /** 767 * Tell if this element only has one text child, for optimization purposes. 768 * @serial 769 */ 770 protected boolean m_hasVariableDecl = false; 771 772 public boolean hasVariableDecl() 773 { 774 return m_hasVariableDecl; 775 } 776 777 /** 778 * Set the "xml:space" attribute. 779 * A text node is preserved if an ancestor element of the text node 780 * has an xml:space attribute with a value of preserve, and 781 * no closer ancestor element has xml:space with a value of default. 782 * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a> 783 * @see <a href="http://www.w3.org/TR/xslt#section-Creating-Text">section-Creating-Text in XSLT Specification</a> 784 * 785 * @param v Enumerated value, either Constants.ATTRVAL_PRESERVE 786 * or Constants.ATTRVAL_STRIP. 787 */ 788 public void setXmlSpace(int v) 789 { 790 m_defaultSpace = ((Constants.ATTRVAL_STRIP == v) ? true : false); 791 } 792 793 /** 794 * Get the "xml:space" attribute. 795 * A text node is preserved if an ancestor element of the text node 796 * has an xml:space attribute with a value of preserve, and 797 * no closer ancestor element has xml:space with a value of default. 798 * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a> 799 * @see <a href="http://www.w3.org/TR/xslt#section-Creating-Text">section-Creating-Text in XSLT Specification</a> 800 * 801 * @return The value of the xml:space attribute 802 */ 803 public boolean getXmlSpace() 804 { 805 return m_defaultSpace; 806 } 807 808 /** 809 * The list of namespace declarations for this element only. 810 * @serial 811 */ 812 private List m_declaredPrefixes; 813 814 /** 815 * Return a table that contains all prefixes available 816 * within this element context. 817 * 818 * @return Vector containing the prefixes available within this 819 * element context 820 */ 821 public List getDeclaredPrefixes() 822 { 823 return m_declaredPrefixes; 824 } 825 826 /** 827 * From the SAX2 helper class, set the namespace table for 828 * this element. Take care to call resolveInheritedNamespaceDecls. 829 * after all namespace declarations have been added. 830 * 831 * @param nsSupport non-null reference to NamespaceSupport from 832 * the ContentHandler. 833 * 834 * @throws TransformerException 835 */ 836 public void setPrefixes(NamespaceSupport nsSupport) throws TransformerException 837 { 838 setPrefixes(nsSupport, false); 839 } 840 841 /** 842 * Copy the namespace declarations from the NamespaceSupport object. 843 * Take care to call resolveInheritedNamespaceDecls. 844 * after all namespace declarations have been added. 845 * 846 * @param nsSupport non-null reference to NamespaceSupport from 847 * the ContentHandler. 848 * @param excludeXSLDecl true if XSLT namespaces should be ignored. 849 * 850 * @throws TransformerException 851 */ 852 public void setPrefixes(NamespaceSupport nsSupport, boolean excludeXSLDecl) 853 throws TransformerException 854 { 855 856 Enumeration decls = nsSupport.getDeclaredPrefixes(); 857 858 while (decls.hasMoreElements()) 859 { 860 String prefix = (String) decls.nextElement(); 861 862 if (null == m_declaredPrefixes) 863 m_declaredPrefixes = new ArrayList(); 864 865 String uri = nsSupport.getURI(prefix); 866 867 if (excludeXSLDecl && uri.equals(Constants.S_XSLNAMESPACEURL)) 868 continue; 869 870 // System.out.println("setPrefixes - "+prefix+", "+uri); 871 XMLNSDecl decl = new XMLNSDecl(prefix, uri, false); 872 873 m_declaredPrefixes.add(decl); 874 } 875 } 876 877 /** 878 * Fullfill the PrefixResolver interface. Calling this for this class 879 * will throw an error. 880 * 881 * @param prefix The prefix to look up, which may be an empty string ("") 882 * for the default Namespace. 883 * @param context The node context from which to look up the URI. 884 * 885 * @return null if the error listener does not choose to throw an exception. 886 */ 887 public String getNamespaceForPrefix(String prefix, org.w3c.dom.Node context) 888 { 889 this.error(XSLTErrorResources.ER_CANT_RESOLVE_NSPREFIX, null); 890 891 return null; 892 } 893 894 /** 895 * Given a namespace, get the corrisponding prefix. 896 * 9/15/00: This had been iteratively examining the m_declaredPrefixes 897 * field for this node and its parents. That makes life difficult for 898 * the compilation experiment, which doesn't have a static vector of 899 * local declarations. Replaced a recursive solution, which permits 900 * easier subclassing/overriding. 901 * 902 * @param prefix non-null reference to prefix string, which should map 903 * to a namespace URL. 904 * 905 * @return The namespace URL that the prefix maps to, or null if no 906 * mapping can be found. 907 */ 908 public String getNamespaceForPrefix(String prefix) 909 { 910 // if (null != prefix && prefix.equals("xmlns")) 911 // { 912 // return Constants.S_XMLNAMESPACEURI; 913 // } 914 915 List nsDecls = m_declaredPrefixes; 916 917 if (null != nsDecls) 918 { 919 int n = nsDecls.size(); 920 if(prefix.equals(Constants.ATTRVAL_DEFAULT_PREFIX)) 921 { 922 prefix = ""; 923 } 924 925 for (int i = 0; i < n; i++) 926 { 927 XMLNSDecl decl = (XMLNSDecl) nsDecls.get(i); 928 929 if (prefix.equals(decl.getPrefix())) 930 return decl.getURI(); 931 } 932 } 933 934 // Not found; ask our ancestors 935 if (null != m_parentNode) 936 return m_parentNode.getNamespaceForPrefix(prefix); 937 938 // JJK: No ancestors; try implicit 939 // %REVIEW% Are there literals somewhere that we should use instead? 940 // %REVIEW% Is this really the best place to patch? 941 if("xml".equals(prefix)) 942 return "http://www.w3.org/XML/1998/namespace"; 943 944 // No parent, so no definition 945 return null; 946 } 947 948 /** 949 * The table of {@link XMLNSDecl}s for this element 950 * and all parent elements, screened for excluded prefixes. 951 * @serial 952 */ 953 private List m_prefixTable; 954 955 /** 956 * Return a table that contains all prefixes available 957 * within this element context. 958 * 959 * @return reference to vector of {@link XMLNSDecl}s, which may be null. 960 */ 961 List getPrefixTable() 962 { 963 return m_prefixTable; 964 } 965 966 void setPrefixTable(List list) { 967 m_prefixTable = list; 968 } 969 970 /** 971 * Get whether or not the passed URL is contained flagged by 972 * the "extension-element-prefixes" property. This method is overridden 973 * by {@link ElemLiteralResult#containsExcludeResultPrefix}. 974 * @see <a href="http://www.w3.org/TR/xslt#extension-element">extension-element in XSLT Specification</a> 975 * 976 * @param prefix non-null reference to prefix that might be excluded. 977 * 978 * @return true if the prefix should normally be excluded. 979 */ 980 public boolean containsExcludeResultPrefix(String prefix, String uri) 981 { 982 ElemTemplateElement parent = this.getParentElem(); 983 if(null != parent) 984 return parent.containsExcludeResultPrefix(prefix, uri); 985 986 return false; 987 } 988 989 /** 990 * Tell if the result namespace decl should be excluded. Should be called before 991 * namespace aliasing (I think). 992 * 993 * @param prefix non-null reference to prefix. 994 * @param uri reference to namespace that prefix maps to, which is protected 995 * for null, but should really never be passed as null. 996 * 997 * @return true if the given namespace should be excluded. 998 * 999 * @throws TransformerException 1000 */ 1001 private boolean excludeResultNSDecl(String prefix, String uri) 1002 throws TransformerException 1003 { 1004 1005 if (uri != null) 1006 { 1007 if (uri.equals(Constants.S_XSLNAMESPACEURL) 1008 || getStylesheet().containsExtensionElementURI(uri)) 1009 return true; 1010 1011 if (containsExcludeResultPrefix(prefix, uri)) 1012 return true; 1013 } 1014 1015 return false; 1016 } 1017 1018 /** 1019 * Combine the parent's namespaces with this namespace 1020 * for fast processing, taking care to reference the 1021 * parent's namespace if this namespace adds nothing new. 1022 * (Recursive method, walking the elements depth-first, 1023 * processing parents before children). 1024 * Note that this method builds m_prefixTable with aliased 1025 * namespaces, *not* the original namespaces. 1026 * 1027 * @throws TransformerException 1028 */ 1029 public void resolvePrefixTables() throws TransformerException 1030 { 1031 // Always start with a fresh prefix table! 1032 setPrefixTable(null); 1033 1034 // If we have declared declarations, then we look for 1035 // a parent that has namespace decls, and add them 1036 // to this element's decls. Otherwise we just point 1037 // to the parent that has decls. 1038 if (null != this.m_declaredPrefixes) 1039 { 1040 StylesheetRoot stylesheet = this.getStylesheetRoot(); 1041 1042 // Add this element's declared prefixes to the 1043 // prefix table. 1044 int n = m_declaredPrefixes.size(); 1045 1046 for (int i = 0; i < n; i++) 1047 { 1048 XMLNSDecl decl = (XMLNSDecl) m_declaredPrefixes.get(i); 1049 String prefix = decl.getPrefix(); 1050 String uri = decl.getURI(); 1051 if(null == uri) 1052 uri = ""; 1053 boolean shouldExclude = excludeResultNSDecl(prefix, uri); 1054 1055 // Create a new prefix table if one has not already been created. 1056 if (null == m_prefixTable) 1057 setPrefixTable(new ArrayList()); 1058 1059 NamespaceAlias nsAlias = stylesheet.getNamespaceAliasComposed(uri); 1060 if(null != nsAlias) 1061 { 1062 // Should I leave the non-aliased element in the table as 1063 // an excluded element? 1064 1065 // The exclusion should apply to the non-aliased prefix, so 1066 // we don't calculate it here. -sb 1067 // Use stylesheet prefix, as per xsl WG 1068 decl = new XMLNSDecl(nsAlias.getStylesheetPrefix(), 1069 nsAlias.getResultNamespace(), shouldExclude); 1070 } 1071 else 1072 decl = new XMLNSDecl(prefix, uri, shouldExclude); 1073 1074 m_prefixTable.add(decl); 1075 1076 } 1077 } 1078 1079 ElemTemplateElement parent = this.getParentNodeElem(); 1080 1081 if (null != parent) 1082 { 1083 1084 // The prefix table of the parent should never be null! 1085 List prefixes = parent.m_prefixTable; 1086 1087 if (null == m_prefixTable && !needToCheckExclude()) 1088 { 1089 1090 // Nothing to combine, so just use parent's table! 1091 setPrefixTable(parent.m_prefixTable); 1092 } 1093 else 1094 { 1095 1096 // Add the prefixes from the parent's prefix table. 1097 int n = prefixes.size(); 1098 1099 for (int i = 0; i < n; i++) 1100 { 1101 XMLNSDecl decl = (XMLNSDecl) prefixes.get(i); 1102 boolean shouldExclude = excludeResultNSDecl(decl.getPrefix(), 1103 decl.getURI()); 1104 1105 if (shouldExclude != decl.getIsExcluded()) 1106 { 1107 decl = new XMLNSDecl(decl.getPrefix(), decl.getURI(), 1108 shouldExclude); 1109 } 1110 1111 //m_prefixTable.addElement(decl); 1112 addOrReplaceDecls(decl); 1113 } 1114 } 1115 } 1116 else if (null == m_prefixTable) 1117 { 1118 1119 // Must be stylesheet element without any result prefixes! 1120 setPrefixTable(new ArrayList()); 1121 } 1122 } 1123 1124 /** 1125 * Add or replace this namespace declaration in list 1126 * of namespaces in scope for this element. 1127 * 1128 * @param newDecl namespace declaration to add to list 1129 */ 1130 void addOrReplaceDecls(XMLNSDecl newDecl) 1131 { 1132 int n = m_prefixTable.size(); 1133 1134 for (int i = n - 1; i >= 0; i--) 1135 { 1136 XMLNSDecl decl = (XMLNSDecl) m_prefixTable.get(i); 1137 1138 if (decl.getPrefix().equals(newDecl.getPrefix())) 1139 { 1140 return; 1141 } 1142 } 1143 m_prefixTable.add(newDecl); 1144 1145 } 1146 1147 /** 1148 * Return whether we need to check namespace prefixes 1149 * against and exclude result prefixes list. 1150 */ 1151 boolean needToCheckExclude() 1152 { 1153 return false; 1154 } 1155 1156 /** 1157 * Send startPrefixMapping events to the result tree handler 1158 * for all declared prefix mappings in the stylesheet. 1159 * 1160 * @param transformer non-null reference to the the current transform-time state. 1161 * 1162 * @throws TransformerException 1163 */ 1164 void executeNSDecls(TransformerImpl transformer) throws TransformerException 1165 { 1166 executeNSDecls(transformer, null); 1167 } 1168 1169 /** 1170 * Send startPrefixMapping events to the result tree handler 1171 * for all declared prefix mappings in the stylesheet. 1172 * 1173 * @param transformer non-null reference to the the current transform-time state. 1174 * @param ignorePrefix string prefix to not startPrefixMapping 1175 * 1176 * @throws TransformerException 1177 */ 1178 void executeNSDecls(TransformerImpl transformer, String ignorePrefix) throws TransformerException 1179 { 1180 try 1181 { 1182 if (null != m_prefixTable) 1183 { 1184 SerializationHandler rhandler = transformer.getResultTreeHandler(); 1185 int n = m_prefixTable.size(); 1186 1187 for (int i = n - 1; i >= 0; i--) 1188 { 1189 XMLNSDecl decl = (XMLNSDecl) m_prefixTable.get(i); 1190 1191 if (!decl.getIsExcluded() && !(null != ignorePrefix && decl.getPrefix().equals(ignorePrefix))) 1192 { 1193 rhandler.startPrefixMapping(decl.getPrefix(), decl.getURI(), true); 1194 } 1195 } 1196 } 1197 } 1198 catch(org.xml.sax.SAXException se) 1199 { 1200 throw new TransformerException(se); 1201 } 1202 } 1203 1204 /** 1205 * Send endPrefixMapping events to the result tree handler 1206 * for all declared prefix mappings in the stylesheet. 1207 * 1208 * @param transformer non-null reference to the the current transform-time state. 1209 * 1210 * @throws TransformerException 1211 */ 1212 void unexecuteNSDecls(TransformerImpl transformer) throws TransformerException 1213 { 1214 unexecuteNSDecls(transformer, null); 1215 } 1216 1217 /** 1218 * Send endPrefixMapping events to the result tree handler 1219 * for all declared prefix mappings in the stylesheet. 1220 * 1221 * @param transformer non-null reference to the the current transform-time state. 1222 * @param ignorePrefix string prefix to not endPrefixMapping 1223 * 1224 * @throws TransformerException 1225 */ 1226 void unexecuteNSDecls(TransformerImpl transformer, String ignorePrefix) throws TransformerException 1227 { 1228 1229 try 1230 { 1231 if (null != m_prefixTable) 1232 { 1233 SerializationHandler rhandler = transformer.getResultTreeHandler(); 1234 int n = m_prefixTable.size(); 1235 1236 for (int i = 0; i < n; i++) 1237 { 1238 XMLNSDecl decl = (XMLNSDecl) m_prefixTable.get(i); 1239 1240 if (!decl.getIsExcluded() && !(null != ignorePrefix && decl.getPrefix().equals(ignorePrefix))) 1241 { 1242 rhandler.endPrefixMapping(decl.getPrefix()); 1243 } 1244 } 1245 } 1246 } 1247 catch(org.xml.sax.SAXException se) 1248 { 1249 throw new TransformerException(se); 1250 } 1251 } 1252 1253 /** The *relative* document order number of this element. 1254 * @serial */ 1255 protected int m_docOrderNumber = -1; 1256 1257 /** 1258 * Set the UID (document order index). 1259 * 1260 * @param i Index of this child. 1261 */ 1262 public void setUid(int i) 1263 { 1264 m_docOrderNumber = i; 1265 } 1266 1267 /** 1268 * Get the UID (document order index). 1269 * 1270 * @return Index of this child 1271 */ 1272 public int getUid() 1273 { 1274 return m_docOrderNumber; 1275 } 1276 1277 1278 /** 1279 * Parent node. 1280 * @serial 1281 */ 1282 protected ElemTemplateElement m_parentNode; 1283 1284 /** 1285 * Get the parent as a Node. 1286 * 1287 * @return This node's parent node 1288 */ 1289 public Node getParentNode() 1290 { 1291 return m_parentNode; 1292 } 1293 1294 /** 1295 * Get the parent as an ElemTemplateElement. 1296 * 1297 * @return This node's parent as an ElemTemplateElement 1298 */ 1299 public ElemTemplateElement getParentElem() 1300 { 1301 return m_parentNode; 1302 } 1303 1304 /** 1305 * Set the parent as an ElemTemplateElement. 1306 * 1307 * @param p This node's parent as an ElemTemplateElement 1308 */ 1309 public void setParentElem(ElemTemplateElement p) 1310 { 1311 m_parentNode = p; 1312 } 1313 1314 /** 1315 * Next sibling. 1316 * @serial 1317 */ 1318 ElemTemplateElement m_nextSibling; 1319 1320 /** 1321 * Get the next sibling (as a Node) or return null. 1322 * 1323 * @return this node's next sibling or null 1324 */ 1325 public Node getNextSibling() 1326 { 1327 return m_nextSibling; 1328 } 1329 1330 /** 1331 * Get the previous sibling (as a Node) or return null. 1332 * Note that this may be expensive if the parent has many kids; 1333 * we accept that price in exchange for avoiding the prev pointer 1334 * TODO: If we were sure parents and sibs are always ElemTemplateElements, 1335 * we could hit the fields directly rather than thru accessors. 1336 * 1337 * @return This node's previous sibling or null 1338 */ 1339 public Node getPreviousSibling() 1340 { 1341 1342 Node walker = getParentNode(), prev = null; 1343 1344 if (walker != null) 1345 for (walker = walker.getFirstChild(); walker != null; 1346 prev = walker, walker = walker.getNextSibling()) 1347 { 1348 if (walker == this) 1349 return prev; 1350 } 1351 1352 return null; 1353 } 1354 1355 /** 1356 * Get the previous sibling (as a Node) or return null. 1357 * Note that this may be expensive if the parent has many kids; 1358 * we accept that price in exchange for avoiding the prev pointer 1359 * TODO: If we were sure parents and sibs are always ElemTemplateElements, 1360 * we could hit the fields directly rather than thru accessors. 1361 * 1362 * @return This node's previous sibling or null 1363 */ 1364 public ElemTemplateElement getPreviousSiblingElem() 1365 { 1366 1367 ElemTemplateElement walker = getParentNodeElem(); 1368 ElemTemplateElement prev = null; 1369 1370 if (walker != null) 1371 for (walker = walker.getFirstChildElem(); walker != null; 1372 prev = walker, walker = walker.getNextSiblingElem()) 1373 { 1374 if (walker == this) 1375 return prev; 1376 } 1377 1378 return null; 1379 } 1380 1381 1382 /** 1383 * Get the next sibling (as a ElemTemplateElement) or return null. 1384 * 1385 * @return This node's next sibling (as a ElemTemplateElement) or null 1386 */ 1387 public ElemTemplateElement getNextSiblingElem() 1388 { 1389 return m_nextSibling; 1390 } 1391 1392 /** 1393 * Get the parent element. 1394 * 1395 * @return This node's next parent (as a ElemTemplateElement) or null 1396 */ 1397 public ElemTemplateElement getParentNodeElem() 1398 { 1399 return m_parentNode; 1400 } 1401 1402 1403 /** 1404 * First child. 1405 * @serial 1406 */ 1407 ElemTemplateElement m_firstChild; 1408 1409 /** 1410 * Get the first child as a Node. 1411 * 1412 * @return This node's first child or null 1413 */ 1414 public Node getFirstChild() 1415 { 1416 return m_firstChild; 1417 } 1418 1419 /** 1420 * Get the first child as a ElemTemplateElement. 1421 * 1422 * @return This node's first child (as a ElemTemplateElement) or null 1423 */ 1424 public ElemTemplateElement getFirstChildElem() 1425 { 1426 return m_firstChild; 1427 } 1428 1429 /** 1430 * Get the last child. 1431 * 1432 * @return This node's last child 1433 */ 1434 public Node getLastChild() 1435 { 1436 1437 ElemTemplateElement lastChild = null; 1438 1439 for (ElemTemplateElement node = m_firstChild; node != null; 1440 node = node.m_nextSibling) 1441 { 1442 lastChild = node; 1443 } 1444 1445 return lastChild; 1446 } 1447 1448 /** 1449 * Get the last child. 1450 * 1451 * @return This node's last child 1452 */ 1453 public ElemTemplateElement getLastChildElem() 1454 { 1455 1456 ElemTemplateElement lastChild = null; 1457 1458 for (ElemTemplateElement node = m_firstChild; node != null; 1459 node = node.m_nextSibling) 1460 { 1461 lastChild = node; 1462 } 1463 1464 return lastChild; 1465 } 1466 1467 1468 /** DOM backpointer that this element originated from. */ 1469 transient private org.w3c.dom.Node m_DOMBackPointer; 1470 1471 /** 1472 * If this stylesheet was created from a DOM, get the 1473 * DOM backpointer that this element originated from. 1474 * For tooling use. 1475 * 1476 * @return DOM backpointer that this element originated from or null. 1477 */ 1478 public org.w3c.dom.Node getDOMBackPointer() 1479 { 1480 return m_DOMBackPointer; 1481 } 1482 1483 /** 1484 * If this stylesheet was created from a DOM, set the 1485 * DOM backpointer that this element originated from. 1486 * For tooling use. 1487 * 1488 * @param n DOM backpointer that this element originated from. 1489 */ 1490 public void setDOMBackPointer(org.w3c.dom.Node n) 1491 { 1492 m_DOMBackPointer = n; 1493 } 1494 1495 /** 1496 * Compares this object with the specified object for precedence order. 1497 * The order is determined by the getImportCountComposed() of the containing 1498 * composed stylesheet and the getUid() of this element. 1499 * Returns a negative integer, zero, or a positive integer as this 1500 * object is less than, equal to, or greater than the specified object. 1501 * 1502 * @param o The object to be compared to this object 1503 * @return a negative integer, zero, or a positive integer as this object is 1504 * less than, equal to, or greater than the specified object. 1505 * @throws ClassCastException if the specified object's 1506 * type prevents it from being compared to this Object. 1507 */ 1508 public int compareTo(Object o) throws ClassCastException { 1509 1510 ElemTemplateElement ro = (ElemTemplateElement) o; 1511 int roPrecedence = ro.getStylesheetComposed().getImportCountComposed(); 1512 int myPrecedence = this.getStylesheetComposed().getImportCountComposed(); 1513 1514 if (myPrecedence < roPrecedence) 1515 return -1; 1516 else if (myPrecedence > roPrecedence) 1517 return 1; 1518 else 1519 return this.getUid() - ro.getUid(); 1520 } 1521 1522 /** 1523 * Get information about whether or not an element should strip whitespace. 1524 * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a> 1525 * 1526 * @param support The XPath runtime state. 1527 * @param targetElement Element to check 1528 * 1529 * @return true if the whitespace should be stripped. 1530 * 1531 * @throws TransformerException 1532 */ 1533 public boolean shouldStripWhiteSpace( 1534 org.apache.xpath.XPathContext support, 1535 org.w3c.dom.Element targetElement) throws TransformerException 1536 { 1537 StylesheetRoot sroot = this.getStylesheetRoot(); 1538 return (null != sroot) ? sroot.shouldStripWhiteSpace(support, targetElement) :false; 1539 } 1540 1541 /** 1542 * Get information about whether or not whitespace can be stripped. 1543 * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a> 1544 * 1545 * @return true if the whitespace can be stripped. 1546 */ 1547 public boolean canStripWhiteSpace() 1548 { 1549 StylesheetRoot sroot = this.getStylesheetRoot(); 1550 return (null != sroot) ? sroot.canStripWhiteSpace() : false; 1551 } 1552 1553 /** 1554 * Tell if this element can accept variable declarations. 1555 * @return true if the element can accept and process variable declarations. 1556 */ 1557 public boolean canAcceptVariables() 1558 { 1559 return true; 1560 } 1561 1562 //=============== ExpressionNode methods ================ 1563 1564 /** 1565 * Set the parent of this node. 1566 * @param n Must be a ElemTemplateElement. 1567 */ 1568 public void exprSetParent(ExpressionNode n) 1569 { 1570 // This obviously requires that only a ElemTemplateElement can 1571 // parent a node of this type. 1572 setParentElem((ElemTemplateElement)n); 1573 } 1574 1575 /** 1576 * Get the ExpressionNode parent of this node. 1577 */ 1578 public ExpressionNode exprGetParent() 1579 { 1580 return getParentElem(); 1581 } 1582 1583 /** 1584 * This method tells the node to add its argument to the node's 1585 * list of children. 1586 * @param n Must be a ElemTemplateElement. 1587 */ 1588 public void exprAddChild(ExpressionNode n, int i) 1589 { 1590 appendChild((ElemTemplateElement)n); 1591 } 1592 1593 /** This method returns a child node. The children are numbered 1594 from zero, left to right. */ 1595 public ExpressionNode exprGetChild(int i) 1596 { 1597 return (ExpressionNode)item(i); 1598 } 1599 1600 /** Return the number of children the node has. */ 1601 public int exprGetNumChildren() 1602 { 1603 return getLength(); 1604 } 1605 1606 /** 1607 * Accept a visitor and call the appropriate method 1608 * for this class. 1609 * 1610 * @param visitor The visitor whose appropriate method will be called. 1611 * @return true if the children of the object should be visited. 1612 */ 1613 protected boolean accept(XSLTVisitor visitor) 1614 { 1615 return visitor.visitInstruction(this); 1616 } 1617 1618 /** 1619 * @see XSLTVisitable#callVisitors(XSLTVisitor) 1620 */ 1621 public void callVisitors(XSLTVisitor visitor) 1622 { 1623 if(accept(visitor)) 1624 { 1625 callChildVisitors(visitor); 1626 } 1627 } 1628 1629 /** 1630 * Call the children visitors. 1631 * @param visitor The visitor whose appropriate method will be called. 1632 */ 1633 protected void callChildVisitors(XSLTVisitor visitor, boolean callAttributes) 1634 { 1635 for (ElemTemplateElement node = m_firstChild; 1636 node != null; 1637 node = node.m_nextSibling) 1638 { 1639 node.callVisitors(visitor); 1640 } 1641 } 1642 1643 /** 1644 * Call the children visitors. 1645 * @param visitor The visitor whose appropriate method will be called. 1646 */ 1647 protected void callChildVisitors(XSLTVisitor visitor) 1648 { 1649 callChildVisitors(visitor, true); 1650 } 1651 1652 1653 /** 1654 * @see PrefixResolver#handlesNullPrefixes() 1655 */ 1656 public boolean handlesNullPrefixes() { 1657 return false; 1658 } 1659 1660 } 1661