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 */
21package org.apache.xalan.templates;
22
23import java.io.Serializable;
24import java.util.ArrayList;
25import java.util.Enumeration;
26import java.util.List;
27
28import javax.xml.transform.SourceLocator;
29import javax.xml.transform.TransformerException;
30
31import org.apache.xalan.res.XSLMessages;
32import org.apache.xalan.res.XSLTErrorResources;
33import org.apache.xalan.transformer.TransformerImpl;
34import org.apache.xml.serializer.SerializationHandler;
35import org.apache.xml.utils.PrefixResolver;
36import org.apache.xml.utils.UnImplNode;
37import org.apache.xpath.ExpressionNode;
38import org.apache.xpath.WhitespaceStrippingElementMatcher;
39
40import org.w3c.dom.DOMException;
41import org.w3c.dom.Document;
42import org.w3c.dom.Node;
43import org.w3c.dom.NodeList;
44
45import 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 */
60public 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