OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / libcore / luni / src / main / java / org / apache / xalan / templates / ElemTemplateElement.java
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 }