/* * Copyright (c) 2013 Menny Even-Danan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.anysoftkeyboard.utils; import java.io.*; import java.util.Stack; /** * Makes writing XML much much easier. * * @author <a href="mailto:bayard@generationjava.com">Henri Yandell</a> * @author <a href="mailto:menny|AT| evendanan{dot} net">Menny Even Danan - just * added some features on Henri's initial version</a> * @version 0.2 */ public class XmlWriter { private static final String INDENT_STRING = " "; private final boolean thisIsWriterOwner;// is this instance the owner? private final Writer writer; // underlying writer private final int indentingOffset; private final Stack<String> stack; // of xml entity names private final StringBuffer attrs; // current attribute string private boolean empty; // is the current node empty private boolean justWroteText; private boolean closed; // is the current node closed... /** * Create an XmlWriter on top of an existing java.io.Writer. * * @throws IOException */ public XmlWriter(Writer writer, boolean takeOwnership, int indentingOffset, boolean addXmlPrefix) throws IOException { thisIsWriterOwner = takeOwnership; this.indentingOffset = indentingOffset; this.writer = writer; this.closed = true; this.stack = new Stack<String>(); this.attrs = new StringBuffer(); if (addXmlPrefix) this.writer.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); } public XmlWriter(File outputFile) throws IOException { this(new FileWriter(outputFile), true, 0, true); } /** * Begin to output an entity. * * @param String name of entity. */ public XmlWriter writeEntity(String name) throws IOException { closeOpeningTag(true); this.closed = false; for (int tabIndex = 0; tabIndex < stack.size() + indentingOffset; tabIndex++) this.writer.write(INDENT_STRING); this.writer.write("<"); this.writer.write(name); stack.add(name); this.empty = true; this.justWroteText = false; return this; } // close off the opening tag private void closeOpeningTag(final boolean newLine) throws IOException { if (!this.closed) { writeAttributes(); this.closed = true; this.writer.write(">"); if (newLine) this.writer.write("\n"); } } // write out all current attributes private void writeAttributes() throws IOException { this.writer.write(this.attrs.toString()); this.attrs.setLength(0); this.empty = false; } /** * Write an attribute out for the current entity. Any xml characters in the * value are escaped. Currently it does not actually throw the exception, * but the api is set that way for future changes. * * @param String name of attribute. * @param String value of attribute. */ public XmlWriter writeAttribute(String attr, String value) { this.attrs.append(" "); this.attrs.append(attr); this.attrs.append("=\""); this.attrs.append(escapeXml(value)); this.attrs.append("\""); return this; } /** * End the current entity. This will throw an exception if it is called when * there is not a currently open entity. * * @throws IOException */ public XmlWriter endEntity() throws IOException { if (this.stack.empty()) { throw new InvalidObjectException("Called endEntity too many times. "); } String name = this.stack.pop(); if (name != null) { if (this.empty) { writeAttributes(); this.writer.write("/>\n"); } else { if (!this.justWroteText) { for (int tabIndex = 0; tabIndex < stack.size() + indentingOffset; tabIndex++) this.writer.write(INDENT_STRING); } this.writer.write("</"); this.writer.write(name); this.writer.write(">\n"); } this.empty = false; this.closed = true; this.justWroteText = false; } return this; } /** * Close this writer. It does not close the underlying writer, but does * throw an exception if there are as yet unclosed tags. * * @throws IOException */ public void close() throws IOException { if (!this.stack.empty()) { throw new InvalidObjectException("Tags are not all closed. " + "Possibly, " + this.stack.pop() + " is unclosed. "); } if (thisIsWriterOwner) { this.writer.flush(); this.writer.close(); } } /** * Output body text. Any xml characters are escaped. */ public XmlWriter writeText(String text) throws IOException { closeOpeningTag(false); this.empty = false; this.justWroteText = true; this.writer.write(escapeXml(text)); return this; } // Static functions lifted from generationjava helper classes // to make the jar smaller. // from XmlW static public String escapeXml(String str) { str = replaceString(str, "&", "&"); str = replaceString(str, "<", "<"); str = replaceString(str, ">", ">"); str = replaceString(str, "\"", """); str = replaceString(str, "'", "'"); return str; } // from StringW static public String replaceString(String text, String repl, String with) { return replaceString(text, repl, with, -1); } /** * Replace a string with another string inside a larger string, for the * first n values of the search string. * * @param text String to do search and replace in * @param repl String to search for * @param with String to replace with * @param n int values to replace * @return String with n values replacEd */ static public String replaceString(String text, String repl, String with, int max) { if (text == null) { return null; } StringBuffer buffer = new StringBuffer(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(); } // // static public void test1() throws WritingException { // Writer writer = new java.io.StringWriter(); // XmlWriter xmlwriter = new XmlWriter(writer); // xmlwriter.writeEntity("person").writeAttribute("name", // "fred").writeAttribute("age", // "12").writeEntity("phone").writeText("4254343").endEntity().writeEntity("bob").endEntity().endEntity(); // xmlwriter.close(); // System.err.println(writer.toString()); // } // static public void test2() throws WritingException { // Writer writer = new java.io.StringWriter(); // XmlWriter xmlwriter = new XmlWriter(writer); // xmlwriter.writeEntity("person"); // xmlwriter.writeAttribute("name", "fred"); // xmlwriter.writeAttribute("age", "12"); // xmlwriter.writeEntity("phone"); // xmlwriter.writeText("4254343"); // xmlwriter.endEntity(); // xmlwriter.writeEntity("bob"); // xmlwriter.endEntity(); // xmlwriter.endEntity(); // xmlwriter.close(); // System.err.println(writer.toString()); // } }