You are viewing a plain text version of this content. The canonical link for it is here.
Posted to mime4j-dev@james.apache.org by ol...@apache.org on 2012/01/05 23:13:00 UTC
svn commit: r1227867 - in /james/mime4j/trunk/dom/src:
main/java/org/apache/james/mime4j/message/
test/java/org/apache/james/mime4j/message/
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
Re: svn commit: r1227867
Posted by Norman Maurer <no...@googlemail.com>.
Ah I see... I will then start the release process later then.
Bye,
Norman
Sent from my iPhone. Excuse any typos....
Am 07.01.2012 um 14:00 schrieb Oleg Kalnichevski <ol...@apache.org>:
> On Sat, 2012-01-07 at 09:54 +0100, Norman Maurer wrote:
>> Hi Oleg,
>>
>> wouldnt it better be called CharSequenceInputStream?
>>
>> Bye
>> Norman
>>
>
> Hi Norman
>
> This class is package private and therefore its name really makes no
> difference. Feel free to rename it, though.
>
> By the way, I think we are ready for 0.7.2
>
> Cheers
>
> Oleg
>
>
Re: svn commit: r1227867
Posted by Oleg Kalnichevski <ol...@apache.org>.
On Sat, 2012-01-07 at 09:54 +0100, Norman Maurer wrote:
> Hi Oleg,
>
> wouldnt it better be called CharSequenceInputStream?
>
> Bye
> Norman
>
Hi Norman
This class is package private and therefore its name really makes no
difference. Feel free to rename it, though.
By the way, I think we are ready for 0.7.2
Cheers
Oleg
Re: svn commit: r1227867 - in /james/mime4j/trunk/dom/src: main/java/org/apache/james/mime4j/message/ test/java/org/apache/james/mime4j/message/
Posted by Norman Maurer <no...@googlemail.com>.
Hi Oleg,
wouldnt it better be called CharSequenceInputStream?
Bye
Norman
Am 05.01.2012 um 23:13 schrieb olegk@apache.org:
> 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
>
>