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: ToSAXHandler.java 468654 2006-10-28 07:09:23Z minchau $
20  */
21 package org.apache.xml.serializer;
22 
23 import java.util.Vector;
24 
25 import org.xml.sax.Attributes;
26 import org.xml.sax.ContentHandler;
27 import org.xml.sax.ErrorHandler;
28 import org.xml.sax.SAXException;
29 import org.xml.sax.SAXParseException;
30 import org.xml.sax.ext.LexicalHandler;
31 
32 /**
33  * This class is used to provide a base behavior to be inherited
34  * by other To...SAXHandler serializers.
35  *
36  * This class is not a public API.
37  *
38  * @xsl.usage internal
39  */
40 public abstract class ToSAXHandler extends SerializerBase
41 {
ToSAXHandler()42     public ToSAXHandler()
43     {
44     }
45 
ToSAXHandler( ContentHandler hdlr, LexicalHandler lex, String encoding)46     public ToSAXHandler(
47         ContentHandler hdlr,
48         LexicalHandler lex,
49         String encoding)
50     {
51         setContentHandler(hdlr);
52         setLexHandler(lex);
53         setEncoding(encoding);
54     }
ToSAXHandler(ContentHandler handler, String encoding)55     public ToSAXHandler(ContentHandler handler, String encoding)
56     {
57         setContentHandler(handler);
58         setEncoding(encoding);
59     }
60 
61     /**
62      * Underlying SAX handler. Taken from XSLTC
63      */
64     protected ContentHandler m_saxHandler;
65 
66     /**
67      * Underlying LexicalHandler. Taken from XSLTC
68      */
69     protected LexicalHandler m_lexHandler;
70 
71     /**
72      * A startPrefixMapping() call on a ToSAXHandler will pass that call
73      * on to the wrapped ContentHandler, but should we also mirror these calls
74      * with matching attributes, if so this field is true.
75      * For example if this field is true then a call such as
76      * startPrefixMapping("prefix1","uri1") will also cause the additional
77      * internally generated attribute xmlns:prefix1="uri1" to be effectively added
78      * to the attributes passed to the wrapped ContentHandler.
79      */
80     private boolean m_shouldGenerateNSAttribute = true;
81 
82     /** If this is true, then the content handler wrapped by this
83      * serializer implements the TransformState interface which
84      * will give the content handler access to the state of
85      * the transform. */
86     protected TransformStateSetter m_state = null;
87 
88     /**
89      * Pass callback to the SAX Handler
90      */
startDocumentInternal()91     protected void startDocumentInternal() throws SAXException
92     {
93         if (m_needToCallStartDocument)
94         {
95             super.startDocumentInternal();
96 
97             m_saxHandler.startDocument();
98             m_needToCallStartDocument = false;
99         }
100     }
101     /**
102      * Do nothing.
103      * @see org.xml.sax.ext.LexicalHandler#startDTD(String, String, String)
104      */
startDTD(String arg0, String arg1, String arg2)105     public void startDTD(String arg0, String arg1, String arg2)
106         throws SAXException
107     {
108         // do nothing for now
109     }
110 
111     /**
112      * Receive notification of character data.
113      *
114      * @param characters The string of characters to process.
115      *
116      * @throws org.xml.sax.SAXException
117      *
118      * @see ExtendedContentHandler#characters(String)
119      */
characters(String characters)120     public void characters(String characters) throws SAXException
121     {
122         final int len = characters.length();
123         if (len > m_charsBuff.length)
124         {
125            m_charsBuff = new char[len*2 + 1];
126         }
127         characters.getChars(0,len, m_charsBuff, 0);
128         characters(m_charsBuff, 0, len);
129     }
130 
131     /**
132      * Receive notification of a comment.
133      *
134      * @see ExtendedLexicalHandler#comment(String)
135      */
comment(String comment)136     public void comment(String comment) throws SAXException
137     {
138         flushPending();
139 
140         // Ignore if a lexical handler has not been set
141         if (m_lexHandler != null)
142         {
143             final int len = comment.length();
144             if (len > m_charsBuff.length)
145             {
146                m_charsBuff = new char[len*2 + 1];
147             }
148             comment.getChars(0,len, m_charsBuff, 0);
149             m_lexHandler.comment(m_charsBuff, 0, len);
150             // time to fire off comment event
151             if (m_tracer != null)
152                 super.fireCommentEvent(m_charsBuff, 0, len);
153         }
154 
155     }
156 
157     /**
158      * Do nothing as this is an abstract class. All subclasses will need to
159      * define their behavior if it is different.
160      * @see org.xml.sax.ContentHandler#processingInstruction(String, String)
161      */
processingInstruction(String target, String data)162     public void processingInstruction(String target, String data)
163         throws SAXException
164     {
165         // Redefined in SAXXMLOutput
166     }
167 
closeStartTag()168     protected void closeStartTag() throws SAXException
169     {
170     }
171 
closeCDATA()172     protected void closeCDATA() throws SAXException
173     {
174         // Redefined in SAXXMLOutput
175     }
176 
177     /**
178      * Receive notification of the beginning of an element, although this is a
179      * SAX method additional namespace or attribute information can occur before
180      * or after this call, that is associated with this element.
181      *
182      * @throws org.xml.sax.SAXException Any SAX exception, possibly
183      *            wrapping another exception.
184      * @see org.xml.sax.ContentHandler#startElement
185      * @see org.xml.sax.ContentHandler#endElement
186      * @see org.xml.sax.AttributeList
187      *
188      * @throws org.xml.sax.SAXException
189      *
190      * @see org.xml.sax.ContentHandler#startElement(String,String,String,Attributes)
191      */
startElement( String arg0, String arg1, String arg2, Attributes arg3)192     public void startElement(
193         String arg0,
194         String arg1,
195         String arg2,
196         Attributes arg3)
197         throws SAXException
198     {
199         if (m_state != null) {
200             m_state.resetState(getTransformer());
201         }
202 
203         // fire off the start element event
204         if (m_tracer != null)
205             super.fireStartElem(arg2);
206     }
207 
208     /**
209      * Sets the LexicalHandler.
210      * @param _lexHandler The LexicalHandler to set
211      */
setLexHandler(LexicalHandler _lexHandler)212     public void setLexHandler(LexicalHandler _lexHandler)
213     {
214         this.m_lexHandler = _lexHandler;
215     }
216 
217     /**
218      * Sets the SAX ContentHandler.
219      * @param _saxHandler The ContentHandler to set
220      */
setContentHandler(ContentHandler _saxHandler)221     public void setContentHandler(ContentHandler _saxHandler)
222     {
223         this.m_saxHandler = _saxHandler;
224         if (m_lexHandler == null && _saxHandler instanceof LexicalHandler)
225         {
226             // we are not overwriting an existing LexicalHandler, and _saxHandler
227             // is also implements LexicalHandler, so lets use it
228             m_lexHandler = (LexicalHandler) _saxHandler;
229         }
230     }
231 
232     /**
233      * Does nothing. The setting of CDATA section elements has an impact on
234      * stream serializers.
235      * @see SerializationHandler#setCdataSectionElements(java.util.Vector)
236      */
setCdataSectionElements(Vector URI_and_localNames)237     public void setCdataSectionElements(Vector URI_and_localNames)
238     {
239         // do nothing
240     }
241 
242     /** Set whether or not namespace declarations (e.g.
243      * xmlns:foo) should appear as attributes of
244      * elements
245      * @param doOutputNSAttr whether or not namespace declarations
246      * should appear as attributes
247      */
setShouldOutputNSAttr(boolean doOutputNSAttr)248     public void setShouldOutputNSAttr(boolean doOutputNSAttr)
249     {
250         m_shouldGenerateNSAttribute = doOutputNSAttr;
251     }
252 
253     /**
254      * Returns true if namespace declarations from calls such as
255      * startPrefixMapping("prefix1","uri1") should
256      * also be mirrored with self generated additional attributes of elements
257      * that declare the namespace, for example the attribute xmlns:prefix1="uri1"
258      */
getShouldOutputNSAttr()259     boolean getShouldOutputNSAttr()
260     {
261         return m_shouldGenerateNSAttribute;
262     }
263 
264     /**
265      * This method flushes any pending events, which can be startDocument()
266      * closing the opening tag of an element, or closing an open CDATA section.
267      */
flushPending()268     public void flushPending() throws SAXException
269     {
270 
271             if (m_needToCallStartDocument)
272             {
273                 startDocumentInternal();
274                 m_needToCallStartDocument = false;
275             }
276 
277             if (m_elemContext.m_startTagOpen)
278             {
279                 closeStartTag();
280                 m_elemContext.m_startTagOpen = false;
281             }
282 
283             if (m_cdataTagOpen)
284             {
285                 closeCDATA();
286                 m_cdataTagOpen = false;
287             }
288 
289     }
290 
291     /**
292      * Pass in a reference to a TransformState object, which
293      * can be used during SAX ContentHandler events to obtain
294      * information about he state of the transformation. This
295      * method will be called  before each startDocument event.
296      *
297      * @param ts A reference to a TransformState object
298      */
setTransformState(TransformStateSetter ts)299     public void setTransformState(TransformStateSetter ts) {
300         this.m_state = ts;
301     }
302 
303     /**
304      * Receives notification that an element starts, but attributes are not
305      * fully known yet.
306      *
307      * @param uri the URI of the namespace of the element (optional)
308      * @param localName the element name, but without prefix (optional)
309      * @param qName the element name, with prefix, if any (required)
310      *
311      * @see ExtendedContentHandler#startElement(String, String, String)
312      */
startElement(String uri, String localName, String qName)313     public void startElement(String uri, String localName, String qName)
314         throws SAXException {
315 
316         if (m_state != null) {
317             m_state.resetState(getTransformer());
318         }
319 
320         // fire off the start element event
321         if (m_tracer != null)
322             super.fireStartElem(qName);
323     }
324 
325     /**
326      * An element starts, but attributes are not fully known yet.
327      *
328      * @param qName the element name, with prefix (if any).
329 
330      * @see ExtendedContentHandler#startElement(String)
331      */
startElement(String qName)332     public void startElement(String qName) throws SAXException {
333         if (m_state != null) {
334             m_state.resetState(getTransformer());
335         }
336         // fire off the start element event
337         if (m_tracer != null)
338             super.fireStartElem(qName);
339     }
340 
341     /**
342      * This method gets the node's value as a String and uses that String as if
343      * it were an input character notification.
344      * @param node the Node to serialize
345      * @throws org.xml.sax.SAXException
346      */
characters(org.w3c.dom.Node node)347     public void characters(org.w3c.dom.Node node)
348         throws org.xml.sax.SAXException
349     {
350         // remember the current node
351         if (m_state != null)
352         {
353             m_state.setCurrentNode(node);
354         }
355 
356         // Get the node's value as a String and use that String as if
357         // it were an input character notification.
358         String data = node.getNodeValue();
359         if (data != null) {
360             this.characters(data);
361         }
362     }
363 
364     /**
365      * @see org.xml.sax.ErrorHandler#fatalError(SAXParseException)
366      */
fatalError(SAXParseException exc)367     public void fatalError(SAXParseException exc) throws SAXException {
368         super.fatalError(exc);
369 
370         m_needToCallStartDocument = false;
371 
372         if (m_saxHandler instanceof ErrorHandler) {
373             ((ErrorHandler)m_saxHandler).fatalError(exc);
374         }
375     }
376 
377     /**
378      * @see org.xml.sax.ErrorHandler#error(SAXParseException)
379      */
error(SAXParseException exc)380     public void error(SAXParseException exc) throws SAXException {
381         super.error(exc);
382 
383         if (m_saxHandler instanceof ErrorHandler)
384             ((ErrorHandler)m_saxHandler).error(exc);
385 
386     }
387 
388     /**
389      * @see org.xml.sax.ErrorHandler#warning(SAXParseException)
390      */
warning(SAXParseException exc)391     public void warning(SAXParseException exc) throws SAXException {
392         super.warning(exc);
393 
394         if (m_saxHandler instanceof ErrorHandler)
395             ((ErrorHandler)m_saxHandler).warning(exc);
396     }
397 
398 
399     /**
400      * Try's to reset the super class and reset this class for
401      * re-use, so that you don't need to create a new serializer
402      * (mostly for performance reasons).
403      *
404      * @return true if the class was successfuly reset.
405      * @see Serializer#reset()
406      */
reset()407     public boolean reset()
408     {
409         boolean wasReset = false;
410         if (super.reset())
411         {
412             resetToSAXHandler();
413             wasReset = true;
414         }
415         return wasReset;
416     }
417 
418     /**
419      * Reset all of the fields owned by ToSAXHandler class
420      *
421      */
resetToSAXHandler()422     private void resetToSAXHandler()
423     {
424         this.m_lexHandler = null;
425         this.m_saxHandler = null;
426         this.m_state = null;
427         this.m_shouldGenerateNSAttribute = false;
428     }
429 
430     /**
431      * Add a unique attribute
432      */
addUniqueAttribute(String qName, String value, int flags)433     public void addUniqueAttribute(String qName, String value, int flags)
434         throws SAXException
435     {
436         addAttribute(qName, value);
437     }
438 }
439