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 }