diff options
Diffstat (limited to 'main/thirdparty/cgeo/org/kxml2/io/KXmlSerializer.java')
| -rw-r--r-- | main/thirdparty/cgeo/org/kxml2/io/KXmlSerializer.java | 605 |
1 files changed, 605 insertions, 0 deletions
diff --git a/main/thirdparty/cgeo/org/kxml2/io/KXmlSerializer.java b/main/thirdparty/cgeo/org/kxml2/io/KXmlSerializer.java new file mode 100644 index 0000000..027ff53 --- /dev/null +++ b/main/thirdparty/cgeo/org/kxml2/io/KXmlSerializer.java @@ -0,0 +1,605 @@ +/* + * Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package cgeo.org.kxml2.io; + +import org.apache.commons.lang3.StringUtils; +import org.xmlpull.v1.XmlSerializer; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.Locale; + +public class KXmlSerializer implements XmlSerializer { + + // static final String UNDEFINED = ":"; + + // BEGIN android-added + /** size (in characters) for the write buffer */ + private static final int WRITE_BUFFER_SIZE = 500; + // END android-added + + // BEGIN android-changed + // (Guarantee that the writer is always buffered.) + private BufferedWriter writer; + // END android-changed + + private boolean pending; + private int auto; + private int depth; + + private String[] elementStack = new String[12]; + //nsp/prefix/name + private int[] nspCounts = new int[4]; + private String[] nspStack = new String[8]; + //prefix/nsp; both empty are "" + private boolean[] indent = new boolean[4]; + private boolean unicode; + private String encoding; + + private final void check(boolean close) throws IOException { + if (!pending) { + return; + } + + depth++; + pending = false; + + if (indent.length <= depth) { + boolean[] hlp = new boolean[depth + 4]; + System.arraycopy(indent, 0, hlp, 0, depth); + indent = hlp; + } + indent[depth] = indent[depth - 1]; + + for (int i = nspCounts[depth - 1]; i < nspCounts[depth]; i++) { + writer.write(' '); + writer.write("xmlns"); + if (!StringUtils.isEmpty(nspStack[i * 2])) { + writer.write(':'); + writer.write(nspStack[i * 2]); + } + else if (StringUtils.isEmpty(getNamespace()) && !StringUtils.isEmpty(nspStack[i * 2 + 1])) { + throw new IllegalStateException("Cannot set default namespace for elements in no namespace"); + } + writer.write("=\""); + writeEscaped(nspStack[i * 2 + 1], '"'); + writer.write('"'); + } + + if (nspCounts.length <= depth + 1) { + int[] hlp = new int[depth + 8]; + System.arraycopy(nspCounts, 0, hlp, 0, depth + 1); + nspCounts = hlp; + } + + nspCounts[depth + 1] = nspCounts[depth]; + // nspCounts[depth + 2] = nspCounts[depth]; + + writer.write(close ? " />" : ">"); + } + + private final void writeEscaped(String s, int quot) throws IOException { + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + switch (c) { + case '\n': + case '\r': + case '\t': + if (quot == -1) { + writer.write(c); + } else { + writer.write("&#"+((int) c)+';'); + } + break; + case '&': + writer.write("&"); + break; + case '>': + writer.write(">"); + break; + case '<': + writer.write("<"); + break; + default: + if (c == quot) { + writer.write(c == '"' ? """ : "'"); + break; + } + // BEGIN android-changed: refuse to output invalid characters + // See http://www.w3.org/TR/REC-xml/#charsets for definition. + // Corrected for c:geo to handle utf-16 codepoint surrogates correctly + // See http://en.wikipedia.org/wiki/UTF-16#Code_points_U.2B10000_to_U.2B10FFFF + // Note: tab, newline, and carriage return have already been + // handled above. + // Check for lead surrogate + if (c >= 0xd800 && c <= 0xdbff) { + + if (i + 1 < s.length()) { + writer.write(s.substring(i, i + 1)); + i++; + break; + } + // if the lead surrogate is at the string end, it's not valid utf-16 + reportInvalidCharacter(c); + } + boolean valid = (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd); + if (!valid) { + reportInvalidCharacter(c); + } + if (unicode || c < 127) { + writer.write(c); + } else { + writer.write("&#" + ((int) c) + ";"); + } + // END android-changed + } + } + } + + // BEGIN android-added + private static void reportInvalidCharacter(char ch) { + throw new IllegalArgumentException("Illegal character (" + Integer.toHexString((int) ch) + ")"); + } + // END android-added + + /* + * private final void writeIndent() throws IOException { + * writer.write("\r\n"); + * for (int i = 0; i < depth; i++) + * writer.write(' '); + * } + */ + + public void docdecl(String dd) throws IOException { + writer.write("<!DOCTYPE"); + writer.write(dd); + writer.write(">"); + } + + public void endDocument() throws IOException { + while (depth > 0) { + endTag(elementStack[depth * 3 - 3], elementStack[depth * 3 - 1]); + } + flush(); + } + + public void entityRef(String name) throws IOException { + check(false); + writer.write('&'); + writer.write(name); + writer.write(';'); + } + + public boolean getFeature(String name) { + //return false; + return ("http://xmlpull.org/v1/doc/features.html#indent-output" + .equals( + name)) + ? indent[depth] + : false; + } + + public String getPrefix(String namespace, boolean create) { + try { + return getPrefix(namespace, false, create); + } catch (IOException e) { + throw new RuntimeException(e.toString()); + } + } + + private final String getPrefix( + String namespace, + boolean includeDefault, + boolean create) + throws IOException { + + for (int i = nspCounts[depth + 1] * 2 - 2; i >= 0; i -= 2) { + if (nspStack[i + 1].equals(namespace) + && (includeDefault || !StringUtils.isEmpty(nspStack[i]))) { + String cand = nspStack[i]; + for (int j = i + 2; j < nspCounts[depth + 1] * 2; j++) { + if (nspStack[j].equals(cand)) { + cand = null; + break; + } + } + if (cand != null) { + return cand; + } + } + } + + if (!create) { + return null; + } + + String prefix; + + if (StringUtils.isEmpty(namespace)) { + prefix = ""; + } else { + do { + prefix = "n" + (auto++); + for (int i = nspCounts[depth + 1] * 2 - 2; i >= 0; i -= 2) { + if (prefix.equals(nspStack[i])) { + prefix = null; + break; + } + } + } while (prefix == null); + } + + boolean p = pending; + pending = false; + setPrefix(prefix, namespace); + pending = p; + return prefix; + } + + public Object getProperty(String name) { + throw new RuntimeException("Unsupported property"); + } + + public void ignorableWhitespace(String s) + throws IOException { + text(s); + } + + public void setFeature(String name, boolean value) { + if ("http://xmlpull.org/v1/doc/features.html#indent-output" + .equals(name)) { + indent[depth] = value; + } else { + throw new RuntimeException("Unsupported Feature"); + } + } + + public void setProperty(String name, Object value) { + throw new RuntimeException( + "Unsupported Property:" + value); + } + + public void setPrefix(String prefix, String namespace) + throws IOException { + + check(false); + if (prefix == null) { + prefix = ""; + } + if (namespace == null) { + namespace = ""; + } + + String defined = getPrefix(namespace, true, false); + + // boil out if already defined + + if (prefix.equals(defined)) { + return; + } + + int pos = (nspCounts[depth + 1]++) << 1; + + if (nspStack.length < pos + 1) { + String[] hlp = new String[nspStack.length + 16]; + System.arraycopy(nspStack, 0, hlp, 0, pos); + nspStack = hlp; + } + + nspStack[pos++] = prefix; + nspStack[pos] = namespace; + } + + public void setOutput(Writer writer) { + // BEGIN android-changed + // Guarantee that the writer is always buffered. + if (writer instanceof BufferedWriter) { + this.writer = (BufferedWriter) writer; + } else { + this.writer = new BufferedWriter(writer, WRITE_BUFFER_SIZE); + } + // END android-changed + + // elementStack = new String[12]; //nsp/prefix/name + //nspCounts = new int[4]; + //nspStack = new String[8]; //prefix/nsp + //indent = new boolean[4]; + + nspCounts[0] = 2; + nspCounts[1] = 2; + nspStack[0] = ""; + nspStack[1] = ""; + nspStack[2] = "xml"; + nspStack[3] = "http://www.w3.org/XML/1998/namespace"; + pending = false; + auto = 0; + depth = 0; + + unicode = false; + } + + public void setOutput(OutputStream os, String encoding) + throws IOException { + if (os == null) { + throw new IllegalArgumentException("os == null"); + } + setOutput(encoding == null + ? new OutputStreamWriter(os) + : new OutputStreamWriter(os, encoding)); + this.encoding = encoding; + if (encoding != null && encoding.toLowerCase(Locale.US).startsWith("utf")) { + unicode = true; + } + } + + public void startDocument(String encoding, Boolean standalone) throws IOException { + writer.write("<?xml version='1.0' "); + + if (encoding != null) { + this.encoding = encoding; + if (encoding.toLowerCase(Locale.US).startsWith("utf")) { + unicode = true; + } + } + + if (this.encoding != null) { + writer.write("encoding='"); + writer.write(this.encoding); + writer.write("' "); + } + + if (standalone != null) { + writer.write("standalone='"); + writer.write( + standalone.booleanValue() ? "yes" : "no"); + writer.write("' "); + } + writer.write("?>"); + } + + public XmlSerializer startTag(String namespace, String name) + throws IOException { + check(false); + + // if (namespace == null) + // namespace = ""; + + if (indent[depth]) { + writer.write("\r\n"); + for (int i = 0; i < depth; i++) { + writer.write(" "); + } + } + + int esp = depth * 3; + + if (elementStack.length < esp + 3) { + String[] hlp = new String[elementStack.length + 12]; + System.arraycopy(elementStack, 0, hlp, 0, esp); + elementStack = hlp; + } + + String prefix = + namespace == null + ? "" + : getPrefix(namespace, true, true); + + if (namespace != null && StringUtils.isEmpty(namespace)) { + for (int i = nspCounts[depth]; i < nspCounts[depth + 1]; i++) { + if (StringUtils.isEmpty(nspStack[i * 2]) && !StringUtils.isEmpty(nspStack[i * 2 + 1])) { + throw new IllegalStateException("Cannot set default namespace for elements in no namespace"); + } + } + } + + elementStack[esp++] = namespace; + elementStack[esp++] = prefix; + elementStack[esp] = name; + + writer.write('<'); + if (!StringUtils.isEmpty(prefix)) { + writer.write(prefix); + writer.write(':'); + } + + writer.write(name); + + pending = true; + + return this; + } + + public XmlSerializer attribute( + String namespace, + String name, + String value) + throws IOException { + if (!pending) { + throw new IllegalStateException("illegal position for attribute"); + } + + // int cnt = nspCounts[depth]; + + if (namespace == null) { + namespace = ""; + } + + // depth--; + // pending = false; + + String prefix = + StringUtils.isEmpty(namespace) + ? "" + : getPrefix(namespace, false, true); + + // pending = true; + // depth++; + + /* + * if (cnt != nspCounts[depth]) { + * writer.write(' '); + * writer.write("xmlns"); + * if (nspStack[cnt * 2] != null) { + * writer.write(':'); + * writer.write(nspStack[cnt * 2]); + * } + * writer.write("=\""); + * writeEscaped(nspStack[cnt * 2 + 1], '"'); + * writer.write('"'); + * } + */ + + writer.write(' '); + if (!StringUtils.isEmpty(prefix)) { + writer.write(prefix); + writer.write(':'); + } + writer.write(name); + writer.write('='); + char q = value.indexOf('"') == -1 ? '"' : '\''; + writer.write(q); + writeEscaped(value, q); + writer.write(q); + + return this; + } + + public void flush() throws IOException { + check(false); + writer.flush(); + } + + /* + * public void close() throws IOException { + * check(); + * writer.close(); + * } + */ + public XmlSerializer endTag(String namespace, String name) + throws IOException { + + if (!pending) + { + depth--; + // if (namespace == null) + // namespace = ""; + } + + if ((namespace == null + && elementStack[depth * 3] != null) + || (namespace != null + && !namespace.equals(elementStack[depth * 3])) + || !elementStack[depth * 3 + 2].equals(name)) { + throw new IllegalArgumentException("</{"+namespace+"}"+name+"> does not match start"); + } + + if (pending) { + check(true); + depth--; + } + else { + if (indent[depth + 1]) { + writer.write("\r\n"); + for (int i = 0; i < depth; i++) { + writer.write(" "); + } + } + + writer.write("</"); + String prefix = elementStack[depth * 3 + 1]; + if (!StringUtils.isEmpty(prefix)) { + writer.write(prefix); + writer.write(':'); + } + writer.write(name); + writer.write('>'); + } + + nspCounts[depth + 1] = nspCounts[depth]; + return this; + } + + public String getNamespace() { + return getDepth() == 0 ? null : elementStack[getDepth() * 3 - 3]; + } + + public String getName() { + return getDepth() == 0 ? null : elementStack[getDepth() * 3 - 1]; + } + + public int getDepth() { + return pending ? depth + 1 : depth; + } + + public XmlSerializer text(String text) throws IOException { + check(false); + indent[depth] = false; + writeEscaped(text, -1); + return this; + } + + public XmlSerializer text(char[] text, int start, int len) + throws IOException { + text(new String(text, start, len)); + return this; + } + + public void cdsect(String data) throws IOException { + check(false); + // BEGIN android-changed: ]]> is not allowed within a CDATA, + // so break and start a new one when necessary. + data = data.replace("]]>", "]]]]><![CDATA[>"); + char[] chars = data.toCharArray(); + // We also aren't allowed any invalid characters. + for (char ch : chars) { + boolean valid = (ch >= 0x20 && ch <= 0xd7ff) || + (ch == '\t' || ch == '\n' || ch == '\r') || + (ch >= 0xe000 && ch <= 0xfffd); + if (!valid) { + reportInvalidCharacter(ch); + } + } + writer.write("<![CDATA["); + writer.write(chars, 0, chars.length); + writer.write("]]>"); + // END android-changed + } + + public void comment(String comment) throws IOException { + check(false); + writer.write("<!--"); + writer.write(comment); + writer.write("-->"); + } + + public void processingInstruction(String pi) + throws IOException { + check(false); + writer.write("<?"); + writer.write(pi); + writer.write("?>"); + } +}
\ No newline at end of file |
