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