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