diff options
| -rw-r--r-- | core/java/com/android/internal/os/LoggingPrintStream.java | 81 | ||||
| -rw-r--r-- | tests/framework-tests/src/com/android/internal/os/LoggingPrintStreamTest.java | 61 |
2 files changed, 124 insertions, 18 deletions
diff --git a/core/java/com/android/internal/os/LoggingPrintStream.java b/core/java/com/android/internal/os/LoggingPrintStream.java index b3d6f20..451340b 100644 --- a/core/java/com/android/internal/os/LoggingPrintStream.java +++ b/core/java/com/android/internal/os/LoggingPrintStream.java @@ -16,11 +16,17 @@ package com.android.internal.os; -import java.io.PrintStream; -import java.io.OutputStream; import java.io.IOException; -import java.util.Locale; +import java.io.OutputStream; +import java.io.PrintStream; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; import java.util.Formatter; +import java.util.Locale; /** * A print stream which logs output line by line. @@ -31,6 +37,27 @@ abstract class LoggingPrintStream extends PrintStream { private final StringBuilder builder = new StringBuilder(); + /** + * A buffer that is initialized when raw bytes are first written to this + * stream. It may contain the leading bytes of multi-byte characters. + * Between writes this buffer is always ready to receive data; ie. the + * position is at the first unassigned byte and the limit is the capacity. + */ + private ByteBuffer encodedBytes; + + /** + * A buffer that is initialized when raw bytes are first written to this + * stream. Between writes this buffer is always clear; ie. the position is + * zero and the limit is the capacity. + */ + private CharBuffer decodedChars; + + /** + * Decodes bytes to characters using the system default charset. Initialized + * when raw bytes are first written to this stream. + */ + private CharsetDecoder decoder; + protected LoggingPrintStream() { super(new OutputStream() { public void write(int oneByte) throws IOException { @@ -80,20 +107,48 @@ abstract class LoggingPrintStream extends PrintStream { } } - /* - * We have no idea of how these bytes are encoded, so just ignore them. - */ - - /** Ignored. */ - public void write(int oneByte) {} + public void write(int oneByte) { + write(new byte[] { (byte) oneByte }, 0, 1); + } - /** Ignored. */ @Override - public void write(byte buffer[]) {} + public void write(byte[] buffer) { + write(buffer, 0, buffer.length); + } - /** Ignored. */ @Override - public void write(byte bytes[], int start, int count) {} + public synchronized void write(byte bytes[], int start, int count) { + if (decoder == null) { + encodedBytes = ByteBuffer.allocate(80); + decodedChars = CharBuffer.allocate(80); + decoder = Charset.defaultCharset().newDecoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); + } + + int end = start + count; + while (start < end) { + // copy some bytes from the array to the long-lived buffer. This + // way, if we end with a partial character we don't lose it. + int numBytes = Math.min(encodedBytes.remaining(), end - start); + encodedBytes.put(bytes, start, numBytes); + start += numBytes; + + encodedBytes.flip(); + CoderResult coderResult; + do { + // decode bytes from the byte buffer into the char buffer + coderResult = decoder.decode(encodedBytes, decodedChars, false); + + // copy chars from the char buffer into our string builder + decodedChars.flip(); + builder.append(decodedChars); + decodedChars.clear(); + } while (coderResult.isOverflow()); + encodedBytes.compact(); + } + flush(false); + } /** Always returns false. */ @Override diff --git a/tests/framework-tests/src/com/android/internal/os/LoggingPrintStreamTest.java b/tests/framework-tests/src/com/android/internal/os/LoggingPrintStreamTest.java index 8e3a034..4d016d1 100644 --- a/tests/framework-tests/src/com/android/internal/os/LoggingPrintStreamTest.java +++ b/tests/framework-tests/src/com/android/internal/os/LoggingPrintStreamTest.java @@ -18,12 +18,12 @@ package com.android.internal.os; import junit.framework.TestCase; -import java.util.Arrays; -import java.util.List; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; -import java.io.StringWriter; -import java.io.PrintWriter; +import java.util.List; public class LoggingPrintStreamTest extends TestCase { @@ -121,6 +121,58 @@ public class LoggingPrintStreamTest extends TestCase { assertEquals(Arrays.asList("Foo", "4", "a"), out.lines); } + public void testMultiByteCharactersSpanningBuffers() throws Exception { + // assume 3*1000 bytes won't fit in LoggingPrintStream's internal buffer + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < 1000; i++) { + builder.append("\u20AC"); // a Euro character; 3 bytes in UTF-8 + } + String expected = builder.toString(); + + out.write(expected.getBytes("UTF-8")); + out.flush(); + assertEquals(Arrays.asList(expected), out.lines); + } + + public void testWriteOneByteAtATimeMultibyteCharacters() throws Exception { + String expected = " \u20AC \u20AC \u20AC \u20AC "; + for (byte b : expected.getBytes()) { + out.write(b); + } + out.flush(); + assertEquals(Arrays.asList(expected), out.lines); + } + + public void testWriteByteArrayAtATimeMultibyteCharacters() throws Exception { + String expected = " \u20AC \u20AC \u20AC \u20AC "; + out.write(expected.getBytes()); + out.flush(); + assertEquals(Arrays.asList(expected), out.lines); + } + + public void testWriteWithOffsetsMultibyteCharacters() throws Exception { + String expected = " \u20AC \u20AC \u20AC \u20AC "; + byte[] bytes = expected.getBytes(); + int i = 0; + while (i < bytes.length - 5) { + out.write(bytes, i, 5); + i += 5; + } + out.write(bytes, i, bytes.length - i); + out.flush(); + assertEquals(Arrays.asList(expected), out.lines); + } + + public void testWriteFlushesOnNewlines() throws Exception { + String a = " \u20AC \u20AC "; + String b = " \u20AC \u20AC "; + String c = " "; + String toWrite = a + "\n" + b + "\n" + c; + out.write(toWrite.getBytes()); + out.flush(); + assertEquals(Arrays.asList(a, b, c), out.lines); + } + static class TestPrintStream extends LoggingPrintStream { final List<String> lines = new ArrayList<String>(); @@ -129,5 +181,4 @@ public class LoggingPrintStreamTest extends TestCase { lines.add(line); } } - } |
