Hi Oleg, wouldnt it better be called CharSequenceInputStream?
Bye Norman Am 05.01.2012 um 23:13 schrieb [email protected]: > Author: olegk > Date: Thu Jan 5 22:13:00 2012 > New Revision: 1227867 > > URL: http://svn.apache.org/viewvc?rev=1227867&view=rev > Log: > More efficient implementation of TextBody backed by a String; body content > can be streamed without convering the string to a byte array > > Added: > > james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/StringBody.java > (with props) > > james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/StringInputStream.java > (with props) > > james/mime4j/trunk/dom/src/test/java/org/apache/james/mime4j/message/StringInputStreamTest.java > (with props) > Modified: > > james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/BasicBodyFactory.java > > Modified: > james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/BasicBodyFactory.java > URL: > http://svn.apache.org/viewvc/james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/BasicBodyFactory.java?rev=1227867&r1=1227866&r2=1227867&view=diff > ============================================================================== > --- > james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/BasicBodyFactory.java > (original) > +++ > james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/BasicBodyFactory.java > Thu Jan 5 22:13:00 2012 > @@ -23,6 +23,8 @@ import java.io.ByteArrayOutputStream; > import java.io.IOException; > import java.io.InputStream; > import java.io.UnsupportedEncodingException; > +import java.nio.charset.Charset; > +import java.nio.charset.UnsupportedCharsetException; > > import org.apache.james.mime4j.dom.BinaryBody; > import org.apache.james.mime4j.dom.TextBody; > @@ -33,8 +35,6 @@ import org.apache.james.mime4j.util.Char > */ > public class BasicBodyFactory implements BodyFactory { > > - private static String DEFAULT_CHARSET = > CharsetUtil.DEFAULT_CHARSET.name(); > - > public BinaryBody binaryBody(final InputStream is) throws IOException { > return new BasicBinaryBody(bufferContent(is)); > } > @@ -60,11 +60,23 @@ public class BasicBodyFactory implements > if (text == null) { > throw new IllegalArgumentException("Text may not be null"); > } > - return new BasicTextBody(text.getBytes(mimeCharset), mimeCharset); > + Charset charset = Charset.forName(mimeCharset); > + try { > + return new StringBody(text, charset); > + } catch (UnsupportedCharsetException ex) { > + throw new UnsupportedEncodingException(ex.getMessage()); > + } > + } > + > + public TextBody textBody(final String text, final Charset charset) { > + if (text == null) { > + throw new IllegalArgumentException("Text may not be null"); > + } > + return new StringBody(text, charset); > } > > - public TextBody textBody(final String text) throws > UnsupportedEncodingException { > - return textBody(text, DEFAULT_CHARSET); > + public TextBody textBody(final String text) { > + return textBody(text, CharsetUtil.DEFAULT_CHARSET); > } > > public BinaryBody binaryBody(final byte[] buf) { > > Added: > james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/StringBody.java > URL: > http://svn.apache.org/viewvc/james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/StringBody.java?rev=1227867&view=auto > ============================================================================== > --- > james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/StringBody.java > (added) > +++ > james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/StringBody.java > Thu Jan 5 22:13:00 2012 > @@ -0,0 +1,62 @@ > +/**************************************************************** > + * Licensed to the Apache Software Foundation (ASF) under one * > + * or more contributor license agreements. See the NOTICE file * > + * distributed with this work for additional information * > + * regarding copyright ownership. The ASF licenses this file * > + * to you 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 org.apache.james.mime4j.message; > + > +import java.io.IOException; > +import java.io.InputStream; > +import java.io.Reader; > +import java.io.StringReader; > +import java.nio.charset.Charset; > + > +import org.apache.james.mime4j.dom.SingleBody; > +import org.apache.james.mime4j.dom.TextBody; > + > +class StringBody extends TextBody { > + > + private final String content; > + private final Charset charset; > + > + StringBody(final String content, final Charset charset) { > + super(); > + this.content = content; > + this.charset = charset; > + } > + > + @Override > + public String getMimeCharset() { > + return this.charset.name(); > + } > + > + @Override > + public Reader getReader() throws IOException { > + return new StringReader(this.content); > + } > + > + @Override > + public InputStream getInputStream() throws IOException { > + return new StringInputStream(this.content, this.charset, 2048); > + } > + > + @Override > + public SingleBody copy() { > + return new StringBody(this.content, this.charset); > + } > + > +} > > Propchange: > james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/StringBody.java > ------------------------------------------------------------------------------ > svn:eol-style = native > > Propchange: > james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/StringBody.java > ------------------------------------------------------------------------------ > svn:executable = * > > Propchange: > james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/StringBody.java > ------------------------------------------------------------------------------ > svn:keywords = Date Revision > > Propchange: > james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/StringBody.java > ------------------------------------------------------------------------------ > svn:mime-type = text/plain > > Added: > james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/StringInputStream.java > URL: > http://svn.apache.org/viewvc/james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/StringInputStream.java?rev=1227867&view=auto > ============================================================================== > --- > james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/StringInputStream.java > (added) > +++ > james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/StringInputStream.java > Thu Jan 5 22:13:00 2012 > @@ -0,0 +1,150 @@ > +/**************************************************************** > + * Licensed to the Apache Software Foundation (ASF) under one * > + * or more contributor license agreements. See the NOTICE file * > + * distributed with this work for additional information * > + * regarding copyright ownership. The ASF licenses this file * > + * to you 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 org.apache.james.mime4j.message; > + > +import java.io.IOException; > +import java.io.InputStream; > +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.CodingErrorAction; > + > +class StringInputStream extends InputStream { > + > + private final CharsetEncoder encoder; > + private final CharBuffer cbuf; > + private final ByteBuffer bbuf; > + > + private int mark; > + > + StringInputStream(final CharSequence s, final Charset charset, int > bufferSize) { > + super(); > + this.encoder = charset.newEncoder() > + .onMalformedInput(CodingErrorAction.REPLACE) > + .onUnmappableCharacter(CodingErrorAction.REPLACE); > + this.bbuf = ByteBuffer.allocate(124); > + this.bbuf.flip(); > + this.cbuf = CharBuffer.wrap(s); > + this.mark = -1; > + } > + > + StringInputStream(final CharSequence s, final Charset charset) { > + this(s, charset, 2048); > + } > + > + private void fillBuffer() throws IOException { > + this.bbuf.compact(); > + CoderResult result = this.encoder.encode(this.cbuf, this.bbuf, true); > + if (result.isError()) { > + result.throwException(); > + } > + this.bbuf.flip(); > + } > + > + @Override > + public int read(byte[] b, int off, int len) throws IOException { > + if (b == null) { > + throw new NullPointerException("Byte array is null"); > + } > + if (len < 0 || (off + len) > b.length) { > + throw new IndexOutOfBoundsException("Array Size=" + b.length + > + ", offset=" + off + ", length=" + len); > + } > + if (!this.bbuf.hasRemaining() && !this.cbuf.hasRemaining()) { > + return -1; > + } > + int bytesRead = 0; > + while (len > 0) { > + if (this.bbuf.hasRemaining()) { > + int chunk = Math.min(this.bbuf.remaining(), len); > + this.bbuf.get(b, off, chunk); > + off += chunk; > + len -= chunk; > + bytesRead += chunk; > + } else { > + fillBuffer(); > + if (!this.bbuf.hasRemaining() && !this.cbuf.hasRemaining()) { > + break; > + } > + } > + } > + return bytesRead == 0 && !this.cbuf.hasRemaining() ? -1 : bytesRead; > + } > + > + @Override > + public int read() throws IOException { > + for (;;) { > + if (this.bbuf.hasRemaining()) { > + return this.bbuf.get() & 0xFF; > + } else { > + fillBuffer(); > + if (!this.bbuf.hasRemaining() && !this.cbuf.hasRemaining()) { > + return -1; > + } > + } > + } > + } > + > + @Override > + public int read(byte[] b) throws IOException { > + return read(b, 0, b.length); > + } > + > + @Override > + public long skip(long n) throws IOException { > + int skipped = 0; > + while (n > 0 && this.cbuf.hasRemaining()) { > + this.cbuf.get(); > + n--; > + skipped++; > + } > + return skipped; > + } > + > + @Override > + public int available() throws IOException { > + return this.cbuf.remaining(); > + } > + > + @Override > + public void close() throws IOException { > + } > + > + @Override > + public void mark(int readlimit) { > + this.mark = this.cbuf.position(); > + } > + > + @Override > + public void reset() throws IOException { > + if (this.mark != -1) { > + this.cbuf.position(this.mark); > + this.mark = -1; > + } > + } > + > + @Override > + public boolean markSupported() { > + return true; > + } > + > +} > > Propchange: > james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/StringInputStream.java > ------------------------------------------------------------------------------ > svn:eol-style = native > > Propchange: > james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/StringInputStream.java > ------------------------------------------------------------------------------ > svn:executable = * > > Propchange: > james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/StringInputStream.java > ------------------------------------------------------------------------------ > svn:keywords = Date Revision > > Propchange: > james/mime4j/trunk/dom/src/main/java/org/apache/james/mime4j/message/StringInputStream.java > ------------------------------------------------------------------------------ > svn:mime-type = text/plain > > Added: > james/mime4j/trunk/dom/src/test/java/org/apache/james/mime4j/message/StringInputStreamTest.java > URL: > http://svn.apache.org/viewvc/james/mime4j/trunk/dom/src/test/java/org/apache/james/mime4j/message/StringInputStreamTest.java?rev=1227867&view=auto > ============================================================================== > --- > james/mime4j/trunk/dom/src/test/java/org/apache/james/mime4j/message/StringInputStreamTest.java > (added) > +++ > james/mime4j/trunk/dom/src/test/java/org/apache/james/mime4j/message/StringInputStreamTest.java > Thu Jan 5 22:13:00 2012 > @@ -0,0 +1,129 @@ > +/**************************************************************** > + * Licensed to the Apache Software Foundation (ASF) under one * > + * or more contributor license agreements. See the NOTICE file * > + * distributed with this work for additional information * > + * regarding copyright ownership. The ASF licenses this file * > + * to you 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 org.apache.james.mime4j.message; > + > +import java.io.IOException; > +import java.io.InputStream; > +import java.security.SecureRandom; > + > +import org.apache.james.mime4j.util.CharsetUtil; > + > +import junit.framework.TestCase; > + > +public class StringInputStreamTest extends TestCase { > + > + private static final String SWISS_GERMAN_HELLO = "Gr\374ezi_z\344m\344"; > + private static final String RUSSIAN_HELLO = > "\u0412\u0441\u0435\u043C_\u043F\u0440\u0438\u0432\u0435\u0442"; > + private static final String TEST_STRING = "Hello and stuff " + > SWISS_GERMAN_HELLO + " " + RUSSIAN_HELLO; > + private static final String LARGE_TEST_STRING; > + > + static { > + StringBuilder buffer = new StringBuilder(); > + for (int i=0; i<100; i++) { > + buffer.append(TEST_STRING); > + } > + LARGE_TEST_STRING = buffer.toString(); > + } > + > + private static void singleByteReadTest(final String testString) throws > IOException { > + byte[] bytes = testString.getBytes(CharsetUtil.UTF_8.name()); > + InputStream in = new StringInputStream(testString, > CharsetUtil.UTF_8); > + for (byte b : bytes) { > + int read = in.read(); > + assertTrue(read >= 0); > + assertTrue(read <= 255); > + assertEquals(b, (byte)read); > + } > + assertEquals(-1, in.read()); > + } > + > + private static void bufferedReadTest(final String testString) throws > IOException { > + SecureRandom rnd = new SecureRandom(); > + byte[] expected = testString.getBytes(CharsetUtil.UTF_8.name()); > + InputStream in = new StringInputStream(testString, > CharsetUtil.UTF_8); > + byte[] buffer = new byte[128]; > + int offset = 0; > + while (true) { > + int bufferOffset = rnd.nextInt(64); > + int bufferLength = rnd.nextInt(64); > + int read = in.read(buffer, bufferOffset, bufferLength); > + if (read == -1) { > + assertEquals(offset, expected.length); > + break; > + } else { > + assertTrue(read <= bufferLength); > + while (read > 0) { > + assertTrue(offset < expected.length); > + assertEquals(expected[offset], buffer[bufferOffset]); > + offset++; > + bufferOffset++; > + read--; > + } > + } > + } > + } > + > + public void testSingleByteRead() throws IOException { > + singleByteReadTest(TEST_STRING); > + } > + > + public void testLargeSingleByteRead() throws IOException { > + singleByteReadTest(LARGE_TEST_STRING); > + } > + > + public void testBufferedRead() throws IOException { > + bufferedReadTest(TEST_STRING); > + } > + > + public void testLargeBufferedRead() throws IOException { > + bufferedReadTest(LARGE_TEST_STRING); > + } > + > + public void testReadZero() throws Exception { > + InputStream r = new StringInputStream("test", CharsetUtil.UTF_8); > + byte[] bytes = new byte[30]; > + assertEquals(0, r.read(bytes, 0, 0)); > + } > + > + public void testSkip() throws Exception { > + InputStream r = new StringInputStream("test", CharsetUtil.UTF_8); > + r.skip(1); > + r.skip(2); > + assertEquals('t', r.read()); > + r.skip(100); > + assertEquals(-1, r.read()); > + } > + > + public void testMarkReset() throws Exception { > + InputStream r = new StringInputStream("test", CharsetUtil.UTF_8); > + r.skip(2); > + r.mark(0); > + assertEquals('s', r.read()); > + assertEquals('t', r.read()); > + assertEquals(-1, r.read()); > + r.reset(); > + assertEquals('s', r.read()); > + assertEquals('t', r.read()); > + assertEquals(-1, r.read()); > + r.reset(); > + r.reset(); > + } > + > +} > > Propchange: > james/mime4j/trunk/dom/src/test/java/org/apache/james/mime4j/message/StringInputStreamTest.java > ------------------------------------------------------------------------------ > svn:eol-style = native > > Propchange: > james/mime4j/trunk/dom/src/test/java/org/apache/james/mime4j/message/StringInputStreamTest.java > ------------------------------------------------------------------------------ > svn:executable = * > > Propchange: > james/mime4j/trunk/dom/src/test/java/org/apache/james/mime4j/message/StringInputStreamTest.java > ------------------------------------------------------------------------------ > svn:keywords = Date Revision > > Propchange: > james/mime4j/trunk/dom/src/test/java/org/apache/james/mime4j/message/StringInputStreamTest.java > ------------------------------------------------------------------------------ > svn:mime-type = text/plain > >
