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