package com.yoursway.utils; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.util.ArrayList; import java.util.List; public class XmlWriter { static public String escapeXml(String str) { str = replaceString(str, "&", "&"); str = replaceString(str, "<", "<"); str = replaceString(str, ">", ">"); str = replaceString(str, "\"", """); str = replaceString(str, "'", "'"); return str; } public static final String UTF8_ENCODING = "UTF-8"; private Writer writer; private List<String> openElements = new ArrayList<String>(); private StringBuilder attrs = new StringBuilder(); private boolean currentElementIsEmpty; private boolean rightAngleBracketIsWritten = true; private boolean insideProcessingInstruction = false; private String encoding; public XmlWriter(Writer writer) { if (writer == null) throw new NullPointerException("writer is null"); this.writer = writer; } public XmlWriter(OutputStream stream) throws UnsupportedEncodingException { this(stream, UTF8_ENCODING); } public XmlWriter(OutputStream stream, String encoding) throws UnsupportedEncodingException { this(new OutputStreamWriter(stream, encoding)); this.encoding = encoding; } public XmlWriter setEncoding(String encoding) { this.encoding = encoding; return this; } public void finish() throws IOException { if (!openElements.isEmpty()) { throw new IllegalStateException("Tags are not all closed. " + "Possibly, " + pop(openElements) + " is unclosed. "); } writer.flush(); } public XmlWriter end() throws IOException { if (insideProcessingInstruction) { writeAttributes(); writer.write("?>\r\n"); insideProcessingInstruction = false; } else { if (openElements.isEmpty()) throw new IllegalStateException("Called end() too many times."); String name = pop(openElements); if (currentElementIsEmpty) { writeAttributes(); writer.write("/>"); rightAngleBracketIsWritten = true; } else { writer.write("</"); writer.write(name); writer.write(">"); } currentElementIsEmpty = false; } return this; } public XmlWriter start(String name, String attr1, String value1) throws IOException { return start(name).attr(attr1, value1); } public XmlWriter start(String name, String attr1, String value1, String attr2, String value2) throws IOException { return start(name).attr(attr1, value1).attr(attr2, value2); } public XmlWriter start(String name, String attr1, String value1, String attr2, String value2, String attr3, String value3) throws IOException { return start(name).attr(attr1, value1).attr(attr2, value2).attr(attr3, value3); } public XmlWriter start(String name, String attr1, String value1, String attr2, String value2, String attr3, String value3, String attr4, String value4) throws IOException { return start(name).attr(attr1, value1).attr(attr2, value2).attr(attr3, value3).attr(attr4, value4); } public XmlWriter start(String name) throws IOException { if (name == null) throw new NullPointerException("name is null"); closeOpeningTag(); rightAngleBracketIsWritten = false; writer.write("<"); writer.write(name); openElements.add(name); currentElementIsEmpty = true; return this; } public XmlWriter xmlHeader(String attr1, String value1) throws IOException { return startXmlHeader().attr(attr1, value1).end(); } public XmlWriter xmlHeader(String attr1, String value1, String attr2, String value2) throws IOException { return startXmlHeader().attr(attr1, value1).attr(attr2, value2).end(); } public XmlWriter xmlHeader(String attr1, String name1, String attr2, String value2, String attr3, String value3) throws IOException { return startXmlHeader().attr(attr1, name1).attr(attr2, value2).attr( attr3, value3).end(); } public XmlWriter startXmlHeader() throws IOException { startProcessingInstruction("xml").attr("version", "1.0"); if (encoding != null) attr("encoding", encoding); return this; } public XmlWriter processingInstruction(String name, String attr1, String value1) throws IOException { return startProcessingInstruction(name).attr(attr1, value1).end(); } public XmlWriter processingInstruction(String name, String attr1, String value1, String attr2, String value2) throws IOException { return startProcessingInstruction(name).attr(attr1, value1).attr(attr2, value2).end(); } public XmlWriter processingInstruction(String name, String attr1, String value1, String attr2, String value2, String attr3, String value3) throws IOException { return startProcessingInstruction(name).attr(attr1, value1).attr(attr2, value2).attr(attr3, value3).end(); } public XmlWriter processingInstruction(String name) throws IOException { return startProcessingInstruction(name).end(); } public XmlWriter startProcessingInstruction(String name) throws IOException { if (name == null) throw new NullPointerException("name is null"); closeOpeningTag(); writer.write("<?"); writer.write(name); insideProcessingInstruction = true; return this; } public XmlWriter xmlns(String url) throws IOException { return attr("xmlns", url); } public XmlWriter xmlns(String shortcut, String url) throws IOException { if (shortcut == null || shortcut.length() == 0) return attr("xmlns", url); else return attr("xmlns:" + shortcut, url); } public XmlWriter tag(String name, String content) throws IOException { return start(name).text(content).end(); } public XmlWriter tag(String name, String attr1, String value1, String content) throws IOException { return start(name).attr(attr1, value1).text(content).end(); } public XmlWriter tag(String name, String attr1, String value1, String attr2, String value2, String content) throws IOException { return start(name).attr(attr1, value1).attr(attr2, value2) .text(content).end(); } public XmlWriter tag(String name, String attr1, String value1, String attr2, String value2, String attr3, String value3, String content) throws IOException { return start(name).attr(attr1, value1).attr(attr2, value2).attr(attr3, value3).text(content).end(); } public XmlWriter tag(String name) throws IOException { return start(name).end(); } public XmlWriter tag(String name, String attr1, String value1) throws IOException { return start(name).attr(attr1, value1).end(); } public XmlWriter tag(String name, String attr1, String value1, String attr2, String value2) throws IOException { return start(name).attr(attr1, value1).attr(attr2, value2).end(); } public XmlWriter tag(String name, String attr1, String value1, String attr2, String value2, String attr3, String value3) throws IOException { return start(name).attr(attr1, value1).attr(attr2, value2).attr(attr3, value3).end(); } public XmlWriter tag(String name, String attr1, String value1, String attr2, String value2, String attr3, String value3, String attr4, String value4) throws IOException { return start(name).attr(attr1, value1).attr(attr2, value2).attr(attr3, value3).attr(attr4, value4).end(); } public XmlWriter attr(String attr, String value) throws IOException { if (attr == null) throw new NullPointerException("attr is null"); if (value == null) throw new NullPointerException("value is null"); attrs.append(" "); attrs.append(attr); attrs.append("=\""); attrs.append(escapeXml(value)); attrs.append("\""); return this; } public XmlWriter text(String text) throws IOException { if (text == null) throw new NullPointerException("text is null"); closeOpeningTag(); currentElementIsEmpty = false; writer.write(escapeXml(text)); return this; } private void closeOpeningTag() throws IOException { if (insideProcessingInstruction) end(); if (!rightAngleBracketIsWritten) { writeAttributes(); rightAngleBracketIsWritten = true; writer.write(">"); } } private void writeAttributes() throws IOException { if (attrs.length() > 0) { writer.write(attrs.toString()); attrs.setLength(0); currentElementIsEmpty = false; } } private static String replaceString(String text, String repl, String with) { return replaceString(text, repl, with, -1); } private static String replaceString(String text, String repl, String with, int max) { if (text == null) { return null; } StringBuilder buffer = new StringBuilder(text.length()); int start = 0; int end = 0; while ((end = text.indexOf(repl, start)) != -1) { buffer.append(text.substring(start, end)).append(with); start = end + repl.length(); if (--max == 0) { break; } } buffer.append(text.substring(start)); return buffer.toString(); } private static <T> T pop(List<T> list) { return list.remove(list.size() - 1); } }