OSDN Git Service

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