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