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.CoreDocumentImpl; 32 import com.sun.org.apache.xerces.internal.dom.DOMErrorImpl; 33 import com.sun.org.apache.xerces.internal.dom.DOMLocatorImpl; 34 import com.sun.org.apache.xerces.internal.dom.DOMMessageFormatter; 35 import com.sun.org.apache.xerces.internal.dom.DOMNormalizer; 36 import com.sun.org.apache.xerces.internal.dom.DOMStringListImpl; 37 import com.sun.org.apache.xerces.internal.impl.Constants; 38 import com.sun.org.apache.xerces.internal.impl.XMLEntityManager; 39 import com.sun.org.apache.xerces.internal.util.DOMUtil; 40 import com.sun.org.apache.xerces.internal.util.NamespaceSupport; 41 import com.sun.org.apache.xerces.internal.util.SymbolTable; 42 import com.sun.org.apache.xerces.internal.util.XML11Char; 43 import com.sun.org.apache.xerces.internal.util.XMLChar; 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 1.9, Xerces 2.9.0, replaced by 76 * {@link com.sun.org.apache.xml.internal.serializer.dom3.LSSerializerImpl} 77 */ 78 public class DOMSerializerImpl implements LSSerializer, DOMConfiguration { 79 80 // TODO: When DOM Level 3 goes to REC replace method calls using 81 // reflection for: getXmlEncoding, getInputEncoding and getXmlEncoding 82 // with regular static calls on the Document object. 83 // data 84 // serializer 85 private XMLSerializer serializer; 86 87 // XML 1.1 serializer 88 private XML11Serializer xml11Serializer; 89 90 //Recognized parameters 91 private DOMStringList fRecognizedParameters; 92 93 /** 94 * REVISIT: Currently we handle 3 different configurations, would be nice 95 * just have one configuration that has different recognized parameters 96 * depending if it is used in Core/LS. 97 */ 98 protected short features = 0; 99 100 protected final static short NAMESPACES = 0x1 << 0; 101 protected final static short WELLFORMED = 0x1 << 1; 102 protected final static short ENTITIES = 0x1 << 2; 103 protected final static short CDATA = 0x1 << 3; 104 protected final static short SPLITCDATA = 0x1 << 4; 105 protected final static short COMMENTS = 0x1 << 5; 106 protected final static short DISCARDDEFAULT = 0x1 << 6; 107 protected final static short INFOSET = 0x1 << 7; 108 protected final static short XMLDECL = 0x1 << 8; 109 protected final static short NSDECL = 0x1 << 9; 110 protected final static short DOM_ELEMENT_CONTENT_WHITESPACE = 0x1 << 10; 111 protected final static short PRETTY_PRINT = 0x1 << 11; 112 113 // well-formness checking 114 private DOMErrorHandler fErrorHandler = null; 115 private final DOMErrorImpl fError = new DOMErrorImpl(); 116 private final DOMLocatorImpl fLocator = new DOMLocatorImpl(); 117 118 /** 119 * Constructs a new LSSerializer. The constructor turns on the namespace 120 * support in <code>XMLSerializer</code> and initializes the following 121 * fields: fNSBinder, fLocalNSBinder, fSymbolTable, fEmptySymbol, 122 * fXmlSymbol, fXmlnsSymbol, fNamespaceCounter, fFeatures. 123 */ 124 public DOMSerializerImpl() { 125 // set default features 126 features |= NAMESPACES; 127 features |= ENTITIES; 128 features |= COMMENTS; 129 features |= CDATA; 130 features |= SPLITCDATA; 131 features |= WELLFORMED; 132 features |= NSDECL; 133 features |= DOM_ELEMENT_CONTENT_WHITESPACE; 134 features |= DISCARDDEFAULT; 135 features |= XMLDECL; 136 137 serializer = new XMLSerializer(); 138 initSerializer(serializer); 139 } 140 141 // 142 // LSSerializer methods 143 // 144 public DOMConfiguration getDomConfig() { 145 return this; 146 } 147 148 /** 149 * DOM L3-EXPERIMENTAL: Setter for boolean and object parameters 150 */ 151 public void setParameter(String name, Object value) throws DOMException { 152 if (value instanceof Boolean) { 153 boolean state = ((Boolean) value).booleanValue(); 154 if (name.equalsIgnoreCase(Constants.DOM_INFOSET)) { 155 if (state) { 156 features &= ~ENTITIES; 157 features &= ~CDATA; 158 features |= NAMESPACES; 159 features |= NSDECL; 160 features |= WELLFORMED; 161 features |= COMMENTS; 162 } 163 // false does not have any effect 164 } else if (name.equalsIgnoreCase(Constants.DOM_XMLDECL)) { 165 features 166 = (short) (state ? features | XMLDECL : features & ~XMLDECL); 167 } else if (name.equalsIgnoreCase(Constants.DOM_NAMESPACES)) { 168 features 169 = (short) (state 170 ? features | NAMESPACES 171 : features & ~NAMESPACES); 172 serializer.fNamespaces = state; 173 } else if (name.equalsIgnoreCase(Constants.DOM_SPLIT_CDATA)) { 174 features 175 = (short) (state 176 ? features | SPLITCDATA 177 : features & ~SPLITCDATA); 178 } else if (name.equalsIgnoreCase(Constants.DOM_DISCARD_DEFAULT_CONTENT)) { 179 features 180 = (short) (state 181 ? features | DISCARDDEFAULT 182 : features & ~DISCARDDEFAULT); 183 } else if (name.equalsIgnoreCase(Constants.DOM_WELLFORMED)) { 184 features 185 = (short) (state 186 ? features | WELLFORMED 187 : features & ~WELLFORMED); 188 } else if (name.equalsIgnoreCase(Constants.DOM_ENTITIES)) { 189 features 190 = (short) (state 191 ? features | ENTITIES 192 : features & ~ENTITIES); 193 } else if (name.equalsIgnoreCase(Constants.DOM_CDATA_SECTIONS)) { 194 features 195 = (short) (state 196 ? features | CDATA 197 : features & ~CDATA); 198 } else if (name.equalsIgnoreCase(Constants.DOM_COMMENTS)) { 199 features 200 = (short) (state 201 ? features | COMMENTS 202 : features & ~COMMENTS); 203 } else if (name.equalsIgnoreCase(Constants.DOM_FORMAT_PRETTY_PRINT)) { 204 features 205 = (short) (state 206 ? features | PRETTY_PRINT 207 : features & ~PRETTY_PRINT); 208 } else if (name.equalsIgnoreCase(Constants.DOM_CANONICAL_FORM) 209 || name.equalsIgnoreCase(Constants.DOM_VALIDATE_IF_SCHEMA) 210 || name.equalsIgnoreCase(Constants.DOM_VALIDATE) 211 || name.equalsIgnoreCase(Constants.DOM_CHECK_CHAR_NORMALIZATION) 212 || name.equalsIgnoreCase(Constants.DOM_DATATYPE_NORMALIZATION)) { 213 // || name.equalsIgnoreCase(Constants.DOM_NORMALIZE_CHARACTERS)) { 214 // true is not supported 215 if (state) { 216 String msg 217 = DOMMessageFormatter.formatMessage( 218 DOMMessageFormatter.DOM_DOMAIN, 219 "FEATURE_NOT_SUPPORTED", 220 new Object[]{name}); 221 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg); 222 } 223 } else if (name.equalsIgnoreCase(Constants.DOM_NAMESPACE_DECLARATIONS)) { 224 //namespace-declaration has effect only if namespaces is true 225 features 226 = (short) (state 227 ? features | NSDECL 228 : features & ~NSDECL); 229 serializer.fNamespacePrefixes = state; 230 } else if (name.equalsIgnoreCase(Constants.DOM_ELEMENT_CONTENT_WHITESPACE) 231 || name.equalsIgnoreCase(Constants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS)) { 232 // false is not supported 233 if (!state) { 234 String msg 235 = DOMMessageFormatter.formatMessage( 236 DOMMessageFormatter.DOM_DOMAIN, 237 "FEATURE_NOT_SUPPORTED", 238 new Object[]{name}); 239 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg); 240 } 241 } else { 242 String msg 243 = DOMMessageFormatter.formatMessage( 244 DOMMessageFormatter.DOM_DOMAIN, 245 "FEATURE_NOT_FOUND", 246 new Object[]{name}); 247 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg); 248 } 249 } else if (name.equalsIgnoreCase(Constants.DOM_ERROR_HANDLER)) { 250 if (value == null || value instanceof DOMErrorHandler) { 251 fErrorHandler = (DOMErrorHandler) value; 252 } else { 253 String msg 254 = DOMMessageFormatter.formatMessage( 255 DOMMessageFormatter.DOM_DOMAIN, 256 "TYPE_MISMATCH_ERR", 257 new Object[]{name}); 258 throw new DOMException(DOMException.TYPE_MISMATCH_ERR, msg); 259 } 260 } else if (name.equalsIgnoreCase(Constants.DOM_RESOURCE_RESOLVER) 261 || name.equalsIgnoreCase(Constants.DOM_SCHEMA_LOCATION) 262 || name.equalsIgnoreCase(Constants.DOM_SCHEMA_TYPE) 263 || name.equalsIgnoreCase(Constants.DOM_NORMALIZE_CHARACTERS) 264 && value != null) { 265 String msg 266 = DOMMessageFormatter.formatMessage( 267 DOMMessageFormatter.DOM_DOMAIN, 268 "FEATURE_NOT_SUPPORTED", 269 new Object[]{name}); 270 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg); 271 } else { 272 String msg 273 = DOMMessageFormatter.formatMessage( 274 DOMMessageFormatter.DOM_DOMAIN, 275 "FEATURE_NOT_FOUND", 276 new Object[]{name}); 277 throw new DOMException(DOMException.NOT_FOUND_ERR, msg); 278 } 279 } 280 281 /** 282 * DOM L3-EXPERIMENTAL: Check if parameter can be set 283 */ 284 public boolean canSetParameter(String name, Object state) { 285 if (state == null) { 286 return true; 287 } 288 289 if (state instanceof Boolean) { 290 boolean value = ((Boolean) state).booleanValue(); 291 if (name.equalsIgnoreCase(Constants.DOM_NAMESPACES) 292 || name.equalsIgnoreCase(Constants.DOM_SPLIT_CDATA) 293 || name.equalsIgnoreCase(Constants.DOM_DISCARD_DEFAULT_CONTENT) 294 || name.equalsIgnoreCase(Constants.DOM_XMLDECL) 295 || name.equalsIgnoreCase(Constants.DOM_WELLFORMED) 296 || name.equalsIgnoreCase(Constants.DOM_INFOSET) 297 || name.equalsIgnoreCase(Constants.DOM_ENTITIES) 298 || name.equalsIgnoreCase(Constants.DOM_CDATA_SECTIONS) 299 || name.equalsIgnoreCase(Constants.DOM_COMMENTS) 300 || name.equalsIgnoreCase(Constants.DOM_FORMAT_PRETTY_PRINT) 301 || name.equalsIgnoreCase(Constants.DOM_NAMESPACE_DECLARATIONS)) { 302 // both values supported 303 return true; 304 } else if (name.equalsIgnoreCase(Constants.DOM_CANONICAL_FORM) 305 || name.equalsIgnoreCase(Constants.DOM_VALIDATE_IF_SCHEMA) 306 || name.equalsIgnoreCase(Constants.DOM_VALIDATE) 307 || name.equalsIgnoreCase(Constants.DOM_CHECK_CHAR_NORMALIZATION) 308 || name.equalsIgnoreCase(Constants.DOM_DATATYPE_NORMALIZATION)) { 309 // || name.equalsIgnoreCase(Constants.DOM_NORMALIZE_CHARACTERS)) { 310 // true is not supported 311 return !value; 312 } else if (name.equalsIgnoreCase(Constants.DOM_ELEMENT_CONTENT_WHITESPACE) 313 || name.equalsIgnoreCase(Constants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS)) { 314 // false is not supported 315 return value; 316 } 317 } else if (name.equalsIgnoreCase(Constants.DOM_ERROR_HANDLER) 318 && state == null || state instanceof DOMErrorHandler) { 319 return true; 320 } 321 322 return false; 323 } 324 325 /** 326 * DOM Level 3 Core CR - Experimental. 327 * 328 * The list of the parameters supported by this 329 * <code>DOMConfiguration</code> object and for which at least one value can 330 * be set by the application. Note that this list can also contain parameter 331 * names defined outside this specification. 332 */ 333 public DOMStringList getParameterNames() { 334 335 if (fRecognizedParameters == null) { 336 ArrayList parameters = new ArrayList(); 337 338 //Add DOM recognized parameters 339 //REVISIT: Would have been nice to have a list of 340 //recognized parameters. 341 parameters.add(Constants.DOM_NAMESPACES); 342 parameters.add(Constants.DOM_SPLIT_CDATA); 343 parameters.add(Constants.DOM_DISCARD_DEFAULT_CONTENT); 344 parameters.add(Constants.DOM_XMLDECL); 345 parameters.add(Constants.DOM_CANONICAL_FORM); 346 parameters.add(Constants.DOM_VALIDATE_IF_SCHEMA); 347 parameters.add(Constants.DOM_VALIDATE); 348 parameters.add(Constants.DOM_CHECK_CHAR_NORMALIZATION); 349 parameters.add(Constants.DOM_DATATYPE_NORMALIZATION); 350 parameters.add(Constants.DOM_FORMAT_PRETTY_PRINT); 351 //parameters.add(Constants.DOM_NORMALIZE_CHARACTERS); 352 parameters.add(Constants.DOM_WELLFORMED); 353 parameters.add(Constants.DOM_INFOSET); 354 parameters.add(Constants.DOM_NAMESPACE_DECLARATIONS); 355 parameters.add(Constants.DOM_ELEMENT_CONTENT_WHITESPACE); 356 parameters.add(Constants.DOM_ENTITIES); 357 parameters.add(Constants.DOM_CDATA_SECTIONS); 358 parameters.add(Constants.DOM_COMMENTS); 359 parameters.add(Constants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS); 360 parameters.add(Constants.DOM_ERROR_HANDLER); 361 //parameters.add(Constants.DOM_SCHEMA_LOCATION); 362 //parameters.add(Constants.DOM_SCHEMA_TYPE); 363 364 //Add recognized xerces features and properties 365 fRecognizedParameters = new DOMStringListImpl(parameters); 366 } 367 368 return fRecognizedParameters; 369 } 370 371 /** 372 * DOM L3-EXPERIMENTAL: Getter for boolean and object parameters 373 */ 374 public Object getParameter(String name) throws DOMException { 375 if (name.equalsIgnoreCase(Constants.DOM_NORMALIZE_CHARACTERS)) { 376 return null; 377 } else if (name.equalsIgnoreCase(Constants.DOM_COMMENTS)) { 378 return ((features & COMMENTS) != 0) ? Boolean.TRUE : Boolean.FALSE; 379 } else if (name.equalsIgnoreCase(Constants.DOM_NAMESPACES)) { 380 return (features & NAMESPACES) != 0 ? Boolean.TRUE : Boolean.FALSE; 381 } else if (name.equalsIgnoreCase(Constants.DOM_XMLDECL)) { 382 return (features & XMLDECL) != 0 ? Boolean.TRUE : Boolean.FALSE; 383 } else if (name.equalsIgnoreCase(Constants.DOM_CDATA_SECTIONS)) { 384 return (features & CDATA) != 0 ? Boolean.TRUE : Boolean.FALSE; 385 } else if (name.equalsIgnoreCase(Constants.DOM_ENTITIES)) { 386 return (features & ENTITIES) != 0 ? Boolean.TRUE : Boolean.FALSE; 387 } else if (name.equalsIgnoreCase(Constants.DOM_SPLIT_CDATA)) { 388 return (features & SPLITCDATA) != 0 ? Boolean.TRUE : Boolean.FALSE; 389 } else if (name.equalsIgnoreCase(Constants.DOM_WELLFORMED)) { 390 return (features & WELLFORMED) != 0 ? Boolean.TRUE : Boolean.FALSE; 391 } else if (name.equalsIgnoreCase(Constants.DOM_NAMESPACE_DECLARATIONS)) { 392 return (features & NSDECL) != 0 ? Boolean.TRUE : Boolean.FALSE; 393 } else if (name.equalsIgnoreCase(Constants.DOM_ELEMENT_CONTENT_WHITESPACE) 394 || name.equalsIgnoreCase(Constants.DOM_IGNORE_UNKNOWN_CHARACTER_DENORMALIZATIONS)) { 395 return Boolean.TRUE; 396 } else if (name.equalsIgnoreCase(Constants.DOM_DISCARD_DEFAULT_CONTENT)) { 397 return ((features & DISCARDDEFAULT) != 0) ? Boolean.TRUE : Boolean.FALSE; 398 } else if (name.equalsIgnoreCase(Constants.DOM_FORMAT_PRETTY_PRINT)) { 399 return ((features & PRETTY_PRINT) != 0) ? Boolean.TRUE : Boolean.FALSE; 400 } else if (name.equalsIgnoreCase(Constants.DOM_INFOSET)) { 401 if ((features & ENTITIES) == 0 402 && (features & CDATA) == 0 403 && (features & NAMESPACES) != 0 404 && (features & NSDECL) != 0 405 && (features & WELLFORMED) != 0 406 && (features & COMMENTS) != 0) { 407 return Boolean.TRUE; 408 } 409 return Boolean.FALSE; 410 } else if (name.equalsIgnoreCase(Constants.DOM_CANONICAL_FORM) 411 || name.equalsIgnoreCase(Constants.DOM_VALIDATE_IF_SCHEMA) 412 || name.equalsIgnoreCase(Constants.DOM_CHECK_CHAR_NORMALIZATION) 413 || name.equalsIgnoreCase(Constants.DOM_VALIDATE) 414 || name.equalsIgnoreCase(Constants.DOM_VALIDATE_IF_SCHEMA) 415 || name.equalsIgnoreCase(Constants.DOM_DATATYPE_NORMALIZATION)) { 416 return Boolean.FALSE; 417 } else if (name.equalsIgnoreCase(Constants.DOM_ERROR_HANDLER)) { 418 return fErrorHandler; 419 } else if (name.equalsIgnoreCase(Constants.DOM_RESOURCE_RESOLVER) 420 || name.equalsIgnoreCase(Constants.DOM_SCHEMA_LOCATION) 421 || name.equalsIgnoreCase(Constants.DOM_SCHEMA_TYPE)) { 422 String msg 423 = DOMMessageFormatter.formatMessage( 424 DOMMessageFormatter.DOM_DOMAIN, 425 "FEATURE_NOT_SUPPORTED", 426 new Object[]{name}); 427 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg); 428 } else { 429 String msg 430 = DOMMessageFormatter.formatMessage( 431 DOMMessageFormatter.DOM_DOMAIN, 432 "FEATURE_NOT_FOUND", 433 new Object[]{name}); 434 throw new DOMException(DOMException.NOT_FOUND_ERR, msg); 435 } 436 } 437 438 /** 439 * DOM L3 EXPERIMENTAL: Serialize the specified node as described above in 440 * the description of <code>LSSerializer</code>. The result of serializing 441 * the node is returned as a string. Writing a Document or Entity node 442 * produces a serialized form that is well formed XML. Writing other node 443 * types produces a fragment of text in a form that is not fully defined by 444 * this document, but that should be useful to a human for debugging or 445 * diagnostic purposes. 446 * 447 * @param wnode The node to be written. 448 * @return Returns the serialized data 449 * @exception DOMException DOMSTRING_SIZE_ERR: The resulting string is too 450 * long to fit in a <code>DOMString</code>. 451 * @exception LSException SERIALIZE_ERR: Unable to serialize the node. DOM 452 * applications should attach a <code>DOMErrorHandler</code> using the 453 * parameter "<i>error-handler</i>" to get details on error. 454 */ 455 public String writeToString(Node wnode) throws DOMException, LSException { 456 // determine which serializer to use: 457 XMLSerializer ser = null; 458 String ver = _getXmlVersion(wnode); 459 if (ver != null && ver.equals("1.1")) { 460 if (xml11Serializer == null) { 461 xml11Serializer = new XML11Serializer(); 462 initSerializer(xml11Serializer); 463 } 464 // copy setting from "main" serializer to XML 1.1 serializer 465 copySettings(serializer, xml11Serializer); 466 ser = xml11Serializer; 467 } else { 468 ser = serializer; 469 } 470 471 StringWriter destination = new StringWriter(); 472 try { 473 prepareForSerialization(ser, wnode); 474 ser._format.setEncoding("UTF-16"); 475 ser.setOutputCharStream(destination); 476 if (wnode.getNodeType() == Node.DOCUMENT_NODE) { 477 ser.serialize((Document) wnode); 478 } else if (wnode.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) { 479 ser.serialize((DocumentFragment) wnode); 480 } else if (wnode.getNodeType() == Node.ELEMENT_NODE) { 481 ser.serialize((Element) wnode); 482 } else if (wnode.getNodeType() == Node.TEXT_NODE 483 || wnode.getNodeType() == Node.COMMENT_NODE 484 || wnode.getNodeType() == Node.ENTITY_REFERENCE_NODE 485 || wnode.getNodeType() == Node.CDATA_SECTION_NODE 486 || wnode.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) { 487 ser.serialize(wnode); 488 } else { 489 String msg = DOMMessageFormatter.formatMessage( 490 DOMMessageFormatter.SERIALIZER_DOMAIN, 491 "unable-to-serialize-node", null); 492 if (ser.fDOMErrorHandler != null) { 493 DOMErrorImpl error = new DOMErrorImpl(); 494 error.fType = "unable-to-serialize-node"; 495 error.fMessage = msg; 496 error.fSeverity = DOMError.SEVERITY_FATAL_ERROR; 497 ser.fDOMErrorHandler.handleError(error); 498 } 499 throw new LSException(LSException.SERIALIZE_ERR, msg); 500 } 501 } catch (LSException lse) { 502 // Rethrow LSException. 503 throw lse; 504 } catch (RuntimeException e) { 505 if (e == DOMNormalizer.abort) { 506 // stopped at user request 507 return null; 508 } 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 (RuntimeException e) { 737 if (e == DOMNormalizer.abort) { 738 // stopped at user request 739 return false; 740 } 741 throw (LSException) DOMUtil.createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace(); 742 } catch (Exception e) { 743 if (ser.fDOMErrorHandler != null) { 744 DOMErrorImpl error = new DOMErrorImpl(); 745 error.fException = e; 746 error.fMessage = e.getMessage(); 747 error.fSeverity = DOMError.SEVERITY_ERROR; 748 ser.fDOMErrorHandler.handleError(error); 749 750 } 751 throw (LSException) DOMUtil.createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace(); 752 } finally { 753 ser.clearDocumentState(); 754 } 755 return true; 756 757 } //write 758 759 /** 760 * Serialize the specified node as described above in the general 761 * description of the <code>LSSerializer</code> interface. The output is 762 * written to the supplied URI. 763 * <br> When writing to a URI, the encoding is found by looking at the 764 * encoding information that is reachable through the item to be written (or 765 * its owner document) in this order: 766 * <ol> 767 * <li> 768 * <code>Document.inputEncoding</code>, 769 * </li> 770 * <li> 771 * <code>Document.xmlEncoding</code>. 772 * </li> 773 * </ol> 774 * <br> If no encoding is reachable through the above properties, a default 775 * encoding of "UTF-8" will be used. 776 * <br> If the specified encoding is not supported an "unsupported-encoding" 777 * error is raised. 778 * 779 * @param node The node to serialize. 780 * @param URI The URI to write to. 781 * @return Returns <code>true</code> if <code>node</code> was successfully 782 * serialized and <code>false</code> in case the node couldn't be 783 * serialized. 784 */ 785 public boolean writeToURI(Node node, String URI) throws LSException { 786 if (node == null) { 787 return false; 788 } 789 790 XMLSerializer ser = null; 791 String ver = _getXmlVersion(node); 792 793 if (ver != null && ver.equals("1.1")) { 794 if (xml11Serializer == null) { 795 xml11Serializer = new XML11Serializer(); 796 initSerializer(xml11Serializer); 797 } 798 // copy setting from "main" serializer to XML 1.1 serializer 799 copySettings(serializer, xml11Serializer); 800 ser = xml11Serializer; 801 } else { 802 ser = serializer; 803 } 804 805 String encoding = _getInputEncoding(node); 806 if (encoding == null) { 807 encoding = _getXmlEncoding(node); 808 if (encoding == null) { 809 encoding = "UTF-8"; 810 } 811 } 812 813 try { 814 prepareForSerialization(ser, node); 815 ser._format.setEncoding(encoding); 816 ser.setOutputByteStream(XMLEntityManager.createOutputStream(URI)); 817 818 if (node.getNodeType() == Node.DOCUMENT_NODE) { 819 ser.serialize((Document) node); 820 } else if (node.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) { 821 ser.serialize((DocumentFragment) node); 822 } else if (node.getNodeType() == Node.ELEMENT_NODE) { 823 ser.serialize((Element) node); 824 } else if (node.getNodeType() == Node.TEXT_NODE 825 || node.getNodeType() == Node.COMMENT_NODE 826 || node.getNodeType() == Node.ENTITY_REFERENCE_NODE 827 || node.getNodeType() == Node.CDATA_SECTION_NODE 828 || node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) { 829 ser.serialize(node); 830 } else { 831 return false; 832 } 833 } catch (LSException lse) { 834 // Rethrow LSException. 835 throw lse; 836 } catch (RuntimeException e) { 837 if (e == DOMNormalizer.abort) { 838 // stopped at user request 839 return false; 840 } 841 throw (LSException) DOMUtil.createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace(); 842 } catch (Exception e) { 843 if (ser.fDOMErrorHandler != null) { 844 DOMErrorImpl error = new DOMErrorImpl(); 845 error.fException = e; 846 error.fMessage = e.getMessage(); 847 error.fSeverity = DOMError.SEVERITY_ERROR; 848 ser.fDOMErrorHandler.handleError(error); 849 } 850 throw (LSException) DOMUtil.createLSException(LSException.SERIALIZE_ERR, e).fillInStackTrace(); 851 } finally { 852 ser.clearDocumentState(); 853 } 854 return true; 855 } //writeURI 856 857 // 858 // Private methods 859 // 860 private void prepareForSerialization(XMLSerializer ser, Node node) { 861 ser.reset(); 862 ser.features = features; 863 ser.fDOMErrorHandler = fErrorHandler; 864 ser.fNamespaces = (features & NAMESPACES) != 0; 865 ser.fNamespacePrefixes = (features & NSDECL) != 0; 866 ser._format.setIndenting((features & PRETTY_PRINT) != 0); 867 ser._format.setOmitComments((features & COMMENTS) == 0); 868 ser._format.setOmitXMLDeclaration((features & XMLDECL) == 0); 869 870 if ((features & WELLFORMED) != 0) { 871 // REVISIT: this is inefficient implementation of well-formness. Instead, we should check 872 // well-formness as we serialize the tree 873 Node next, root; 874 root = node; 875 Method versionChanged; 876 boolean verifyNames = true; 877 Document document = (node.getNodeType() == Node.DOCUMENT_NODE) 878 ? (Document) node 879 : node.getOwnerDocument(); 880 try { 881 versionChanged = document.getClass().getMethod("isXMLVersionChanged()", new Class[]{}); 882 if (versionChanged != null) { 883 verifyNames = ((Boolean) versionChanged.invoke(document, (Object[]) null)).booleanValue(); 884 } 885 } catch (Exception e) { 886 //no way to test the version... 887 //ignore the exception 888 } 889 if (node.getFirstChild() != null) { 890 while (node != null) { 891 verify(node, verifyNames, false); 892 // Move down to first child 893 next = node.getFirstChild(); 894 // No child nodes, so walk tree 895 while (next == null) { 896 // Move to sibling if possible. 897 next = node.getNextSibling(); 898 if (next == null) { 899 node = node.getParentNode(); 900 if (root == node) { 901 next = null; 902 break; 903 } 904 next = node.getNextSibling(); 905 } 906 } 907 node = next; 908 } 909 } else { 910 verify(node, verifyNames, false); 911 } 912 } 913 } 914 915 private void verify(Node node, boolean verifyNames, boolean xml11Version) { 916 917 int type = node.getNodeType(); 918 fLocator.fRelatedNode = node; 919 boolean wellformed; 920 switch (type) { 921 case Node.DOCUMENT_NODE: { 922 break; 923 } 924 case Node.DOCUMENT_TYPE_NODE: { 925 break; 926 } 927 case Node.ELEMENT_NODE: { 928 if (verifyNames) { 929 if ((features & NAMESPACES) != 0) { 930 wellformed = CoreDocumentImpl.isValidQName(node.getPrefix(), node.getLocalName(), xml11Version); 931 } else { 932 wellformed = CoreDocumentImpl.isXMLName(node.getNodeName(), xml11Version); 933 } 934 if (!wellformed) { 935 if (fErrorHandler != null) { 936 String msg = DOMMessageFormatter.formatMessage( 937 DOMMessageFormatter.DOM_DOMAIN, 938 "wf-invalid-character-in-node-name", 939 new Object[]{"Element", node.getNodeName()}); 940 DOMNormalizer.reportDOMError(fErrorHandler, fError, fLocator, msg, DOMError.SEVERITY_FATAL_ERROR, 941 "wf-invalid-character-in-node-name"); 942 } 943 } 944 } 945 946 NamedNodeMap attributes = (node.hasAttributes()) ? node.getAttributes() : null; 947 if (attributes != null) { 948 for (int i = 0; i < attributes.getLength(); ++i) { 949 Attr attr = (Attr) attributes.item(i); 950 fLocator.fRelatedNode = attr; 951 DOMNormalizer.isAttrValueWF(fErrorHandler, fError, fLocator, 952 attributes, attr, attr.getValue(), xml11Version); 953 if (verifyNames) { 954 wellformed = CoreDocumentImpl.isXMLName(attr.getNodeName(), xml11Version); 955 if (!wellformed) { 956 String msg 957 = DOMMessageFormatter.formatMessage( 958 DOMMessageFormatter.DOM_DOMAIN, 959 "wf-invalid-character-in-node-name", 960 new Object[]{"Attr", node.getNodeName()}); 961 DOMNormalizer.reportDOMError(fErrorHandler, fError, fLocator, msg, DOMError.SEVERITY_FATAL_ERROR, 962 "wf-invalid-character-in-node-name"); 963 } 964 } 965 } 966 967 } 968 969 break; 970 } 971 972 case Node.COMMENT_NODE: { 973 // only verify well-formness if comments included in the tree 974 if ((features & COMMENTS) != 0) { 975 DOMNormalizer.isCommentWF(fErrorHandler, fError, fLocator, ((Comment) node).getData(), xml11Version); 976 } 977 break; 978 } 979 case Node.ENTITY_REFERENCE_NODE: { 980 // only if entity is preserved in the tree 981 if (verifyNames && (features & ENTITIES) != 0) { 982 CoreDocumentImpl.isXMLName(node.getNodeName(), xml11Version); 983 } 984 break; 985 986 } 987 case Node.CDATA_SECTION_NODE: { 988 // verify content 989 DOMNormalizer.isXMLCharWF(fErrorHandler, fError, fLocator, node.getNodeValue(), xml11Version); 990 // the ]]> string will be checked during serialization 991 break; 992 } 993 case Node.TEXT_NODE: { 994 DOMNormalizer.isXMLCharWF(fErrorHandler, fError, fLocator, node.getNodeValue(), xml11Version); 995 break; 996 } 997 case Node.PROCESSING_INSTRUCTION_NODE: { 998 ProcessingInstruction pinode = (ProcessingInstruction) node; 999 String target = pinode.getTarget(); 1000 if (verifyNames) { 1001 if (xml11Version) { 1002 wellformed = XML11Char.isXML11ValidName(target); 1003 } else { 1004 wellformed = XMLChar.isValidName(target); 1005 } 1006 1007 if (!wellformed) { 1008 String msg 1009 = DOMMessageFormatter.formatMessage( 1010 DOMMessageFormatter.DOM_DOMAIN, 1011 "wf-invalid-character-in-node-name", 1012 new Object[]{"Element", node.getNodeName()}); 1013 DOMNormalizer.reportDOMError( 1014 fErrorHandler, 1015 fError, 1016 fLocator, 1017 msg, 1018 DOMError.SEVERITY_FATAL_ERROR, 1019 "wf-invalid-character-in-node-name"); 1020 } 1021 } 1022 DOMNormalizer.isXMLCharWF(fErrorHandler, fError, fLocator, pinode.getData(), xml11Version); 1023 break; 1024 } 1025 } 1026 fLocator.fRelatedNode = null; 1027 } 1028 1029 private String _getXmlVersion(Node node) { 1030 Document doc = (node.getNodeType() == Node.DOCUMENT_NODE) 1031 ? (Document) node : node.getOwnerDocument(); 1032 if (doc != null) { 1033 try { 1034 return doc.getXmlVersion(); 1035 } // The VM ran out of memory or there was some other serious problem. Re-throw. 1036 catch (VirtualMachineError | ThreadDeath vme) { 1037 throw vme; 1038 } // Ignore all other exceptions and errors 1039 catch (Throwable t) { 1040 } 1041 } 1042 return null; 1043 } 1044 1045 private String _getInputEncoding(Node node) { 1046 Document doc = (node.getNodeType() == Node.DOCUMENT_NODE) 1047 ? (Document) node : node.getOwnerDocument(); 1048 if (doc != null) { 1049 try { 1050 return doc.getInputEncoding(); 1051 } // The VM ran out of memory or there was some other serious problem. Re-throw. 1052 catch (VirtualMachineError | ThreadDeath vme) { 1053 throw vme; 1054 } // Ignore all other exceptions and errors 1055 catch (Throwable t) { 1056 } 1057 } 1058 return null; 1059 } 1060 1061 private String _getXmlEncoding(Node node) { 1062 Document doc = (node.getNodeType() == Node.DOCUMENT_NODE) 1063 ? (Document) node : node.getOwnerDocument(); 1064 if (doc != null) { 1065 try { 1066 return doc.getXmlEncoding(); 1067 } // The VM ran out of memory or there was some other serious problem. Re-throw. 1068 catch (VirtualMachineError | ThreadDeath vme) { 1069 throw vme; 1070 } // Ignore all other exceptions and errors 1071 catch (Throwable t) { 1072 } 1073 } 1074 return null; 1075 } 1076 1077 } //DOMSerializerImpl