From mime4j-dev-return-1811-apmail-james-mime4j-dev-archive=james.apache.org@james.apache.org Thu Jan 5 22:13:28 2012 Return-Path: X-Original-To: apmail-james-mime4j-dev-archive@minotaur.apache.org Delivered-To: apmail-james-mime4j-dev-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id AE6C69046 for ; Thu, 5 Jan 2012 22:13:28 +0000 (UTC) Received: (qmail 74340 invoked by uid 500); 5 Jan 2012 22:13:28 -0000 Delivered-To: apmail-james-mime4j-dev-archive@james.apache.org Received: (qmail 74271 invoked by uid 500); 5 Jan 2012 22:13:27 -0000 Mailing-List: contact mime4j-dev-help@james.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: mime4j-dev@james.apache.org Delivered-To: mailing list mime4j-dev@james.apache.org Received: (qmail 74262 invoked by uid 99); 5 Jan 2012 22:13:27 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 05 Jan 2012 22:13:27 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 05 Jan 2012 22:13:23 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 210D3238889B; Thu, 5 Jan 2012 22:13:01 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1227867 - in /james/mime4j/trunk/dom/src: main/java/org/apache/james/mime4j/message/ test/java/org/apache/james/mime4j/message/ Date: Thu, 05 Jan 2012 22:13:00 -0000 To: mime4j-dev@james.apache.org From: olegk@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20120105221301.210D3238889B@eris.apache.org> X-Virus-Checked: Checked by ClamAV on 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