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