1 /* 2 * XMLWriter.java 3 * 4 * Copyright (c) 2015 Emmanuel PUYBARET / eTeks <info@eteks.com>. All Rights Reserved. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 */ 20 package com.eteks.sweethome3d.io; 21 22 import java.io.FilterWriter; 23 import java.io.IOException; 24 import java.io.OutputStream; 25 import java.io.OutputStreamWriter; 26 import java.math.BigDecimal; 27 import java.util.Stack; 28 29 /** 30 * A simple XML writer able to write XML elements, their attributes and texts, indenting child elements. 31 * @author Emmanuel Puybaret 32 */ 33 public class XMLWriter extends FilterWriter { 34 private Stack<String> elements = new Stack<String>(); 35 private boolean emptyElement; 36 private boolean elementWithText; 37 38 /** 39 * Creates a writer in the given output stream encoded in UTF-8. 40 */ XMLWriter(OutputStream out)41 public XMLWriter(OutputStream out) throws IOException { 42 super(new OutputStreamWriter(out, "UTF-8")); 43 this.out.write("<?xml version='1.0'?>\n"); 44 } 45 46 /** 47 * Writes a start tag for the given element. 48 */ writeStartElement(String element)49 public void writeStartElement(String element) throws IOException { 50 if (this.elements.size() > 0) { 51 if (this.emptyElement) { 52 this.out.write(">"); 53 } 54 writeIndentation(); 55 } 56 this.out.write("<" + element); 57 this.elements.push(element); 58 this.emptyElement = true; 59 this.elementWithText = false; 60 } 61 62 /** 63 * Writes an end tag for the given element. 64 */ writeEndElement()65 public void writeEndElement() throws IOException { 66 String element = this.elements.pop(); 67 if (this.emptyElement) { 68 this.out.write("/>"); 69 } else { 70 if (!this.elementWithText) { 71 writeIndentation(); 72 } 73 this.out.write("</" + element + ">"); 74 } 75 this.emptyElement = false; 76 this.elementWithText = false; 77 } 78 79 /** 80 * Adds spaces according to the current depth of XML tree. 81 */ writeIndentation()82 private void writeIndentation() throws IOException { 83 this.out.write("\n"); 84 for (int i = 0; i < this.elements.size(); i++) { 85 this.out.write(" "); 86 } 87 } 88 89 /** 90 * Writes the attribute of the given <code>name</code> with its <code>value</code> 91 * in the tag of the last started element. 92 */ writeAttribute(String name, String value)93 public void writeAttribute(String name, String value) throws IOException { 94 this.out.write(" " + name + "='" + replaceByEntities(value) + "'"); 95 } 96 97 /** 98 * Writes the name and the value of an attribute in the tag of the last started element, 99 * except if <code>value</code> equals <code>defaultValue</code>. 100 */ writeAttribute(String name, String value, String defaultValue)101 public void writeAttribute(String name, String value, String defaultValue) throws IOException { 102 if ((value != null || value != defaultValue) 103 && !value.equals(defaultValue)) { 104 writeAttribute(name, value); 105 } 106 } 107 108 /** 109 * Writes the attribute of the given <code>name</code> with its integer <code>value</code> 110 * in the tag of the last started element. 111 */ writeIntegerAttribute(String name, int value)112 public void writeIntegerAttribute(String name, int value) throws IOException { 113 writeAttribute(name, String.valueOf(value)); 114 } 115 116 /** 117 * Writes the name and the integer value of an attribute in the tag of the last started element, 118 * except if <code>value</code> equals <code>defaultValue</code>. 119 */ writeIntegerAttribute(String name, int value, int defaultValue)120 public void writeIntegerAttribute(String name, int value, int defaultValue) throws IOException { 121 if (value != defaultValue) { 122 writeAttribute(name, String.valueOf(value)); 123 } 124 } 125 126 /** 127 * Writes the attribute of the given <code>name</code> with its long <code>value</code> 128 * in the tag of the last started element. 129 */ writeLongAttribute(String name, long value)130 public void writeLongAttribute(String name, long value) throws IOException { 131 writeAttribute(name, String.valueOf(value)); 132 } 133 134 /** 135 * Writes the name and the long value of an attribute in the tag of the last started element, 136 * except if <code>value</code> equals <code>null</code>. 137 */ writeLongAttribute(String name, Long value)138 public void writeLongAttribute(String name, Long value) throws IOException { 139 if (value != null) { 140 writeAttribute(name, value.toString()); 141 } 142 } 143 144 /** 145 * Writes the attribute of the given <code>name</code> with its float <code>value</code> 146 * in the tag of the last started element. 147 */ writeFloatAttribute(String name, float value)148 public void writeFloatAttribute(String name, float value) throws IOException { 149 writeAttribute(name, String.valueOf(value)); 150 } 151 152 /** 153 * Writes the name and the float value of an attribute in the tag of the last started element, 154 * except if <code>value</code> equals <code>defaultValue</code>. 155 */ writeFloatAttribute(String name, float value, float defaultValue)156 public void writeFloatAttribute(String name, float value, float defaultValue) throws IOException { 157 if (value != defaultValue) { 158 writeFloatAttribute(name, value); 159 } 160 } 161 162 /** 163 * Writes the name and the float value of an attribute in the tag of the last started element, 164 * except if <code>value</code> equals <code>null</code>. 165 */ writeFloatAttribute(String name, Float value)166 public void writeFloatAttribute(String name, Float value) throws IOException { 167 if (value != null) { 168 writeAttribute(name, value.toString()); 169 } 170 } 171 172 /** 173 * Writes the name and the value of an attribute in the tag of the last started element, 174 * except if <code>value</code> equals <code>null</code>. 175 */ writeBigDecimalAttribute(String name, BigDecimal value)176 public void writeBigDecimalAttribute(String name, BigDecimal value) throws IOException { 177 if (value != null) { 178 writeAttribute(name, String.valueOf(value)); 179 } 180 } 181 182 /** 183 * Writes the name and the boolean value of an attribute in the tag of the last started element, 184 * except if <code>value</code> equals <code>defaultValue</code>. 185 */ writeBooleanAttribute(String name, boolean value, boolean defaultValue)186 public void writeBooleanAttribute(String name, boolean value, boolean defaultValue) throws IOException { 187 if (value != defaultValue) { 188 writeAttribute(name, String.valueOf(value)); 189 } 190 } 191 192 /** 193 * Writes the name and the color value of an attribute in the tag of the last started element, 194 * except if <code>value</code> equals <code>null</code>. The color is written in hexadecimal. 195 */ writeColorAttribute(String name, Integer color)196 public void writeColorAttribute(String name, Integer color) throws IOException { 197 if (color != null) { 198 writeAttribute(name, String.format("%08X", color)); 199 } 200 } 201 202 /** 203 * Writes the given <code>text</code> as the content of the current element. 204 */ writeText(String text)205 public void writeText(String text) throws IOException { 206 if (this.emptyElement) { 207 this.out.write(">"); 208 this.emptyElement = false; 209 this.elementWithText = true; 210 } 211 super.out.write(replaceByEntities(text)); 212 } 213 214 /** 215 * Returns the string in parameter with &, <, ', " and feed line characters replaced by their matching entities. 216 */ replaceByEntities(String s)217 private static String replaceByEntities(String s) { 218 return s.replace("&", "&").replace("<", "<").replace("'", "'").replace("\"", """).replace("\n", " "); 219 } 220 221 /** 222 * Writes the given character as the content of the current element. 223 */ write(int c)224 public void write(int c) throws IOException { 225 writeText(String.valueOf((char)c)); 226 } 227 228 /** 229 * Writes the given characters array as the content of the current element. 230 */ write(char buffer[], int offset, int length)231 public void write(char buffer[], int offset, int length) throws IOException { 232 writeText(new String(buffer, offset, length)); 233 } 234 235 /** 236 * Writes the given string as the content of the current element. 237 */ write(String str, int offset, int length)238 public void write(String str, int offset, int length) throws IOException { 239 writeText(str.substring(offset, offset + length)); 240 } 241 }