james-mime4j-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ol...@apache.org
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 GMT
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



Mime
View raw message