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