Merge "Make FastXmlSerializer more suitable to persist arbitrary strings" into nyc-dev

This commit is contained in:
Makoto Onuki
2016-04-06 00:36:51 +00:00
committed by Android (Google) Code Review
2 changed files with 117 additions and 9 deletions

View File

@@ -28,6 +28,7 @@ import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
@@ -38,14 +39,14 @@ import java.nio.charset.UnsupportedCharsetException;
*/
public 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
"�", "", "", "", "", "", "", "", // 0-7
"", "	", "
", "", "", "
", "", "", // 8-15
"", "", "", "", "", "", "", "", // 16-23
"", "", "", "", "", "", "", "", // 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;
@@ -310,7 +311,9 @@ public class FastXmlSerializer implements XmlSerializer {
throw new IllegalArgumentException();
if (true) {
try {
mCharset = Charset.forName(encoding).newEncoder();
mCharset = Charset.forName(encoding).newEncoder()
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE);
} catch (IllegalCharsetNameException e) {
throw (UnsupportedEncodingException) (new UnsupportedEncodingException(
encoding).initCause(e));

View File

@@ -16,16 +16,32 @@
package com.android.internal.util;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import android.util.Xml;
import junit.framework.TestCase;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
/**
* Tests for {@link FastXmlSerializer}
*/
@SmallTest
public class FastXmlSerializerTest extends TestCase {
private static final String TAG = "FastXmlSerializerTest";
private static final boolean ENABLE_DUMP = false; // DO NOT SUBMIT WITH TRUE.
private static final String ROOT_TAG = "root";
private static final String ATTR = "attr";
public void testEmptyText() throws Exception {
final ByteArrayOutputStream stream = new ByteArrayOutputStream();
@@ -44,4 +60,93 @@ public class FastXmlSerializerTest extends TestCase {
assertEquals("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<string name=\"meow\"></string>\n", stream.toString());
}
private boolean checkPreserved(String description, String str) {
boolean ok = true;
byte[] data;
try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
final XmlSerializer out = new FastXmlSerializer();
out.setOutput(baos, StandardCharsets.UTF_16.name());
out.startDocument(null, true);
out.startTag(null, ROOT_TAG);
out.attribute(null, ATTR, str);
out.text(str);
out.endTag(null, ROOT_TAG);
out.endDocument();
baos.flush();
data = baos.toByteArray();
} catch (Exception e) {
Log.e(TAG, "Unable to serialize: " + description, e);
return false;
}
if (ENABLE_DUMP) {
Log.d(TAG, "Dump:");
Log.d(TAG, new String(data));
}
try (final ByteArrayInputStream baos = new ByteArrayInputStream(data)) {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(baos, StandardCharsets.UTF_16.name());
int type;
String tag = null;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.START_TAG) {
tag = parser.getName();
if (ROOT_TAG.equals(tag)) {
String read = parser.getAttributeValue(null, ATTR);
if (!str.equals(read)) {
Log.e(TAG, "Attribute not preserved: " + description
+ " input=\"" + str + "\", but read=\"" + read + "\"");
ok = false;
}
}
}
if (type == XmlPullParser.TEXT && ROOT_TAG.equals(tag)) {
String read = parser.getText();
if (!str.equals(parser.getText())) {
Log.e(TAG, "Text not preserved: " + description
+ " input=\"" + str + "\", but read=\"" + read + "\"");
ok = false;
}
}
}
} catch (Exception e) {
Log.e(TAG, "Unable to parse: " + description, e);
return false;
}
return ok;
}
private boolean check(String description, String str) throws Exception {
boolean ok = false;
ok |= checkPreserved(description, str);
ok |= checkPreserved(description + " wrapped with spaces" ," " + str + " ");
return ok;
}
@LargeTest
public void testAllCharacters() throws Exception {
boolean ok = true;
for (int i = 0; i < 0xffff; i++) {
if (0xd800 <= i && i <= 0xdfff) {
// Surrogate pair characters.
continue;
}
ok &= check("char: " + i, String.valueOf((char) i));
}
// Dangling surrogate pairs. We can't preserve them.
assertFalse(check("+ud800", "\ud800"));
assertFalse(check("+udc00", "\udc00"));
for (int i = 0xd800; i < 0xdc00; i ++) {
for (int j = 0xdc00; j < 0xe000; j++) {
ok &= check("char: " + i, String.valueOf((char) i) + String.valueOf((char) j));
}
}
assertTrue("Some tests failed. See logcat for details.", ok);
}
}