1/* 2 * Copyright (c) 2009-2010 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33package com.jme3.export.xml; 34 35import java.io.*; 36import java.nio.charset.Charset; 37import org.w3c.dom.*; 38 39/** 40 * The DOMSerializer was based primarily off the DOMSerializer.java class from the 41 * "Java and XML" 3rd Edition book by Brett McLaughlin, and Justin Edelson. Some 42 * modifications were made to support formatting of elements and attributes. 43 * 44 * @author Brett McLaughlin, Justin Edelson - Original creation for "Java and XML" book. 45 * @author Doug Daniels (dougnukem) - adjustments for XML formatting 46 * @version $Revision: 4207 $, $Date: 2009-03-29 11:19:16 -0400 (Sun, 29 Mar 2009) $ 47 */ 48public class DOMSerializer { 49 50 /** The encoding to use for output (default is UTF-8) */ 51 private Charset encoding = Charset.forName("utf-8"); 52 53 /** The amount of indentation to use (default is 4 spaces). */ 54 private int indent = 4; 55 56 /** The line separator to use (default is the based on the current system settings). */ 57 private String lineSeparator = System.getProperty("line.separator", "\n"); 58 59 private void escape(Writer writer, String s) throws IOException { 60 if (s == null) { return; } 61 for (int i = 0, len = s.length(); i < len; i++) { 62 char c = s.charAt(i); 63 switch (c) { 64 case '<': 65 writer.write("<"); 66 break; 67 case '>': 68 writer.write(">"); 69 break; 70 case '&': 71 writer.write("&"); 72 break; 73 case '\r': 74 writer.write("
"); 75 break; 76 default: 77 writer.write(c); 78 } 79 } 80 } 81 82 /** 83 * Serialize {@code doc} to {@code out} 84 * 85 * @param doc the document to serialize. 86 * @param file the file to serialize to. 87 * @throws IOException 88 */ 89 public void serialize(Document doc, File file) throws IOException { 90 serialize(doc, new FileOutputStream(file)); 91 } 92 93 /** 94 * Serialize {@code doc} to {@code out} 95 * 96 * @param doc the document to serialize. 97 * @param out the stream to serialize to. 98 * @throws IOException 99 */ 100 public void serialize(Document doc, OutputStream out) throws IOException { 101 Writer writer = new OutputStreamWriter(out, encoding); 102 write(doc, writer, 0); 103 writer.flush(); 104 } 105 106 /** 107 * Set the encoding used by this serializer. 108 * 109 * @param encoding the encoding to use, passing in {@code null} results in the 110 * default encoding (UTF-8) being set. 111 * @throws IllegalCharsetNameException if the given charset name is illegal. 112 * @throws UnsupportedCharsetException if the given charset is not supported by the 113 * current JVM. 114 */ 115 public void setEncoding(String encoding) { 116 this.encoding = Charset.forName(encoding); 117 } 118 119 /** 120 * Set the number of spaces to use for indentation. 121 * <p> 122 * The default is to use 4 spaces. 123 * 124 * @param indent the number of spaces to use for indentation, values less than or 125 * equal to zero result in no indentation being used. 126 */ 127 public void setIndent(int indent) { 128 this.indent = indent >= 0 ? indent : 0; 129 } 130 131 /** 132 * Set the line separator that will be used when serializing documents. 133 * <p> 134 * If this is not called then the serializer uses a default based on the 135 * {@code line.separator} system property. 136 * 137 * @param lineSeparator the line separator to set. 138 */ 139 public void setLineSeparator(String lineSeparator) { 140 this.lineSeparator = lineSeparator; 141 } 142 143 private void write(Node node, Writer writer, int depth) throws IOException { 144 switch (node.getNodeType()) { 145 case Node.DOCUMENT_NODE: 146 writeDocument((Document) node, writer); 147 break; 148 case Node.ELEMENT_NODE: 149 writeElement((Element) node, writer, depth); 150 break; 151 case Node.TEXT_NODE: 152 escape(writer, node.getNodeValue()); 153 break; 154 case Node.CDATA_SECTION_NODE: 155 writer.write("<![CDATA["); 156 escape(writer, node.getNodeValue()); 157 writer.write("]]>"); 158 break; 159 case Node.COMMENT_NODE: 160 for (int i = 0; i < depth; ++i) { writer.append(' '); } 161 writer.append("<!-- ").append(node.getNodeValue()).append(" -->").append(lineSeparator); 162 break; 163 case Node.PROCESSING_INSTRUCTION_NODE: 164 String n = node.getNodeName(); 165 String v = node.getNodeValue(); 166 for (int i = 0; i < depth; ++i) { writer.append(' '); } 167 writer.append("<?").append(n).append(' ').append(v).append("?>").append(lineSeparator); 168 break; 169 case Node.ENTITY_REFERENCE_NODE: 170 writer.append('&').append(node.getNodeName()).append(';'); 171 break; 172 case Node.DOCUMENT_TYPE_NODE: 173 writeDocumentType((DocumentType) node, writer, depth); 174 break; 175 } 176 } 177 178 private void writeDocument(Document document, Writer writer) throws IOException { 179 String v = document.getXmlVersion(); 180 181 writer.append("<?xml "); 182 writer.append(" version='").append(v == null ? "1.0" : v).append("'"); 183 writer.append(" encoding='").append(encoding.name()).append("'"); 184 if (document.getXmlStandalone()) { 185 writer.append(" standalone='yes'"); 186 } 187 writer.append("?>").append(lineSeparator); 188 189 NodeList nodes = document.getChildNodes(); 190 for (int i = 0, imax = nodes.getLength(); i < imax; ++i) { 191 write(nodes.item(i), writer, 0); 192 } 193 } 194 195 private void writeDocumentType(DocumentType docType, Writer writer, int depth) throws IOException { 196 String publicId = docType.getPublicId(); 197 String internalSubset = docType.getInternalSubset(); 198 199 for (int i = 0; i < depth; ++i) { writer.append(' '); } 200 writer.append("<!DOCTYPE ").append(docType.getName()); 201 if (publicId != null) { 202 writer.append(" PUBLIC '").append(publicId).append("' "); 203 } else { 204 writer.write(" SYSTEM "); 205 } 206 writer.append("'").append(docType.getSystemId()).append("'"); 207 if (internalSubset != null) { 208 writer.append(" [").append(internalSubset).append("]"); 209 } 210 writer.append('>').append(lineSeparator); 211 } 212 213 private void writeElement(Element element, Writer writer, int depth) throws IOException { 214 for (int i = 0; i < depth; ++i) { writer.append(' '); } 215 writer.append('<').append(element.getTagName()); 216 NamedNodeMap attrs = element.getAttributes(); 217 for (int i = 0, imax = attrs.getLength(); i < imax; ++i) { 218 Attr attr = (Attr) attrs.item(i); 219 writer.append(' ').append(attr.getName()).append("='").append(attr.getValue()).append("'"); 220 } 221 NodeList nodes = element.getChildNodes(); 222 if (nodes.getLength() == 0) { 223 // no children, so just close off the element and return 224 writer.append("/>").append(lineSeparator); 225 return; 226 } 227 writer.append('>').append(lineSeparator); 228 for (int i = 0, imax = nodes.getLength(); i < imax; ++i) { 229 Node n = nodes.item(i); 230 if (n.getNodeType() == Node.ATTRIBUTE_NODE) { continue; } 231 write(n, writer, depth + indent); 232 } 233 for (int i = 0; i < depth; ++i) { writer.append(' '); } 234 writer.append("</").append(element.getTagName()).append('>').append(lineSeparator); 235 } 236 237} 238