/* * Copyright (C) 2006 The Android Open Source Project * * 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.doist.jobschedulercompat.util; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import androidx.annotation.RestrictTo; /** * Same as com.android.internal.util.XmlUtils, with minor modifications and unused code removed. */ @RestrictTo(RestrictTo.Scope.LIBRARY) public class XmlUtils { private static void writeMapXml(Map val, String name, XmlSerializer out) throws XmlPullParserException, IOException { if (val == null) { out.startTag(null, "null"); out.endTag(null, "null"); return; } out.startTag(null, "map"); if (name != null) { out.attribute(null, "name", name); } writeMapXml(val, out); out.endTag(null, "map"); } public static final void writeMapXml(Map val, XmlSerializer out) throws XmlPullParserException, IOException { if (val == null) { return; } Set s = val.entrySet(); Iterator i = s.iterator(); while (i.hasNext()) { Map.Entry e = (Map.Entry) i.next(); writeValueXml(e.getValue(), (String) e.getKey(), out); } } private static void writeListXml(List val, String name, XmlSerializer out) throws XmlPullParserException, IOException { if (val == null) { out.startTag(null, "null"); out.endTag(null, "null"); return; } out.startTag(null, "list"); if (name != null) { out.attribute(null, "name", name); } int N = val.size(); int i = 0; while (i < N) { writeValueXml(val.get(i), null, out); i++; } out.endTag(null, "list"); } private static void writeSetXml(Set val, String name, XmlSerializer out) throws XmlPullParserException, IOException { if (val == null) { out.startTag(null, "null"); out.endTag(null, "null"); return; } out.startTag(null, "set"); if (name != null) { out.attribute(null, "name", name); } for (Object v : val) { writeValueXml(v, null, out); } out.endTag(null, "set"); } private static void writeByteArrayXml(byte[] val, String name, XmlSerializer out) throws XmlPullParserException, IOException { if (val == null) { out.startTag(null, "null"); out.endTag(null, "null"); return; } out.startTag(null, "byte-array"); if (name != null) { out.attribute(null, "name", name); } final int N = val.length; out.attribute(null, "num", Integer.toString(N)); StringBuilder sb = new StringBuilder(val.length * 2); for (int i = 0; i < N; i++) { int b = val[i]; int h = (b >> 4) & 0x0f; sb.append((char) (h >= 10 ? ('a' + h - 10) : ('0' + h))); h = b & 0x0f; sb.append((char) (h >= 10 ? ('a' + h - 10) : ('0' + h))); } out.text(sb.toString()); out.endTag(null, "byte-array"); } private static void writeIntArrayXml(int[] val, String name, XmlSerializer out) throws XmlPullParserException, IOException { if (val == null) { out.startTag(null, "null"); out.endTag(null, "null"); return; } out.startTag(null, "int-array"); if (name != null) { out.attribute(null, "name", name); } final int N = val.length; out.attribute(null, "num", Integer.toString(N)); for (int i = 0; i < N; i++) { out.startTag(null, "item"); out.attribute(null, "value", Integer.toString(val[i])); out.endTag(null, "item"); } out.endTag(null, "int-array"); } private static void writeLongArrayXml(long[] val, String name, XmlSerializer out) throws XmlPullParserException, IOException { if (val == null) { out.startTag(null, "null"); out.endTag(null, "null"); return; } out.startTag(null, "long-array"); if (name != null) { out.attribute(null, "name", name); } final int N = val.length; out.attribute(null, "num", Integer.toString(N)); for (int i = 0; i < N; i++) { out.startTag(null, "item"); out.attribute(null, "value", Long.toString(val[i])); out.endTag(null, "item"); } out.endTag(null, "long-array"); } private static void writeDoubleArrayXml(double[] val, String name, XmlSerializer out) throws XmlPullParserException, IOException { if (val == null) { out.startTag(null, "null"); out.endTag(null, "null"); return; } out.startTag(null, "double-array"); if (name != null) { out.attribute(null, "name", name); } final int N = val.length; out.attribute(null, "num", Integer.toString(N)); for (int i = 0; i < N; i++) { out.startTag(null, "item"); out.attribute(null, "value", Double.toString(val[i])); out.endTag(null, "item"); } out.endTag(null, "double-array"); } private static void writeStringArrayXml(String[] val, String name, XmlSerializer out) throws XmlPullParserException, IOException { if (val == null) { out.startTag(null, "null"); out.endTag(null, "null"); return; } out.startTag(null, "string-array"); if (name != null) { out.attribute(null, "name", name); } final int N = val.length; out.attribute(null, "num", Integer.toString(N)); for (int i = 0; i < N; i++) { out.startTag(null, "item"); out.attribute(null, "value", val[i]); out.endTag(null, "item"); } out.endTag(null, "string-array"); } private static void writeBooleanArrayXml(boolean[] val, String name, XmlSerializer out) throws XmlPullParserException, IOException { if (val == null) { out.startTag(null, "null"); out.endTag(null, "null"); return; } out.startTag(null, "boolean-array"); if (name != null) { out.attribute(null, "name", name); } final int N = val.length; out.attribute(null, "num", Integer.toString(N)); for (int i = 0; i < N; i++) { out.startTag(null, "item"); out.attribute(null, "value", Boolean.toString(val[i])); out.endTag(null, "item"); } out.endTag(null, "boolean-array"); } private static void writeValueXml(Object v, String name, XmlSerializer out) throws XmlPullParserException, IOException { String typeStr; if (v == null) { out.startTag(null, "null"); if (name != null) { out.attribute(null, "name", name); } out.endTag(null, "null"); return; } else if (v instanceof String) { out.startTag(null, "string"); if (name != null) { out.attribute(null, "name", name); } out.text(v.toString()); out.endTag(null, "string"); return; } else if (v instanceof Integer) { typeStr = "int"; } else if (v instanceof Long) { typeStr = "long"; } else if (v instanceof Float) { typeStr = "float"; } else if (v instanceof Double) { typeStr = "double"; } else if (v instanceof Boolean) { typeStr = "boolean"; } else if (v instanceof byte[]) { writeByteArrayXml((byte[]) v, name, out); return; } else if (v instanceof int[]) { writeIntArrayXml((int[]) v, name, out); return; } else if (v instanceof long[]) { writeLongArrayXml((long[]) v, name, out); return; } else if (v instanceof double[]) { writeDoubleArrayXml((double[]) v, name, out); return; } else if (v instanceof String[]) { writeStringArrayXml((String[]) v, name, out); return; } else if (v instanceof boolean[]) { writeBooleanArrayXml((boolean[]) v, name, out); return; } else if (v instanceof Map) { writeMapXml((Map) v, name, out); return; } else if (v instanceof List) { writeListXml((List) v, name, out); return; } else if (v instanceof Set) { writeSetXml((Set) v, name, out); return; } else if (v instanceof CharSequence) { // XXX This is to allow us to at least write something if // we encounter styled text... but it means we will drop all // of the styling information. :( out.startTag(null, "string"); if (name != null) { out.attribute(null, "name", name); } out.text(v.toString()); out.endTag(null, "string"); return; } else { throw new RuntimeException("writeValueXml: unable to write value " + v); } out.startTag(null, typeStr); if (name != null) { out.attribute(null, "name", name); } out.attribute(null, "value", v.toString()); out.endTag(null, typeStr); } public static final HashMap<String, ?> readMapXml(XmlPullParser parser, String endTag) throws XmlPullParserException, IOException { return readThisMapXml(parser, endTag, new String[1]); } private static HashMap<String, ?> readThisMapXml(XmlPullParser parser, String endTag, String[] name) throws XmlPullParserException, IOException { HashMap<String, Object> map = new HashMap<>(); int eventType = parser.getEventType(); do { if (eventType == XmlPullParser.START_TAG) { Object val = readThisValueXml(parser, name); map.put(name[0], val); } else if (eventType == XmlPullParser.END_TAG) { if (parser.getName().equals(endTag)) { return map; } throw new XmlPullParserException("Expected " + endTag + " end tag at: " + parser.getName()); } eventType = parser.next(); } while (eventType != XmlPullParser.END_DOCUMENT); throw new XmlPullParserException("Document ended before " + endTag + " end tag"); } @SuppressWarnings("unchecked") private static ArrayList readThisListXml(XmlPullParser parser, String endTag, String[] name) throws XmlPullParserException, IOException { ArrayList list = new ArrayList(); int eventType = parser.getEventType(); do { if (eventType == XmlPullParser.START_TAG) { Object val = readThisValueXml(parser, name); list.add(val); } else if (eventType == XmlPullParser.END_TAG) { if (parser.getName().equals(endTag)) { return list; } throw new XmlPullParserException("Expected " + endTag + " end tag at: " + parser.getName()); } eventType = parser.next(); } while (eventType != XmlPullParser.END_DOCUMENT); throw new XmlPullParserException("Document ended before " + endTag + " end tag"); } @SuppressWarnings("unchecked") private static HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name) throws XmlPullParserException, IOException { HashSet set = new HashSet(); int eventType = parser.getEventType(); do { if (eventType == XmlPullParser.START_TAG) { Object val = readThisValueXml(parser, name); set.add(val); } else if (eventType == XmlPullParser.END_TAG) { if (parser.getName().equals(endTag)) { return set; } throw new XmlPullParserException("Expected " + endTag + " end tag at: " + parser.getName()); } eventType = parser.next(); } while (eventType != XmlPullParser.END_DOCUMENT); throw new XmlPullParserException("Document ended before " + endTag + " end tag"); } private static byte[] readThisByteArrayXml(XmlPullParser parser, String endTag) throws XmlPullParserException, IOException { int num; try { num = Integer.parseInt(parser.getAttributeValue(null, "num")); } catch (NullPointerException e) { throw new XmlPullParserException("Need num attribute in byte-array"); } catch (NumberFormatException e) { throw new XmlPullParserException("Not a number in num attribute in byte-array"); } byte[] array = new byte[num]; int eventType = parser.getEventType(); do { if (eventType == XmlPullParser.TEXT) { if (num > 0) { String values = parser.getText(); if (values == null || values.length() != num * 2) { throw new XmlPullParserException("Invalid value found in byte-array: " + values); } // This is ugly, but keeping it to mirror the logic in #writeByteArrayXml. for (int i = 0; i < num; i++) { char nibbleHighChar = values.charAt(2 * i); char nibbleLowChar = values.charAt(2 * i + 1); int nibbleHigh = nibbleHighChar > 'a' ? (nibbleHighChar - 'a' + 10) : (nibbleHighChar - '0'); int nibbleLow = nibbleLowChar > 'a' ? (nibbleLowChar - 'a' + 10) : (nibbleLowChar - '0'); array[i] = (byte) ((nibbleHigh & 0x0F) << 4 | (nibbleLow & 0x0F)); } } } else if (eventType == XmlPullParser.END_TAG) { if (parser.getName().equals(endTag)) { return array; } else { throw new XmlPullParserException("Expected " + endTag + " end tag at: " + parser.getName()); } } eventType = parser.next(); } while (eventType != XmlPullParser.END_DOCUMENT); throw new XmlPullParserException("Document ended before " + endTag + " end tag"); } private static int[] readThisIntArrayXml(XmlPullParser parser, String endTag) throws XmlPullParserException, IOException { int num; try { num = Integer.parseInt(parser.getAttributeValue(null, "num")); } catch (NullPointerException e) { throw new XmlPullParserException("Need num attribute in int-array"); } catch (NumberFormatException e) { throw new XmlPullParserException("Not a number in num attribute in int-array"); } parser.next(); int[] array = new int[num]; int i = 0; int eventType = parser.getEventType(); do { if (eventType == XmlPullParser.START_TAG) { if (parser.getName().equals("item")) { try { array[i] = Integer.parseInt(parser.getAttributeValue(null, "value")); } catch (NullPointerException e) { throw new XmlPullParserException("Need value attribute in item"); } catch (NumberFormatException e) { throw new XmlPullParserException("Not a number in value attribute in item"); } } else { throw new XmlPullParserException("Expected item tag at: " + parser.getName()); } } else if (eventType == XmlPullParser.END_TAG) { if (parser.getName().equals(endTag)) { return array; } else if (parser.getName().equals("item")) { i++; } else { throw new XmlPullParserException("Expected " + endTag + " end tag at: " + parser.getName()); } } eventType = parser.next(); } while (eventType != XmlPullParser.END_DOCUMENT); throw new XmlPullParserException("Document ended before " + endTag + " end tag"); } private static long[] readThisLongArrayXml(XmlPullParser parser, String endTag) throws XmlPullParserException, IOException { int num; try { num = Integer.parseInt(parser.getAttributeValue(null, "num")); } catch (NullPointerException e) { throw new XmlPullParserException("Need num attribute in long-array"); } catch (NumberFormatException e) { throw new XmlPullParserException("Not a number in num attribute in long-array"); } parser.next(); long[] array = new long[num]; int i = 0; int eventType = parser.getEventType(); do { if (eventType == XmlPullParser.START_TAG) { if (parser.getName().equals("item")) { try { array[i] = Long.parseLong(parser.getAttributeValue(null, "value")); } catch (NullPointerException e) { throw new XmlPullParserException("Need value attribute in item"); } catch (NumberFormatException e) { throw new XmlPullParserException("Not a number in value attribute in item"); } } else { throw new XmlPullParserException("Expected item tag at: " + parser.getName()); } } else if (eventType == XmlPullParser.END_TAG) { if (parser.getName().equals(endTag)) { return array; } else if (parser.getName().equals("item")) { i++; } else { throw new XmlPullParserException("Expected " + endTag + " end tag at: " + parser.getName()); } } eventType = parser.next(); } while (eventType != XmlPullParser.END_DOCUMENT); throw new XmlPullParserException("Document ended before " + endTag + " end tag"); } private static double[] readThisDoubleArrayXml(XmlPullParser parser, String endTag) throws XmlPullParserException, IOException { int num; try { num = Integer.parseInt(parser.getAttributeValue(null, "num")); } catch (NullPointerException e) { throw new XmlPullParserException("Need num attribute in double-array"); } catch (NumberFormatException e) { throw new XmlPullParserException("Not a number in num attribute in double-array"); } parser.next(); double[] array = new double[num]; int i = 0; int eventType = parser.getEventType(); do { if (eventType == XmlPullParser.START_TAG) { if (parser.getName().equals("item")) { try { array[i] = Double.parseDouble(parser.getAttributeValue(null, "value")); } catch (NullPointerException e) { throw new XmlPullParserException("Need value attribute in item"); } catch (NumberFormatException e) { throw new XmlPullParserException("Not a number in value attribute in item"); } } else { throw new XmlPullParserException("Expected item tag at: " + parser.getName()); } } else if (eventType == XmlPullParser.END_TAG) { if (parser.getName().equals(endTag)) { return array; } else if (parser.getName().equals("item")) { i++; } else { throw new XmlPullParserException("Expected " + endTag + " end tag at: " + parser.getName()); } } eventType = parser.next(); } while (eventType != XmlPullParser.END_DOCUMENT); throw new XmlPullParserException("Document ended before " + endTag + " end tag"); } private static String[] readThisStringArrayXml(XmlPullParser parser, String endTag) throws XmlPullParserException, IOException { int num; try { num = Integer.parseInt(parser.getAttributeValue(null, "num")); } catch (NullPointerException e) { throw new XmlPullParserException("Need num attribute in string-array"); } catch (NumberFormatException e) { throw new XmlPullParserException("Not a number in num attribute in string-array"); } parser.next(); String[] array = new String[num]; int i = 0; int eventType = parser.getEventType(); do { if (eventType == XmlPullParser.START_TAG) { if (parser.getName().equals("item")) { try { array[i] = parser.getAttributeValue(null, "value"); } catch (NullPointerException e) { throw new XmlPullParserException("Need value attribute in item"); } catch (NumberFormatException e) { throw new XmlPullParserException("Not a number in value attribute in item"); } } else { throw new XmlPullParserException("Expected item tag at: " + parser.getName()); } } else if (eventType == XmlPullParser.END_TAG) { if (parser.getName().equals(endTag)) { return array; } else if (parser.getName().equals("item")) { i++; } else { throw new XmlPullParserException("Expected " + endTag + " end tag at: " + parser.getName()); } } eventType = parser.next(); } while (eventType != XmlPullParser.END_DOCUMENT); throw new XmlPullParserException("Document ended before " + endTag + " end tag"); } private static boolean[] readThisBooleanArrayXml(XmlPullParser parser, String endTag) throws XmlPullParserException, IOException { int num; try { num = Integer.parseInt(parser.getAttributeValue(null, "num")); } catch (NullPointerException e) { throw new XmlPullParserException("Need num attribute in string-array"); } catch (NumberFormatException e) { throw new XmlPullParserException("Not a number in num attribute in string-array"); } parser.next(); boolean[] array = new boolean[num]; int i = 0; int eventType = parser.getEventType(); do { if (eventType == XmlPullParser.START_TAG) { if (parser.getName().equals("item")) { try { array[i] = Boolean.parseBoolean(parser.getAttributeValue(null, "value")); } catch (NullPointerException e) { throw new XmlPullParserException("Need value attribute in item"); } catch (NumberFormatException e) { throw new XmlPullParserException("Not a number in value attribute in item"); } } else { throw new XmlPullParserException("Expected item tag at: " + parser.getName()); } } else if (eventType == XmlPullParser.END_TAG) { if (parser.getName().equals(endTag)) { return array; } else if (parser.getName().equals("item")) { i++; } else { throw new XmlPullParserException("Expected " + endTag + " end tag at: " + parser.getName()); } } eventType = parser.next(); } while (eventType != XmlPullParser.END_DOCUMENT); throw new XmlPullParserException("Document ended before " + endTag + " end tag"); } private static Object readThisValueXml(XmlPullParser parser, String[] name) throws XmlPullParserException, IOException { final String valueName = parser.getAttributeValue(null, "name"); final String tagName = parser.getName(); Object res; if (tagName.equals("null")) { res = null; } else if (tagName.equals("string")) { String value = ""; int eventType; while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) { if (eventType == XmlPullParser.END_TAG) { if (parser.getName().equals("string")) { name[0] = valueName; return value; } throw new XmlPullParserException("Unexpected end tag in <string>: " + parser.getName()); } else if (eventType == XmlPullParser.TEXT) { value += parser.getText(); } else if (eventType == XmlPullParser.START_TAG) { throw new XmlPullParserException("Unexpected start tag in <string>: " + parser.getName()); } } throw new XmlPullParserException("Unexpected end of document in <string>"); } else if ((res = readThisPrimitiveValueXml(parser, tagName)) != null) { // All work already done by readThisPrimitiveValueXml. } else if (tagName.equals("byte-array")) { res = readThisByteArrayXml(parser, "byte-array"); name[0] = valueName; return res; } else if (tagName.equals("int-array")) { res = readThisIntArrayXml(parser, "int-array"); name[0] = valueName; return res; } else if (tagName.equals("long-array")) { res = readThisLongArrayXml(parser, "long-array"); name[0] = valueName; return res; } else if (tagName.equals("double-array")) { res = readThisDoubleArrayXml(parser, "double-array"); name[0] = valueName; return res; } else if (tagName.equals("string-array")) { res = readThisStringArrayXml(parser, "string-array"); name[0] = valueName; return res; } else if (tagName.equals("boolean-array")) { res = readThisBooleanArrayXml(parser, "boolean-array"); name[0] = valueName; return res; } else if (tagName.equals("map")) { parser.next(); res = readThisMapXml(parser, "map", name); name[0] = valueName; return res; } else if (tagName.equals("list")) { parser.next(); res = readThisListXml(parser, "list", name); name[0] = valueName; return res; } else if (tagName.equals("set")) { parser.next(); res = readThisSetXml(parser, "set", name); name[0] = valueName; return res; } else { throw new XmlPullParserException("Unknown tag: " + tagName); } // Skip through to end tag. int eventType; while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) { if (eventType == XmlPullParser.END_TAG) { if (parser.getName().equals(tagName)) { name[0] = valueName; return res; } throw new XmlPullParserException("Unexpected end tag in <" + tagName + ">: " + parser.getName()); } else if (eventType == XmlPullParser.TEXT) { throw new XmlPullParserException("Unexpected text in <" + tagName + ">: " + parser.getName()); } else if (eventType == XmlPullParser.START_TAG) { throw new XmlPullParserException("Unexpected start tag in <" + tagName + ">: " + parser.getName()); } } throw new XmlPullParserException("Unexpected end of document in <" + tagName + ">"); } private static Object readThisPrimitiveValueXml(XmlPullParser parser, String tagName) throws XmlPullParserException, IOException { try { if (tagName.equals("int")) { return Integer.parseInt(parser.getAttributeValue(null, "value")); } else if (tagName.equals("long")) { return Long.valueOf(parser.getAttributeValue(null, "value")); } else if (tagName.equals("float")) { return Float.valueOf(parser.getAttributeValue(null, "value")); } else if (tagName.equals("double")) { return Double.valueOf(parser.getAttributeValue(null, "value")); } else if (tagName.equals("boolean")) { return Boolean.valueOf(parser.getAttributeValue(null, "value")); } else { return null; } } catch (NullPointerException e) { throw new XmlPullParserException("Need value attribute in <" + tagName + ">"); } catch (NumberFormatException e) { throw new XmlPullParserException("Not a number in value attribute in <" + tagName + ">"); } } public static class FastXmlSerializer implements XmlSerializer { private static final String ESCAPE_TABLE[] = new String[]{ null, null, null, null, null, null, null, null, // 0-7 null, null, null, null, null, null, null, null, // 8-15 null, null, null, null, null, null, null, null, // 16-23 null, null, null, null, null, null, null, null, // 24-31 null, null, """, null, null, null, "&", null, // 32-39 null, null, null, null, null, null, null, null, // 40-47 null, null, null, null, null, null, null, null, // 48-55 null, null, null, null, "<", null, ">", null, // 56-63 }; private static final int BUFFER_LEN = 8192; private final char[] text = new char[BUFFER_LEN]; private int pos; private Writer writer; private OutputStream out; private CharsetEncoder charset; private ByteBuffer bytes = ByteBuffer.allocate(BUFFER_LEN); private boolean inTag; private void append(char c) throws IOException { int pos = this.pos; if (pos >= (BUFFER_LEN - 1)) { flush(); pos = this.pos; } text[pos] = c; this.pos = pos + 1; } private void append(String str, int i, final int length) throws IOException { if (length > BUFFER_LEN) { final int end = i + length; while (i < end) { int next = i + BUFFER_LEN; append(str, i, next < end ? BUFFER_LEN : (end - i)); i = next; } return; } int pos = this.pos; if ((pos + length) > BUFFER_LEN) { flush(); pos = this.pos; } str.getChars(i, i + length, text, pos); this.pos = pos + length; } private void append(char[] buf, int i, final int length) throws IOException { if (length > BUFFER_LEN) { final int end = i + length; while (i < end) { int next = i + BUFFER_LEN; append(buf, i, next < end ? BUFFER_LEN : (end - i)); i = next; } return; } int pos = this.pos; if ((pos + length) > BUFFER_LEN) { flush(); pos = this.pos; } System.arraycopy(buf, i, text, pos, length); this.pos = pos + length; } private void append(String str) throws IOException { append(str, 0, str.length()); } private void escapeAndAppendString(final String string) throws IOException { final int N = string.length(); final char NE = (char) ESCAPE_TABLE.length; final String[] escapes = ESCAPE_TABLE; int lastPos = 0; int pos; for (pos = 0; pos < N; pos++) { char c = string.charAt(pos); if (c >= NE) { continue; } String escape = escapes[c]; if (escape == null) { continue; } if (lastPos < pos) { append(string, lastPos, pos - lastPos); } lastPos = pos + 1; append(escape); } if (lastPos < pos) { append(string, lastPos, pos - lastPos); } } private void escapeAndAppendString(char[] buf, int start, int len) throws IOException { final char NE = (char) ESCAPE_TABLE.length; final String[] escapes = ESCAPE_TABLE; int end = start + len; int lastPos = start; int pos; for (pos = start; pos < end; pos++) { char c = buf[pos]; if (c >= NE) { continue; } String escape = escapes[c]; if (escape == null) { continue; } if (lastPos < pos) { append(buf, lastPos, pos - lastPos); } lastPos = pos + 1; append(escape); } if (lastPos < pos) { append(buf, lastPos, pos - lastPos); } } public XmlSerializer attribute(String namespace, String name, String value) throws IOException, IllegalArgumentException, IllegalStateException { append(' '); if (namespace != null) { append(namespace); append(':'); } append(name); append("=\""); escapeAndAppendString(value); append('"'); return this; } public void cdsect(String text) throws IOException, IllegalArgumentException, IllegalStateException { throw new UnsupportedOperationException(); } public void comment(String text) throws IOException, IllegalArgumentException, IllegalStateException { throw new UnsupportedOperationException(); } public void docdecl(String text) throws IOException, IllegalArgumentException, IllegalStateException { throw new UnsupportedOperationException(); } public void endDocument() throws IOException, IllegalArgumentException, IllegalStateException { flush(); } public XmlSerializer endTag(String namespace, String name) throws IOException, IllegalArgumentException, IllegalStateException { if (inTag) { append(" />\n"); } else { append("</"); if (namespace != null) { append(namespace); append(':'); } append(name); append(">\n"); } inTag = false; return this; } public void entityRef(String text) throws IOException, IllegalArgumentException, IllegalStateException { throw new UnsupportedOperationException(); } private void flushBytes() throws IOException { int position; if ((position = bytes.position()) > 0) { bytes.flip(); out.write(bytes.array(), 0, position); bytes.clear(); } } public void flush() throws IOException { if (pos > 0) { if (out != null) { CharBuffer charBuffer = CharBuffer.wrap(text, 0, pos); CoderResult result = charset.encode(charBuffer, bytes, true); while (true) { if (result.isError()) { throw new IOException(result.toString()); } else if (result.isOverflow()) { flushBytes(); result = charset.encode(charBuffer, bytes, true); continue; } break; } flushBytes(); out.flush(); } else { writer.write(text, 0, pos); writer.flush(); } pos = 0; } } public int getDepth() { throw new UnsupportedOperationException(); } public boolean getFeature(String name) { throw new UnsupportedOperationException(); } public String getName() { throw new UnsupportedOperationException(); } public String getNamespace() { throw new UnsupportedOperationException(); } public String getPrefix(String namespace, boolean generatePrefix) throws IllegalArgumentException { throw new UnsupportedOperationException(); } public Object getProperty(String name) { throw new UnsupportedOperationException(); } public void ignorableWhitespace(String text) throws IOException, IllegalArgumentException, IllegalStateException { throw new UnsupportedOperationException(); } public void processingInstruction(String text) throws IOException, IllegalArgumentException, IllegalStateException { throw new UnsupportedOperationException(); } public void setFeature(String name, boolean state) throws IllegalArgumentException, IllegalStateException { if (name.equals("http://xmlpull.org/v1/doc/features.html#indent-output")) { return; } throw new UnsupportedOperationException(); } public void setOutput(OutputStream os, String encoding) throws IOException, IllegalArgumentException, IllegalStateException { if (os == null) { throw new IllegalArgumentException(); } try { charset = Charset.forName(encoding).newEncoder(); } catch (IllegalCharsetNameException | UnsupportedCharsetException e) { throw (UnsupportedEncodingException) (new UnsupportedEncodingException(encoding).initCause(e)); } out = os; } public void setOutput(Writer writer) throws IOException, IllegalArgumentException, IllegalStateException { this.writer = writer; } public void setPrefix(String prefix, String namespace) throws IOException, IllegalArgumentException, IllegalStateException { throw new UnsupportedOperationException(); } public void setProperty(String name, Object value) throws IllegalArgumentException, IllegalStateException { throw new UnsupportedOperationException(); } public void startDocument(String encoding, Boolean standalone) throws IOException, IllegalArgumentException, IllegalStateException { append("<?xml version='1.0' encoding='utf-8' standalone='" + (standalone ? "yes" : "no") + "' ?>\n"); } public XmlSerializer startTag(String namespace, String name) throws IOException, IllegalArgumentException, IllegalStateException { if (inTag) { append(">\n"); } append('<'); if (namespace != null) { append(namespace); append(':'); } append(name); inTag = true; return this; } public XmlSerializer text(char[] buf, int start, int len) throws IOException, IllegalArgumentException, IllegalStateException { if (inTag) { append(">"); inTag = false; } escapeAndAppendString(buf, start, len); return this; } public XmlSerializer text(String text) throws IOException, IllegalArgumentException, IllegalStateException { if (inTag) { append(">"); inTag = false; } escapeAndAppendString(text); return this; } } }