View Javadoc
1   /*
2    * reserved comment block
3    * DO NOT REMOVE OR ALTER!
4    */
5   /*
6    * Copyright 2001-2004 The Apache Software Foundation.
7    *
8    * Licensed under the Apache License, Version 2.0 (the "License");
9    * you may not use this file except in compliance with the License.
10   * You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  /*
21   * $Id: ToXMLSAXHandler.java,v 1.3 2005/09/28 13:49:08 pvedula Exp $
22   */
23   package com.sun.org.apache.xml.internal.serializer;
24  
25  import java.io.IOException;
26  import java.io.OutputStream;
27  import java.io.Writer;
28  import java.util.Properties;
29  
30  import javax.xml.transform.Result;
31  
32  import org.w3c.dom.Node;
33  import org.xml.sax.Attributes;
34  import org.xml.sax.ContentHandler;
35  import org.xml.sax.Locator;
36  import org.xml.sax.SAXException;
37  import org.xml.sax.ext.LexicalHandler;
38  
39  /**
40   * This class receives notification of SAX-like events, and with gathered
41   * information over these calls it will invoke the equivalent SAX methods
42   * on a handler, the ultimate xsl:output method is known to be "xml".
43   *
44   * This class is not a public API, it is only public because it is used by Xalan.
45   * @xsl.usage internal
46   */
47  public final class ToXMLSAXHandler extends ToSAXHandler
48  {
49  
50      /**
51       * Keeps track of whether output escaping is currently enabled
52       */
53      protected boolean m_escapeSetting = true;
54  
55      public ToXMLSAXHandler()
56      {
57          // default constructor (need to set content handler ASAP !)
58          m_prefixMap = new NamespaceMappings();
59          initCDATA();
60      }
61  
62      /**
63       * @see Serializer#getOutputFormat()
64       */
65      public Properties getOutputFormat()
66      {
67          return null;
68      }
69  
70      /**
71       * @see Serializer#getOutputStream()
72       */
73      public OutputStream getOutputStream()
74      {
75          return null;
76      }
77  
78      /**
79       * @see Serializer#getWriter()
80       */
81      public Writer getWriter()
82      {
83          return null;
84      }
85  
86      /**
87       * Do nothing for SAX.
88       */
89      public void indent(int n) throws SAXException
90      {
91      }
92  
93  
94      /**
95       * @see DOMSerializer#serialize(Node)
96       */
97      public void serialize(Node node) throws IOException
98      {
99      }
100 
101     /**
102      * @see SerializationHandler#setEscaping(boolean)
103      */
104     public boolean setEscaping(boolean escape) throws SAXException
105     {
106         boolean oldEscapeSetting = m_escapeSetting;
107         m_escapeSetting = escape;
108 
109         if (escape) {
110             processingInstruction(Result.PI_ENABLE_OUTPUT_ESCAPING, "");
111         } else {
112             processingInstruction(Result.PI_DISABLE_OUTPUT_ESCAPING, "");
113         }
114 
115         return oldEscapeSetting;
116     }
117 
118     /**
119      * @see Serializer#setOutputFormat(Properties)
120      */
121     public void setOutputFormat(Properties format)
122     {
123     }
124 
125     /**
126      * @see Serializer#setOutputStream(OutputStream)
127      */
128     public void setOutputStream(OutputStream output)
129     {
130     }
131 
132     /**
133      * @see Serializer#setWriter(Writer)
134      */
135     public void setWriter(Writer writer)
136     {
137     }
138 
139     /**
140      * @see org.xml.sax.ext.DeclHandler#attributeDecl(String, String, String, String, String)
141      */
142     public void attributeDecl(
143         String arg0,
144         String arg1,
145         String arg2,
146         String arg3,
147         String arg4)
148         throws SAXException
149     {
150     }
151 
152     /**
153      * @see org.xml.sax.ext.DeclHandler#elementDecl(String, String)
154      */
155     public void elementDecl(String arg0, String arg1) throws SAXException
156     {
157     }
158 
159     /**
160      * @see org.xml.sax.ext.DeclHandler#externalEntityDecl(String, String, String)
161      */
162     public void externalEntityDecl(String arg0, String arg1, String arg2)
163         throws SAXException
164     {
165     }
166 
167     /**
168      * @see org.xml.sax.ext.DeclHandler#internalEntityDecl(String, String)
169      */
170     public void internalEntityDecl(String arg0, String arg1)
171         throws SAXException
172     {
173     }
174 
175     /**
176      * Receives notification of the end of the document.
177      * @see org.xml.sax.ContentHandler#endDocument()
178      */
179     public void endDocument() throws SAXException
180     {
181 
182         flushPending();
183 
184         // Close output document
185         m_saxHandler.endDocument();
186 
187         if (m_tracer != null)
188             super.fireEndDoc();
189     }
190 
191     /**
192      * This method is called when all the data needed for a call to the
193      * SAX handler's startElement() method has been gathered.
194      */
195     protected void closeStartTag() throws SAXException
196     {
197 
198         m_elemContext.m_startTagOpen = false;
199 
200         final String localName = getLocalName(m_elemContext.m_elementName);
201         final String uri = getNamespaceURI(m_elemContext.m_elementName, true);
202 
203         // Now is time to send the startElement event
204         if (m_needToCallStartDocument)
205         {
206             startDocumentInternal();
207         }
208         m_saxHandler.startElement(uri, localName, m_elemContext.m_elementName, m_attributes);
209         // we've sent the official SAX attributes on their way,
210         // now we don't need them anymore.
211         m_attributes.clear();
212 
213         if(m_state != null)
214           m_state.setCurrentNode(null);
215     }
216 
217     /**
218      * Closes ane open cdata tag, and
219      * unlike the this.endCDATA() method (from the LexicalHandler) interface,
220      * this "internal" method will send the endCDATA() call to the wrapped
221      * handler.
222      *
223      */
224     public void closeCDATA() throws SAXException
225     {
226 
227         // Output closing bracket - "]]>"
228         if (m_lexHandler != null && m_cdataTagOpen) {
229             m_lexHandler.endCDATA();
230         }
231 
232 
233         // There are no longer any calls made to
234         // m_lexHandler.startCDATA() without a balancing call to
235         // m_lexHandler.endCDATA()
236         // so we set m_cdataTagOpen to false to remember this.
237         m_cdataTagOpen = false;
238     }
239 
240     /**
241      * @see org.xml.sax.ContentHandler#endElement(String, String, String)
242      */
243     public void endElement(String namespaceURI, String localName, String qName)
244         throws SAXException
245     {
246         // Close any open elements etc.
247         flushPending();
248 
249         if (namespaceURI == null)
250         {
251             if (m_elemContext.m_elementURI != null)
252                 namespaceURI = m_elemContext.m_elementURI;
253             else
254                 namespaceURI = getNamespaceURI(qName, true);
255         }
256 
257         if (localName == null)
258         {
259             if (m_elemContext.m_elementLocalName != null)
260                 localName = m_elemContext.m_elementLocalName;
261             else
262                 localName = getLocalName(qName);
263         }
264 
265         m_saxHandler.endElement(namespaceURI, localName, qName);
266 
267         if (m_tracer != null)
268             super.fireEndElem(qName);
269 
270         /* Pop all namespaces at the current element depth.
271          * We are not waiting for official endPrefixMapping() calls.
272          */
273         m_prefixMap.popNamespaces(m_elemContext.m_currentElemDepth,
274             m_saxHandler);
275         m_elemContext = m_elemContext.m_prev;
276     }
277 
278     /**
279      * @see org.xml.sax.ContentHandler#endPrefixMapping(String)
280      */
281     public void endPrefixMapping(String prefix) throws SAXException
282     {
283         /* poping all prefix mappings should have been done
284          * in endElement() already
285          */
286          return;
287     }
288 
289     /**
290      * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
291      */
292     public void ignorableWhitespace(char[] arg0, int arg1, int arg2)
293         throws SAXException
294     {
295         m_saxHandler.ignorableWhitespace(arg0,arg1,arg2);
296     }
297 
298     /**
299      * @see org.xml.sax.ContentHandler#setDocumentLocator(Locator)
300      */
301     public void setDocumentLocator(Locator arg0)
302     {
303         super.setDocumentLocator(arg0);
304         m_saxHandler.setDocumentLocator(arg0);
305     }
306 
307     /**
308      * @see org.xml.sax.ContentHandler#skippedEntity(String)
309      */
310     public void skippedEntity(String arg0) throws SAXException
311     {
312         m_saxHandler.skippedEntity(arg0);
313     }
314 
315     /**
316      * @see org.xml.sax.ContentHandler#startPrefixMapping(String, String)
317      * @param prefix The prefix that maps to the URI
318      * @param uri The URI for the namespace
319      */
320     public void startPrefixMapping(String prefix, String uri)
321         throws SAXException
322     {
323        startPrefixMapping(prefix, uri, true);
324     }
325 
326     /**
327      * Remember the prefix/uri mapping at the current nested element depth.
328      *
329      * @see org.xml.sax.ContentHandler#startPrefixMapping(String, String)
330      * @param prefix The prefix that maps to the URI
331      * @param uri The URI for the namespace
332      * @param shouldFlush a flag indicating if the mapping applies to the
333      * current element or an up coming child (not used).
334      */
335 
336     public boolean startPrefixMapping(
337         String prefix,
338         String uri,
339         boolean shouldFlush)
340         throws org.xml.sax.SAXException
341     {
342 
343         /* Remember the mapping, and at what depth it was declared
344          * This is one greater than the current depth because these
345          * mappings will apply to the next depth. This is in
346          * consideration that startElement() will soon be called
347          */
348 
349         boolean pushed;
350         int pushDepth;
351         if (shouldFlush)
352         {
353             flushPending();
354             // the prefix mapping applies to the child element (one deeper)
355             pushDepth = m_elemContext.m_currentElemDepth + 1;
356         }
357         else
358         {
359             // the prefix mapping applies to the current element
360             pushDepth = m_elemContext.m_currentElemDepth;
361         }
362         pushed = m_prefixMap.pushNamespace(prefix, uri, pushDepth);
363 
364         if (pushed)
365         {
366             m_saxHandler.startPrefixMapping(prefix,uri);
367 
368             if (getShouldOutputNSAttr())
369             {
370 
371                       /* Brian M.: don't know if we really needto do this. The
372                        * callers of this object should have injected both
373                        * startPrefixMapping and the attributes.  We are
374                        * just covering our butt here.
375                        */
376                       String name;
377                     if (EMPTYSTRING.equals(prefix))
378                     {
379                         name = "xmlns";
380                         addAttributeAlways(XMLNS_URI, name, name,"CDATA",uri, false);
381                     }
382                     else
383                 {
384                         if (!EMPTYSTRING.equals(uri)) // hack for XSLTC attribset16 test
385                         {                             // that maps ns1 prefix to "" URI
386                             name = "xmlns:" + prefix;
387 
388                             /* for something like xmlns:abc="w3.pretend.org"
389                                      *  the uri is the value, that is why we pass it in the
390                                      * value, or 5th slot of addAttributeAlways()
391                                    */
392                             addAttributeAlways(XMLNS_URI, prefix, name,"CDATA",uri, false );
393                         }
394                     }
395             }
396         }
397         return pushed;
398     }
399 
400 
401     /**
402      * @see org.xml.sax.ext.LexicalHandler#comment(char[], int, int)
403      */
404     public void comment(char[] arg0, int arg1, int arg2) throws SAXException
405     {
406         flushPending();
407         if (m_lexHandler != null)
408             m_lexHandler.comment(arg0, arg1, arg2);
409 
410         if (m_tracer != null)
411             super.fireCommentEvent(arg0, arg1, arg2);
412     }
413 
414     /**
415      * @see org.xml.sax.ext.LexicalHandler#endCDATA()
416      */
417     public void endCDATA() throws SAXException
418     {
419         /* Normally we would do somthing with this but we ignore it.
420          * The neccessary call to m_lexHandler.endCDATA() will be made
421          * in flushPending().
422          *
423          * This is so that if we get calls like these:
424          *   this.startCDATA();
425          *   this.characters(chars1, off1, len1);
426          *   this.endCDATA();
427          *   this.startCDATA();
428          *   this.characters(chars2, off2, len2);
429          *   this.endCDATA();
430          *
431          * that we will only make these calls to the wrapped handlers:
432          *
433          *   m_lexHandler.startCDATA();
434          *   m_saxHandler.characters(chars1, off1, len1);
435          *   m_saxHandler.characters(chars1, off2, len2);
436          *   m_lexHandler.endCDATA();
437          *
438          * We will merge adjacent CDATA blocks.
439          */
440     }
441 
442     /**
443      * @see org.xml.sax.ext.LexicalHandler#endDTD()
444      */
445     public void endDTD() throws SAXException
446     {
447         if (m_lexHandler != null)
448             m_lexHandler.endDTD();
449     }
450 
451     /**
452      * @see org.xml.sax.ext.LexicalHandler#startEntity(String)
453      */
454     public void startEntity(String arg0) throws SAXException
455     {
456         if (m_lexHandler != null)
457             m_lexHandler.startEntity(arg0);
458     }
459 
460     /**
461      * @see ExtendedContentHandler#characters(String)
462      */
463     public void characters(String chars) throws SAXException
464     {
465         final int length = chars.length();
466         if (length > m_charsBuff.length)
467         {
468             m_charsBuff = new char[length*2 + 1];
469         }
470         chars.getChars(0, length, m_charsBuff, 0);
471         this.characters(m_charsBuff, 0, length);
472     }
473 
474     /////////////////// from XSLTC //////////////
475     public ToXMLSAXHandler(ContentHandler handler, String encoding)
476     {
477         super(handler, encoding);
478 
479         initCDATA();
480         // initNamespaces();
481         m_prefixMap = new NamespaceMappings();
482     }
483 
484     public ToXMLSAXHandler(
485         ContentHandler handler,
486         LexicalHandler lex,
487         String encoding)
488     {
489         super(handler, lex, encoding);
490 
491         initCDATA();
492         //      initNamespaces();
493         m_prefixMap = new NamespaceMappings();
494     }
495 
496     /**
497      * Start an element in the output document. This might be an XML element
498      * (<elem>data</elem> type) or a CDATA section.
499      */
500     public void startElement(
501     String elementNamespaceURI,
502     String elementLocalName,
503     String elementName) throws SAXException
504     {
505         startElement(
506             elementNamespaceURI,elementLocalName,elementName, null);
507 
508 
509     }
510     public void startElement(String elementName) throws SAXException
511     {
512         startElement(null, null, elementName, null);
513     }
514 
515 
516     public void characters(char[] ch, int off, int len) throws SAXException
517     {
518         // We do the first two things in flushPending() but we don't
519         // close any open CDATA calls.
520         if (m_needToCallStartDocument)
521         {
522             startDocumentInternal();
523             m_needToCallStartDocument = false;
524         }
525 
526         if (m_elemContext.m_startTagOpen)
527         {
528             closeStartTag();
529             m_elemContext.m_startTagOpen = false;
530         }
531 
532         if (m_elemContext.m_isCdataSection && !m_cdataTagOpen
533         && m_lexHandler != null)
534         {
535             m_lexHandler.startCDATA();
536             // We have made a call to m_lexHandler.startCDATA() with
537             // no balancing call to m_lexHandler.endCDATA()
538             // so we set m_cdataTagOpen true to remember this.
539             m_cdataTagOpen = true;
540         }
541 
542         /* If there are any occurances of "]]>" in the character data
543          * let m_saxHandler worry about it, we've already warned them with
544          * the previous call of m_lexHandler.startCDATA();
545          */
546         m_saxHandler.characters(ch, off, len);
547 
548         // time to generate characters event
549         if (m_tracer != null)
550             fireCharEvent(ch, off, len);
551     }
552 
553 
554     /**
555      * @see ExtendedContentHandler#endElement(String)
556      */
557     public void endElement(String elemName) throws SAXException
558     {
559         endElement(null, null, elemName);
560     }
561 
562 
563     /**
564      * Send a namespace declaration in the output document. The namespace
565      * declaration will not be include if the namespace is already in scope
566      * with the same prefix.
567      */
568     public void namespaceAfterStartElement(
569         final String prefix,
570         final String uri)
571         throws SAXException
572     {
573         startPrefixMapping(prefix,uri,false);
574     }
575 
576     /**
577      *
578      * @see org.xml.sax.ContentHandler#processingInstruction(String, String)
579      * Send a processing instruction to the output document
580      */
581     public void processingInstruction(String target, String data)
582         throws SAXException
583     {
584         flushPending();
585 
586         // Pass the processing instruction to the SAX handler
587         m_saxHandler.processingInstruction(target, data);
588 
589         // we don't want to leave serializer to fire off this event,
590         // so do it here.
591         if (m_tracer != null)
592             super.fireEscapingEvent(target, data);
593     }
594 
595     /**
596      * Undeclare the namespace that is currently pointed to by a given
597      * prefix. Inform SAX handler if prefix was previously mapped.
598      */
599     protected boolean popNamespace(String prefix)
600     {
601         try
602         {
603             if (m_prefixMap.popNamespace(prefix))
604             {
605                 m_saxHandler.endPrefixMapping(prefix);
606                 return true;
607             }
608         }
609         catch (SAXException e)
610         {
611             // falls through
612         }
613         return false;
614     }
615 
616     public void startCDATA() throws SAXException
617     {
618         /* m_cdataTagOpen can only be true here if we have ignored the
619          * previous call to this.endCDATA() and the previous call
620          * this.startCDATA() before that is still "open". In this way
621          * we merge adjacent CDATA. If anything else happened after the
622          * ignored call to this.endCDATA() and this call then a call to
623          * flushPending() would have been made which would have
624          * closed the CDATA and set m_cdataTagOpen to false.
625          */
626         if (!m_cdataTagOpen )
627         {
628             flushPending();
629             if (m_lexHandler != null) {
630                 m_lexHandler.startCDATA();
631 
632                 // We have made a call to m_lexHandler.startCDATA() with
633                 // no balancing call to m_lexHandler.endCDATA()
634                 // so we set m_cdataTagOpen true to remember this.
635                 m_cdataTagOpen = true;
636             }
637         }
638     }
639 
640     /**
641      * @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes)
642      */
643     public void startElement(
644     String namespaceURI,
645     String localName,
646     String name,
647     Attributes atts)
648         throws SAXException
649     {
650         flushPending();
651         super.startElement(namespaceURI, localName, name, atts);
652 
653         // Handle document type declaration (for first element only)
654          if (m_needToOutputDocTypeDecl)
655          {
656              String doctypeSystem = getDoctypeSystem();
657              if (doctypeSystem != null && m_lexHandler != null)
658              {
659                  String doctypePublic = getDoctypePublic();
660                  if (doctypeSystem != null)
661                      m_lexHandler.startDTD(
662                          name,
663                          doctypePublic,
664                          doctypeSystem);
665              }
666              m_needToOutputDocTypeDecl = false;
667          }
668         m_elemContext = m_elemContext.push(namespaceURI, localName, name);
669 
670         // ensurePrefixIsDeclared depends on the current depth, so
671         // the previous increment is necessary where it is.
672         if (namespaceURI != null)
673             ensurePrefixIsDeclared(namespaceURI, name);
674 
675         // add the attributes to the collected ones
676         if (atts != null)
677             addAttributes(atts);
678 
679 
680         // do we really need this CDATA section state?
681         m_elemContext.m_isCdataSection = isCdataSection();
682 
683     }
684 
685     private void ensurePrefixIsDeclared(String ns, String rawName)
686         throws org.xml.sax.SAXException
687     {
688 
689         if (ns != null && ns.length() > 0)
690         {
691             int index;
692             final boolean no_prefix = ((index = rawName.indexOf(":")) < 0);
693             String prefix = (no_prefix) ? "" : rawName.substring(0, index);
694 
695 
696             if (null != prefix)
697             {
698                 String foundURI = m_prefixMap.lookupNamespace(prefix);
699 
700                 if ((null == foundURI) || !foundURI.equals(ns))
701                 {
702                     this.startPrefixMapping(prefix, ns, false);
703 
704                     if (getShouldOutputNSAttr()) {
705                         // Bugzilla1133: Generate attribute as well as namespace event.
706                         // SAX does expect both.
707                         this.addAttributeAlways(
708                             "http://www.w3.org/2000/xmlns/",
709                             no_prefix ? "xmlns" : prefix,  // local name
710                             no_prefix ? "xmlns" : ("xmlns:"+ prefix), // qname
711                             "CDATA",
712                             ns,
713                             false);
714                     }
715                 }
716 
717             }
718         }
719     }
720     /**
721      * Adds the given attribute to the set of attributes, and also makes sure
722      * that the needed prefix/uri mapping is declared, but only if there is a
723      * currently open element.
724      *
725      * @param uri the URI of the attribute
726      * @param localName the local name of the attribute
727      * @param rawName    the qualified name of the attribute
728      * @param type the type of the attribute (probably CDATA)
729      * @param value the value of the attribute
730      * @param XSLAttribute true if this attribute is coming from an xsl:attribute element
731      * @see ExtendedContentHandler#addAttribute(String, String, String, String, String)
732      */
733     public void addAttribute(
734         String uri,
735         String localName,
736         String rawName,
737         String type,
738         String value,
739         boolean XSLAttribute)
740         throws SAXException
741     {
742         if (m_elemContext.m_startTagOpen)
743         {
744             ensurePrefixIsDeclared(uri, rawName);
745             addAttributeAlways(uri, localName, rawName, type, value, false);
746         }
747 
748     }
749 
750     /**
751      * Try's to reset the super class and reset this class for
752      * re-use, so that you don't need to create a new serializer
753      * (mostly for performance reasons).
754      *
755      * @return true if the class was successfuly reset.
756      * @see Serializer#reset()
757      */
758     public boolean reset()
759     {
760         boolean wasReset = false;
761         if (super.reset())
762         {
763             resetToXMLSAXHandler();
764             wasReset = true;
765         }
766         return wasReset;
767     }
768 
769     /**
770      * Reset all of the fields owned by ToXMLSAXHandler class
771      *
772      */
773     private void resetToXMLSAXHandler()
774     {
775         this.m_escapeSetting = true;
776     }
777 
778 }