diff options
| author | Android (Google) Code Review <android-gerrit@google.com> | 2009-12-21 18:39:19 -0800 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2009-12-21 18:39:19 -0800 |
| commit | f966afa1461c602fb5b50aeb5d6429dc525014be (patch) | |
| tree | b35042dffb3a4882fe99a24f3ee0770910a27171 | |
| parent | ee0511d76ce0ceae2f54782ca33e400e62d08137 (diff) | |
| parent | f41de2a4a1c22e3f3ee9a8cd65ec7997c9587cdb (diff) | |
| download | frameworks_base-f966afa1461c602fb5b50aeb5d6429dc525014be.zip frameworks_base-f966afa1461c602fb5b50aeb5d6429dc525014be.tar.gz frameworks_base-f966afa1461c602fb5b50aeb5d6429dc525014be.tar.bz2 | |
Merge change I34a427a5
* changes:
Adding support for LoggingPrintStream.write(byte[]) and friends.
| -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); } } - } |
