001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: ElemLiteralResult.java 1225375 2011-12-28 23:03:43Z mrglavas $
020     */
021    package org.apache.xalan.templates;
022    
023    import java.util.ArrayList;
024    import java.util.Iterator;
025    import java.util.List;
026    
027    import javax.xml.transform.TransformerException;
028    
029    import org.apache.xalan.res.XSLMessages;
030    import org.apache.xalan.res.XSLTErrorResources;
031    import org.apache.xalan.transformer.TransformerImpl;
032    import org.apache.xml.serializer.SerializationHandler;
033    import org.apache.xml.utils.StringVector;
034    import org.apache.xpath.XPathContext;
035    import org.w3c.dom.Attr;
036    import org.w3c.dom.DOMException;
037    import org.w3c.dom.Document;
038    import org.w3c.dom.Element;
039    import org.w3c.dom.NamedNodeMap;
040    import org.w3c.dom.Node;
041    import org.w3c.dom.NodeList;
042    import org.w3c.dom.TypeInfo;
043    import org.w3c.dom.UserDataHandler;
044    import org.xml.sax.SAXException;
045    
046    /**
047     * Implement a Literal Result Element.
048     * @see <a href="http://www.w3.org/TR/xslt#literal-result-element">literal-result-element in XSLT Specification</a>
049     * @xsl.usage advanced
050     */
051    public class ElemLiteralResult extends ElemUse
052    {
053        static final long serialVersionUID = -8703409074421657260L;
054    
055        /** The return value as Empty String. */
056        private static final String EMPTYSTRING = "";
057    
058      /**
059       * Tells if this element represents a root element
060       * that is also the stylesheet element.
061       * TODO: This should be a derived class.
062       * @serial
063       */
064      private boolean isLiteralResultAsStylesheet = false;
065    
066      /**
067       * Set whether this element represents a root element
068       * that is also the stylesheet element.
069       *
070       *
071       * @param b boolean flag indicating whether this element
072       * represents a root element that is also the stylesheet element.
073       */
074      public void setIsLiteralResultAsStylesheet(boolean b)
075      {
076        isLiteralResultAsStylesheet = b;
077      }
078    
079      /**
080       * Return whether this element represents a root element
081       * that is also the stylesheet element.
082       *
083       *
084       * @return boolean flag indicating whether this element
085       * represents a root element that is also the stylesheet element.
086       */
087      public boolean getIsLiteralResultAsStylesheet()
088      {
089        return isLiteralResultAsStylesheet;
090      }
091      
092      /**
093       * This function is called after everything else has been
094       * recomposed, and allows the template to set remaining
095       * values that may be based on some other property that
096       * depends on recomposition.
097       */
098      public void compose(StylesheetRoot sroot) throws TransformerException
099      {
100        super.compose(sroot);
101        StylesheetRoot.ComposeState cstate = sroot.getComposeState();
102        java.util.Vector vnames = cstate.getVariableNames();
103        if (null != m_avts)
104        {
105          int nAttrs = m_avts.size();
106    
107          for (int i = (nAttrs - 1); i >= 0; i--)
108          {
109            AVT avt = (AVT) m_avts.get(i);
110            avt.fixupVariables(vnames, cstate.getGlobalsSize());
111          } 
112        }   
113      }
114      
115      /**
116       * The created element node will have the attribute nodes
117       * that were present on the element node in the stylesheet tree,
118       * other than attributes with names in the XSLT namespace.
119       * @serial
120       */
121      private List m_avts = null;
122    
123      /** List of attributes with the XSLT namespace.
124       *  @serial */
125      private List m_xslAttr = null;
126    
127      /**
128       * Set a literal result attribute (AVTs only).
129       *
130       * @param avt literal result attribute to add (AVT only)
131       */
132      public void addLiteralResultAttribute(AVT avt)
133      {
134    
135        if (null == m_avts)
136          m_avts = new ArrayList();
137    
138        m_avts.add(avt);
139      }
140    
141      /**
142       * Set a literal result attribute (used for xsl attributes).
143       *
144       * @param att literal result attribute to add
145       */
146      public void addLiteralResultAttribute(String att)
147      {
148    
149        if (null == m_xslAttr)
150          m_xslAttr = new ArrayList();
151    
152        m_xslAttr.add(att);
153      }
154      
155      /**
156       * Set the "xml:space" attribute.
157       * A text node is preserved if an ancestor element of the text node
158       * has an xml:space attribute with a value of preserve, and
159       * no closer ancestor element has xml:space with a value of default.
160       * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
161       * @see <a href="http://www.w3.org/TR/xslt#section-Creating-Text">section-Creating-Text in XSLT Specification</a>
162       *
163       * @param avt  Enumerated value, either Constants.ATTRVAL_PRESERVE 
164       * or Constants.ATTRVAL_STRIP.
165       */
166      public void setXmlSpace(AVT avt)
167      {
168        // This function is a bit-o-hack, I guess...
169        addLiteralResultAttribute(avt);
170        String val = avt.getSimpleString();
171        if(val.equals("default"))
172        {
173          super.setXmlSpace(Constants.ATTRVAL_STRIP);
174        }
175        else if(val.equals("preserve"))
176        {
177          super.setXmlSpace(Constants.ATTRVAL_PRESERVE);
178        }
179        // else maybe it's a real AVT, so we can't resolve it at this time.
180      }
181    
182      /**
183       * Get a literal result attribute by name.
184       *
185       * @param namespaceURI Namespace URI of attribute node to get
186       * @param localName Local part of qualified name of attribute node to get
187       *
188       * @return literal result attribute (AVT)
189       */
190      public AVT getLiteralResultAttributeNS(String namespaceURI, String localName)
191      {
192    
193        if (null != m_avts)
194        {
195          int nAttrs = m_avts.size();
196    
197          for (int i = (nAttrs - 1); i >= 0; i--)
198          {
199            AVT avt = (AVT) m_avts.get(i);
200    
201            if (avt.getName().equals(localName) && 
202                    avt.getURI().equals(namespaceURI))
203            {
204              return avt;
205            }
206          }  // end for
207        }
208    
209        return null;
210      }
211    
212      /**
213       * Return the raw value of the attribute.
214       *
215       * @param namespaceURI Namespace URI of attribute node to get
216       * @param localName Local part of qualified name of attribute node to get
217       *
218       * @return The Attr value as a string, or the empty string if that attribute 
219       * does not have a specified or default value
220       */
221      public String getAttributeNS(String namespaceURI, String localName)
222      {
223    
224        AVT avt = getLiteralResultAttributeNS(namespaceURI, localName);
225    
226        if ((null != avt))
227        {
228          return avt.getSimpleString();
229        }
230    
231        return EMPTYSTRING;
232      }
233    
234      /**
235       * Get a literal result attribute by name. The name is namespaceURI:localname  
236       * if namespace is not null.
237       *
238       * @param name Name of literal result attribute to get
239       *
240       * @return literal result attribute (AVT)
241       */
242      public AVT getLiteralResultAttribute(String name)
243      {
244    
245        if (null != m_avts)
246        {
247          int nAttrs = m_avts.size();
248          String namespace = null;
249          for (int i = (nAttrs - 1); i >= 0; i--)
250          {
251            AVT avt = (AVT) m_avts.get(i);
252            namespace = avt.getURI();
253            
254            if ((namespace != null && (namespace.length() != 0) && (namespace 
255                    +":"+avt.getName()).equals(name))|| ((namespace == null || 
256                    namespace.length() == 0)&& avt.getRawName().equals(name)))
257            {
258              return avt;
259            }
260          }  // end for
261        }
262    
263        return null;
264      }
265    
266      /**
267       * Return the raw value of the attribute.
268       *
269       * @param namespaceURI:localName or localName if the namespaceURI is null of 
270       * the attribute to get
271       *
272       * @return The Attr value as a string, or the empty string if that attribute 
273       * does not have a specified or default value
274       */
275      public String getAttribute(String rawName)
276      {
277    
278        AVT avt = getLiteralResultAttribute(rawName);
279    
280        if ((null != avt))
281        {
282          return avt.getSimpleString();
283        }
284    
285        return EMPTYSTRING;
286      }
287      
288      /**
289       * Get whether or not the passed URL is flagged by
290       * the "extension-element-prefixes" or "exclude-result-prefixes"
291       * properties.
292       * @see <a href="http://www.w3.org/TR/xslt#extension-element">extension-element in XSLT Specification</a>
293       *
294       * @param prefix non-null reference to prefix that might be excluded.(not currently used)
295       * @param uri reference to namespace that prefix maps to
296       *
297       * @return true if the prefix should normally be excluded.
298       */
299      public boolean containsExcludeResultPrefix(String prefix, String uri)
300      {
301        if (uri == null ||
302                    (null == m_excludeResultPrefixes &&
303                     null == m_ExtensionElementURIs)
304                    )
305          return super.containsExcludeResultPrefix(prefix, uri);
306    
307        if (prefix.length() == 0)
308          prefix = Constants.ATTRVAL_DEFAULT_PREFIX;
309    
310        // This loop is ok here because this code only runs during
311        // stylesheet compile time.    
312            if(m_excludeResultPrefixes!=null)
313                for (int i =0; i< m_excludeResultPrefixes.size(); i++)
314                {
315                    if (uri.equals(getNamespaceForPrefix(m_excludeResultPrefixes.elementAt(i))))
316                        return true;
317                }    
318            
319            // JJK Bugzilla 1133: Also check locally-scoped extensions
320        if(m_ExtensionElementURIs!=null && m_ExtensionElementURIs.contains(uri))
321           return true;
322    
323            return super.containsExcludeResultPrefix(prefix, uri);
324      }
325    
326      /**
327       * Augment resolvePrefixTables, resolving the namespace aliases once
328       * the superclass has resolved the tables.
329       *
330       * @throws TransformerException
331       */
332      public void resolvePrefixTables() throws TransformerException
333      {
334    
335        super.resolvePrefixTables();
336    
337        StylesheetRoot stylesheet = getStylesheetRoot();
338    
339        if ((null != m_namespace) && (m_namespace.length() > 0))
340        {
341          NamespaceAlias nsa = stylesheet.getNamespaceAliasComposed(m_namespace);
342    
343          if (null != nsa)
344          {
345            m_namespace = nsa.getResultNamespace();
346    
347            // String resultPrefix = nsa.getResultPrefix();
348            String resultPrefix = nsa.getStylesheetPrefix();  // As per xsl WG, Mike Kay
349    
350            if ((null != resultPrefix) && (resultPrefix.length() > 0))
351              m_rawName = resultPrefix + ":" + m_localName;
352            else
353              m_rawName = m_localName;
354          }
355        }
356    
357        if (null != m_avts)
358        {
359          int n = m_avts.size();
360    
361          for (int i = 0; i < n; i++)
362          {
363            AVT avt = (AVT) m_avts.get(i);
364    
365            // Should this stuff be a method on AVT?
366            String ns = avt.getURI();
367    
368            if ((null != ns) && (ns.length() > 0))
369            {
370              NamespaceAlias nsa =
371                stylesheet.getNamespaceAliasComposed(m_namespace); // %REVIEW% ns?
372    
373              if (null != nsa)
374              {
375                String namespace = nsa.getResultNamespace();
376    
377                // String resultPrefix = nsa.getResultPrefix();
378                String resultPrefix = nsa.getStylesheetPrefix();  // As per XSL WG
379                String rawName = avt.getName();
380    
381                if ((null != resultPrefix) && (resultPrefix.length() > 0))
382                  rawName = resultPrefix + ":" + rawName;
383    
384                avt.setURI(namespace);
385                avt.setRawName(rawName);
386              }
387            }
388          }
389        }
390      }
391    
392      /**
393       * Return whether we need to check namespace prefixes
394       * against the exclude result prefixes or extensions lists.
395       * Note that this will create a new prefix table if one
396       * has not been created already.
397       *
398       * NEEDSDOC ($objectName$) @return
399       */
400      boolean needToCheckExclude()
401      {
402        if (null == m_excludeResultPrefixes && null == getPrefixTable()
403                    && m_ExtensionElementURIs==null     // JJK Bugzilla 1133
404                    )
405          return false;
406        else
407        {
408    
409          // Create a new prefix table if one has not already been created.
410          if (null == getPrefixTable())
411            setPrefixTable(new java.util.ArrayList());
412    
413          return true;
414        }
415      }
416    
417      /**
418       * The namespace of the element to be created.
419       * @serial
420       */
421      private String m_namespace;
422    
423      /**
424       * Set the namespace URI of the result element to be created.
425       * Note that after resolvePrefixTables has been called, this will
426       * return the aliased result namespace, not the original stylesheet
427       * namespace.
428       *
429       * @param ns The Namespace URI, or the empty string if the
430       *        element has no Namespace URI.
431       */
432      public void setNamespace(String ns)
433      {
434        if(null == ns) // defensive, shouldn't have to do this.
435          ns = "";
436        m_namespace = ns;
437      }
438    
439      /**
440       * Get the original namespace of the Literal Result Element.
441       * 
442       * %REVIEW% Why isn't this overriding the getNamespaceURI method
443       * rather than introducing a new one?
444       *
445       * @return The Namespace URI, or the empty string if the
446       *        element has no Namespace URI.
447       */
448      public String getNamespace()
449      {
450        return m_namespace;
451      }
452    
453      /**
454       * The local name of the element to be created.
455       * @serial
456       */
457      private String m_localName;
458    
459      /**
460       * Set the local name of the LRE.
461       *
462       * @param localName The local name (without prefix) of the result element
463       *                  to be created.
464       */
465      public void setLocalName(String localName)
466      {
467        m_localName = localName;
468      }
469    
470      /**
471       * Get the local name of the Literal Result Element.
472       * Note that after resolvePrefixTables has been called, this will
473       * return the aliased name prefix, not the original stylesheet
474       * namespace prefix.
475       *
476       * @return The local name (without prefix) of the result element
477       *                  to be created.
478       */
479      public String getLocalName()
480      {
481        return m_localName;
482      }
483    
484      /**
485       * The raw name of the element to be created.
486       * @serial
487       */
488      private String m_rawName;
489    
490      /**
491       * Set the raw name of the LRE.
492       *
493       * @param rawName The qualified name (with prefix), or the
494       *        empty string if qualified names are not available.
495       */
496      public void setRawName(String rawName)
497      {
498        m_rawName = rawName;
499      }
500    
501      /**
502       * Get the raw name of the Literal Result Element.
503       *
504       * @return  The qualified name (with prefix), or the
505       *        empty string if qualified names are not available.
506       */
507      public String getRawName()
508      {
509        return m_rawName;
510      }
511        
512     /**
513       * Get the prefix part of the raw name of the Literal Result Element.
514       *
515       * @return The prefix, or the empty string if noprefix was provided.
516       */
517      public String getPrefix()
518      {
519            int len=m_rawName.length()-m_localName.length()-1;
520        return (len>0)
521                ? m_rawName.substring(0,len)
522                : "";
523      }
524    
525    
526      /**
527       * The "extension-element-prefixes" property, actually contains URIs.
528       * @serial
529       */
530      private StringVector m_ExtensionElementURIs;
531    
532      /**
533       * Set the "extension-element-prefixes" property.
534       * @see <a href="http://www.w3.org/TR/xslt#extension-element">extension-element in XSLT Specification</a>
535       *
536       * @param v Vector of URIs (not prefixes) to set as the "extension-element-prefixes" property
537       */
538      public void setExtensionElementPrefixes(StringVector v)
539      {
540        m_ExtensionElementURIs = v;
541      }
542    
543      /**
544       * @see org.w3c.dom.Node
545       *
546       * @return NamedNodeMap
547       */
548      public NamedNodeMap getAttributes()
549      {
550            return new LiteralElementAttributes();
551      }
552    
553      public class LiteralElementAttributes implements NamedNodeMap{
554              private int m_count = -1;
555              
556              /**
557               * Construct a NameNodeMap.
558               *
559               */
560              public LiteralElementAttributes(){         
561              }
562              
563              /**
564               * Return the number of Attributes on this Element
565               *
566               * @return The number of nodes in this map. The range of valid child 
567               * node indices is <code>0</code> to <code>length-1</code> inclusive
568               */
569              public int getLength()
570              {
571                if (m_count == -1)
572                {
573                   if (null != m_avts) m_count = m_avts.size();
574                   else m_count = 0;
575                }
576                return m_count;
577              }
578    
579              /**
580               * Retrieves a node specified by name.
581               * @param name The <code>nodeName</code> of a node to retrieve.
582               * @return A <code>Node</code> (of any type) with the specified
583               *   <code>nodeName</code>, or <code>null</code> if it does not 
584               *   identify any node in this map.
585               */
586              public Node getNamedItem(String name)
587              {
588                    if (getLength() == 0) return null;
589                    String uri = null;
590                    String localName = name; 
591                    int index = name.indexOf(":"); 
592                    if (-1 != index){
593                             uri = name.substring(0, index);
594                             localName = name.substring(index+1);
595                    }
596                    Node retNode = null;
597                    Iterator eum = m_avts.iterator();
598                    while (eum.hasNext()){
599                            AVT avt = (AVT) eum.next();
600                            if (localName.equals(avt.getName()))
601                            {
602                              String nsURI = avt.getURI(); 
603                              if ((uri == null && nsURI == null)
604                                || (uri != null && uri.equals(nsURI)))
605                              {
606                                retNode = new Attribute(avt, ElemLiteralResult.this);
607                                break;
608                              }
609                            }
610                    }
611                    return retNode;
612              }
613    
614              /**
615               * Retrieves a node specified by local name and namespace URI.
616               * @param namespaceURI Namespace URI of attribute node to get
617               * @param localName Local part of qualified name of attribute node to 
618               * get
619               * @return A <code>Node</code> (of any type) with the specified
620               *   <code>nodeName</code>, or <code>null</code> if it does not 
621               *   identify any node in this map.
622               */
623              public Node getNamedItemNS(String namespaceURI, String localName)
624              {
625                      if (getLength() == 0) return null;
626                      Node retNode = null;
627                      Iterator eum = m_avts.iterator();
628                      while (eum.hasNext())
629                      {
630                        AVT avt = (AVT) eum.next();      
631                        if (localName.equals(avt.getName()))
632                        {
633                          String nsURI = avt.getURI(); 
634                          if ((namespaceURI == null && nsURI == null)
635                            || (namespaceURI != null && namespaceURI.equals(nsURI)))
636                          {
637                            retNode = new Attribute(avt, ElemLiteralResult.this);
638                            break;
639                          }
640                        }
641                      }
642                      return retNode;
643              }
644              
645              /**
646               * Returns the <code>index</code>th item in the map. If <code>index
647               * </code> is greater than or equal to the number of nodes in this 
648               * map, this returns <code>null</code>.
649               * @param i The index of the requested item.
650               * @return The node at the <code>index</code>th position in the map, 
651               *   or <code>null</code> if that is not a valid index.
652               */
653              public Node item(int i)
654              {
655                    if (getLength() == 0 || i >= m_avts.size()) return null;
656                    else return 
657                        new Attribute(((AVT)m_avts.get(i)), 
658                            ElemLiteralResult.this);
659              }
660              
661              /**
662               * @see org.w3c.dom.NamedNodeMap
663               *
664               * @param name of the node to remove
665               * 
666               * @return The node removed from this map if a node with such 
667               * a name exists. 
668               *
669               * @throws DOMException
670               */
671              public Node removeNamedItem(String name) throws DOMException
672              {
673                      throwDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
674                          XSLTErrorResources.NO_MODIFICATION_ALLOWED_ERR); 
675                      return null;
676              }
677              
678              /**
679               * @see org.w3c.dom.NamedNodeMap
680               *
681               * @param namespaceURI Namespace URI of the node to remove
682               * @param localName Local part of qualified name of the node to remove
683               * 
684               * @return The node removed from this map if a node with such a local
685               *  name and namespace URI exists
686               *
687               * @throws DOMException
688               */
689              public Node removeNamedItemNS(String namespaceURI, String localName) 
690                    throws DOMException
691              {
692                      throwDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
693                          XSLTErrorResources.NO_MODIFICATION_ALLOWED_ERR); 
694                      return null;
695              } 
696              
697              /**
698               * Unimplemented. See org.w3c.dom.NamedNodeMap
699               *
700               * @param A node to store in this map
701               * 
702               * @return If the new Node replaces an existing node the replaced 
703               * Node is returned, otherwise null is returned
704               *
705               * @throws DOMException
706               */
707              public Node setNamedItem(Node arg) throws DOMException
708              {
709                      throwDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
710                          XSLTErrorResources.NO_MODIFICATION_ALLOWED_ERR); 
711                      return null;
712              }
713              
714              /**
715               * Unimplemented. See org.w3c.dom.NamedNodeMap
716               *
717               * @param A node to store in this map
718               * 
719               * @return If the new Node replaces an existing node the replaced 
720               * Node is returned, otherwise null is returned
721               *
722               * @throws DOMException
723               */
724              public Node setNamedItemNS(Node arg) throws DOMException
725              {
726                      throwDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
727                          XSLTErrorResources.NO_MODIFICATION_ALLOWED_ERR); 
728                      return null;
729              }                                                                         
730      }
731    
732      public class Attribute implements Attr{
733              private AVT m_attribute;
734              private Element m_owner = null;
735              /**
736               * Construct a Attr.
737               *
738               */
739              public Attribute(AVT avt, Element elem){
740                    m_attribute = avt;
741                    m_owner = elem;
742              }
743    
744              /**
745               * @see org.w3c.dom.Node
746               *
747               * @param newChild New node to append to the list of this node's 
748               * children
749               *
750               *
751               * @throws DOMException
752               */
753              public Node appendChild(Node newChild) throws DOMException
754              {
755                      throwDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
756                          XSLTErrorResources.NO_MODIFICATION_ALLOWED_ERR); 
757                      return null;
758              }
759    
760              /**
761               * @see org.w3c.dom.Node
762               *
763               * @param deep Flag indicating whether to clone deep 
764               * (clone member variables)
765               *
766               * @return Returns a duplicate of this node
767               */
768              public Node cloneNode(boolean deep)
769              {
770                      return new Attribute(m_attribute, m_owner);
771              }
772              
773              /**
774               * @see org.w3c.dom.Node
775               *
776               * @return null
777               */
778              public NamedNodeMap getAttributes()
779              {
780                return null;
781              }
782    
783              /**
784               * @see org.w3c.dom.Node
785               *
786               * @return a NodeList containing no nodes. 
787               */
788              public NodeList getChildNodes()
789              {
790                      return new NodeList(){
791                              public int getLength(){
792                                      return 0;
793                              }
794                              public Node item(int index){
795                                      return null;
796                              }
797                      };
798              }
799    
800              /**
801               * @see org.w3c.dom.Node
802               *
803               * @return null
804               */
805              public Node getFirstChild()
806              {
807                      return null;
808              }
809    
810              /**
811               * @see org.w3c.dom.Node
812               *
813               * @return null
814               */
815              public Node getLastChild()
816              {
817                      return null;
818              }
819              
820              /**
821               * @see org.w3c.dom.Node
822               *
823               * @return the local part of the qualified name of this node
824               */
825              public String getLocalName()
826              {
827                      return m_attribute.getName();
828              }
829              
830              /**
831               * @see org.w3c.dom.Node
832               *
833               * @return The namespace URI of this node, or null if it is 
834               * unspecified
835               */
836              public String getNamespaceURI()
837              {
838                      String uri = m_attribute.getURI();
839                      return (uri.length() == 0)?null:uri;
840              }
841    
842              /**
843               * @see org.w3c.dom.Node
844               *
845               * @return null
846               */
847              public Node getNextSibling()
848              {
849                    return null;
850              }
851              
852              /**
853               * @see org.w3c.dom.Node
854               *
855               * @return The name of the attribute
856               */
857              public String getNodeName()
858              {
859                      String uri = m_attribute.getURI();
860                      String localName = getLocalName();
861                      return (uri.length() == 0)?localName:uri+":"+localName;
862              }
863              
864              /**
865               * @see org.w3c.dom.Node
866               *
867               * @return The node is an Attr
868               */
869              public short getNodeType()
870              {
871                      return ATTRIBUTE_NODE;
872              }
873              
874              /**
875               * @see org.w3c.dom.Node
876               *
877               * @return The value of the attribute
878               *
879               * @throws DOMException
880               */
881              public String getNodeValue() throws DOMException
882              {
883                      return m_attribute.getSimpleString();
884              }
885    
886              /**
887               * @see org.w3c.dom.Node
888               *
889               * @return null
890               */
891              public Document getOwnerDocument()
892              {
893                return m_owner.getOwnerDocument();
894              }
895    
896              /**
897               * @see org.w3c.dom.Node
898               *
899               * @return the containing element node
900               */
901              public Node getParentNode()
902              {
903                      return m_owner;
904              }
905                        
906              /**
907               * @see org.w3c.dom.Node
908               *
909               * @return The namespace prefix of this node, or null if it is 
910               * unspecified
911               */
912              public String getPrefix()
913              {
914                      String uri = m_attribute.getURI();
915                      String rawName = m_attribute.getRawName();
916                      return (uri.length() == 0)? 
917                            null:rawName.substring(0, rawName.indexOf(":"));
918              }
919    
920              /**
921               * @see org.w3c.dom.Node
922               *
923               * @return null
924               */
925              public Node getPreviousSibling()
926              {
927                      return null;
928              }
929              
930              /**
931               * @see org.w3c.dom.Node
932               *
933               * @return false
934               */
935              public boolean hasAttributes()
936              {
937                      return false;
938              }
939              
940              /**
941               * @see org.w3c.dom.Node
942               *
943               * @return false
944               */
945              public boolean hasChildNodes()
946              {
947                      return false;
948              }                    
949    
950              /**
951               * @see org.w3c.dom.Node
952               *
953               * @param newChild New child node to insert
954               * @param refChild Insert in front of this child
955               *
956               * @return null
957               *
958               * @throws DOMException
959               */
960              public Node insertBefore(Node newChild, Node refChild) 
961                    throws DOMException
962              {
963                      throwDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
964                          XSLTErrorResources.NO_MODIFICATION_ALLOWED_ERR);
965                      return null;
966              }
967    
968              /**
969               * @see org.w3c.dom.Node
970               *
971               * @return Returns <code>false</code>
972               * @since DOM Level 2
973               */
974              public boolean isSupported(String feature, String version)
975              {
976                return false;
977              }
978    
979              /** @see org.w3c.dom.Node */
980              public void normalize(){}
981              
982              /**
983               * @see org.w3c.dom.Node
984               *
985               * @param oldChild Child to be removed
986               *
987               * @return null
988               *
989               * @throws DOMException
990               */
991              public Node removeChild(Node oldChild) throws DOMException
992              {
993                      throwDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
994                          XSLTErrorResources.NO_MODIFICATION_ALLOWED_ERR); 
995                      return null;
996              }         
997    
998              /**
999               * @see org.w3c.dom.Node
1000               *
1001               * @param newChild Replace existing child with this one
1002               * @param oldChild Existing child to be replaced
1003               *
1004               * @return null
1005               *
1006               * @throws DOMException
1007               */
1008              public Node replaceChild(Node newChild, Node oldChild) throws DOMException
1009              {
1010                      throwDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
1011                          XSLTErrorResources.NO_MODIFICATION_ALLOWED_ERR); 
1012                      return null;
1013              }
1014    
1015              /**
1016               * @see org.w3c.dom.Node
1017               *
1018               * @param nodeValue Value to set this node to
1019               *
1020               * @throws DOMException
1021               */
1022              public void setNodeValue(String nodeValue) throws DOMException
1023              {
1024                      throwDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
1025                          XSLTErrorResources.NO_MODIFICATION_ALLOWED_ERR); 
1026              }
1027    
1028              /**
1029               * @see org.w3c.dom.Node
1030               *
1031               * @param prefix Prefix to set for this node
1032               *
1033               * @throws DOMException
1034               */
1035              public void setPrefix(String prefix) throws DOMException
1036              {
1037                      throwDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
1038                          XSLTErrorResources.NO_MODIFICATION_ALLOWED_ERR);
1039              }
1040                                                                          
1041              /**
1042               *
1043               * @return The name of this attribute
1044               */          
1045              public String getName(){
1046                      return m_attribute.getName();                            
1047              }
1048    
1049              /**
1050               *
1051               * @return The value of this attribute returned as string
1052               */          
1053              public String getValue(){
1054                      return m_attribute.getSimpleString();                            
1055              }
1056              
1057              /**
1058               *
1059               * @return The Element node this attribute is attached to 
1060               * or null if this attribute is not in use
1061               */                    
1062              public Element getOwnerElement(){
1063                      return m_owner;
1064              }
1065              
1066              /**
1067               *
1068               * @return true
1069               */          
1070              public boolean getSpecified(){
1071                      return true;
1072              }
1073              
1074              /**
1075               * @see org.w3c.dom.Attr
1076               *
1077               * @param value Value to set this node to
1078               *
1079               * @throws DOMException
1080               */
1081              public void setValue(String value) throws DOMException
1082              {
1083                throwDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
1084                    XSLTErrorResources.NO_MODIFICATION_ALLOWED_ERR); 
1085              }
1086    
1087              public TypeInfo getSchemaTypeInfo() { return null; }
1088        
1089              public boolean isId( ) { return false; }
1090    
1091              public Object setUserData(String key,
1092                                        Object data,
1093                                        UserDataHandler handler) {
1094                    return getOwnerDocument().setUserData( key, data, handler);
1095              }
1096    
1097              public Object getUserData(String key) {
1098                    return getOwnerDocument().getUserData( key);
1099              } 
1100    
1101              public Object getFeature(String feature, String version) {
1102                    return isSupported(feature, version) ? this : null;
1103              }
1104              
1105              public boolean isEqualNode(Node arg) {
1106                    return arg == this;
1107              }
1108              
1109              public String lookupNamespaceURI(String specifiedPrefix) {
1110                    return null;
1111              }
1112              
1113              public boolean isDefaultNamespace(String namespaceURI) {
1114                    return false;
1115              }
1116    
1117              public String lookupPrefix(String namespaceURI) {
1118                    return null;
1119              }
1120              
1121              public boolean isSameNode(Node other) {
1122                    // we do not use any wrapper so the answer is obvious
1123                    return this == other;
1124              }
1125              
1126              public void setTextContent(String textContent)
1127                    throws DOMException {
1128                    setNodeValue(textContent);
1129              }
1130    
1131              public String getTextContent() throws DOMException {
1132                    return getNodeValue();  // overriden in some subclasses
1133              }
1134    
1135              public short compareDocumentPosition(Node other) throws DOMException {
1136                    return 0;
1137              }
1138    
1139              public String getBaseURI() {
1140                    return null;
1141              }
1142      }        
1143      
1144      /**
1145       * Get an "extension-element-prefix" property.
1146       * @see <a href="http://www.w3.org/TR/xslt#extension-element">extension-element in XSLT Specification</a>
1147       *
1148       * @param i Index of URI ("extension-element-prefix" property) to get
1149       *
1150       * @return URI at given index ("extension-element-prefix" property)
1151       *
1152       * @throws ArrayIndexOutOfBoundsException
1153       */
1154      public String getExtensionElementPrefix(int i)
1155              throws ArrayIndexOutOfBoundsException
1156      {
1157    
1158        if (null == m_ExtensionElementURIs)
1159          throw new ArrayIndexOutOfBoundsException();
1160    
1161        return m_ExtensionElementURIs.elementAt(i);
1162      }
1163    
1164      /**
1165       * Get the number of "extension-element-prefixes" Strings.
1166       * @see <a href="http://www.w3.org/TR/xslt#extension-element">extension-element in XSLT Specification</a>
1167       *
1168       * @return the number of "extension-element-prefixes" Strings
1169       */
1170      public int getExtensionElementPrefixCount()
1171      {
1172        return (null != m_ExtensionElementURIs)
1173               ? m_ExtensionElementURIs.size() : 0;
1174      }
1175    
1176      /**
1177       * Find out if the given "extension-element-prefix" property is defined.
1178       * @see <a href="http://www.w3.org/TR/xslt#extension-element">extension-element in XSLT Specification</a>
1179       *
1180       * @param uri The URI to find
1181       *
1182       * @return True if the given URI is found
1183       */
1184      public boolean containsExtensionElementURI(String uri)
1185      {
1186    
1187        if (null == m_ExtensionElementURIs)
1188          return false;
1189    
1190        return m_ExtensionElementURIs.contains(uri);
1191      }
1192    
1193      /**
1194       * Get an int constant identifying the type of element.
1195       * @see org.apache.xalan.templates.Constants
1196       *
1197       * @return The token ID for this element
1198       */
1199      public int getXSLToken()
1200      {
1201        return Constants.ELEMNAME_LITERALRESULT;
1202      }
1203    
1204      /**
1205       * Return the node name.
1206       *
1207       * @return The element's name
1208       */
1209      public String getNodeName()
1210      {
1211    
1212        // TODO: Need prefix.
1213        return m_rawName;
1214      }
1215    
1216      /**
1217       * The XSLT version as specified by this element.
1218       * @serial
1219       */
1220      private String m_version;
1221    
1222      /**
1223       * Set the "version" property.
1224       * @see <a href="http://www.w3.org/TR/xslt#forwards">forwards in XSLT Specification</a>
1225       *
1226       * @param v Version property value to set
1227       */
1228      public void setVersion(String v)
1229      {
1230        m_version = v;
1231      }
1232      
1233      /**
1234       * Get the "version" property.
1235       * @see <a href="http://www.w3.org/TR/xslt#forwards">forwards in XSLT Specification</a>
1236       *
1237       * @return Version property value
1238       */
1239      public String getVersion()
1240      {
1241        return m_version;
1242      }
1243    
1244      /**
1245       * The "exclude-result-prefixes" property.
1246       * @serial
1247       */
1248      private StringVector m_excludeResultPrefixes;
1249    
1250      /**
1251       * Set the "exclude-result-prefixes" property.
1252       * The designation of a namespace as an excluded namespace is
1253       * effective within the subtree of the stylesheet rooted at
1254       * the element bearing the exclude-result-prefixes or
1255       * xsl:exclude-result-prefixes attribute; a subtree rooted
1256       * at an xsl:stylesheet element does not include any stylesheets
1257       * imported or included by children of that xsl:stylesheet element.
1258       * @see <a href="http://www.w3.org/TR/xslt#literal-result-element">literal-result-element in XSLT Specification</a>
1259       *
1260       * @param v vector of prefixes that are resolvable to strings.
1261       */
1262      public void setExcludeResultPrefixes(StringVector v)
1263      {
1264        m_excludeResultPrefixes = v;
1265      }
1266    
1267      /**
1268       * Tell if the result namespace decl should be excluded.  Should be called before
1269       * namespace aliasing (I think).
1270       *
1271       * @param prefix Prefix of namespace to check
1272       * @param uri URI of namespace to check
1273       *
1274       * @return True if the given namespace should be excluded
1275       *
1276       * @throws TransformerException
1277       */
1278      private boolean excludeResultNSDecl(String prefix, String uri)
1279              throws TransformerException
1280      {
1281    
1282        if (null != m_excludeResultPrefixes)
1283        {
1284          return containsExcludeResultPrefix(prefix, uri);
1285        }
1286    
1287        return false;
1288      }
1289      
1290      /**
1291       * Copy a Literal Result Element into the Result tree, copy the
1292       * non-excluded namespace attributes, copy the attributes not
1293       * of the XSLT namespace, and execute the children of the LRE.
1294       * @see <a href="http://www.w3.org/TR/xslt#literal-result-element">literal-result-element in XSLT Specification</a>
1295       *
1296       * @param transformer non-null reference to the the current transform-time state.
1297       *
1298       * @throws TransformerException
1299       */
1300        public void execute(TransformerImpl transformer)
1301            throws TransformerException
1302        {
1303            SerializationHandler rhandler = transformer.getSerializationHandler();
1304    
1305            try
1306            {
1307                if (transformer.getDebug()) {
1308                    // flush any buffered pending processing before
1309                    // the trace event.
1310                    rhandler.flushPending();
1311                    transformer.getTraceManager().fireTraceEvent(this);
1312                }
1313    
1314                // JJK Bugzilla 3464, test namespace85 -- make sure LRE's
1315                // namespace is asserted even if default, since xsl:element
1316                // may have changed the context.
1317                rhandler.startPrefixMapping(getPrefix(), getNamespace());
1318    
1319                // Add namespace declarations.
1320                executeNSDecls(transformer);
1321                rhandler.startElement(getNamespace(), getLocalName(), getRawName());
1322            }
1323            catch (SAXException se)
1324            {
1325                throw new TransformerException(se);
1326            }
1327    
1328            /*
1329             * If we make it to here we have done a successful startElement()
1330             * we will do an endElement() call for balance, no matter what happens
1331             * in the middle.  
1332             */
1333    
1334            // tException remembers if we had an exception "in the middle"
1335            TransformerException tException = null;
1336            try
1337            {
1338    
1339                // Process any possible attributes from xsl:use-attribute-sets first
1340                super.execute(transformer);
1341    
1342                //xsl:version, excludeResultPrefixes???
1343                // Process the list of avts next
1344                if (null != m_avts)
1345                {
1346                    int nAttrs = m_avts.size();
1347    
1348                    for (int i = (nAttrs - 1); i >= 0; i--)
1349                    {
1350                        AVT avt = (AVT) m_avts.get(i);
1351                        XPathContext xctxt = transformer.getXPathContext();
1352                        int sourceNode = xctxt.getCurrentNode();
1353                        String stringedValue =
1354                            avt.evaluate(xctxt, sourceNode, this);
1355    
1356                        if (null != stringedValue)
1357                        {
1358    
1359                            // Important Note: I'm not going to check for excluded namespace 
1360                            // prefixes here.  It seems like it's too expensive, and I'm not 
1361                            // even sure this is right.  But I could be wrong, so this needs 
1362                            // to be tested against other implementations.
1363    
1364                            rhandler.addAttribute(
1365                                avt.getURI(),
1366                                avt.getName(),
1367                                avt.getRawName(),
1368                                "CDATA",
1369                                stringedValue, false);
1370                        }
1371                    } // end for
1372                }
1373    
1374                // Now process all the elements in this subtree
1375                // TODO: Process m_extensionElementPrefixes && m_attributeSetsNames
1376                transformer.executeChildTemplates(this, true);
1377            }
1378            catch (TransformerException te)
1379            {
1380                // thrown in finally to prevent original exception consumed by subsequent exceptions
1381                tException = te;
1382            }
1383            catch (SAXException se)
1384            {
1385                tException = new TransformerException(se);
1386            }
1387    
1388            try
1389            {
1390                /* we need to do this endElement() to balance the
1391                 * successful startElement() call even if 
1392                 * there was an exception in the middle.
1393                 * Otherwise an exception in the middle could cause a system to hang.
1394                 */
1395                if (transformer.getDebug()) {
1396                    // flush any buffered pending processing before
1397                    // the trace event.
1398                    //rhandler.flushPending();
1399                    transformer.getTraceManager().fireTraceEndEvent(this);
1400                }
1401                rhandler.endElement(getNamespace(), getLocalName(), getRawName());
1402            }
1403            catch (SAXException se)
1404            {
1405                /* we did call endElement(). If thee was an exception
1406                 * in the middle throw that one, otherwise if there
1407                 * was an exception from endElement() throw that one.
1408                 */
1409                if (tException != null)
1410                    throw tException;
1411                else
1412                    throw new TransformerException(se);
1413            }
1414            
1415            /* If an exception was thrown in the middle but not with startElement() or
1416             * or endElement() then its time to let it percolate.
1417             */ 
1418            if (tException != null)
1419                throw tException; 
1420            
1421            unexecuteNSDecls(transformer);
1422    
1423            // JJK Bugzilla 3464, test namespace85 -- balance explicit start.
1424            try
1425            {
1426                rhandler.endPrefixMapping(getPrefix());
1427            }
1428            catch (SAXException se)
1429            {
1430                throw new TransformerException(se);
1431            }
1432        }
1433    
1434      /**
1435       * Compiling templates requires that we be able to list the AVTs
1436       * ADDED 9/5/2000 to support compilation experiment
1437       *
1438       * @return an Enumeration of the literal result attributes associated
1439       * with this element.
1440       */
1441      public Iterator enumerateLiteralResultAttributes()
1442      {
1443        return (null == m_avts) ? null : m_avts.iterator();
1444      }
1445      
1446        /**
1447         * Accept a visitor and call the appropriate method 
1448         * for this class.
1449         * 
1450         * @param visitor The visitor whose appropriate method will be called.
1451         * @return true if the children of the object should be visited.
1452         */
1453        protected boolean accept(XSLTVisitor visitor)
1454        {
1455          return visitor.visitLiteralResultElement(this);
1456        }
1457    
1458        /**
1459         * Call the children visitors.
1460         * @param visitor The visitor whose appropriate method will be called.
1461         */
1462        protected void callChildVisitors(XSLTVisitor visitor, boolean callAttrs)
1463        {
1464          if (callAttrs && null != m_avts)
1465          {
1466            int nAttrs = m_avts.size();
1467    
1468            for (int i = (nAttrs - 1); i >= 0; i--)
1469            {
1470              AVT avt = (AVT) m_avts.get(i);
1471              avt.callVisitors(visitor);
1472            }
1473          }
1474          super.callChildVisitors(visitor, callAttrs);
1475        }
1476    
1477        /**
1478         * Throw a DOMException
1479         *
1480         * @param msg key of the error that occured.
1481         */
1482        public void throwDOMException(short code, String msg)
1483        {
1484    
1485          String themsg = XSLMessages.createMessage(msg, null);
1486    
1487          throw new DOMException(code, themsg);
1488        }
1489    
1490    }