diff --git a/gson/src/main/java/com/google/gson/internal/Streams.java b/gson/src/main/java/com/google/gson/internal/Streams.java index 0bb73aa18e..1fc6839baa 100644 --- a/gson/src/main/java/com/google/gson/internal/Streams.java +++ b/gson/src/main/java/com/google/gson/internal/Streams.java @@ -93,12 +93,31 @@ private static final class AppendableWriter extends Writer { appendable.append(currentWrite, offset, offset + length); } + @Override public void flush() {} + @Override public void close() {} + + // Override these methods for better performance + // They would otherwise unnecessarily create Strings or char arrays + @Override public void write(int i) throws IOException { appendable.append((char) i); } - @Override public void flush() {} - @Override public void close() {} + @Override public void write(String str, int off, int len) throws IOException { + // Appendable.append turns null -> "null", which is not desired here + $Gson$Preconditions.checkNotNull(str); + appendable.append(str, off, off + len); + } + + @Override public Writer append(CharSequence csq) throws IOException { + appendable.append(csq); + return this; + } + + @Override public Writer append(CharSequence csq, int start, int end) throws IOException { + appendable.append(csq, start, end); + return this; + } /** * A mutable char sequence pointing at a single char[]. diff --git a/gson/src/test/java/com/google/gson/internal/StreamsTest.java b/gson/src/test/java/com/google/gson/internal/StreamsTest.java new file mode 100644 index 0000000000..d0cb90aa5b --- /dev/null +++ b/gson/src/test/java/com/google/gson/internal/StreamsTest.java @@ -0,0 +1,68 @@ +package com.google.gson.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.io.Writer; +import org.junit.Test; + +public class StreamsTest { + @Test + public void testWriterForAppendable() throws IOException { + StringBuilder stringBuilder = new StringBuilder(); + Writer writer = Streams.writerForAppendable(stringBuilder); + + writer.append('a'); + writer.append('\u1234'); + writer.append("test"); + writer.append(null); // test custom null handling mandated by `append` + writer.append("abcdef", 2, 4); + writer.append(null, 1, 3); // test custom null handling mandated by `append` + writer.append(','); + + writer.write('a'); + writer.write('\u1234'); + // Should only consider the 16 low-order bits + writer.write(0x4321_1234); + writer.append(','); + + writer.write("chars".toCharArray()); + try { + writer.write((char[]) null); + fail(); + } catch (NullPointerException e) { + } + + writer.write("chars".toCharArray(), 1, 2); + try { + writer.write((char[]) null, 1, 2); + fail(); + } catch (NullPointerException e) { + } + writer.append(','); + + writer.write("string"); + try { + writer.write((String) null); + fail(); + } catch (NullPointerException e) { + } + + writer.write("string", 1, 2); + try { + writer.write((String) null, 1, 2); + fail(); + } catch (NullPointerException e) { + } + + String actualOutput = stringBuilder.toString(); + assertEquals("a\u1234testnullcdul,a\u1234\u1234,charsha,stringtr", actualOutput); + + writer.flush(); + writer.close(); + + // flush() and close() calls should have had no effect + assertEquals(actualOutput, stringBuilder.toString()); + } +}