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 */
21package org.apache.xml.serializer;
22
23import java.util.Vector;
24
25import org.xml.sax.Attributes;
26import org.xml.sax.ContentHandler;
27import org.xml.sax.ErrorHandler;
28import org.xml.sax.SAXException;
29import org.xml.sax.SAXParseException;
30import 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 */
40public abstract class ToSAXHandler extends SerializerBase
41{
42    public ToSAXHandler()
43    {
44    }
45
46    public ToSAXHandler(
47        ContentHandler hdlr,
48        LexicalHandler lex,
49        String encoding)
50    {
51        setContentHandler(hdlr);
52        setLexHandler(lex);
53        setEncoding(encoding);
54    }
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     */
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     */
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     */
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     */
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     */
162    public void processingInstruction(String target, String data)
163        throws SAXException
164    {
165        // Redefined in SAXXMLOutput
166    }
167
168    protected void closeStartTag() throws SAXException
169    {
170    }
171
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
433    public void addUniqueAttribute(String qName, String value, int flags)
434        throws SAXException
435    {
436        addAttribute(qName, value);
437    }
438}
439