0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: // Sep 14, 2000:
0019: // Fixed problem with namespace handling. Contributed by
0020: // David Blondeau <blondeau@intalio.com>
0021: // Sep 14, 2000:
0022: // Fixed serializer to report IO exception directly, instead at
0023: // the end of document processing.
0024: // Reported by Patrick Higgins <phiggins@transzap.com>
0025: // Aug 21, 2000:
0026: // Fixed bug in startDocument not calling prepare.
0027: // Reported by Mikael Staldal <d96-mst-ingen-reklam@d.kth.se>
0028: // Aug 21, 2000:
0029: // Added ability to omit DOCTYPE declaration.
0030: package org.apache.xml.serialize;
0031:
0032: import java.io.IOException;
0033: import java.io.OutputStream;
0034: import java.io.Writer;
0035: import java.util.Enumeration;
0036:
0037: import org.apache.xerces.dom.DOMMessageFormatter;
0038: import org.apache.xerces.util.NamespaceSupport;
0039: import org.apache.xerces.util.SymbolTable;
0040: import org.apache.xerces.util.XMLChar;
0041: import org.apache.xerces.util.XMLSymbols;
0042: import org.apache.xerces.xni.NamespaceContext;
0043: import org.w3c.dom.Attr;
0044: import org.w3c.dom.DOMError;
0045: import org.w3c.dom.Element;
0046: import org.w3c.dom.NamedNodeMap;
0047: import org.w3c.dom.Node;
0048: import org.w3c.dom.traversal.NodeFilter;
0049: import org.xml.sax.AttributeList;
0050: import org.xml.sax.Attributes;
0051: import org.xml.sax.SAXException;
0052: import org.xml.sax.helpers.AttributesImpl;
0053:
0054: /**
0055: * Implements an XML serializer supporting both DOM and SAX pretty
0056: * serializing. For usage instructions see {@link Serializer}.
0057: * <p>
0058: * If an output stream is used, the encoding is taken from the
0059: * output format (defaults to <tt>UTF-8</tt>). If a writer is
0060: * used, make sure the writer uses the same encoding (if applies)
0061: * as specified in the output format.
0062: * <p>
0063: * The serializer supports both DOM and SAX. SAX serializing is done by firing
0064: * SAX events and using the serializer as a document handler. DOM serializing is done
0065: * by calling {@link #serialize(Document)} or by using DOM Level 3
0066: * {@link org.w3c.dom.ls.LSSerializer} and
0067: * serializing with {@link org.w3c.dom.ls.LSSerializer#write},
0068: * {@link org.w3c.dom.ls.LSSerializer#writeToString}.
0069: * <p>
0070: * If an I/O exception occurs while serializing, the serializer
0071: * will not throw an exception directly, but only throw it
0072: * at the end of serializing (either DOM or SAX's {@link
0073: * org.xml.sax.DocumentHandler#endDocument}.
0074: * <p>
0075: * For elements that are not specified as whitespace preserving,
0076: * the serializer will potentially break long text lines at space
0077: * boundaries, indent lines, and serialize elements on separate
0078: * lines. Line terminators will be regarded as spaces, and
0079: * spaces at beginning of line will be stripped.
0080: *
0081: * @deprecated This class was deprecated in Xerces 2.9.0. It is recommended
0082: * that new applications use the DOM Level 3 LSSerializer or JAXP's Transformation
0083: * API for XML (TrAX) for serializing XML. See the Xerces documentation for more
0084: * information.
0085: * @author <a href="mailto:arkin@intalio.com">Assaf Arkin</a>
0086: * @author <a href="mailto:rahul.srivastava@sun.com">Rahul Srivastava</a>
0087: * @author Elena Litani IBM
0088: * @version $Revision: 476047 $ $Date: 2006-11-16 23:27:45 -0500 (Thu, 16 Nov 2006) $
0089: * @see Serializer
0090: */
0091: public class XMLSerializer extends BaseMarkupSerializer {
0092:
0093: //
0094: // constants
0095: //
0096:
0097: protected static final boolean DEBUG = false;
0098:
0099: //
0100: // data
0101: //
0102:
0103: //
0104: // DOM Level 3 implementation: variables intialized in DOMSerializerImpl
0105: //
0106:
0107: /** stores namespaces in scope */
0108: protected NamespaceSupport fNSBinder;
0109:
0110: /** stores all namespace bindings on the current element */
0111: protected NamespaceSupport fLocalNSBinder;
0112:
0113: /** symbol table for serialization */
0114: protected SymbolTable fSymbolTable;
0115:
0116: protected final static String PREFIX = "NS";
0117:
0118: /**
0119: * Controls whether namespace fixup should be performed during
0120: * the serialization.
0121: * NOTE: if this field is set to true the following
0122: * fields need to be initialized: fNSBinder, fLocalNSBinder, fSymbolTable,
0123: * XMLSymbols.EMPTY_STRING, fXmlSymbol, fXmlnsSymbol
0124: */
0125: protected boolean fNamespaces = false;
0126:
0127: /**
0128: * Controls whether namespace prefixes will be printed out during serialization
0129: */
0130: protected boolean fNamespacePrefixes = true;
0131:
0132: private boolean fPreserveSpace;
0133:
0134: /**
0135: * Constructs a new serializer. The serializer cannot be used without
0136: * calling {@link #setOutputCharStream} or {@link #setOutputByteStream}
0137: * first.
0138: */
0139: public XMLSerializer() {
0140: super (new OutputFormat(Method.XML, null, false));
0141: }
0142:
0143: /**
0144: * Constructs a new serializer. The serializer cannot be used without
0145: * calling {@link #setOutputCharStream} or {@link #setOutputByteStream}
0146: * first.
0147: */
0148: public XMLSerializer(OutputFormat format) {
0149: super (format != null ? format : new OutputFormat(Method.XML,
0150: null, false));
0151: _format.setMethod(Method.XML);
0152: }
0153:
0154: /**
0155: * Constructs a new serializer that writes to the specified writer
0156: * using the specified output format. If <tt>format</tt> is null,
0157: * will use a default output format.
0158: *
0159: * @param writer The writer to use
0160: * @param format The output format to use, null for the default
0161: */
0162: public XMLSerializer(Writer writer, OutputFormat format) {
0163: super (format != null ? format : new OutputFormat(Method.XML,
0164: null, false));
0165: _format.setMethod(Method.XML);
0166: setOutputCharStream(writer);
0167: }
0168:
0169: /**
0170: * Constructs a new serializer that writes to the specified output
0171: * stream using the specified output format. If <tt>format</tt>
0172: * is null, will use a default output format.
0173: *
0174: * @param output The output stream to use
0175: * @param format The output format to use, null for the default
0176: */
0177: public XMLSerializer(OutputStream output, OutputFormat format) {
0178: super (format != null ? format : new OutputFormat(Method.XML,
0179: null, false));
0180: _format.setMethod(Method.XML);
0181: setOutputByteStream(output);
0182: }
0183:
0184: public void setOutputFormat(OutputFormat format) {
0185: super .setOutputFormat(format != null ? format
0186: : new OutputFormat(Method.XML, null, false));
0187: }
0188:
0189: /**
0190: * This methods turns on namespace fixup algorithm during
0191: * DOM serialization.
0192: * @see org.w3c.dom.ls.LSSerializer
0193: *
0194: * @param namespaces
0195: */
0196: public void setNamespaces(boolean namespaces) {
0197: fNamespaces = namespaces;
0198: if (fNSBinder == null) {
0199: fNSBinder = new NamespaceSupport();
0200: fLocalNSBinder = new NamespaceSupport();
0201: fSymbolTable = new SymbolTable();
0202: }
0203: }
0204:
0205: //-----------------------------------------//
0206: // SAX content handler serializing methods //
0207: //-----------------------------------------//
0208:
0209: public void startElement(String namespaceURI, String localName,
0210: String rawName, Attributes attrs) throws SAXException {
0211: int i;
0212: boolean preserveSpace;
0213: ElementState state;
0214: String name;
0215: String value;
0216:
0217: if (DEBUG) {
0218: System.out.println("==>startElement(" + namespaceURI + ","
0219: + localName + "," + rawName + ")");
0220: }
0221:
0222: try {
0223: if (_printer == null) {
0224: String msg = DOMMessageFormatter.formatMessage(
0225: DOMMessageFormatter.SERIALIZER_DOMAIN,
0226: "NoWriterSupplied", null);
0227: throw new IllegalStateException(msg);
0228: }
0229:
0230: state = getElementState();
0231: if (isDocumentState()) {
0232: // If this is the root element handle it differently.
0233: // If the first root element in the document, serialize
0234: // the document's DOCTYPE. Space preserving defaults
0235: // to that of the output format.
0236: if (!_started)
0237: startDocument((localName == null || localName
0238: .length() == 0) ? rawName : localName);
0239: } else {
0240: // For any other element, if first in parent, then
0241: // close parent's opening tag and use the parnet's
0242: // space preserving.
0243: if (state.empty)
0244: _printer.printText('>');
0245: // Must leave CData section first
0246: if (state.inCData) {
0247: _printer.printText("]]>");
0248: state.inCData = false;
0249: }
0250: // Indent this element on a new line if the first
0251: // content of the parent element or immediately
0252: // following an element or a comment
0253: if (_indenting
0254: && !state.preserveSpace
0255: && (state.empty || state.afterElement || state.afterComment))
0256: _printer.breakLine();
0257: }
0258: preserveSpace = state.preserveSpace;
0259:
0260: //We remove the namespaces from the attributes list so that they will
0261: //be in _prefixes
0262: attrs = extractNamespaces(attrs);
0263:
0264: // Do not change the current element state yet.
0265: // This only happens in endElement().
0266: if (rawName == null || rawName.length() == 0) {
0267: if (localName == null) {
0268: String msg = DOMMessageFormatter.formatMessage(
0269: DOMMessageFormatter.SERIALIZER_DOMAIN,
0270: "NoName", null);
0271: throw new SAXException(msg);
0272: }
0273: if (namespaceURI != null && !namespaceURI.equals("")) {
0274: String prefix;
0275: prefix = getPrefix(namespaceURI);
0276: if (prefix != null && prefix.length() > 0) {
0277: rawName = prefix + ":" + localName;
0278: } else {
0279: rawName = localName;
0280: }
0281: } else {
0282: rawName = localName;
0283: }
0284: }
0285:
0286: _printer.printText('<');
0287: _printer.printText(rawName);
0288: _printer.indent();
0289:
0290: // For each attribute print it's name and value as one part,
0291: // separated with a space so the element can be broken on
0292: // multiple lines.
0293: if (attrs != null) {
0294: for (i = 0; i < attrs.getLength(); ++i) {
0295: _printer.printSpace();
0296:
0297: name = attrs.getQName(i);
0298: if (name != null && name.length() == 0) {
0299: String prefix;
0300: String attrURI;
0301:
0302: name = attrs.getLocalName(i);
0303: attrURI = attrs.getURI(i);
0304: if ((attrURI != null && attrURI.length() != 0)
0305: && (namespaceURI == null
0306: || namespaceURI.length() == 0 || !attrURI
0307: .equals(namespaceURI))) {
0308: prefix = getPrefix(attrURI);
0309: if (prefix != null && prefix.length() > 0)
0310: name = prefix + ":" + name;
0311: }
0312: }
0313:
0314: value = attrs.getValue(i);
0315: if (value == null)
0316: value = "";
0317: _printer.printText(name);
0318: _printer.printText("=\"");
0319: printEscaped(value);
0320: _printer.printText('"');
0321:
0322: // If the attribute xml:space exists, determine whether
0323: // to preserve spaces in this and child nodes based on
0324: // its value.
0325: if (name.equals("xml:space")) {
0326: if (value.equals("preserve"))
0327: preserveSpace = true;
0328: else
0329: preserveSpace = _format.getPreserveSpace();
0330: }
0331: }
0332: }
0333:
0334: if (_prefixes != null) {
0335: Enumeration keys;
0336:
0337: keys = _prefixes.keys();
0338: while (keys.hasMoreElements()) {
0339: _printer.printSpace();
0340: value = (String) keys.nextElement();
0341: name = (String) _prefixes.get(value);
0342: if (name.length() == 0) {
0343: _printer.printText("xmlns=\"");
0344: printEscaped(value);
0345: _printer.printText('"');
0346: } else {
0347: _printer.printText("xmlns:");
0348: _printer.printText(name);
0349: _printer.printText("=\"");
0350: printEscaped(value);
0351: _printer.printText('"');
0352: }
0353: }
0354: }
0355:
0356: // Now it's time to enter a new element state
0357: // with the tag name and space preserving.
0358: // We still do not change the curent element state.
0359: state = enterElementState(namespaceURI, localName, rawName,
0360: preserveSpace);
0361: name = (localName == null || localName.length() == 0) ? rawName
0362: : namespaceURI + "^" + localName;
0363: state.doCData = _format.isCDataElement(name);
0364: state.unescaped = _format.isNonEscapingElement(name);
0365: } catch (IOException except) {
0366: throw new SAXException(except);
0367: }
0368: }
0369:
0370: public void endElement(String namespaceURI, String localName,
0371: String rawName) throws SAXException {
0372: try {
0373: endElementIO(namespaceURI, localName, rawName);
0374: } catch (IOException except) {
0375: throw new SAXException(except);
0376: }
0377: }
0378:
0379: public void endElementIO(String namespaceURI, String localName,
0380: String rawName) throws IOException {
0381: ElementState state;
0382: if (DEBUG) {
0383: System.out.println("==>endElement: " + rawName);
0384: }
0385: // Works much like content() with additions for closing
0386: // an element. Note the different checks for the closed
0387: // element's state and the parent element's state.
0388: _printer.unindent();
0389: state = getElementState();
0390: if (state.empty) {
0391: _printer.printText("/>");
0392: } else {
0393: // Must leave CData section first
0394: if (state.inCData)
0395: _printer.printText("]]>");
0396: // This element is not empty and that last content was
0397: // another element, so print a line break before that
0398: // last element and this element's closing tag.
0399: if (_indenting && !state.preserveSpace
0400: && (state.afterElement || state.afterComment))
0401: _printer.breakLine();
0402: _printer.printText("</");
0403: _printer.printText(state.rawName);
0404: _printer.printText('>');
0405: }
0406: // Leave the element state and update that of the parent
0407: // (if we're not root) to not empty and after element.
0408: state = leaveElementState();
0409: state.afterElement = true;
0410: state.afterComment = false;
0411: state.empty = false;
0412: if (isDocumentState())
0413: _printer.flush();
0414: }
0415:
0416: //------------------------------------------//
0417: // SAX document handler serializing methods //
0418: //------------------------------------------//
0419:
0420: public void startElement(String tagName, AttributeList attrs)
0421: throws SAXException {
0422: int i;
0423: boolean preserveSpace;
0424: ElementState state;
0425: String name;
0426: String value;
0427:
0428: if (DEBUG) {
0429: System.out.println("==>startElement(" + tagName + ")");
0430: }
0431:
0432: try {
0433: if (_printer == null) {
0434: String msg = DOMMessageFormatter.formatMessage(
0435: DOMMessageFormatter.SERIALIZER_DOMAIN,
0436: "NoWriterSupplied", null);
0437: throw new IllegalStateException(msg);
0438: }
0439:
0440: state = getElementState();
0441: if (isDocumentState()) {
0442: // If this is the root element handle it differently.
0443: // If the first root element in the document, serialize
0444: // the document's DOCTYPE. Space preserving defaults
0445: // to that of the output format.
0446: if (!_started)
0447: startDocument(tagName);
0448: } else {
0449: // For any other element, if first in parent, then
0450: // close parent's opening tag and use the parnet's
0451: // space preserving.
0452: if (state.empty)
0453: _printer.printText('>');
0454: // Must leave CData section first
0455: if (state.inCData) {
0456: _printer.printText("]]>");
0457: state.inCData = false;
0458: }
0459: // Indent this element on a new line if the first
0460: // content of the parent element or immediately
0461: // following an element.
0462: if (_indenting
0463: && !state.preserveSpace
0464: && (state.empty || state.afterElement || state.afterComment))
0465: _printer.breakLine();
0466: }
0467: preserveSpace = state.preserveSpace;
0468:
0469: // Do not change the current element state yet.
0470: // This only happens in endElement().
0471:
0472: _printer.printText('<');
0473: _printer.printText(tagName);
0474: _printer.indent();
0475:
0476: // For each attribute print it's name and value as one part,
0477: // separated with a space so the element can be broken on
0478: // multiple lines.
0479: if (attrs != null) {
0480: for (i = 0; i < attrs.getLength(); ++i) {
0481: _printer.printSpace();
0482: name = attrs.getName(i);
0483: value = attrs.getValue(i);
0484: if (value != null) {
0485: _printer.printText(name);
0486: _printer.printText("=\"");
0487: printEscaped(value);
0488: _printer.printText('"');
0489: }
0490:
0491: // If the attribute xml:space exists, determine whether
0492: // to preserve spaces in this and child nodes based on
0493: // its value.
0494: if (name.equals("xml:space")) {
0495: if (value.equals("preserve"))
0496: preserveSpace = true;
0497: else
0498: preserveSpace = _format.getPreserveSpace();
0499: }
0500: }
0501: }
0502: // Now it's time to enter a new element state
0503: // with the tag name and space preserving.
0504: // We still do not change the curent element state.
0505: state = enterElementState(null, null, tagName,
0506: preserveSpace);
0507: state.doCData = _format.isCDataElement(tagName);
0508: state.unescaped = _format.isNonEscapingElement(tagName);
0509: } catch (IOException except) {
0510: throw new SAXException(except);
0511: }
0512:
0513: }
0514:
0515: public void endElement(String tagName) throws SAXException {
0516: endElement(null, null, tagName);
0517: }
0518:
0519: //------------------------------------------//
0520: // Generic node serializing methods methods //
0521: //------------------------------------------//
0522:
0523: /**
0524: * Called to serialize the document's DOCTYPE by the root element.
0525: * The document type declaration must name the root element,
0526: * but the root element is only known when that element is serialized,
0527: * and not at the start of the document.
0528: * <p>
0529: * This method will check if it has not been called before ({@link #_started}),
0530: * will serialize the document type declaration, and will serialize all
0531: * pre-root comments and PIs that were accumulated in the document
0532: * (see {@link #serializePreRoot}). Pre-root will be serialized even if
0533: * this is not the first root element of the document.
0534: */
0535: protected void startDocument(String rootTagName) throws IOException {
0536: int i;
0537: String dtd;
0538:
0539: dtd = _printer.leaveDTD();
0540: if (!_started) {
0541:
0542: if (!_format.getOmitXMLDeclaration()) {
0543: StringBuffer buffer;
0544:
0545: // Serialize the document declaration appreaing at the head
0546: // of very XML document (unless asked not to).
0547: buffer = new StringBuffer("<?xml version=\"");
0548: if (_format.getVersion() != null)
0549: buffer.append(_format.getVersion());
0550: else
0551: buffer.append("1.0");
0552: buffer.append('"');
0553: String format_encoding = _format.getEncoding();
0554: if (format_encoding != null) {
0555: buffer.append(" encoding=\"");
0556: buffer.append(format_encoding);
0557: buffer.append('"');
0558: }
0559: if (_format.getStandalone() && _docTypeSystemId == null
0560: && _docTypePublicId == null)
0561: buffer.append(" standalone=\"yes\"");
0562: buffer.append("?>");
0563: _printer.printText(buffer);
0564: _printer.breakLine();
0565: }
0566:
0567: if (!_format.getOmitDocumentType()) {
0568: if (_docTypeSystemId != null) {
0569: // System identifier must be specified to print DOCTYPE.
0570: // If public identifier is specified print 'PUBLIC
0571: // <public> <system>', if not, print 'SYSTEM <system>'.
0572: _printer.printText("<!DOCTYPE ");
0573: _printer.printText(rootTagName);
0574: if (_docTypePublicId != null) {
0575: _printer.printText(" PUBLIC ");
0576: printDoctypeURL(_docTypePublicId);
0577: if (_indenting) {
0578: _printer.breakLine();
0579: for (i = 0; i < 18 + rootTagName.length(); ++i)
0580: _printer.printText(" ");
0581: } else
0582: _printer.printText(" ");
0583: printDoctypeURL(_docTypeSystemId);
0584: } else {
0585: _printer.printText(" SYSTEM ");
0586: printDoctypeURL(_docTypeSystemId);
0587: }
0588:
0589: // If we accumulated any DTD contents while printing.
0590: // this would be the place to print it.
0591: if (dtd != null && dtd.length() > 0) {
0592: _printer.printText(" [");
0593: printText(dtd, true, true);
0594: _printer.printText(']');
0595: }
0596:
0597: _printer.printText(">");
0598: _printer.breakLine();
0599: } else if (dtd != null && dtd.length() > 0) {
0600: _printer.printText("<!DOCTYPE ");
0601: _printer.printText(rootTagName);
0602: _printer.printText(" [");
0603: printText(dtd, true, true);
0604: _printer.printText("]>");
0605: _printer.breakLine();
0606: }
0607: }
0608: }
0609: _started = true;
0610: // Always serialize these, even if not te first root element.
0611: serializePreRoot();
0612: }
0613:
0614: /**
0615: * Called to serialize a DOM element. Equivalent to calling {@link
0616: * #startElement}, {@link #endElement} and serializing everything
0617: * inbetween, but better optimized.
0618: */
0619: protected void serializeElement(Element elem) throws IOException {
0620: Attr attr;
0621: NamedNodeMap attrMap;
0622: int i;
0623: Node child;
0624: ElementState state;
0625: String name;
0626: String value;
0627: String tagName;
0628:
0629: String prefix, localUri;
0630: String uri;
0631: if (fNamespaces) {
0632: // local binder stores namespace declaration
0633: // that has been printed out during namespace fixup of
0634: // the current element
0635: fLocalNSBinder.reset();
0636:
0637: // add new namespace context
0638: fNSBinder.pushContext();
0639: }
0640:
0641: if (DEBUG) {
0642: System.out.println("==>startElement: " + elem.getNodeName()
0643: + " ns=" + elem.getNamespaceURI());
0644: }
0645: tagName = elem.getTagName();
0646: state = getElementState();
0647: if (isDocumentState()) {
0648: // If this is the root element handle it differently.
0649: // If the first root element in the document, serialize
0650: // the document's DOCTYPE. Space preserving defaults
0651: // to that of the output format.
0652:
0653: if (!_started) {
0654: startDocument(tagName);
0655: }
0656: } else {
0657: // For any other element, if first in parent, then
0658: // close parent's opening tag and use the parent's
0659: // space preserving.
0660: if (state.empty)
0661: _printer.printText('>');
0662: // Must leave CData section first
0663: if (state.inCData) {
0664: _printer.printText("]]>");
0665: state.inCData = false;
0666: }
0667: // Indent this element on a new line if the first
0668: // content of the parent element or immediately
0669: // following an element.
0670: if (_indenting
0671: && !state.preserveSpace
0672: && (state.empty || state.afterElement || state.afterComment))
0673: _printer.breakLine();
0674: }
0675:
0676: // Do not change the current element state yet.
0677: // This only happens in endElement().
0678: fPreserveSpace = state.preserveSpace;
0679:
0680: int length = 0;
0681: attrMap = null;
0682: // retrieve attributes
0683: if (elem.hasAttributes()) {
0684: attrMap = elem.getAttributes();
0685: length = attrMap.getLength();
0686: }
0687:
0688: if (!fNamespaces) { // no namespace fixup should be performed
0689:
0690: // serialize element name
0691: _printer.printText('<');
0692: _printer.printText(tagName);
0693: _printer.indent();
0694:
0695: // For each attribute print it's name and value as one part,
0696: // separated with a space so the element can be broken on
0697: // multiple lines.
0698: for (i = 0; i < length; ++i) {
0699: attr = (Attr) attrMap.item(i);
0700: name = attr.getName();
0701: value = attr.getValue();
0702: if (value == null)
0703: value = "";
0704: printAttribute(name, value, attr.getSpecified(), attr);
0705: }
0706: } else { // do namespace fixup
0707:
0708: // REVISIT: some optimization could probably be done to avoid traversing
0709: // attributes twice.
0710: //
0711:
0712: // ---------------------------------------
0713: // record all valid namespace declarations
0714: // before attempting to fix element's namespace
0715: // ---------------------------------------
0716:
0717: for (i = 0; i < length; i++) {
0718:
0719: attr = (Attr) attrMap.item(i);
0720: uri = attr.getNamespaceURI();
0721: // check if attribute is a namespace decl
0722: if (uri != null
0723: && uri.equals(NamespaceContext.XMLNS_URI)) {
0724:
0725: value = attr.getNodeValue();
0726: if (value == null) {
0727: value = XMLSymbols.EMPTY_STRING;
0728: }
0729:
0730: if (value.equals(NamespaceContext.XMLNS_URI)) {
0731: if (fDOMErrorHandler != null) {
0732: String msg = DOMMessageFormatter
0733: .formatMessage(
0734: DOMMessageFormatter.XML_DOMAIN,
0735: "CantBindXMLNS", null);
0736: modifyDOMError(msg,
0737: DOMError.SEVERITY_ERROR, null, attr);
0738: boolean continueProcess = fDOMErrorHandler
0739: .handleError(fDOMError);
0740: if (!continueProcess) {
0741: // stop the namespace fixup and validation
0742: throw new RuntimeException(
0743: DOMMessageFormatter
0744: .formatMessage(
0745: DOMMessageFormatter.SERIALIZER_DOMAIN,
0746: "SerializationStopped",
0747: null));
0748: }
0749: }
0750: } else {
0751: prefix = attr.getPrefix();
0752: prefix = (prefix == null || prefix.length() == 0) ? XMLSymbols.EMPTY_STRING
0753: : fSymbolTable.addSymbol(prefix);
0754: String localpart = fSymbolTable.addSymbol(attr
0755: .getLocalName());
0756: if (prefix == XMLSymbols.PREFIX_XMLNS) { //xmlns:prefix
0757: value = fSymbolTable.addSymbol(value);
0758: // record valid decl
0759: if (value.length() != 0) {
0760: fNSBinder.declarePrefix(localpart,
0761: value);
0762: } else {
0763: // REVISIT: issue error on invalid declarations
0764: // xmlns:foo = ""
0765: }
0766: continue;
0767: }
0768: // xmlns --- empty prefix is always bound ("" or some string)
0769: value = fSymbolTable.addSymbol(value);
0770: fNSBinder.declarePrefix(
0771: XMLSymbols.EMPTY_STRING, value);
0772: continue;
0773: } // end-else: valid declaration
0774: } // end-if: namespace declaration
0775: } // end-for
0776:
0777: //-----------------------
0778: // get element uri/prefix
0779: //-----------------------
0780: uri = elem.getNamespaceURI();
0781: prefix = elem.getPrefix();
0782:
0783: //----------------------
0784: // output element name
0785: //----------------------
0786: // REVISIT: this could be removed if we always convert empty string to null
0787: // for the namespaces.
0788: if ((uri != null && prefix != null) && uri.length() == 0
0789: && prefix.length() != 0) {
0790: // uri is an empty string and element has some prefix
0791: // the namespace alg later will fix up the namespace attributes
0792: // remove element prefix
0793: prefix = null;
0794: _printer.printText('<');
0795: _printer.printText(elem.getLocalName());
0796: _printer.indent();
0797: } else {
0798: _printer.printText('<');
0799: _printer.printText(tagName);
0800: _printer.indent();
0801: }
0802:
0803: // ---------------------------------------------------------
0804: // Fix up namespaces for element: per DOM L3
0805: // Need to consider the following cases:
0806: //
0807: // case 1: <foo:elem xmlns:ns1="myURI" xmlns="default"/>
0808: // Assume "foo", "ns1" are declared on the parent. We should not miss
0809: // redeclaration for both "ns1" and default namespace. To solve this
0810: // we add a local binder that stores declaration only for current element.
0811: // This way we avoid outputing duplicate declarations for the same element
0812: // as well as we are not omitting redeclarations.
0813: //
0814: // case 2: <elem xmlns="" xmlns="default"/>
0815: // We need to bind default namespace to empty string, to be able to
0816: // omit duplicate declarations for the same element
0817: //
0818: // case 3: <xsl:stylesheet xmlns:xsl="http://xsl">
0819: // We create another element body bound to the "http://xsl" namespace
0820: // as well as namespace attribute rebounding xsl to another namespace.
0821: // <xsl:body xmlns:xsl="http://another">
0822: // Need to make sure that the new namespace decl value is changed to
0823: // "http://xsl"
0824: //
0825: // ---------------------------------------------------------
0826: // check if prefix/namespace is correct for current element
0827: // ---------------------------------------------------------
0828:
0829: if (uri != null) { // Element has a namespace
0830: uri = fSymbolTable.addSymbol(uri);
0831: prefix = (prefix == null || prefix.length() == 0) ? XMLSymbols.EMPTY_STRING
0832: : fSymbolTable.addSymbol(prefix);
0833: if (fNSBinder.getURI(prefix) == uri) {
0834: // The xmlns:prefix=namespace or xmlns="default" was declared at parent.
0835: // The binder always stores mapping of empty prefix to "".
0836: // (NOTE: local binder does not store this kind of binding!)
0837: // Thus the case where element was declared with uri="" (with or without a prefix)
0838: // will be covered here.
0839:
0840: } else {
0841: // the prefix is either undeclared
0842: // or
0843: // conflict: the prefix is bound to another URI
0844: if (fNamespacePrefixes) {
0845: printNamespaceAttr(prefix, uri);
0846: }
0847: fLocalNSBinder.declarePrefix(prefix, uri);
0848: fNSBinder.declarePrefix(prefix, uri);
0849: }
0850: } else { // Element has no namespace
0851: if (elem.getLocalName() == null) {
0852: // DOM Level 1 node!
0853: if (fDOMErrorHandler != null) {
0854: String msg = DOMMessageFormatter.formatMessage(
0855: DOMMessageFormatter.DOM_DOMAIN,
0856: "NullLocalElementName",
0857: new Object[] { elem.getNodeName() });
0858: modifyDOMError(msg, DOMError.SEVERITY_ERROR,
0859: null, elem);
0860: boolean continueProcess = fDOMErrorHandler
0861: .handleError(fDOMError);
0862: // REVISIT: should we terminate upon request?
0863: if (!continueProcess) {
0864: throw new RuntimeException(
0865: DOMMessageFormatter
0866: .formatMessage(
0867: DOMMessageFormatter.SERIALIZER_DOMAIN,
0868: "SerializationStopped",
0869: null));
0870: }
0871: }
0872: } else { // uri=null and no colon (DOM L2 node)
0873: uri = fNSBinder.getURI(XMLSymbols.EMPTY_STRING);
0874:
0875: if (uri != null && uri.length() > 0) {
0876: // there is a default namespace decl that is bound to
0877: // non-zero length uri, output xmlns=""
0878: if (fNamespacePrefixes) {
0879: printNamespaceAttr(XMLSymbols.EMPTY_STRING,
0880: XMLSymbols.EMPTY_STRING);
0881: }
0882: fLocalNSBinder.declarePrefix(
0883: XMLSymbols.EMPTY_STRING,
0884: XMLSymbols.EMPTY_STRING);
0885: fNSBinder.declarePrefix(
0886: XMLSymbols.EMPTY_STRING,
0887: XMLSymbols.EMPTY_STRING);
0888: }
0889: }
0890: }
0891:
0892: // -----------------------------------------
0893: // Fix up namespaces for attributes: per DOM L3
0894: // check if prefix/namespace is correct the attributes
0895: // -----------------------------------------
0896:
0897: for (i = 0; i < length; i++) {
0898:
0899: attr = (Attr) attrMap.item(i);
0900: value = attr.getValue();
0901: name = attr.getNodeName();
0902:
0903: uri = attr.getNamespaceURI();
0904:
0905: // Fix attribute that was declared with a prefix and namespace=""
0906: if (uri != null && uri.length() == 0) {
0907: uri = null;
0908: // we must remove prefix for this attribute
0909: name = attr.getLocalName();
0910: }
0911:
0912: if (DEBUG) {
0913: System.out.println("==>process attribute: "
0914: + attr.getNodeName());
0915: }
0916: // make sure that value is never null.
0917: if (value == null) {
0918: value = XMLSymbols.EMPTY_STRING;
0919: }
0920:
0921: if (uri != null) { // attribute has namespace !=null
0922: prefix = attr.getPrefix();
0923: prefix = prefix == null ? XMLSymbols.EMPTY_STRING
0924: : fSymbolTable.addSymbol(prefix);
0925: String localpart = fSymbolTable.addSymbol(attr
0926: .getLocalName());
0927:
0928: // ---------------------------------------------------
0929: // print namespace declarations namespace declarations
0930: // ---------------------------------------------------
0931: if (uri != null
0932: && uri.equals(NamespaceContext.XMLNS_URI)) {
0933: // check if we need to output this declaration
0934: prefix = attr.getPrefix();
0935: prefix = (prefix == null || prefix.length() == 0) ? XMLSymbols.EMPTY_STRING
0936: : fSymbolTable.addSymbol(prefix);
0937: localpart = fSymbolTable.addSymbol(attr
0938: .getLocalName());
0939: if (prefix == XMLSymbols.PREFIX_XMLNS) { //xmlns:prefix
0940: localUri = fLocalNSBinder.getURI(localpart); // local prefix mapping
0941: value = fSymbolTable.addSymbol(value);
0942: if (value.length() != 0) {
0943: if (localUri == null) {
0944: // declaration was not printed while fixing element namespace binding
0945:
0946: // If the DOM Level 3 namespace-prefixes feature is set to false
0947: // do not print xmlns attributes
0948: if (fNamespacePrefixes) {
0949: printNamespaceAttr(localpart,
0950: value);
0951: }
0952:
0953: // case 4: <elem xmlns:xx="foo" xx:attr=""/>
0954: // where attribute is bound to "bar".
0955: // If the xmlns:xx is output here first, later we should not
0956: // redeclare "xx" prefix. Instead we would pick up different prefix
0957: // for the attribute.
0958: // final: <elem xmlns:xx="foo" NS1:attr="" xmlns:NS1="bar"/>
0959: fLocalNSBinder.declarePrefix(
0960: localpart, value);
0961: }
0962: } else {
0963: // REVISIT: issue error on invalid declarations
0964: // xmlns:foo = ""
0965: }
0966: continue;
0967: }
0968: // xmlns --- empty prefix is always bound ("" or some string)
0969: uri = fNSBinder.getURI(XMLSymbols.EMPTY_STRING);
0970: localUri = fLocalNSBinder
0971: .getURI(XMLSymbols.EMPTY_STRING);
0972: value = fSymbolTable.addSymbol(value);
0973: if (localUri == null) {
0974: // declaration was not printed while fixing element namespace binding
0975: if (fNamespacePrefixes) {
0976: printNamespaceAttr(
0977: XMLSymbols.EMPTY_STRING, value);
0978: }
0979: // case 4 does not apply here since attributes can't use
0980: // default namespace
0981: }
0982: continue;
0983:
0984: }
0985: uri = fSymbolTable.addSymbol(uri);
0986:
0987: // find if for this prefix a URI was already declared
0988: String declaredURI = fNSBinder.getURI(prefix);
0989:
0990: if (prefix == XMLSymbols.EMPTY_STRING
0991: || declaredURI != uri) {
0992: // attribute has no prefix (default namespace decl does not apply to attributes)
0993: // OR
0994: // attribute prefix is not declared
0995: // OR
0996: // conflict: attr URI does not match the prefix in scope
0997:
0998: name = attr.getNodeName();
0999: // Find if any prefix for attributes namespace URI is available
1000: // in the scope
1001: String declaredPrefix = fNSBinder
1002: .getPrefix(uri);
1003:
1004: if (declaredPrefix != null
1005: && declaredPrefix != XMLSymbols.EMPTY_STRING) {
1006: // use the prefix that was found
1007: prefix = declaredPrefix;
1008: name = prefix + ":" + localpart;
1009: } else {
1010: if (DEBUG) {
1011: System.out
1012: .println("==> cound not find prefix for the attribute: "
1013: + prefix);
1014: }
1015:
1016: if (prefix != XMLSymbols.EMPTY_STRING
1017: && fLocalNSBinder.getURI(prefix) == null) {
1018: // the current prefix is not null and it has no in scope declaration
1019:
1020: // use this prefix
1021: } else {
1022: // find a prefix following the pattern "NS" +index (starting at 1)
1023: // make sure this prefix is not declared in the current scope.
1024: int counter = 1;
1025: prefix = fSymbolTable.addSymbol(PREFIX
1026: + counter++);
1027: while (fLocalNSBinder.getURI(prefix) != null) {
1028: prefix = fSymbolTable
1029: .addSymbol(PREFIX
1030: + counter++);
1031: }
1032: name = prefix + ":" + localpart;
1033: }
1034: // add declaration for the new prefix
1035: if (fNamespacePrefixes) {
1036: printNamespaceAttr(prefix, uri);
1037: }
1038: value = fSymbolTable.addSymbol(value);
1039: fLocalNSBinder.declarePrefix(prefix, value);
1040: fNSBinder.declarePrefix(prefix, uri);
1041: }
1042:
1043: // change prefix for this attribute
1044: }
1045:
1046: printAttribute(name,
1047: (value == null) ? XMLSymbols.EMPTY_STRING
1048: : value, attr.getSpecified(), attr);
1049: } else { // attribute uri == null
1050: if (attr.getLocalName() == null) {
1051: if (fDOMErrorHandler != null) {
1052: String msg = DOMMessageFormatter
1053: .formatMessage(
1054: DOMMessageFormatter.DOM_DOMAIN,
1055: "NullLocalAttrName",
1056: new Object[] { attr
1057: .getNodeName() });
1058: modifyDOMError(msg,
1059: DOMError.SEVERITY_ERROR, null, attr);
1060: boolean continueProcess = fDOMErrorHandler
1061: .handleError(fDOMError);
1062: if (!continueProcess) {
1063: // stop the namespace fixup and validation
1064: throw new RuntimeException(
1065: DOMMessageFormatter
1066: .formatMessage(
1067: DOMMessageFormatter.SERIALIZER_DOMAIN,
1068: "SerializationStopped",
1069: null));
1070: }
1071: }
1072: printAttribute(name, value,
1073: attr.getSpecified(), attr);
1074: } else { // uri=null and no colon
1075:
1076: // no fix up is needed: default namespace decl does not
1077: // apply to attributes
1078: printAttribute(name, value,
1079: attr.getSpecified(), attr);
1080: }
1081: }
1082: } // end loop for attributes
1083:
1084: }// end namespace fixup algorithm
1085:
1086: // If element has children, then serialize them, otherwise
1087: // serialize en empty tag.
1088: if (elem.hasChildNodes()) {
1089: // Enter an element state, and serialize the children
1090: // one by one. Finally, end the element.
1091: state = enterElementState(null, null, tagName,
1092: fPreserveSpace);
1093: state.doCData = _format.isCDataElement(tagName);
1094: state.unescaped = _format.isNonEscapingElement(tagName);
1095: child = elem.getFirstChild();
1096: while (child != null) {
1097: serializeNode(child);
1098: child = child.getNextSibling();
1099: }
1100: if (fNamespaces) {
1101: fNSBinder.popContext();
1102: }
1103: endElementIO(null, null, tagName);
1104: } else {
1105: if (DEBUG) {
1106: System.out.println("==>endElement: "
1107: + elem.getNodeName());
1108: }
1109: if (fNamespaces) {
1110: fNSBinder.popContext();
1111: }
1112: _printer.unindent();
1113: _printer.printText("/>");
1114: // After element but parent element is no longer empty.
1115: state.afterElement = true;
1116: state.afterComment = false;
1117: state.empty = false;
1118: if (isDocumentState())
1119: _printer.flush();
1120: }
1121: }
1122:
1123: /**
1124: * Serializes a namespace attribute with the given prefix and value for URI.
1125: * In case prefix is empty will serialize default namespace declaration.
1126: *
1127: * @param prefix
1128: * @param uri
1129: * @exception IOException
1130: */
1131:
1132: private void printNamespaceAttr(String prefix, String uri)
1133: throws IOException {
1134: _printer.printSpace();
1135: if (prefix == XMLSymbols.EMPTY_STRING) {
1136: if (DEBUG) {
1137: System.out.println("=>add xmlns=\"" + uri
1138: + "\" declaration");
1139: }
1140: _printer.printText(XMLSymbols.PREFIX_XMLNS);
1141: } else {
1142: if (DEBUG) {
1143: System.out.println("=>add xmlns:" + prefix + "=\""
1144: + uri + "\" declaration");
1145: }
1146: _printer.printText("xmlns:" + prefix);
1147: }
1148: _printer.printText("=\"");
1149: printEscaped(uri);
1150: _printer.printText('"');
1151: }
1152:
1153: /**
1154: * Prints attribute.
1155: * NOTE: xml:space attribute modifies output format
1156: *
1157: * @param name
1158: * @param value
1159: * @param isSpecified
1160: * @exception IOException
1161: */
1162: private void printAttribute(String name, String value,
1163: boolean isSpecified, Attr attr) throws IOException {
1164:
1165: if (isSpecified
1166: || (features & DOMSerializerImpl.DISCARDDEFAULT) == 0) {
1167: if (fDOMFilter != null
1168: && (fDOMFilter.getWhatToShow() & NodeFilter.SHOW_ATTRIBUTE) != 0) {
1169: short code = fDOMFilter.acceptNode(attr);
1170: switch (code) {
1171: case NodeFilter.FILTER_REJECT:
1172: case NodeFilter.FILTER_SKIP: {
1173: return;
1174: }
1175: default: {
1176: // fall through
1177: }
1178: }
1179: }
1180: _printer.printSpace();
1181: _printer.printText(name);
1182: _printer.printText("=\"");
1183: printEscaped(value);
1184: _printer.printText('"');
1185: }
1186:
1187: // If the attribute xml:space exists, determine whether
1188: // to preserve spaces in this and child nodes based on
1189: // its value.
1190: if (name.equals("xml:space")) {
1191: if (value.equals("preserve"))
1192: fPreserveSpace = true;
1193: else
1194: fPreserveSpace = _format.getPreserveSpace();
1195: }
1196: }
1197:
1198: protected String getEntityRef(int ch) {
1199: // Encode special XML characters into the equivalent character references.
1200: // These five are defined by default for all XML documents.
1201: switch (ch) {
1202: case '<':
1203: return "lt";
1204: case '>':
1205: return "gt";
1206: case '"':
1207: return "quot";
1208: case '\'':
1209: return "apos";
1210: case '&':
1211: return "amp";
1212: }
1213: return null;
1214: }
1215:
1216: /** Retrieve and remove the namespaces declarations from the list of attributes.
1217: *
1218: */
1219: private Attributes extractNamespaces(Attributes attrs)
1220: throws SAXException {
1221: AttributesImpl attrsOnly;
1222: String rawName;
1223: int i;
1224: int length;
1225:
1226: if (attrs == null) {
1227: return null;
1228: }
1229: length = attrs.getLength();
1230: attrsOnly = new AttributesImpl(attrs);
1231:
1232: for (i = length - 1; i >= 0; --i) {
1233: rawName = attrsOnly.getQName(i);
1234:
1235: //We have to exclude the namespaces declarations from the attributes
1236: //Append only when the feature http://xml.org/sax/features/namespace-prefixes"
1237: //is TRUE
1238: if (rawName.startsWith("xmlns")) {
1239: if (rawName.length() == 5) {
1240: startPrefixMapping("", attrs.getValue(i));
1241: attrsOnly.removeAttribute(i);
1242: } else if (rawName.charAt(5) == ':') {
1243: startPrefixMapping(rawName.substring(6), attrs
1244: .getValue(i));
1245: attrsOnly.removeAttribute(i);
1246: }
1247: }
1248: }
1249: return attrsOnly;
1250: }
1251:
1252: //
1253: // Printing attribute value
1254: //
1255: protected void printEscaped(String source) throws IOException {
1256: int length = source.length();
1257: for (int i = 0; i < length; ++i) {
1258: int ch = source.charAt(i);
1259: if (!XMLChar.isValid(ch)) {
1260: if (++i < length) {
1261: surrogates(ch, source.charAt(i), false);
1262: } else {
1263: fatalError("The character '" + (char) ch
1264: + "' is an invalid XML character");
1265: }
1266: continue;
1267: }
1268: // escape NL, CR, TAB
1269: if (ch == '\n' || ch == '\r' || ch == '\t') {
1270: printHex(ch);
1271: } else if (ch == '<') {
1272: _printer.printText("<");
1273: } else if (ch == '&') {
1274: _printer.printText("&");
1275: } else if (ch == '"') {
1276: _printer.printText(""");
1277: } else if ((ch >= ' ' && _encodingInfo
1278: .isPrintable((char) ch))) {
1279: _printer.printText((char) ch);
1280: } else {
1281: printHex(ch);
1282: }
1283: }
1284: }
1285:
1286: /** print text data */
1287: protected void printXMLChar(int ch) throws IOException {
1288: if (ch == '\r') {
1289: printHex(ch);
1290: } else if (ch == '<') {
1291: _printer.printText("<");
1292: } else if (ch == '&') {
1293: _printer.printText("&");
1294: } else if (ch == '>') {
1295: // character sequence "]]>" can't appear in content, therefore
1296: // we should escape '>'
1297: _printer.printText(">");
1298: } else if (ch == '\n' || ch == '\t'
1299: || (ch >= ' ' && _encodingInfo.isPrintable((char) ch))) {
1300: _printer.printText((char) ch);
1301: } else {
1302: printHex(ch);
1303: }
1304: }
1305:
1306: protected void printText(String text, boolean preserveSpace,
1307: boolean unescaped) throws IOException {
1308: int index;
1309: char ch;
1310: int length = text.length();
1311: if (preserveSpace) {
1312: // Preserving spaces: the text must print exactly as it is,
1313: // without breaking when spaces appear in the text and without
1314: // consolidating spaces. If a line terminator is used, a line
1315: // break will occur.
1316: for (index = 0; index < length; ++index) {
1317: ch = text.charAt(index);
1318: if (!XMLChar.isValid(ch)) {
1319: // check if it is surrogate
1320: if (++index < length) {
1321: surrogates(ch, text.charAt(index), true);
1322: } else {
1323: fatalError("The character '" + ch
1324: + "' is an invalid XML character");
1325: }
1326: continue;
1327: }
1328: if (unescaped) {
1329: _printer.printText(ch);
1330: } else
1331: printXMLChar(ch);
1332: }
1333: } else {
1334: // Not preserving spaces: print one part at a time, and
1335: // use spaces between parts to break them into different
1336: // lines. Spaces at beginning of line will be stripped
1337: // by printing mechanism. Line terminator is treated
1338: // no different than other text part.
1339: for (index = 0; index < length; ++index) {
1340: ch = text.charAt(index);
1341: if (!XMLChar.isValid(ch)) {
1342: // check if it is surrogate
1343: if (++index < length) {
1344: surrogates(ch, text.charAt(index), true);
1345: } else {
1346: fatalError("The character '" + ch
1347: + "' is an invalid XML character");
1348: }
1349: continue;
1350: }
1351:
1352: if (unescaped)
1353: _printer.printText(ch);
1354: else
1355: printXMLChar(ch);
1356: }
1357: }
1358: }
1359:
1360: protected void printText(char[] chars, int start, int length,
1361: boolean preserveSpace, boolean unescaped)
1362: throws IOException {
1363:
1364: if (preserveSpace) {
1365: // Preserving spaces: the text must print exactly as it is,
1366: // without breaking when spaces appear in the text and without
1367: // consolidating spaces. If a line terminator is used, a line
1368: // break will occur.
1369: while (length-- > 0) {
1370: char ch = chars[start++];
1371: if (!XMLChar.isValid(ch)) {
1372: // check if it is surrogate
1373: if (length-- > 0) {
1374: surrogates(ch, chars[start++], true);
1375: } else {
1376: fatalError("The character '" + ch
1377: + "' is an invalid XML character");
1378: }
1379: continue;
1380: }
1381: if (unescaped) {
1382: _printer.printText(ch);
1383: } else {
1384: printXMLChar(ch);
1385: }
1386: }
1387: } else {
1388: // Not preserving spaces: print one part at a time, and
1389: // use spaces between parts to break them into different
1390: // lines. Spaces at beginning of line will be stripped
1391: // by printing mechanism. Line terminator is treated
1392: // no different than other text part.
1393: while (length-- > 0) {
1394: char ch = chars[start++];
1395: if (!XMLChar.isValid(ch)) {
1396: // check if it is surrogate
1397: if (length-- > 0) {
1398: surrogates(ch, chars[start++], true);
1399: } else {
1400: fatalError("The character '" + ch
1401: + "' is an invalid XML character");
1402: }
1403: continue;
1404: }
1405: if (unescaped) {
1406: _printer.printText(ch);
1407: } else {
1408: printXMLChar(ch);
1409: }
1410: }
1411: }
1412: }
1413:
1414: /**
1415: * DOM Level 3:
1416: * Check a node to determine if it contains unbound namespace prefixes.
1417: *
1418: * @param node The node to check for unbound namespace prefices
1419: */
1420: protected void checkUnboundNamespacePrefixedNode(Node node)
1421: throws IOException {
1422:
1423: if (fNamespaces) {
1424:
1425: if (DEBUG) {
1426: System.out.println("==>serializeNode("
1427: + node.getNodeName()
1428: + ") [Entity Reference - Namespaces on]");
1429: System.out.println("==>Declared Prefix Count: "
1430: + fNSBinder.getDeclaredPrefixCount());
1431: System.out.println("==>Node Name: "
1432: + node.getNodeName());
1433: System.out.println("==>First Child Node Name: "
1434: + node.getFirstChild().getNodeName());
1435: System.out.println("==>First Child Node Prefix: "
1436: + node.getFirstChild().getPrefix());
1437: System.out.println("==>First Child Node NamespaceURI: "
1438: + node.getFirstChild().getNamespaceURI());
1439: }
1440:
1441: Node child, next;
1442: for (child = node.getFirstChild(); child != null; child = next) {
1443: next = child.getNextSibling();
1444: if (DEBUG) {
1445: System.out.println("==>serializeNode("
1446: + child.getNodeName() + ") [Child Node]");
1447: System.out.println("==>serializeNode("
1448: + child.getPrefix()
1449: + ") [Child Node Prefix]");
1450: }
1451:
1452: //If a NamespaceURI is not declared for the current
1453: //node's prefix, raise a fatal error.
1454: String prefix = child.getPrefix();
1455: prefix = (prefix == null || prefix.length() == 0) ? XMLSymbols.EMPTY_STRING
1456: : fSymbolTable.addSymbol(prefix);
1457: if (fNSBinder.getURI(prefix) == null && prefix != null) {
1458: fatalError("The replacement text of the entity node '"
1459: + node.getNodeName()
1460: + "' contains an element node '"
1461: + child.getNodeName()
1462: + "' with an undeclared prefix '"
1463: + prefix
1464: + "'.");
1465: }
1466:
1467: if (child.getNodeType() == Node.ELEMENT_NODE) {
1468:
1469: NamedNodeMap attrs = child.getAttributes();
1470:
1471: for (int i = 0; i < attrs.getLength(); i++) {
1472:
1473: String attrPrefix = attrs.item(i).getPrefix();
1474: attrPrefix = (attrPrefix == null || attrPrefix
1475: .length() == 0) ? XMLSymbols.EMPTY_STRING
1476: : fSymbolTable.addSymbol(attrPrefix);
1477: if (fNSBinder.getURI(attrPrefix) == null
1478: && attrPrefix != null) {
1479: fatalError("The replacement text of the entity node '"
1480: + node.getNodeName()
1481: + "' contains an element node '"
1482: + child.getNodeName()
1483: + "' with an attribute '"
1484: + attrs.item(i).getNodeName()
1485: + "' an undeclared prefix '"
1486: + attrPrefix + "'.");
1487: }
1488:
1489: }
1490:
1491: }
1492:
1493: if (child.hasChildNodes()) {
1494: checkUnboundNamespacePrefixedNode(child);
1495: }
1496: }
1497: }
1498: }
1499:
1500: public boolean reset() {
1501: super .reset();
1502: if (fNSBinder != null) {
1503: fNSBinder.reset();
1504: // during serialization always have a mapping to empty string
1505: // so we assume there is a declaration.
1506: fNSBinder.declarePrefix(XMLSymbols.EMPTY_STRING,
1507: XMLSymbols.EMPTY_STRING);
1508: }
1509: return true;
1510: }
1511:
1512: }
|