001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: ToSAXHandler.java 468654 2006-10-28 07:09:23Z minchau $
020     */
021    package org.apache.xml.serializer;
022    
023    import java.util.Vector;
024    
025    import org.xml.sax.Attributes;
026    import org.xml.sax.ContentHandler;
027    import org.xml.sax.ErrorHandler;
028    import org.xml.sax.SAXException;
029    import org.xml.sax.SAXParseException;
030    import org.xml.sax.ext.LexicalHandler;
031    
032    /**
033     * This class is used to provide a base behavior to be inherited
034     * by other To...SAXHandler serializers.
035     * 
036     * This class is not a public API.
037     * 
038     * @xsl.usage internal
039     */
040    public abstract class ToSAXHandler extends SerializerBase 
041    {
042        public ToSAXHandler()
043        {
044        }
045    
046        public ToSAXHandler(
047            ContentHandler hdlr,
048            LexicalHandler lex,
049            String encoding)
050        {
051            setContentHandler(hdlr);
052            setLexHandler(lex);
053            setEncoding(encoding);
054        }
055        public ToSAXHandler(ContentHandler handler, String encoding)
056        {
057            setContentHandler(handler);
058            setEncoding(encoding);
059        }
060    
061        /**
062         * Underlying SAX handler. Taken from XSLTC
063         */
064        protected ContentHandler m_saxHandler;
065    
066        /**
067         * Underlying LexicalHandler. Taken from XSLTC
068         */
069        protected LexicalHandler m_lexHandler;
070    
071        /**
072         * A startPrefixMapping() call on a ToSAXHandler will pass that call
073         * on to the wrapped ContentHandler, but should we also mirror these calls
074         * with matching attributes, if so this field is true.
075         * For example if this field is true then a call such as
076         * startPrefixMapping("prefix1","uri1") will also cause the additional
077         * internally generated attribute xmlns:prefix1="uri1" to be effectively added
078         * to the attributes passed to the wrapped ContentHandler.
079         */ 
080        private boolean m_shouldGenerateNSAttribute = true;
081        
082        /** If this is true, then the content handler wrapped by this
083         * serializer implements the TransformState interface which
084         * will give the content handler access to the state of
085         * the transform. */
086        protected TransformStateSetter m_state = null;
087    
088        /**
089         * Pass callback to the SAX Handler
090         */
091        protected void startDocumentInternal() throws SAXException
092        {
093            if (m_needToCallStartDocument)  
094            {
095                super.startDocumentInternal();
096    
097                m_saxHandler.startDocument();
098                m_needToCallStartDocument = false;
099            }
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    }