1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 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 package com.sun.org.apache.xml.internal.serialize; 22 23 import java.io.IOException; 24 import java.io.OutputStream; 25 import java.io.StringWriter; 26 import java.io.UnsupportedEncodingException; 27 import java.io.Writer; 28 import java.lang.reflect.Method; 29 import java.util.ArrayList; 30 31 import com.sun.org.apache.xerces.internal.dom.AbortException; 32 import com.sun.org.apache.xerces.internal.dom.CoreDocumentImpl; 33 import com.sun.org.apache.xerces.internal.dom.DOMErrorImpl; 34 import com.sun.org.apache.xerces.internal.dom.DOMLocatorImpl; 35 import com.sun.org.apache.xerces.internal.dom.DOMMessageFormatter; 36 import com.sun.org.apache.xerces.internal.dom.DOMNormalizer; 37 import com.sun.org.apache.xerces.internal.dom.DOMStringListImpl; 38 import com.sun.org.apache.xerces.internal.impl.Constants; 39 import com.sun.org.apache.xerces.internal.impl.XMLEntityManager; 40 import com.sun.org.apache.xerces.internal.util.DOMUtil; 41 import com.sun.org.apache.xerces.internal.util.NamespaceSupport; 42 import com.sun.org.apache.xerces.internal.util.SymbolTable; 43 import com.sun.org.apache.xerces.internal.util.XML11Char; 44 import com.sun.org.apache.xerces.internal.util.XMLChar; 45 import org.w3c.dom.Attr; 46 import org.w3c.dom.Comment; 47 import org.w3c.dom.DOMConfiguration; 48 import org.w3c.dom.DOMError; 49 import org.w3c.dom.DOMErrorHandler; 50 import org.w3c.dom.DOMException; 51 import org.w3c.dom.DOMStringList; 52 import org.w3c.dom.Document; 53 import org.w3c.dom.DocumentFragment; 54 import org.w3c.dom.Element; 55 import org.w3c.dom.NamedNodeMap; 56 import org.w3c.dom.Node; 57 import org.w3c.dom.ProcessingInstruction; 58 import org.w3c.dom.ls.LSException; 59 import org.w3c.dom.ls.LSOutput; 60 import org.w3c.dom.ls.LSSerializer; 61 import org.w3c.dom.ls.LSSerializerFilter; 62 63 /** 64 * EXPERIMENTAL: Implemenatation of DOM Level 3 org.w3c.ls.LSSerializer by 65 * delegating serialization calls to <CODE>XMLSerializer</CODE>. LSSerializer 66 * provides an API for serializing (writing) a DOM document out in an XML 67 * document. The XML data is written to an output stream. During serialization 68 * of XML data, namespace fixup is done when possible as defined in DOM Level 3 69 * Core, Appendix B. 70 * 71 * @author Elena Litani, IBM 72 * @author Gopal Sharma, Sun Microsystems 73 * @author Arun Yadav, Sun Microsystems 74 * @author Sunitha Reddy, Sun Microsystems 75 * 76 * @deprecated As of JDK 9, Xerces 2.9.0, replaced by 77 * {@link com.sun.org.apache.xml.internal.serializer.dom3.LSSerializerImpl} 78 */ 79 public class DOMSerializerImpl implements LSSerializer, DOMConfiguration { 80 81 // TODO: When DOM Level 3 goes to REC replace method calls using 82 // reflection for: getXmlEncoding, getInputEncoding and getXmlEncoding 83 // with regular static calls on the Document object. 84 // data 85 // serializer 86 private XMLSerializer serializer; 87 88 // XML 1.1 serializer 89 private XML11Serializer xml11Serializer; 90 91 //Recognized parameters 92 private DOMStringList fRecognizedParameters; 93 94 /** 95 * REVISIT: Currently we handle 3 different configurations, would be nice 96 * just have one configuration that has different recognized parameters 97 * depending if it is used in Core/LS. 98 */ 99 protected short features = 0; 100 101 protected final static short NAMESPACES = 0x1 << 0; 102 protected final static short WELLFORMED = 0x1 << 1; 103 protected final static short ENTITIES = 0x1 << 2; 104 protected final static short CDATA = 0x1 << 3; 105 protected final static short SPLITCDATA = 0x1 << 4; 106 protected final static short COMMENTS = 0x1 << 5; 107 protected final static short DISCARDDEFAULT = 0x1 << 6; 108 protected final static short INFOSET = 0x1 << 7; 109 protected final static short XMLDECL = 0x1 << 8; 110 protected final static short NSDECL = 0x1 << 9; 111 protected final static short DOM_ELEMENT_CONTENT_WHITESPACE = 0x1 << 10; 112 protected final static short PRETTY_PRINT = 0x1 << 11; 113 114 // well-formness checking 115 private DOMErrorHandler fErrorHandler = null; 116 private final DOMErrorImpl fError = new DOMErrorImpl(); 117 private final DOMLocatorImpl fLocator = new DOMLocatorImpl(); 118 119 /** 120 * Constructs a new LSSerializer. The constructor turns on the namespace 121 * support in <code>XMLSerializer</code> and initializes the following 122 * fields: fNSBinder, fLocalNSBinder, fSymbolTable, fEmptySymbol, 123 * fXmlSymbol, fXmlnsSymbol, fNamespaceCounter, fFeatures. 124 */ 125 public DOMSerializerImpl() { 126 // set default features 127 features |= NAMESPACES; 128 features |= ENTITIES; 129 features |= COMMENTS; 130 features |= CDATA; 131 features |= SPLITCDATA; 132 features |= WELLFORMED; 133 features |= NSDECL; 134 features |= DOM_ELEMENT_CONTENT_WHITESPACE; 135 features |= DISCARDDEFAULT; 136 features |= XMLDECL; 137 138 serializer = new XMLSerializer(); 139 initSerializer(serializer); 140 } 141 142 // 143 // LSSerializer methods 144 // 145 public DOMConfiguration getDomConfig() { 146 return this; 147 } 148 149 /** 150 * DOM L3-EXPERIMENTAL: Setter for boolean and object parameters 151 */ 152 public void setParameter(String name, Object value) throws DOMException { 153 if (value instanceof Boolean) { 154 boolean state = ((Boolean) value).booleanValue(); 155 if (name.equalsIgnoreCase(Constants.DOM_INFOSET)) { 156 if (state) { 157 features &= ~ENTITIES; 158 features &= ~CDATA; 159 features |= NAMESPACES; 160 features |= NSDECL; 161 features |= WELLFORMED; 162 features |= COMMENTS; 163 } 164 // false does not have any effect 165 } else if (name.equalsIgnoreCase(Constants.DOM_XMLDECL)) { 166 features 167 = (short) (state ? features | XMLDECL : features & ~XMLDECL); 168 } else if (name.equalsIgnoreCase(Constants.DOM_NAMESPACES)) { 169 features 170 = (short) (state 171 ? features | NAMESPACES 172 : features & ~NAMESPACES); 173 serializer.fNamespaces = state; 174 } else if (name.equalsIgnoreCase(Constants.DOM_SPLIT_CDATA)) { 175 features 176 = (short) (state 177 ? features | SPLITCDATA 178 : features & ~SPLITCDATA); 179 } else if (name.equalsIgnoreCase(Constants.DOM_DISCARD_DEFAULT_CONTENT)) { 180 features 181 = (short) (state 182 ? features | DISCARDDEFAULT 183 : features & ~DISCARDDEFAULT); 184 } else if (name.equalsIgnoreCase(Constants.DOM_WELLFORMED)) { 185 features 186 = (short) (state 187 ? features | WELLFORMED 188 : features & ~WELLFORMED); 189 } else if (name.equalsIgnoreCase(Constants.DOM_ENTITIES)) { 190 features 191 = (short) (state 192 ? features | ENTITIES 193 : features & ~ENTITIES); 194 } else if (name.equalsIgnoreCase(Constants.DOM_CDATA_SECTIONS)) { 195 features 196 = (short) (state 197 ? features | CDATA 198 : features & ~CDATA); 199 } else if (name.equalsIgnoreCase(Constants.DOM_COMMENTS)) { 200 features 201 = (short) (state 202 ? features | COMMENTS 203 : features & ~COMMENTS); 204 } else if (name.equalsIgnoreCase(Constants.DOM_FORMAT_PRETTY_PRINT)) { 205 features 206 = (short) (state 207 ? features | PRETTY_PRINT 208 : features & ~PRETTY_PRINT); 209 } else if (name.equalsIgnoreCase(Constants.DOM_CANONICAL_FORM) 210 || name.equalsIgnoreCase(Constants.DOM_VALIDATE_IF_SCHEMA) 211 || name.equalsIgnoreCase(Constants.DOM_VALIDATE) 212 || name.equalsIgnoreCase(Constants.DOM_CHECK_CHAR_NORMALIZATION) 213 || name.equalsIgnoreCase(Constants.DOM_DATATYPE_NORMALIZATION)) { 214 // || name.equalsIgnoreCase(Constants.DOM_NORMALIZE_CHARACTERS)) { 215 // true is not supported 216 if (state) { 217 String msg 218 = DOMMessageFormatter.formatMessage( 219 DOMMessageFormatter.DOM_DOMAIN, 220 "FEATURE_NOT_SUPPORTED", 221 new Object[]{name}); 222 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg); 223 } 224 } else if (name.equalsIgnoreCase(Constants.DOM_NAMESPACE_DECLARATIONS)) { 225 //namespace-declaration has effect only if namespaces is true 226 features 227 = (short) (state 228 ? features | NSDECL 229 : features & ~NSDECL); 230 serializer.fNamespacePrefixes = state; 231 } else if (name.equalsIgnoreCase(Constants.DOM_ELEMENT_CONTENT_WHITESPACE) 232 || name.equalsIgnoreCase(Constants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS)) { 233 // false is not supported 234 if (!state) { 235 String msg 236 = DOMMessageFormatter.formatMessage( 237 DOMMessageFormatter.DOM_DOMAIN, 238 "FEATURE_NOT_SUPPORTED", 239 new Object[]{name}); 240 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg); 241 } 242 } else { 243 String msg 244 = DOMMessageFormatter.formatMessage( 245 DOMMessageFormatter.DOM_DOMAIN, 246 "FEATURE_NOT_FOUND", 247 new Object[]{name}); 248 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg); 249 } 250 } else if (name.equalsIgnoreCase(Constants.DOM_ERROR_HANDLER)) { 251 if (value == null || value instanceof DOMErrorHandler) { 252 fErrorHandler = (DOMErrorHandler) value; 253 } else { 254 String msg 255 = DOMMessageFormatter.formatMessage( 256 DOMMessageFormatter.DOM_DOMAIN, 257 "TYPE_MISMATCH_ERR", 258 new Object[]{name}); 259 throw new DOMException(DOMException.TYPE_MISMATCH_ERR, msg); 260 } 261 } else if (name.equalsIgnoreCase(Constants.DOM_RESOURCE_RESOLVER) 262 || name.equalsIgnoreCase(Constants.DOM_SCHEMA_LOCATION) 263 || name.equalsIgnoreCase(Constants.DOM_SCHEMA_TYPE) 264 || name.equalsIgnoreCase(Constants.DOM_NORMALIZE_CHARACTERS) 265 && value != null) { 266 String msg 267 = DOMMessageFormatter.formatMessage( 268 DOMMessageFormatter.DOM_DOMAIN, 269 "FEATURE_NOT_SUPPORTED", 270 new Object[]{name}); 271 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg); 272 } else { 273 String msg 274 = DOMMessageFormatter.formatMessage( 275 DOMMessageFormatter.DOM_DOMAIN, 276 "FEATURE_NOT_FOUND", 277 new Object[]{name}); 278 throw new DOMException(DOMException.NOT_FOUND_ERR, msg); 279 } 280 } 281 282 /** 283 * DOM L3-EXPERIMENTAL: Check if parameter can be set 284 */ 285 public boolean canSetParameter(String name, Object state) { 286 if (state == null) { 287 return true; 288 } 289 290 if (state instanceof Boolean) { 291 boolean value = ((Boolean) state).booleanValue(); 292 if (name.equalsIgnoreCase(Constants.DOM_NAMESPACES) 293 || name.equalsIgnoreCase(Constants.DOM_SPLIT_CDATA) 294 || name.equalsIgnoreCase(Constants.DOM_DISCARD_DEFAULT_CONTENT) 295 || name.equalsIgnoreCase(Constants.DOM_XMLDECL) 296 || name.equalsIgnoreCase(Constants.DOM_WELLFORMED) 297 || name.equalsIgnoreCase(Constants.DOM_INFOSET) 298 || name.equalsIgnoreCase(Constants.DOM_ENTITIES) 299 || name.equalsIgnoreCase(Constants.DOM_CDATA_SECTIONS) 300 || name.equalsIgnoreCase(Constants.DOM_COMMENTS) 301 || name.equalsIgnoreCase(Constants.DOM_FORMAT_PRETTY_PRINT) 302 || name.equalsIgnoreCase(Constants.DOM_NAMESPACE_DECLARATIONS)) { 303 // both values supported 304 return true; 305 } else if (name.equalsIgnoreCase(Constants.DOM_CANONICAL_FORM) 306 || name.equalsIgnoreCase(Constants.DOM_VALIDATE_IF_SCHEMA) 307 || name.equalsIgnoreCase(Constants.DOM_VALIDATE) 308 || name.equalsIgnoreCase(Constants.DOM_CHECK_CHAR_NORMALIZATION) 309 || name.equalsIgnoreCase(Constants.DOM_DATATYPE_NORMALIZATION)) { 310 // || name.equalsIgnoreCase(Constants.DOM_NORMALIZE_CHARACTERS)) { 311 // true is not supported 312 return !value; 313 } else if (name.equalsIgnoreCase(Constants.DOM_ELEMENT_CONTENT_WHITESPACE) 314 || name.equalsIgnoreCase(Constants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS)) { 315 // false is not supported 316 return value; 317 } 318 } else if (name.equalsIgnoreCase(Constants.DOM_ERROR_HANDLER) 319 && state == null || state instanceof DOMErrorHandler) { 320 return true; 321 } 322 323 return false; 324 } 325 326 /** 327 * DOM Level 3 Core CR - Experimental. 328 * 329 * The list of the parameters supported by this 330 * <code>DOMConfiguration</code> object and for which at least one value can 331 * be set by the application. Note that this list can also contain parameter 332 * names defined outside this specification. 333 */ 334 public DOMStringList getParameterNames() { 335 336 if (fRecognizedParameters == null) { 337 ArrayList parameters = new ArrayList(); 338 339 //Add DOM recognized parameters 340 //REVISIT: Would have been nice to have a list of 341 //recognized parameters. 342 parameters.add(Constants.DOM_NAMESPACES); 343 parameters.add(Constants.DOM_SPLIT_CDATA); 344 parameters.add(Constants.DOM_DISCARD_DEFAULT_CONTENT); 345 parameters.add(Constants.DOM_XMLDECL); 346 parameters.add(Constants.DOM_CANONICAL_FORM); 347 parameters.add(Constants.DOM_VALIDATE_IF_SCHEMA); 348 parameters.add(Constants.DOM_VALIDATE); 349 parameters.add(Constants.DOM_CHECK_CHAR_NORMALIZATION); 350 parameters.add(Constants.DOM_DATATYPE_NORMALIZATION); 351 parameters.add(Constants.DOM_FORMAT_PRETTY_PRINT); 352 //parameters.add(Constants.DOM_NORMALIZE_CHARACTERS); 353 parameters.add(Constants.DOM_WELLFORMED); 354 parameters.add(Constants.DOM_INFOSET); 355 parameters.add(Constants.DOM_NAMESPACE_DECLARATIONS); 356 parameters.add(Constants.DOM_ELEMENT_CONTENT_WHITESPACE); 357 parameters.add(Constants.DOM_ENTITIES); 358 parameters.add(Constants.DOM_CDATA_SECTIONS); 359 parameters.add(Constants.DOM_COMMENTS); 360 parameters.add(Constants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS); 361 parameters.add(Constants.DOM_ERROR_HANDLER); 362 //parameters.add(Constants.DOM_SCHEMA_LOCATION); 363 //parameters.add(Constants.DOM_SCHEMA_TYPE); 364 365 //Add recognized xerces features and properties 366 fRecognizedParameters = new DOMStringListImpl(parameters); 367 } 368 369 return fRecognizedParameters; 370 } 371 372 /** 373 * DOM L3-EXPERIMENTAL: Getter for boolean and object parameters 374 */ 375 public Object getParameter(String name) throws DOMException { 376 if (name.equalsIgnoreCase(Constants.DOM_NORMALIZE_CHARACTERS)) { 377 return null; 378 } else if (name.equalsIgnoreCase(Constants.DOM_COMMENTS)) { 379 return ((features & COMMENTS) != 0) ? Boolean.TRUE : Boolean.FALSE; 380 } else if (name.equalsIgnoreCase(Constants.DOM_NAMESPACES)) { 381 return (features & NAMESPACES) != 0 ? Boolean.TRUE : Boolean.FALSE; 382 } else if (name.equalsIgnoreCase(Constants.DOM_XMLDECL)) { 383 return (features & XMLDECL) != 0 ? Boolean.TRUE : Boolean.FALSE; 384 } else if (name.equalsIgnoreCase(Constants.DOM_CDATA_SECTIONS)) { 385 return (features & CDATA) != 0 ? Boolean.TRUE : Boolean.FALSE; 386 } else if (name.equalsIgnoreCase(Constants.DOM_ENTITIES)) { 387 return (features & ENTITIES) != 0 ? Boolean.TRUE : Boolean.FALSE; 388 } else if (name.equalsIgnoreCase(Constants.DOM_SPLIT_CDATA)) { 389 return (features & SPLITCDATA) != 0 ? Boolean.TRUE : Boolean.FALSE; 390 } else if (name.equalsIgnoreCase(Constants.DOM_WELLFORMED)) { 391 return (features & WELLFORMED) != 0 ? Boolean.TRUE : Boolean.FALSE; 392 } else if (name.equalsIgnoreCase(Constants.DOM_NAMESPACE_DECLARATIONS)) { 393 return (features & NSDECL) != 0 ? Boolean.TRUE : Boolean.FALSE; 394 } else if (name.equalsIgnoreCase(Constants.DOM_ELEMENT_CONTENT_WHITESPACE) 395 || name.equalsIgnoreCase(Constants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS)) { 396 return Boolean.TRUE; 397 } else if (name.equalsIgnoreCase(Constants.DOM_DISCARD_DEFAULT_CONTENT)) { 398 return ((features & DISCARDDEFAULT) != 0) ? Boolean.TRUE : Boolean.FALSE; 399 } else if (name.equalsIgnoreCase(Constants.DOM_FORMAT_PRETTY_PRINT)) { 400 return ((features & PRETTY_PRINT) != 0) ? Boolean.TRUE : Boolean.FALSE; 401 } else if (name.equalsIgnoreCase(Constants.DOM_INFOSET)) { 402 if ((features & ENTITIES) == 0 403 && (features & CDATA) == 0 404 && (features & NAMESPACES) != 0 405 && (features & NSDECL) != 0 406 && (features & WELLFORMED) != 0 407 && (features & COMMENTS) != 0) { 408 return Boolean.TRUE; 409 } 410 return Boolean.FALSE; 411 } else if (name.equalsIgnoreCase(Constants.DOM_CANONICAL_FORM) 412 || name.equalsIgnoreCase(Constants.DOM_VALIDATE_IF_SCHEMA) 413 || name.equalsIgnoreCase(Constants.DOM_CHECK_CHAR_NORMALIZATION) 414 || name.equalsIgnoreCase(Constants.DOM_VALIDATE) 415 || name.equalsIgnoreCase(Constants.DOM_VALIDATE_IF_SCHEMA) 416 || name.equalsIgnoreCase(Constants.DOM_DATATYPE_NORMALIZATION)) { 417 return Boolean.FALSE; 418 } else if (name.equalsIgnoreCase(Constants.DOM_ERROR_HANDLER)) { 419 return fErrorHandler; 420 } else if (name.equalsIgnoreCase(Constants.DOM_RESOURCE_RESOLVER) 421 || name.equalsIgnoreCase(Constants.DOM_SCHEMA_LOCATION) 422 || name.equalsIgnoreCase(Constants.DOM_SCHEMA_TYPE)) { 423 String msg 424 = DOMMessageFormatter.formatMessage( 425 DOMMessageFormatter.DOM_DOMAIN, 426 "FEATURE_NOT_SUPPORTED", 427 new Object[]{name}); 428 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg); 429 } else { 430 String msg 431 = DOMMessageFormatter.formatMessage( 432 DOMMessageFormatter.DOM_DOMAIN, 433 "FEATURE_NOT_FOUND", 434 new Object[]{name}); 435 throw new DOMException(DOMException.NOT_FOUND_ERR, msg); 436 } 437 } 438 439 /** 440 * DOM L3 EXPERIMENTAL: Serialize the specified node as described above in 441 * the description of <code>LSSerializer</code>. The result of serializing 442 * the node is returned as a string. Writing a Document or Entity node 443 * produces a serialized form that is well formed XML. Writing other node 444 * types produces a fragment of text in a form that is not fully defined by 445 * this document, but that should be useful to a human for debugging or 446 * diagnostic purposes. 447 * 448 * @param wnode The node to be written. 449 * @return Returns the serialized data 450 * @exception DOMException DOMSTRING_SIZE_ERR: The resulting string is too 451 * long to fit in a <code>DOMString</code>. 452 * @exception LSException SERIALIZE_ERR: Unable to serialize the node. DOM 453 * applications should attach a <code>DOMErrorHandler</code> using the 454 * parameter "<i>error-handler</i>" to get details on error. 455 */ 456 public String writeToString(Node wnode) throws DOMException, LSException { 457 // determine which serializer to use: 458 XMLSerializer ser = null; 459 String ver = _getXmlVersion(wnode); 460 if (ver != null && ver.equals("1.1")) { 461 if (xml11Serializer == null) { 462 xml11Serializer = new XML11Serializer(); 463 initSerializer(xml11Serializer); 464 } 465 // copy setting from "main" serializer to XML 1.1 serializer 466 copySettings(serializer, xml11Serializer); 467 ser = xml11Serializer; 468 } else { 469 ser = serializer; 470 } 471 472 StringWriter destination = new StringWriter(); 473 try { 474 prepareForSerialization(ser, wnode); 475 ser._format.setEncoding("UTF-16"); 476 ser.setOutputCharStream(destination); 477 if (wnode.getNodeType() == Node.DOCUMENT_NODE) { 478 ser.serialize((Document) wnode); 479 } else if (wnode.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) { 480 ser.serialize((DocumentFragment) wnode); 481 } else if (wnode.getNodeType() == Node.ELEMENT_NODE) { 482 ser.serialize((Element) wnode); 483 } else if (wnode.getNodeType() == Node.TEXT_NODE 484 || wnode.getNodeType() == Node.COMMENT_NODE 485 || wnode.getNodeType() == Node.ENTITY_REFERENCE_NODE 486 || wnode.getNodeType() == Node.CDATA_SECTION_NODE 487 || wnode.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) { 488 ser.serialize(wnode); 489 } else { 490 String msg = DOMMessageFormatter.formatMessage( 491 DOMMessageFormatter.SERIALIZER_DOMAIN, 492 "unable-to-serialize-node", null); 493 if (ser.fDOMErrorHandler != null) { 494 DOMErrorImpl error = new DOMErrorImpl(); 495 error.fType = "unable-to-serialize-node"; 496 error.fMessage = msg; 497 error.fSeverity = DOMError.SEVERITY_FATAL_ERROR; 498 ser.fDOMErrorHandler.handleError(error); 499 } 500 throw new LSException(LSException.SERIALIZE_ERR, msg); 501 } 502 } catch (LSException lse) { 503 // Rethrow LSException. 504 throw lse; 505 } catch (AbortException e) { 506 return null; 507 } catch (RuntimeException e) { 508 throw (LSException) DOMUtil.createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace(); 509 } catch (IOException ioe) { 510 // REVISIT: A generic IOException doesn't provide enough information 511 // to determine that the serialized document is too large to fit 512 // into a string. This could have thrown for some other reason. -- mrglavas 513 String msg = DOMMessageFormatter.formatMessage( 514 DOMMessageFormatter.DOM_DOMAIN, 515 "STRING_TOO_LONG", 516 new Object[]{ioe.getMessage()}); 517 throw new DOMException(DOMException.DOMSTRING_SIZE_ERR, msg); 518 } finally { 519 ser.clearDocumentState(); 520 } 521 return destination.toString(); 522 } 523 524 /** 525 * DOM L3 EXPERIMENTAL: The end-of-line sequence of characters to be used in 526 * the XML being written out. The only permitted values are these: 527 * <dl> 528 * <dt><code>null</code></dt> 529 * <dd> 530 * Use a default end-of-line sequence. DOM implementations should choose the 531 * default to match the usual convention for text files in the environment 532 * being used. Implementations must choose a default sequence that matches 533 * one of those allowed by 2.11 "End-of-Line Handling". </dd> 534 * <dt>CR</dt> 535 * <dd>The carriage-return character (#xD).</dd> 536 * <dt>CR-LF</dt> 537 * <dd> The carriage-return and line-feed characters (#xD #xA). </dd> 538 * <dt>LF</dt> 539 * <dd> The line-feed character (#xA). </dd> 540 * </dl> 541 * <br>The default value for this attribute is <code>null</code>. 542 */ 543 public void setNewLine(String newLine) { 544 serializer._format.setLineSeparator(newLine); 545 } 546 547 /** 548 * DOM L3 EXPERIMENTAL: The end-of-line sequence of characters to be used in 549 * the XML being written out. The only permitted values are these: 550 * <dl> 551 * <dt><code>null</code></dt> 552 * <dd> 553 * Use a default end-of-line sequence. DOM implementations should choose the 554 * default to match the usual convention for text files in the environment 555 * being used. Implementations must choose a default sequence that matches 556 * one of those allowed by 2.11 "End-of-Line Handling". </dd> 557 * <dt>CR</dt> 558 * <dd>The carriage-return character (#xD).</dd> 559 * <dt>CR-LF</dt> 560 * <dd> The carriage-return and line-feed characters (#xD #xA). </dd> 561 * <dt>LF</dt> 562 * <dd> The line-feed character (#xA). </dd> 563 * </dl> 564 * <br>The default value for this attribute is <code>null</code>. 565 */ 566 public String getNewLine() { 567 return serializer._format.getLineSeparator(); 568 } 569 570 /** 571 * When the application provides a filter, the serializer will call out to 572 * the filter before serializing each Node. Attribute nodes are never passed 573 * to the filter. The filter implementation can choose to remove the node 574 * from the stream or to terminate the serialization early. 575 */ 576 public LSSerializerFilter getFilter() { 577 return serializer.fDOMFilter; 578 } 579 580 /** 581 * When the application provides a filter, the serializer will call out to 582 * the filter before serializing each Node. Attribute nodes are never passed 583 * to the filter. The filter implementation can choose to remove the node 584 * from the stream or to terminate the serialization early. 585 */ 586 public void setFilter(LSSerializerFilter filter) { 587 serializer.fDOMFilter = filter; 588 } 589 590 // this initializes a newly-created serializer 591 private void initSerializer(XMLSerializer ser) { 592 ser.fNSBinder = new NamespaceSupport(); 593 ser.fLocalNSBinder = new NamespaceSupport(); 594 ser.fSymbolTable = new SymbolTable(); 595 } 596 597 // copies all settings that could have been modified 598 // by calls to LSSerializer methods from one serializer to another. 599 // IMPORTANT: if new methods are implemented or more settings of 600 // the serializer are made alterable, this must be 601 // reflected in this method! 602 private void copySettings(XMLSerializer src, XMLSerializer dest) { 603 dest.fDOMErrorHandler = fErrorHandler; 604 dest._format.setEncoding(src._format.getEncoding()); 605 dest._format.setLineSeparator(src._format.getLineSeparator()); 606 dest.fDOMFilter = src.fDOMFilter; 607 }//copysettings 608 609 /** 610 * Serialize the specified node as described above in the general 611 * description of the <code>LSSerializer</code> interface. The output is 612 * written to the supplied <code>LSOutput</code>. 613 * <br> When writing to a <code>LSOutput</code>, the encoding is found by 614 * looking at the encoding information that is reachable through the 615 * <code>LSOutput</code> and the item to be written (or its owner document) 616 * in this order: 617 * <ol> 618 * <li> <code>LSOutput.encoding</code>, 619 * </li> 620 * <li> 621 * <code>Document.actualEncoding</code>, 622 * </li> 623 * <li> 624 * <code>Document.xmlEncoding</code>. 625 * </li> 626 * </ol> 627 * <br> If no encoding is reachable through the above properties, a default 628 * encoding of "UTF-8" will be used. 629 * <br> If the specified encoding is not supported an "unsupported-encoding" 630 * error is raised. 631 * <br> If no output is specified in the <code>LSOutput</code>, a 632 * "no-output-specified" error is raised. 633 * 634 * @param node The node to serialize. 635 * @param destination The destination for the serialized DOM. 636 * @return Returns <code>true</code> if <code>node</code> was successfully 637 * serialized and <code>false</code> in case the node couldn't be 638 * serialized. 639 */ 640 public boolean write(Node node, LSOutput destination) throws LSException { 641 642 if (node == null) { 643 return false; 644 } 645 646 XMLSerializer ser = null; 647 String ver = _getXmlVersion(node); 648 //determine which serializer to use: 649 if (ver != null && ver.equals("1.1")) { 650 if (xml11Serializer == null) { 651 xml11Serializer = new XML11Serializer(); 652 initSerializer(xml11Serializer); 653 } 654 //copy setting from "main" serializer to XML 1.1 serializer 655 copySettings(serializer, xml11Serializer); 656 ser = xml11Serializer; 657 } else { 658 ser = serializer; 659 } 660 661 String encoding = null; 662 if ((encoding = destination.getEncoding()) == null) { 663 encoding = _getInputEncoding(node); 664 if (encoding == null) { 665 encoding = _getXmlEncoding(node); 666 if (encoding == null) { 667 encoding = "UTF-8"; 668 } 669 } 670 } 671 try { 672 prepareForSerialization(ser, node); 673 ser._format.setEncoding(encoding); 674 OutputStream outputStream = destination.getByteStream(); 675 Writer writer = destination.getCharacterStream(); 676 String uri = destination.getSystemId(); 677 if (writer == null) { 678 if (outputStream == null) { 679 if (uri == null) { 680 String msg = DOMMessageFormatter.formatMessage( 681 DOMMessageFormatter.SERIALIZER_DOMAIN, 682 "no-output-specified", null); 683 if (ser.fDOMErrorHandler != null) { 684 DOMErrorImpl error = new DOMErrorImpl(); 685 error.fType = "no-output-specified"; 686 error.fMessage = msg; 687 error.fSeverity = DOMError.SEVERITY_FATAL_ERROR; 688 ser.fDOMErrorHandler.handleError(error); 689 } 690 throw new LSException(LSException.SERIALIZE_ERR, msg); 691 } else { 692 ser.setOutputByteStream(XMLEntityManager.createOutputStream(uri)); 693 } 694 } else { 695 // byte stream was specified 696 ser.setOutputByteStream(outputStream); 697 } 698 } else { 699 // character stream is specified 700 ser.setOutputCharStream(writer); 701 } 702 703 if (node.getNodeType() == Node.DOCUMENT_NODE) { 704 ser.serialize((Document) node); 705 } else if (node.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) { 706 ser.serialize((DocumentFragment) node); 707 } else if (node.getNodeType() == Node.ELEMENT_NODE) { 708 ser.serialize((Element) node); 709 } else if (node.getNodeType() == Node.TEXT_NODE 710 || node.getNodeType() == Node.COMMENT_NODE 711 || node.getNodeType() == Node.ENTITY_REFERENCE_NODE 712 || node.getNodeType() == Node.CDATA_SECTION_NODE 713 || node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) { 714 ser.serialize(node); 715 } else { 716 return false; 717 } 718 } catch (UnsupportedEncodingException ue) { 719 if (ser.fDOMErrorHandler != null) { 720 DOMErrorImpl error = new DOMErrorImpl(); 721 error.fException = ue; 722 error.fType = "unsupported-encoding"; 723 error.fMessage = ue.getMessage(); 724 error.fSeverity = DOMError.SEVERITY_FATAL_ERROR; 725 ser.fDOMErrorHandler.handleError(error); 726 } 727 throw new LSException(LSException.SERIALIZE_ERR, 728 DOMMessageFormatter.formatMessage( 729 DOMMessageFormatter.SERIALIZER_DOMAIN, 730 "unsupported-encoding", null)); 731 //return false; 732 } catch (LSException lse) { 733 // Rethrow LSException. 734 throw lse; 735 } catch (AbortException e) { 736 return false; 737 } catch (RuntimeException e) { 738 throw (LSException) DOMUtil.createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace(); 739 } catch (Exception e) { 740 if (ser.fDOMErrorHandler != null) { 741 DOMErrorImpl error = new DOMErrorImpl(); 742 error.fException = e; 743 error.fMessage = e.getMessage(); 744 error.fSeverity = DOMError.SEVERITY_ERROR; 745 ser.fDOMErrorHandler.handleError(error); 746 747 } 748 throw (LSException) DOMUtil.createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace(); 749 } finally { 750 ser.clearDocumentState(); 751 } 752 return true; 753 754 } //write 755 756 /** 757 * Serialize the specified node as described above in the general 758 * description of the <code>LSSerializer</code> interface. The output is 759 * written to the supplied URI. 760 * <br> When writing to a URI, the encoding is found by looking at the 761 * encoding information that is reachable through the item to be written (or 762 * its owner document) in this order: 763 * <ol> 764 * <li> 765 * <code>Document.inputEncoding</code>, 766 * </li> 767 * <li> 768 * <code>Document.xmlEncoding</code>. 769 * </li> 770 * </ol> 771 * <br> If no encoding is reachable through the above properties, a default 772 * encoding of "UTF-8" will be used. 773 * <br> If the specified encoding is not supported an "unsupported-encoding" 774 * error is raised. 775 * 776 * @param node The node to serialize. 777 * @param URI The URI to write to. 778 * @return Returns <code>true</code> if <code>node</code> was successfully 779 * serialized and <code>false</code> in case the node couldn't be 780 * serialized. 781 */ 782 public boolean writeToURI(Node node, String URI) throws LSException { 783 if (node == null) { 784 return false; 785 } 786 787 XMLSerializer ser = null; 788 String ver = _getXmlVersion(node); 789 790 if (ver != null && ver.equals("1.1")) { 791 if (xml11Serializer == null) { 792 xml11Serializer = new XML11Serializer(); 793 initSerializer(xml11Serializer); 794 } 795 // copy setting from "main" serializer to XML 1.1 serializer 796 copySettings(serializer, xml11Serializer); 797 ser = xml11Serializer; 798 } else { 799 ser = serializer; 800 } 801 802 String encoding = _getInputEncoding(node); 803 if (encoding == null) { 804 encoding = _getXmlEncoding(node); 805 if (encoding == null) { 806 encoding = "UTF-8"; 807 } 808 } 809 810 try { 811 prepareForSerialization(ser, node); 812 ser._format.setEncoding(encoding); 813 ser.setOutputByteStream(XMLEntityManager.createOutputStream(URI)); 814 815 if (node.getNodeType() == Node.DOCUMENT_NODE) { 816 ser.serialize((Document) node); 817 } else if (node.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) { 818 ser.serialize((DocumentFragment) node); 819 } else if (node.getNodeType() == Node.ELEMENT_NODE) { 820 ser.serialize((Element) node); 821 } else if (node.getNodeType() == Node.TEXT_NODE 822 || node.getNodeType() == Node.COMMENT_NODE 823 || node.getNodeType() == Node.ENTITY_REFERENCE_NODE 824 || node.getNodeType() == Node.CDATA_SECTION_NODE 825 || node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) { 826 ser.serialize(node); 827 } else { 828 return false; 829 } 830 } catch (LSException lse) { 831 // Rethrow LSException. 832 throw lse; 833 } catch (AbortException e) { 834 return false; 835 } catch (RuntimeException e) { 836 throw (LSException) DOMUtil.createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace(); 837 } catch (Exception e) { 838 if (ser.fDOMErrorHandler != null) { 839 DOMErrorImpl error = new DOMErrorImpl(); 840 error.fException = e; 841 error.fMessage = e.getMessage(); 842 error.fSeverity = DOMError.SEVERITY_ERROR; 843 ser.fDOMErrorHandler.handleError(error); 844 } 845 throw (LSException) DOMUtil.createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace(); 846 } finally { 847 ser.clearDocumentState(); 848 } 849 return true; 850 } //writeURI 851 852 // 853 // Private methods 854 // 855 private void prepareForSerialization(XMLSerializer ser, Node node) { 856 ser.reset(); 857 ser.features = features; 858 ser.fDOMErrorHandler = fErrorHandler; 859 ser.fNamespaces = (features & NAMESPACES) != 0; 860 ser.fNamespacePrefixes = (features & NSDECL) != 0; 861 ser._format.setIndenting((features & PRETTY_PRINT) != 0); 862 ser._format.setOmitComments((features & COMMENTS) == 0); 863 ser._format.setOmitXMLDeclaration((features & XMLDECL) == 0); 864 865 if ((features & WELLFORMED) != 0) { 866 // REVISIT: this is inefficient implementation of well-formness. Instead, we should check 867 // well-formness as we serialize the tree 868 Node next, root; 869 root = node; 870 Method versionChanged; 871 boolean verifyNames = true; 872 Document document = (node.getNodeType() == Node.DOCUMENT_NODE) 873 ? (Document) node 874 : node.getOwnerDocument(); 875 try { 876 versionChanged = document.getClass().getMethod("isXMLVersionChanged()", new Class[]{}); 877 if (versionChanged != null) { 878 verifyNames = ((Boolean) versionChanged.invoke(document, (Object[]) null)).booleanValue(); 879 } 880 } catch (Exception e) { 881 //no way to test the version... 882 //ignore the exception 883 } 884 if (node.getFirstChild() != null) { 885 while (node != null) { 886 verify(node, verifyNames, false); 887 // Move down to first child 888 next = node.getFirstChild(); 889 // No child nodes, so walk tree 890 while (next == null) { 891 // Move to sibling if possible. 892 next = node.getNextSibling(); 893 if (next == null) { 894 node = node.getParentNode(); 895 if (root == node) { 896 next = null; 897 break; 898 } 899 next = node.getNextSibling(); 900 } 901 } 902 node = next; 903 } 904 } else { 905 verify(node, verifyNames, false); 906 } 907 } 908 } 909 910 private void verify(Node node, boolean verifyNames, boolean xml11Version) { 911 912 int type = node.getNodeType(); 913 fLocator.fRelatedNode = node; 914 boolean wellformed; 915 switch (type) { 916 case Node.DOCUMENT_NODE: { 917 break; 918 } 919 case Node.DOCUMENT_TYPE_NODE: { 920 break; 921 } 922 case Node.ELEMENT_NODE: { 923 if (verifyNames) { 924 if ((features & NAMESPACES) != 0) { 925 wellformed = CoreDocumentImpl.isValidQName(node.getPrefix(), node.getLocalName(), xml11Version); 926 } else { 927 wellformed = CoreDocumentImpl.isXMLName(node.getNodeName(), xml11Version); 928 } 929 if (!wellformed) { 930 if (fErrorHandler != null) { 931 String msg = DOMMessageFormatter.formatMessage( 932 DOMMessageFormatter.DOM_DOMAIN, 933 "wf-invalid-character-in-node-name", 934 new Object[]{"Element", node.getNodeName()}); 935 DOMNormalizer.reportDOMError(fErrorHandler, fError, fLocator, msg, DOMError.SEVERITY_FATAL_ERROR, 936 "wf-invalid-character-in-node-name"); 937 } 938 } 939 } 940 941 NamedNodeMap attributes = (node.hasAttributes()) ? node.getAttributes() : null; 942 if (attributes != null) { 943 for (int i = 0; i < attributes.getLength(); ++i) { 944 Attr attr = (Attr) attributes.item(i); 945 fLocator.fRelatedNode = attr; 946 DOMNormalizer.isAttrValueWF(fErrorHandler, fError, fLocator, 947 attributes, attr, attr.getValue(), xml11Version); 948 if (verifyNames) { 949 wellformed = CoreDocumentImpl.isXMLName(attr.getNodeName(), xml11Version); 950 if (!wellformed) { 951 String msg 952 = DOMMessageFormatter.formatMessage( 953 DOMMessageFormatter.DOM_DOMAIN, 954 "wf-invalid-character-in-node-name", 955 new Object[]{"Attr", node.getNodeName()}); 956 DOMNormalizer.reportDOMError(fErrorHandler, fError, fLocator, msg, DOMError.SEVERITY_FATAL_ERROR, 957 "wf-invalid-character-in-node-name"); 958 } 959 } 960 } 961 962 } 963 964 break; 965 } 966 967 case Node.COMMENT_NODE: { 968 // only verify well-formness if comments included in the tree 969 if ((features & COMMENTS) != 0) { 970 DOMNormalizer.isCommentWF(fErrorHandler, fError, fLocator, ((Comment) node).getData(), xml11Version); 971 } 972 break; 973 } 974 case Node.ENTITY_REFERENCE_NODE: { 975 // only if entity is preserved in the tree 976 if (verifyNames && (features & ENTITIES) != 0) { 977 CoreDocumentImpl.isXMLName(node.getNodeName(), xml11Version); 978 } 979 break; 980 981 } 982 case Node.CDATA_SECTION_NODE: { 983 // verify content 984 DOMNormalizer.isXMLCharWF(fErrorHandler, fError, fLocator, node.getNodeValue(), xml11Version); 985 // the ]]> string will be checked during serialization 986 break; 987 } 988 case Node.TEXT_NODE: { 989 DOMNormalizer.isXMLCharWF(fErrorHandler, fError, fLocator, node.getNodeValue(), xml11Version); 990 break; 991 } 992 case Node.PROCESSING_INSTRUCTION_NODE: { 993 ProcessingInstruction pinode = (ProcessingInstruction) node; 994 String target = pinode.getTarget(); 995 if (verifyNames) { 996 if (xml11Version) { 997 wellformed = XML11Char.isXML11ValidName(target); 998 } else { 999 wellformed = XMLChar.isValidName(target); 1000 } 1001 1002 if (!wellformed) { 1003 String msg 1004 = DOMMessageFormatter.formatMessage( 1005 DOMMessageFormatter.DOM_DOMAIN, 1006 "wf-invalid-character-in-node-name", 1007 new Object[]{"Element", node.getNodeName()}); 1008 DOMNormalizer.reportDOMError( 1009 fErrorHandler, 1010 fError, 1011 fLocator, 1012 msg, 1013 DOMError.SEVERITY_FATAL_ERROR, 1014 "wf-invalid-character-in-node-name"); 1015 } 1016 } 1017 DOMNormalizer.isXMLCharWF(fErrorHandler, fError, fLocator, pinode.getData(), xml11Version); 1018 break; 1019 } 1020 } 1021 fLocator.fRelatedNode = null; 1022 } 1023 1024 private String _getXmlVersion(Node node) { 1025 Document doc = (node.getNodeType() == Node.DOCUMENT_NODE) 1026 ? (Document) node : node.getOwnerDocument(); 1027 if (doc != null) { 1028 try { 1029 return doc.getXmlVersion(); 1030 } // The VM ran out of memory or there was some other serious problem. Re-throw. 1031 catch (VirtualMachineError | ThreadDeath vme) { 1032 throw vme; 1033 } // Ignore all other exceptions and errors 1034 catch (Throwable t) { 1035 } 1036 } 1037 return null; 1038 } 1039 1040 private String _getInputEncoding(Node node) { 1041 Document doc = (node.getNodeType() == Node.DOCUMENT_NODE) 1042 ? (Document) node : node.getOwnerDocument(); 1043 if (doc != null) { 1044 try { 1045 return doc.getInputEncoding(); 1046 } // The VM ran out of memory or there was some other serious problem. Re-throw. 1047 catch (VirtualMachineError | ThreadDeath vme) { 1048 throw vme; 1049 } // Ignore all other exceptions and errors 1050 catch (Throwable t) { 1051 } 1052 } 1053 return null; 1054 } 1055 1056 private String _getXmlEncoding(Node node) { 1057 Document doc = (node.getNodeType() == Node.DOCUMENT_NODE) 1058 ? (Document) node : node.getOwnerDocument(); 1059 if (doc != null) { 1060 try { 1061 return doc.getXmlEncoding(); 1062 } // The VM ran out of memory or there was some other serious problem. Re-throw. 1063 catch (VirtualMachineError | ThreadDeath vme) { 1064 throw vme; 1065 } // Ignore all other exceptions and errors 1066 catch (Throwable t) { 1067 } 1068 } 1069 return null; 1070 } 1071 1072 } //DOMSerializerImpl