1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999-2002 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xerces" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, International
  53. * Business Machines, Inc., http://www.apache.org. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. // Sep 14, 2000:
  58. // Fixed problem with namespace handling. Contributed by
  59. // David Blondeau <blondeau@intalio.com>
  60. // Sep 14, 2000:
  61. // Fixed serializer to report IO exception directly, instead at
  62. // the end of document processing.
  63. // Reported by Patrick Higgins <phiggins@transzap.com>
  64. // Aug 21, 2000:
  65. // Fixed bug in startDocument not calling prepare.
  66. // Reported by Mikael Staldal <d96-mst-ingen-reklam@d.kth.se>
  67. // Aug 21, 2000:
  68. // Added ability to omit DOCTYPE declaration.
  69. package com.sun.org.apache.xml.internal.serialize;
  70. import java.io.IOException;
  71. import java.io.OutputStream;
  72. import java.io.Writer;
  73. import java.util.Enumeration;
  74. import com.sun.org.apache.xerces.internal.dom.DOMMessageFormatter;
  75. import org.w3c.dom.DOMError;
  76. import com.sun.org.apache.xerces.internal.util.NamespaceSupport;
  77. import com.sun.org.apache.xerces.internal.util.SymbolTable;
  78. import com.sun.org.apache.xerces.internal.util.XMLChar;
  79. import com.sun.org.apache.xerces.internal.util.XMLSymbols;
  80. import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
  81. import org.w3c.dom.Attr;
  82. import org.w3c.dom.Element;
  83. import org.w3c.dom.NamedNodeMap;
  84. import org.w3c.dom.Node;
  85. import org.w3c.dom.traversal.NodeFilter;
  86. import org.xml.sax.AttributeList;
  87. import org.xml.sax.Attributes;
  88. import org.xml.sax.SAXException;
  89. import org.xml.sax.helpers.AttributesImpl;
  90. /**
  91. * Implements an XML serializer supporting both DOM and SAX pretty
  92. * serializing. For usage instructions see {@link Serializer}.
  93. * <p>
  94. * If an output stream is used, the encoding is taken from the
  95. * output format (defaults to <tt>UTF-8</tt>). If a writer is
  96. * used, make sure the writer uses the same encoding (if applies)
  97. * as specified in the output format.
  98. * <p>
  99. * The serializer supports both DOM and SAX. SAX serializing is done by firing
  100. * SAX events and using the serializer as a document handler. DOM serializing is done
  101. * by calling {@link #serialize(Document)} or by using DOM Level 3
  102. * {@link org.w3c.dom.ls.DOMSerializer} and
  103. * serializing with {@link org.w3c.dom.ls.DOMSerializer#write},
  104. * {@link org.w3c.dom.ls.DOMSerializer#writeToString}.
  105. * <p>
  106. * If an I/O exception occurs while serializing, the serializer
  107. * will not throw an exception directly, but only throw it
  108. * at the end of serializing (either DOM or SAX's {@link
  109. * org.xml.sax.DocumentHandler#endDocument}.
  110. * <p>
  111. * For elements that are not specified as whitespace preserving,
  112. * the serializer will potentially break long text lines at space
  113. * boundaries, indent lines, and serialize elements on separate
  114. * lines. Line terminators will be regarded as spaces, and
  115. * spaces at beginning of line will be stripped.
  116. * @author <a href="mailto:arkin@intalio.com">Assaf Arkin</a>
  117. * @author <a href="mailto:rahul.srivastava@sun.com">Rahul Srivastava</a>
  118. * @author Elena Litani IBM
  119. * @version $Revision: 1.60 $ $Date: 2004/04/13 17:30:01 $
  120. * @see Serializer
  121. */
  122. public class XMLSerializer
  123. extends BaseMarkupSerializer {
  124. //
  125. // constants
  126. //
  127. protected static final boolean DEBUG = false;
  128. //
  129. // data
  130. //
  131. //
  132. // DOM Level 3 implementation: variables intialized in DOMSerializerImpl
  133. //
  134. /** stores namespaces in scope */
  135. protected NamespaceSupport fNSBinder;
  136. /** stores all namespace bindings on the current element */
  137. protected NamespaceSupport fLocalNSBinder;
  138. /** symbol table for serialization */
  139. protected SymbolTable fSymbolTable;
  140. protected final static String PREFIX = "NS";
  141. /**
  142. * Controls whether namespace fixup should be performed during
  143. * the serialization.
  144. * NOTE: if this field is set to true the following
  145. * fields need to be initialized: fNSBinder, fLocalNSBinder, fSymbolTable,
  146. * XMLSymbols.EMPTY_STRING, fXmlSymbol, fXmlnsSymbol
  147. */
  148. protected boolean fNamespaces = false;
  149. private boolean fPreserveSpace;
  150. /**
  151. * Constructs a new serializer. The serializer cannot be used without
  152. * calling {@link #setOutputCharStream} or {@link #setOutputByteStream}
  153. * first.
  154. */
  155. public XMLSerializer() {
  156. super( new OutputFormat( Method.XML, null, false ) );
  157. }
  158. /**
  159. * Constructs a new serializer. The serializer cannot be used without
  160. * calling {@link #setOutputCharStream} or {@link #setOutputByteStream}
  161. * first.
  162. */
  163. public XMLSerializer( OutputFormat format ) {
  164. super( format != null ? format : new OutputFormat( Method.XML, null, false ) );
  165. _format.setMethod( Method.XML );
  166. }
  167. /**
  168. * Constructs a new serializer that writes to the specified writer
  169. * using the specified output format. If <tt>format</tt> is null,
  170. * will use a default output format.
  171. *
  172. * @param writer The writer to use
  173. * @param format The output format to use, null for the default
  174. */
  175. public XMLSerializer( Writer writer, OutputFormat format ) {
  176. super( format != null ? format : new OutputFormat( Method.XML, null, false ) );
  177. _format.setMethod( Method.XML );
  178. setOutputCharStream( writer );
  179. }
  180. /**
  181. * Constructs a new serializer that writes to the specified output
  182. * stream using the specified output format. If <tt>format</tt>
  183. * is null, will use a default output format.
  184. *
  185. * @param output The output stream to use
  186. * @param format The output format to use, null for the default
  187. */
  188. public XMLSerializer( OutputStream output, OutputFormat format ) {
  189. super( format != null ? format : new OutputFormat( Method.XML, null, false ) );
  190. _format.setMethod( Method.XML );
  191. setOutputByteStream( output );
  192. }
  193. public void setOutputFormat( OutputFormat format ) {
  194. super.setOutputFormat( format != null ? format : new OutputFormat( Method.XML, null, false ) );
  195. }
  196. /**
  197. * This methods turns on namespace fixup algorithm during
  198. * DOM serialization.
  199. * @see org.w3c.dom.ls.DOMSerializer
  200. *
  201. * @param namespaces
  202. */
  203. public void setNamespaces (boolean namespaces){
  204. fNamespaces = namespaces;
  205. if (fNSBinder == null) {
  206. fNSBinder = new NamespaceSupport();
  207. fLocalNSBinder = new NamespaceSupport();
  208. fSymbolTable = new SymbolTable();
  209. }
  210. }
  211. //-----------------------------------------//
  212. // SAX content handler serializing methods //
  213. //-----------------------------------------//
  214. public void startElement( String namespaceURI, String localName,
  215. String rawName, Attributes attrs )
  216. throws SAXException
  217. {
  218. int i;
  219. boolean preserveSpace;
  220. ElementState state;
  221. String name;
  222. String value;
  223. boolean addNSAttr = false;
  224. if (DEBUG) {
  225. System.out.println("==>startElement("+namespaceURI+","+localName+
  226. ","+rawName+")");
  227. }
  228. try {
  229. if (_printer == null) {
  230. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN, "NoWriterSupplied", null);
  231. throw new IllegalStateException(msg);
  232. }
  233. state = getElementState();
  234. if (isDocumentState()) {
  235. // If this is the root element handle it differently.
  236. // If the first root element in the document, serialize
  237. // the document's DOCTYPE. Space preserving defaults
  238. // to that of the output format.
  239. if (! _started)
  240. startDocument( ( localName == null || localName.length() == 0 ) ? rawName : localName );
  241. } else {
  242. // For any other element, if first in parent, then
  243. // close parent's opening tag and use the parnet's
  244. // space preserving.
  245. if (state.empty)
  246. _printer.printText( '>' );
  247. // Must leave CData section first
  248. if (state.inCData) {
  249. _printer.printText( "]]>" );
  250. state.inCData = false;
  251. }
  252. // Indent this element on a new line if the first
  253. // content of the parent element or immediately
  254. // following an element or a comment
  255. if (_indenting && ! state.preserveSpace &&
  256. ( state.empty || state.afterElement || state.afterComment))
  257. _printer.breakLine();
  258. }
  259. preserveSpace = state.preserveSpace;
  260. //We remove the namespaces from the attributes list so that they will
  261. //be in _prefixes
  262. attrs = extractNamespaces(attrs);
  263. // Do not change the current element state yet.
  264. // This only happens in endElement().
  265. if (rawName == null || rawName.length() == 0) {
  266. if (localName == null) {
  267. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN, "NoName", null);
  268. throw new SAXException(msg);
  269. }
  270. if (namespaceURI != null && ! namespaceURI.equals( "" )) {
  271. String prefix;
  272. prefix = getPrefix( namespaceURI );
  273. if (prefix != null && prefix.length() > 0)
  274. rawName = prefix + ":" + localName;
  275. else
  276. rawName = localName;
  277. } else
  278. rawName = localName;
  279. addNSAttr = true;
  280. }
  281. _printer.printText( '<' );
  282. _printer.printText( rawName );
  283. _printer.indent();
  284. // For each attribute print it's name and value as one part,
  285. // separated with a space so the element can be broken on
  286. // multiple lines.
  287. if (attrs != null) {
  288. for (i = 0 ; i < attrs.getLength() ; ++i) {
  289. _printer.printSpace();
  290. name = attrs.getQName( i );
  291. if (name != null && name.length() == 0) {
  292. String prefix;
  293. String attrURI;
  294. name = attrs.getLocalName( i );
  295. attrURI = attrs.getURI( i );
  296. if (( attrURI != null && attrURI.length() != 0 ) &&
  297. ( namespaceURI == null || namespaceURI.length() == 0 ||
  298. ! attrURI.equals( namespaceURI ) )) {
  299. prefix = getPrefix( attrURI );
  300. if (prefix != null && prefix.length() > 0)
  301. name = prefix + ":" + name;
  302. }
  303. }
  304. value = attrs.getValue( i );
  305. if (value == null)
  306. value = "";
  307. _printer.printText( name );
  308. _printer.printText( "=\"" );
  309. printEscaped( value );
  310. _printer.printText( '"' );
  311. // If the attribute xml:space exists, determine whether
  312. // to preserve spaces in this and child nodes based on
  313. // its value.
  314. if (name.equals( "xml:space" )) {
  315. if (value.equals( "preserve" ))
  316. preserveSpace = true;
  317. else
  318. preserveSpace = _format.getPreserveSpace();
  319. }
  320. }
  321. }
  322. if (_prefixes != null) {
  323. Enumeration keys;
  324. keys = _prefixes.keys();
  325. while (keys.hasMoreElements()) {
  326. _printer.printSpace();
  327. value = (String) keys.nextElement();
  328. name = (String) _prefixes.get( value );
  329. if (name.length() == 0) {
  330. _printer.printText( "xmlns=\"" );
  331. printEscaped( value );
  332. _printer.printText( '"' );
  333. } else {
  334. _printer.printText( "xmlns:" );
  335. _printer.printText( name );
  336. _printer.printText( "=\"" );
  337. printEscaped( value );
  338. _printer.printText( '"' );
  339. }
  340. }
  341. }
  342. // Now it's time to enter a new element state
  343. // with the tag name and space preserving.
  344. // We still do not change the curent element state.
  345. state = enterElementState( namespaceURI, localName, rawName, preserveSpace );
  346. name = ( localName == null || localName.length() == 0 ) ? rawName : namespaceURI + "^" + localName;
  347. state.doCData = _format.isCDataElement( name );
  348. state.unescaped = _format.isNonEscapingElement( name );
  349. } catch (IOException except) {
  350. throw new SAXException( except );
  351. }
  352. }
  353. public void endElement( String namespaceURI, String localName,
  354. String rawName )
  355. throws SAXException
  356. {
  357. try {
  358. endElementIO( namespaceURI, localName, rawName );
  359. } catch (IOException except) {
  360. throw new SAXException( except );
  361. }
  362. }
  363. public void endElementIO( String namespaceURI, String localName,
  364. String rawName )
  365. throws IOException
  366. {
  367. ElementState state;
  368. if (DEBUG) {
  369. System.out.println("==>endElement: " +rawName);
  370. }
  371. // Works much like content() with additions for closing
  372. // an element. Note the different checks for the closed
  373. // element's state and the parent element's state.
  374. _printer.unindent();
  375. state = getElementState();
  376. if (state.empty) {
  377. _printer.printText( "/>" );
  378. } else {
  379. // Must leave CData section first
  380. if (state.inCData)
  381. _printer.printText( "]]>" );
  382. // This element is not empty and that last content was
  383. // another element, so print a line break before that
  384. // last element and this element's closing tag.
  385. if (_indenting && ! state.preserveSpace && (state.afterElement || state.afterComment))
  386. _printer.breakLine();
  387. _printer.printText( "</" );
  388. _printer.printText( state.rawName );
  389. _printer.printText( '>' );
  390. }
  391. // Leave the element state and update that of the parent
  392. // (if we're not root) to not empty and after element.
  393. state = leaveElementState();
  394. state.afterElement = true;
  395. state.afterComment = false;
  396. state.empty = false;
  397. if (isDocumentState())
  398. _printer.flush();
  399. }
  400. //------------------------------------------//
  401. // SAX document handler serializing methods //
  402. //------------------------------------------//
  403. public void startElement( String tagName, AttributeList attrs )
  404. throws SAXException
  405. {
  406. int i;
  407. boolean preserveSpace;
  408. ElementState state;
  409. String name;
  410. String value;
  411. if (DEBUG) {
  412. System.out.println("==>startElement("+tagName+")");
  413. }
  414. try {
  415. if (_printer == null) {
  416. String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN, "NoWriterSupplied", null);
  417. throw new IllegalStateException(msg);
  418. }
  419. state = getElementState();
  420. if (isDocumentState()) {
  421. // If this is the root element handle it differently.
  422. // If the first root element in the document, serialize
  423. // the document's DOCTYPE. Space preserving defaults
  424. // to that of the output format.
  425. if (! _started)
  426. startDocument( tagName );
  427. } else {
  428. // For any other element, if first in parent, then
  429. // close parent's opening tag and use the parnet's
  430. // space preserving.
  431. if (state.empty)
  432. _printer.printText( '>' );
  433. // Must leave CData section first
  434. if (state.inCData) {
  435. _printer.printText( "]]>" );
  436. state.inCData = false;
  437. }
  438. // Indent this element on a new line if the first
  439. // content of the parent element or immediately
  440. // following an element.
  441. if (_indenting && ! state.preserveSpace &&
  442. ( state.empty || state.afterElement || state.afterComment))
  443. _printer.breakLine();
  444. }
  445. preserveSpace = state.preserveSpace;
  446. // Do not change the current element state yet.
  447. // This only happens in endElement().
  448. _printer.printText( '<' );
  449. _printer.printText( tagName );
  450. _printer.indent();
  451. // For each attribute print it's name and value as one part,
  452. // separated with a space so the element can be broken on
  453. // multiple lines.
  454. if (attrs != null) {
  455. for (i = 0 ; i < attrs.getLength() ; ++i) {
  456. _printer.printSpace();
  457. name = attrs.getName( i );
  458. value = attrs.getValue( i );
  459. if (value != null) {
  460. _printer.printText( name );
  461. _printer.printText( "=\"" );
  462. printEscaped( value );
  463. _printer.printText( '"' );
  464. }
  465. // If the attribute xml:space exists, determine whether
  466. // to preserve spaces in this and child nodes based on
  467. // its value.
  468. if (name.equals( "xml:space" )) {
  469. if (value.equals( "preserve" ))
  470. preserveSpace = true;
  471. else
  472. preserveSpace = _format.getPreserveSpace();
  473. }
  474. }
  475. }
  476. // Now it's time to enter a new element state
  477. // with the tag name and space preserving.
  478. // We still do not change the curent element state.
  479. state = enterElementState( null, null, tagName, preserveSpace );
  480. state.doCData = _format.isCDataElement( tagName );
  481. state.unescaped = _format.isNonEscapingElement( tagName );
  482. } catch (IOException except) {
  483. throw new SAXException( except );
  484. }
  485. }
  486. public void endElement( String tagName )
  487. throws SAXException
  488. {
  489. endElement( null, null, tagName );
  490. }
  491. //------------------------------------------//
  492. // Generic node serializing methods methods //
  493. //------------------------------------------//
  494. /**
  495. * Called to serialize the document's DOCTYPE by the root element.
  496. * The document type declaration must name the root element,
  497. * but the root element is only known when that element is serialized,
  498. * and not at the start of the document.
  499. * <p>
  500. * This method will check if it has not been called before ({@link #_started}),
  501. * will serialize the document type declaration, and will serialize all
  502. * pre-root comments and PIs that were accumulated in the document
  503. * (see {@link #serializePreRoot}). Pre-root will be serialized even if
  504. * this is not the first root element of the document.
  505. */
  506. protected void startDocument( String rootTagName )
  507. throws IOException
  508. {
  509. int i;
  510. String dtd;
  511. dtd = _printer.leaveDTD();
  512. if (! _started) {
  513. if (! _format.getOmitXMLDeclaration()) {
  514. StringBuffer buffer;
  515. // Serialize the document declaration appreaing at the head
  516. // of very XML document (unless asked not to).
  517. buffer = new StringBuffer( "<?xml version=\"" );
  518. if (_format.getVersion() != null)
  519. buffer.append( _format.getVersion() );
  520. else
  521. buffer.append( "1.0" );
  522. buffer.append( '"' );
  523. String format_encoding = _format.getEncoding();
  524. if (format_encoding != null) {
  525. buffer.append( " encoding=\"" );
  526. buffer.append( format_encoding );
  527. buffer.append( '"' );
  528. }
  529. if (_format.getStandalone() && _docTypeSystemId == null &&
  530. _docTypePublicId == null)
  531. buffer.append( " standalone=\"yes\"" );
  532. buffer.append( "?>" );
  533. _printer.printText( buffer );
  534. _printer.breakLine();
  535. }
  536. if (! _format.getOmitDocumentType()) {
  537. if (_docTypeSystemId != null) {
  538. // System identifier must be specified to print DOCTYPE.
  539. // If public identifier is specified print 'PUBLIC
  540. // <public> <system>', if not, print 'SYSTEM <system>'.
  541. _printer.printText( "<!DOCTYPE " );
  542. _printer.printText( rootTagName );
  543. if (_docTypePublicId != null) {
  544. _printer.printText( " PUBLIC " );
  545. printDoctypeURL( _docTypePublicId );
  546. if (_indenting) {
  547. _printer.breakLine();
  548. for (i = 0 ; i < 18 + rootTagName.length() ; ++i)
  549. _printer.printText( " " );
  550. } else
  551. _printer.printText( " " );
  552. printDoctypeURL( _docTypeSystemId );
  553. } else {
  554. _printer.printText( " SYSTEM " );
  555. printDoctypeURL( _docTypeSystemId );
  556. }
  557. // If we accumulated any DTD contents while printing.
  558. // this would be the place to print it.
  559. if (dtd != null && dtd.length() > 0) {
  560. _printer.printText( " [" );
  561. printText( dtd, true, true );
  562. _printer.printText( ']' );
  563. }
  564. _printer.printText( ">" );
  565. _printer.breakLine();
  566. } else if (dtd != null && dtd.length() > 0) {
  567. _printer.printText( "<!DOCTYPE " );
  568. _printer.printText( rootTagName );
  569. _printer.printText( " [" );
  570. printText( dtd, true, true );
  571. _printer.printText( "]>" );
  572. _printer.breakLine();
  573. }
  574. }
  575. }
  576. _started = true;
  577. // Always serialize these, even if not te first root element.
  578. serializePreRoot();
  579. }
  580. /**
  581. * Called to serialize a DOM element. Equivalent to calling {@link
  582. * #startElement}, {@link #endElement} and serializing everything
  583. * inbetween, but better optimized.
  584. */
  585. protected void serializeElement( Element elem )
  586. throws IOException
  587. {
  588. Attr attr;
  589. NamedNodeMap attrMap;
  590. int i;
  591. Node child;
  592. ElementState state;
  593. String name;
  594. String value;
  595. String tagName;
  596. String prefix, localUri;
  597. String uri;
  598. if (fNamespaces) {
  599. // local binder stores namespace declaration
  600. // that has been printed out during namespace fixup of
  601. // the current element
  602. fLocalNSBinder.reset();
  603. // add new namespace context
  604. fNSBinder.pushContext();
  605. }
  606. if (DEBUG) {
  607. System.out.println("==>startElement: " +elem.getNodeName() +" ns="+elem.getNamespaceURI());
  608. }
  609. tagName = elem.getTagName();
  610. state = getElementState();
  611. if (isDocumentState()) {
  612. // If this is the root element handle it differently.
  613. // If the first root element in the document, serialize
  614. // the document's DOCTYPE. Space preserving defaults
  615. // to that of the output format.
  616. if (! _started) {
  617. startDocument( tagName);
  618. }
  619. } else {
  620. // For any other element, if first in parent, then
  621. // close parent's opening tag and use the parent's
  622. // space preserving.
  623. if (state.empty)
  624. _printer.printText( '>' );
  625. // Must leave CData section first
  626. if (state.inCData) {
  627. _printer.printText( "]]>" );
  628. state.inCData = false;
  629. }
  630. // Indent this element on a new line if the first
  631. // content of the parent element or immediately
  632. // following an element.
  633. if (_indenting && ! state.preserveSpace &&
  634. ( state.empty || state.afterElement || state.afterComment))
  635. _printer.breakLine();
  636. }
  637. // Do not change the current element state yet.
  638. // This only happens in endElement().
  639. fPreserveSpace = state.preserveSpace;
  640. int length = 0;
  641. attrMap = null;
  642. // retrieve attributes
  643. if (elem.hasAttributes()) {
  644. attrMap = elem.getAttributes();
  645. length = attrMap.getLength();
  646. }
  647. if (!fNamespaces) { // no namespace fixup should be performed
  648. // serialize element name
  649. _printer.printText( '<' );
  650. _printer.printText( tagName );
  651. _printer.indent();
  652. // For each attribute print it's name and value as one part,
  653. // separated with a space so the element can be broken on
  654. // multiple lines.
  655. for ( i = 0 ; i < length ; ++i ) {
  656. attr = (Attr) attrMap.item( i );
  657. name = attr.getName();
  658. value = attr.getValue();
  659. if ( value == null )
  660. value = "";
  661. printAttribute (name, value, attr.getSpecified(), attr);
  662. }
  663. } else { // do namespace fixup
  664. // REVISIT: some optimization could probably be done to avoid traversing
  665. // attributes twice.
  666. //
  667. // ---------------------------------------
  668. // record all valid namespace declarations
  669. // before attempting to fix element's namespace
  670. // ---------------------------------------
  671. for (i = 0;i < length;i++) {
  672. attr = (Attr) attrMap.item( i );
  673. uri = attr.getNamespaceURI();
  674. // check if attribute is a namespace decl
  675. if (uri != null && uri.equals(NamespaceContext.XMLNS_URI)) {
  676. value = attr.getNodeValue();
  677. if (value == null) {
  678. value=XMLSymbols.EMPTY_STRING;
  679. }
  680. if (value.equals(NamespaceContext.XMLNS_URI)) {
  681. if (fDOMErrorHandler != null) {
  682. String msg = DOMMessageFormatter.formatMessage(
  683. DOMMessageFormatter.XML_DOMAIN,"CantBindXMLNS",null );
  684. modifyDOMError(msg, DOMError.SEVERITY_ERROR, attr);
  685. boolean continueProcess = fDOMErrorHandler.handleError(fDOMError);
  686. if (!continueProcess) {
  687. // stop the namespace fixup and validation
  688. throw new RuntimeException(
  689. DOMMessageFormatter.formatMessage(
  690. DOMMessageFormatter.SERIALIZER_DOMAIN,
  691. "SerializationStopped", null));
  692. }
  693. }
  694. } else {
  695. prefix = attr.getPrefix();
  696. prefix = (prefix == null ||
  697. prefix.length() == 0) ? XMLSymbols.EMPTY_STRING :fSymbolTable.addSymbol(prefix);
  698. String localpart = fSymbolTable.addSymbol( attr.getLocalName());
  699. if (prefix == XMLSymbols.PREFIX_XMLNS) { //xmlns:prefix
  700. value = fSymbolTable.addSymbol(value);
  701. // record valid decl
  702. if (value.length() != 0) {
  703. fNSBinder.declarePrefix(localpart, value);
  704. } else {
  705. // REVISIT: issue error on invalid declarations
  706. // xmlns:foo = ""
  707. }
  708. continue;
  709. } else { // xmlns
  710. // empty prefix is always bound ("" or some string)
  711. value = fSymbolTable.addSymbol(value);
  712. fNSBinder.declarePrefix(XMLSymbols.EMPTY_STRING, value);
  713. continue;
  714. }
  715. } // end-else: valid declaration
  716. } // end-if: namespace declaration
  717. } // end-for
  718. //-----------------------
  719. // get element uri/prefix
  720. //-----------------------
  721. uri = elem.getNamespaceURI();
  722. prefix = elem.getPrefix();
  723. //----------------------
  724. // output element name
  725. //----------------------
  726. // REVISIT: this could be removed if we always convert empty string to null
  727. // for the namespaces.
  728. if ((uri !=null && prefix !=null ) && uri.length() == 0 && prefix.length()!=0) {
  729. // uri is an empty string and element has some prefix
  730. // the namespace alg later will fix up the namespace attributes
  731. // remove element prefix
  732. prefix = null;
  733. _printer.printText( '<' );
  734. _printer.printText( elem.getLocalName() );
  735. _printer.indent();
  736. } else {
  737. _printer.printText( '<' );
  738. _printer.printText( tagName );
  739. _printer.indent();
  740. }
  741. // ---------------------------------------------------------
  742. // Fix up namespaces for element: per DOM L3
  743. // Need to consider the following cases:
  744. //
  745. // case 1: <foo:elem xmlns:ns1="myURI" xmlns="default"/>
  746. // Assume "foo", "ns1" are declared on the parent. We should not miss
  747. // redeclaration for both "ns1" and default namespace. To solve this
  748. // we add a local binder that stores declaration only for current element.
  749. // This way we avoid outputing duplicate declarations for the same element
  750. // as well as we are not omitting redeclarations.
  751. //
  752. // case 2: <elem xmlns="" xmlns="default"/>
  753. // We need to bind default namespace to empty string, to be able to
  754. // omit duplicate declarations for the same element
  755. //
  756. // case 3: <xsl:stylesheet xmlns:xsl="http://xsl">
  757. // We create another element body bound to the "http://xsl" namespace
  758. // as well as namespace attribute rebounding xsl to another namespace.
  759. // <xsl:body xmlns:xsl="http://another">
  760. // Need to make sure that the new namespace decl value is changed to
  761. // "http://xsl"
  762. //
  763. // ---------------------------------------------------------
  764. // check if prefix/namespace is correct for current element
  765. // ---------------------------------------------------------
  766. if (uri != null) { // Element has a namespace
  767. uri = fSymbolTable.addSymbol(uri);
  768. prefix = (prefix == null ||
  769. prefix.length() == 0) ? XMLSymbols.EMPTY_STRING :fSymbolTable.addSymbol(prefix);
  770. if (fNSBinder.getURI(prefix) == uri) {
  771. // The xmlns:prefix=namespace or xmlns="default" was declared at parent.
  772. // The binder always stores mapping of empty prefix to "".
  773. // (NOTE: local binder does not store this kind of binding!)
  774. // Thus the case where element was declared with uri="" (with or without a prefix)
  775. // will be covered here.
  776. } else {
  777. // the prefix is either undeclared
  778. // or
  779. // conflict: the prefix is bound to another URI
  780. printNamespaceAttr(prefix, uri);
  781. fLocalNSBinder.declarePrefix(prefix, uri);
  782. fNSBinder.declarePrefix(prefix, uri);
  783. }
  784. } else { // Element has no namespace
  785. if (elem.getLocalName() == null) {
  786. // DOM Level 1 node!
  787. if (fDOMErrorHandler != null) {
  788. String msg = DOMMessageFormatter.formatMessage(
  789. DOMMessageFormatter.DOM_DOMAIN, "NullLocalElementName",
  790. new Object[]{elem.getNodeName()});
  791. modifyDOMError(msg,DOMError.SEVERITY_ERROR, elem);
  792. boolean continueProcess = fDOMErrorHandler.handleError(fDOMError);
  793. // REVISIT: should we terminate upon request?
  794. if (!continueProcess) {
  795. throw new RuntimeException(
  796. DOMMessageFormatter.formatMessage(
  797. DOMMessageFormatter.SERIALIZER_DOMAIN,
  798. "SerializationStopped", null));
  799. }
  800. }
  801. } else { // uri=null and no colon (DOM L2 node)
  802. uri = fNSBinder.getURI(XMLSymbols.EMPTY_STRING);
  803. if (uri !=null && uri.length() > 0) {
  804. // there is a default namespace decl that is bound to
  805. // non-zero length uri, output xmlns=""
  806. printNamespaceAttr(XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING);
  807. fLocalNSBinder.declarePrefix(XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING);
  808. fNSBinder.declarePrefix(XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING);
  809. }
  810. }
  811. }
  812. // -----------------------------------------
  813. // Fix up namespaces for attributes: per DOM L3
  814. // check if prefix/namespace is correct the attributes
  815. // -----------------------------------------
  816. for (i = 0; i < length; i++) {
  817. attr = (Attr) attrMap.item( i );
  818. value = attr.getValue();
  819. name = attr.getNodeName();
  820. uri = attr.getNamespaceURI();
  821. // Fix attribute that was declared with a prefix and namespace=""
  822. if (uri !=null && uri.length() == 0) {
  823. uri=null;
  824. // we must remove prefix for this attribute
  825. name=attr.getLocalName();
  826. }
  827. if (DEBUG) {
  828. System.out.println("==>process attribute: "+attr.getNodeName());
  829. }
  830. // make sure that value is never null.
  831. if (value == null) {
  832. value=XMLSymbols.EMPTY_STRING;
  833. }
  834. if (uri != null) { // attribute has namespace !=null
  835. prefix = attr.getPrefix();
  836. prefix = prefix == null ? XMLSymbols.EMPTY_STRING :fSymbolTable.addSymbol(prefix);
  837. String localpart = fSymbolTable.addSymbol( attr.getLocalName());
  838. // ---------------------------------------------------
  839. // print namespace declarations namespace declarations
  840. // ---------------------------------------------------
  841. if (uri != null && uri.equals(NamespaceContext.XMLNS_URI)) {
  842. // check if we need to output this declaration
  843. prefix = attr.getPrefix();
  844. prefix = (prefix == null ||
  845. prefix.length() == 0) ? XMLSymbols.EMPTY_STRING :fSymbolTable.addSymbol(prefix);
  846. localpart = fSymbolTable.addSymbol( attr.getLocalName());
  847. if (prefix == XMLSymbols.PREFIX_XMLNS) { //xmlns:prefix
  848. localUri = fLocalNSBinder.getURI(localpart); // local prefix mapping
  849. value = fSymbolTable.addSymbol(value);
  850. if (value.length() != 0 ) {
  851. if (localUri == null) {
  852. // declaration was not printed while fixing element namespace binding
  853. printNamespaceAttr(localpart, value);
  854. // case 4: <elem xmlns:xx="foo" xx:attr=""/>
  855. // where attribute is bound to "bar".
  856. // If the xmlns:xx is output here first, later we should not
  857. // redeclare "xx" prefix. Instead we would pick up different prefix
  858. // for the attribute.
  859. // final: <elem xmlns:xx="foo" NS1:attr="" xmlns:NS1="bar"/>
  860. fLocalNSBinder.declarePrefix(localpart, value);
  861. }
  862. } else {
  863. // REVISIT: issue error on invalid declarations
  864. // xmlns:foo = ""
  865. }
  866. continue;
  867. } else { // xmlns
  868. // empty prefix is always bound ("" or some string)
  869. uri = fNSBinder.getURI(XMLSymbols.EMPTY_STRING);
  870. localUri=fLocalNSBinder.getURI(XMLSymbols.EMPTY_STRING);
  871. value = fSymbolTable.addSymbol(value);
  872. if (localUri == null ){
  873. // declaration was not printed while fixing element namespace binding
  874. printNamespaceAttr(XMLSymbols.EMPTY_STRING, value);
  875. // case 4 does not apply here since attributes can't use
  876. // default namespace
  877. }
  878. continue;
  879. }
  880. }
  881. uri = fSymbolTable.addSymbol(uri);
  882. // find if for this prefix a URI was already declared
  883. String declaredURI = fNSBinder.getURI(prefix);
  884. if (prefix == XMLSymbols.EMPTY_STRING || declaredURI != uri) {
  885. // attribute has no prefix (default namespace decl does not apply to attributes)
  886. // OR
  887. // attribute prefix is not declared
  888. // OR
  889. // conflict: attr URI does not match the prefix in scope
  890. name = attr.getNodeName();
  891. // Find if any prefix for attributes namespace URI is available
  892. // in the scope
  893. String declaredPrefix = fNSBinder.getPrefix(uri);
  894. if (declaredPrefix !=null && declaredPrefix !=XMLSymbols.EMPTY_STRING) {
  895. // use the prefix that was found
  896. prefix = declaredPrefix;
  897. name=prefix+":"+localpart;
  898. } else {
  899. if (DEBUG) {
  900. System.out.println("==> cound not find prefix for the attribute: " +prefix);
  901. }
  902. if (prefix != XMLSymbols.EMPTY_STRING && fLocalNSBinder.getURI(prefix) == null) {
  903. // the current prefix is not null and it has no in scope declaration
  904. // use this prefix
  905. } else {
  906. // find a prefix following the pattern "NS" +index (starting at 1)
  907. // make sure this prefix is not declared in the current scope.
  908. int counter = 1;
  909. prefix = fSymbolTable.addSymbol(PREFIX + counter++);
  910. while (fLocalNSBinder.getURI(prefix)!=null) {
  911. prefix = fSymbolTable.addSymbol(PREFIX +counter++);
  912. }
  913. name=prefix+":"+localpart;
  914. }
  915. // add declaration for the new prefix
  916. printNamespaceAttr(prefix, uri);
  917. value = fSymbolTable.addSymbol(value);
  918. fLocalNSBinder.declarePrefix(prefix, value);
  919. fNSBinder.declarePrefix(prefix, uri);
  920. }
  921. // change prefix for this attribute
  922. }
  923. printAttribute (name, (value==null)?XMLSymbols.EMPTY_STRING:value, attr.getSpecified(), attr);
  924. } else { // attribute uri == null
  925. if (attr.getLocalName() == null) {
  926. if (fDOMErrorHandler != null) {
  927. String msg = DOMMessageFormatter.formatMessage(
  928. DOMMessageFormatter.DOM_DOMAIN,
  929. "NullLocalAttrName", new Object[]{attr.getNodeName()});
  930. modifyDOMError(msg, DOMError.SEVERITY_ERROR, attr);
  931. boolean continueProcess = fDOMErrorHandler.handleError(fDOMError);
  932. if (!continueProcess) {
  933. // stop the namespace fixup and validation
  934. throw new RuntimeException(
  935. DOMMessageFormatter.formatMessage(
  936. DOMMessageFormatter.SERIALIZER_DOMAIN,
  937. "SerializationStopped", null));
  938. }
  939. }
  940. printAttribute (name, value, attr.getSpecified(), attr);
  941. } else { // uri=null and no colon
  942. // no fix up is needed: default namespace decl does not
  943. // apply to attributes
  944. printAttribute (name, value, attr.getSpecified(), attr);
  945. }
  946. }
  947. } // end loop for attributes
  948. }// end namespace fixup algorithm
  949. // If element has children, then serialize them, otherwise
  950. // serialize en empty tag.
  951. if (elem.hasChildNodes()) {
  952. // Enter an element state, and serialize the children
  953. // one by one. Finally, end the element.
  954. state = enterElementState( null, null, tagName, fPreserveSpace );
  955. state.doCData = _format.isCDataElement( tagName );
  956. state.unescaped = _format.isNonEscapingElement( tagName );
  957. child = elem.getFirstChild();
  958. while (child != null) {
  959. serializeNode( child );
  960. child = child.getNextSibling();
  961. }
  962. if (fNamespaces) {
  963. fNSBinder.popContext();
  964. }
  965. endElementIO( null, null, tagName );
  966. } else {
  967. if (DEBUG) {
  968. System.out.println("==>endElement: " +elem.getNodeName());
  969. }
  970. if (fNamespaces) {
  971. fNSBinder.popContext();
  972. }
  973. _printer.unindent();
  974. _printer.printText( "/>" );
  975. // After element but parent element is no longer empty.
  976. state.afterElement = true;
  977. state.afterComment = false;
  978. state.empty = false;
  979. if (isDocumentState())
  980. _printer.flush();
  981. }
  982. }
  983. /**
  984. * Serializes a namespace attribute with the given prefix and value for URI.
  985. * In case prefix is empty will serialize default namespace declaration.
  986. *
  987. * @param prefix
  988. * @param uri
  989. * @exception IOException
  990. */
  991. private void printNamespaceAttr(String prefix, String uri) throws IOException{
  992. _printer.printSpace();
  993. if (prefix == XMLSymbols.EMPTY_STRING) {
  994. if (DEBUG) {
  995. System.out.println("=>add xmlns=\""+uri+"\" declaration");
  996. }
  997. _printer.printText( XMLSymbols.PREFIX_XMLNS );
  998. } else {
  999. if (DEBUG) {
  1000. System.out.println("=>add xmlns:"+prefix+"=\""+uri+"\" declaration");
  1001. }
  1002. _printer.printText( "xmlns:"+prefix );
  1003. }
  1004. _printer.printText( "=\"" );
  1005. printEscaped( uri );
  1006. _printer.printText( '"' );
  1007. }
  1008. /**
  1009. * Prints attribute.
  1010. * NOTE: xml:space attribute modifies output format
  1011. *
  1012. * @param name
  1013. * @param value
  1014. * @param isSpecified
  1015. * @exception IOException
  1016. */
  1017. private void printAttribute (String name, String value, boolean isSpecified, Attr attr) throws IOException{
  1018. if (isSpecified || (features & DOMSerializerImpl.DISCARDDEFAULT) == 0) {
  1019. if (fDOMFilter !=null &&
  1020. (fDOMFilter.getWhatToShow() & NodeFilter.SHOW_ATTRIBUTE)!= 0) {
  1021. short code = fDOMFilter.acceptNode(attr);
  1022. switch (code) {
  1023. case NodeFilter.FILTER_REJECT:
  1024. case NodeFilter.FILTER_SKIP: {
  1025. return;
  1026. }
  1027. default: {
  1028. // fall through
  1029. }
  1030. }
  1031. }
  1032. _printer.printSpace();
  1033. _printer.printText( name );
  1034. _printer.printText( "=\"" );
  1035. printEscaped( value );
  1036. _printer.printText( '"' );
  1037. }
  1038. // If the attribute xml:space exists, determine whether
  1039. // to preserve spaces in this and child nodes based on
  1040. // its value.
  1041. if (name.equals( "xml:space" )) {
  1042. if (value.equals( "preserve" ))
  1043. fPreserveSpace = true;
  1044. else
  1045. fPreserveSpace = _format.getPreserveSpace();
  1046. }
  1047. }
  1048. protected String getEntityRef( int ch ) {
  1049. // Encode special XML characters into the equivalent character references.
  1050. // These five are defined by default for all XML documents.
  1051. switch (ch) {
  1052. case '<':
  1053. return "lt";
  1054. case '>':
  1055. return "gt";
  1056. case '"':
  1057. return "quot";
  1058. case '\'':
  1059. return "apos";
  1060. case '&':
  1061. return "amp";
  1062. }
  1063. return null;
  1064. }
  1065. /** Retrieve and remove the namespaces declarations from the list of attributes.
  1066. *
  1067. */
  1068. private Attributes extractNamespaces( Attributes attrs )
  1069. throws SAXException
  1070. {
  1071. AttributesImpl attrsOnly;
  1072. String rawName;
  1073. int i;
  1074. int indexColon;
  1075. String prefix;
  1076. int length;
  1077. if (attrs == null) {
  1078. return null;
  1079. }
  1080. length = attrs.getLength();
  1081. attrsOnly = new AttributesImpl( attrs );
  1082. for (i = length - 1 ; i >= 0 ; --i) {
  1083. rawName = attrsOnly.getQName( i );
  1084. //We have to exclude the namespaces declarations from the attributes
  1085. //Append only when the feature http://xml.org/sax/features/namespace-prefixes"
  1086. //is TRUE
  1087. if (rawName.startsWith( "xmlns" )) {
  1088. if (rawName.length() == 5) {
  1089. startPrefixMapping( "", attrs.getValue( i ) );
  1090. attrsOnly.removeAttribute( i );
  1091. } else if (rawName.charAt(5) == ':') {
  1092. startPrefixMapping(rawName.substring(6), attrs.getValue(i));
  1093. attrsOnly.removeAttribute( i );
  1094. }
  1095. }
  1096. }
  1097. return attrsOnly;
  1098. }
  1099. //
  1100. // Printing attribute value
  1101. //
  1102. protected void printEscaped(String source) throws IOException {
  1103. int length = source.length();
  1104. for (int i = 0; i < length; ++i) {
  1105. int ch = source.charAt(i);
  1106. if (!XMLChar.isValid(ch)) {
  1107. if (++i < length) {
  1108. surrogates(ch, source.charAt(i));
  1109. } else {
  1110. fatalError("The character '" + (char) ch + "' is an invalid XML character");
  1111. }
  1112. continue;
  1113. }
  1114. // escape NL, CR, TAB
  1115. if (ch == '\n' || ch == '\r' || ch == '\t') {
  1116. printHex(ch);
  1117. } else if (ch == '<') {
  1118. _printer.printText("<");
  1119. } else if (ch == '&') {
  1120. _printer.printText("&");
  1121. } else if (ch == '"') {
  1122. _printer.printText(""");
  1123. } else if ((ch >= ' ' && _encodingInfo.isPrintable((char) ch))) {
  1124. _printer.printText((char) ch);
  1125. } else {
  1126. printHex(ch);
  1127. }
  1128. }
  1129. }
  1130. /** print text data */
  1131. protected void printXMLChar( int ch) throws IOException {
  1132. if (ch == '\r') {
  1133. printHex(ch);
  1134. } else if ( ch == '<') {
  1135. _printer.printText("<");
  1136. } else if (ch == '&') {
  1137. _printer.printText("&");
  1138. } else if (ch == '>'){
  1139. // character sequence "]]>" can't appear in content, therefore
  1140. // we should escape '>'
  1141. _printer.printText(">");
  1142. } else if ( ch == '\n' || ch == '\t' ||
  1143. ( ch >= ' ' && _encodingInfo.isPrintable((char)ch))) {
  1144. _printer.printText((char)ch);
  1145. } else {
  1146. printHex(ch);
  1147. }
  1148. }
  1149. protected void printText( String text, boolean preserveSpace, boolean unescaped )
  1150. throws IOException {
  1151. int index;
  1152. char ch;
  1153. int length = text.length();
  1154. if ( preserveSpace ) {
  1155. // Preserving spaces: the text must print exactly as it is,
  1156. // without breaking when spaces appear in the text and without
  1157. // consolidating spaces. If a line terminator is used, a line
  1158. // break will occur.
  1159. for ( index = 0 ; index < length ; ++index ) {
  1160. ch = text.charAt( index );
  1161. if (!XMLChar.isValid(ch)) {
  1162. // check if it is surrogate
  1163. if (++index <length) {
  1164. surrogates(ch, text.charAt(index));
  1165. } else {
  1166. fatalError("The character '"+(char)ch+"' is an invalid XML character");
  1167. }
  1168. continue;
  1169. }
  1170. if ( unescaped ) {
  1171. _printer.printText( ch );
  1172. } else
  1173. printXMLChar( ch );
  1174. }
  1175. } else {
  1176. // Not preserving spaces: print one part at a time, and
  1177. // use spaces between parts to break them into different
  1178. // lines. Spaces at beginning of line will be stripped
  1179. // by printing mechanism. Line terminator is treated
  1180. // no different than other text part.
  1181. for ( index = 0 ; index < length ; ++index ) {
  1182. ch = text.charAt( index );
  1183. if (!XMLChar.isValid(ch)) {
  1184. // check if it is surrogate
  1185. if (++index <length) {
  1186. surrogates(ch, text.charAt(index));
  1187. } else {
  1188. fatalError("The character '"+(char)ch+"' is an invalid XML character");
  1189. }
  1190. continue;
  1191. }
  1192. if ( unescaped )
  1193. _printer.printText( ch );
  1194. else
  1195. printXMLChar( ch);
  1196. }
  1197. }
  1198. }
  1199. protected void printText( char[] chars, int start, int length,
  1200. boolean preserveSpace, boolean unescaped ) throws IOException {
  1201. int index;
  1202. char ch;
  1203. if ( preserveSpace ) {
  1204. // Preserving spaces: the text must print exactly as it is,
  1205. // without breaking when spaces appear in the text and without
  1206. // consolidating spaces. If a line terminator is used, a line
  1207. // break will occur.
  1208. while ( length-- > 0 ) {
  1209. ch = chars[ start ];
  1210. ++start;
  1211. if (!XMLChar.isValid(ch)) {
  1212. // check if it is surrogate
  1213. if (++start <length) {
  1214. surrogates(ch, chars[start]);
  1215. } else {
  1216. fatalError("The character '"+(char)ch+"' is an invalid XML character");
  1217. }
  1218. continue;
  1219. }
  1220. if ( unescaped )
  1221. _printer.printText( ch );
  1222. else
  1223. printXMLChar( ch );
  1224. }
  1225. } else {
  1226. // Not preserving spaces: print one part at a time, and
  1227. // use spaces between parts to break them into different
  1228. // lines. Spaces at beginning of line will be stripped
  1229. // by printing mechanism. Line terminator is treated
  1230. // no different than other text part.
  1231. while ( length-- > 0 ) {
  1232. ch = chars[ start ];
  1233. ++start;
  1234. if (!XMLChar.isValid(ch)) {
  1235. // check if it is surrogate
  1236. if (++start <length) {
  1237. surrogates(ch, chars[start]);
  1238. } else {
  1239. fatalError("The character '"+(char)ch+"' is an invalid XML character");
  1240. }
  1241. continue;
  1242. }
  1243. if ( unescaped )
  1244. _printer.printText( ch );
  1245. else
  1246. printXMLChar( ch );
  1247. }
  1248. }
  1249. }
  1250. /**
  1251. * DOM Level 3:
  1252. * Check a node to determine if it contains unbound namespace prefixes.
  1253. *
  1254. * @param node The node to check for unbound namespace prefices
  1255. */
  1256. protected void checkUnboundNamespacePrefixedNode (Node node) throws IOException{
  1257. if (fNamespaces) {
  1258. if (DEBUG) {
  1259. System.out.println("==>serializeNode("+node.getNodeName()+") [Entity Reference - Namespaces on]");
  1260. System.out.println("==>Declared Prefix Count: " + fNSBinder.getDeclaredPrefixCount());
  1261. System.out.println("==>Node Name: " + node.getNodeName());
  1262. System.out.println("==>First Child Node Name: " + node.getFirstChild().getNodeName());
  1263. System.out.println("==>First Child Node Prefix: " + node.getFirstChild().getPrefix());
  1264. System.out.println("==>First Child Node NamespaceURI: " + node.getFirstChild().getNamespaceURI());
  1265. }
  1266. Node child, next;
  1267. for (child = node.getFirstChild(); child != null; child = next) {
  1268. next = child.getNextSibling();
  1269. if (DEBUG) {
  1270. System.out.println("==>serializeNode("+child.getNodeName()+") [Child Node]");
  1271. System.out.println("==>serializeNode("+child.getPrefix()+") [Child Node Prefix]");
  1272. }
  1273. //If a NamespaceURI is not declared for the current
  1274. //node's prefix, raise a fatal error.
  1275. String prefix = child.getPrefix();
  1276. if (fNSBinder.getURI(prefix) == null && prefix != null) {
  1277. fatalError("The replacement text of the entity node '"
  1278. + node.getNodeName()
  1279. + "' contains an element node '"
  1280. + child.getNodeName()
  1281. + "' with an undeclared prefix '"
  1282. + prefix + "'.");
  1283. }
  1284. if (child.getNodeType() == Node.ELEMENT_NODE) {
  1285. NamedNodeMap attrs = child.getAttributes();
  1286. for (int i = 0; i< attrs.getLength(); i++ ) {
  1287. String attrPrefix = attrs.item(i).getPrefix();
  1288. if (fNSBinder.getURI(attrPrefix) == null && attrPrefix != null) {
  1289. fatalError("The replacement text of the entity node '"
  1290. + node.getNodeName()
  1291. + "' contains an element node '"
  1292. + child.getNodeName()
  1293. + "' with an attribute '"
  1294. + attrs.item(i).getNodeName()
  1295. + "' an undeclared prefix '"
  1296. + attrPrefix + "'.");
  1297. }
  1298. }
  1299. }
  1300. if (child.hasChildNodes()) {
  1301. checkUnboundNamespacePrefixedNode(child);
  1302. }
  1303. }
  1304. }
  1305. }
  1306. public boolean reset() {
  1307. super.reset();
  1308. if (fNSBinder != null){
  1309. fNSBinder.reset();
  1310. // during serialization always have a mapping to empty string
  1311. // so we assume there is a declaration.
  1312. fNSBinder.declarePrefix(XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING);
  1313. }
  1314. return true;
  1315. }
  1316. }