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