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