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