You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by rd...@apache.org on 2008/07/08 22:44:06 UTC

svn commit: r674944 [1/2] - in /james/mime4j/trunk/src: main/java/org/apache/james/mime4j/ main/java/org/apache/james/mime4j/util/ test/java/org/apache/james/mime4j/

Author: rdonkin
Date: Tue Jul  8 13:44:06 2008
New Revision: 674944

URL: http://svn.apache.org/viewvc?rev=674944&view=rev
Log:
MIME4J-5 Performance patch 3, https://issues.apache.org/jira/browse/MIME4J-5. Contributed by Oleg Kalnichevski. This patch eliminates one-byte-reads for common use cases; eliminates the synchronised StringBuffer and reduces memory footprint.

Added:
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BasicBufferingInputStream.java
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BufferingInputStreamAdaptor.java
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/ByteArrayBuffer.java
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/CharArrayBuffer.java
    james/mime4j/trunk/src/test/java/org/apache/james/mime4j/BasicBufferingInputStreamTest.java
    james/mime4j/trunk/src/test/java/org/apache/james/mime4j/BufferingInputStreamAdaptorTest.java
    james/mime4j/trunk/src/test/java/org/apache/james/mime4j/TestByteArrayBuffer.java
    james/mime4j/trunk/src/test/java/org/apache/james/mime4j/TestCharArrayBuffer.java
Removed:
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/EOFSensitiveInputStream.java
Modified:
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/AbstractEntity.java
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BufferingInputStream.java
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/Event.java
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/InputBuffer.java
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeBoundaryInputStream.java
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeEntity.java
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeTokenStream.java
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/MessageUtils.java
    james/mime4j/trunk/src/test/java/org/apache/james/mime4j/InputBufferTest.java
    james/mime4j/trunk/src/test/java/org/apache/james/mime4j/MimeBoundaryInputStreamTest.java
    james/mime4j/trunk/src/test/java/org/apache/james/mime4j/MimeEntityTest.java
    james/mime4j/trunk/src/test/java/org/apache/james/mime4j/StrictMimeTokenStreamTest.java

Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/AbstractEntity.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/AbstractEntity.java?rev=674944&r1=674943&r2=674944&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/AbstractEntity.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/AbstractEntity.java Tue Jul  8 13:44:06 2008
@@ -20,11 +20,11 @@
 package org.apache.james.mime4j;
 
 import java.io.IOException;
-import java.io.InputStream;
 import java.util.BitSet;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.james.mime4j.util.MessageUtils;
 
 /**
  * Abstract MIME entity.
@@ -33,7 +33,6 @@
 
     protected final Log log;
     
-    protected final RootInputStream rootStream;
     protected final BodyDescriptor parent;
     protected final int startState;
     protected final int endState;
@@ -43,12 +42,12 @@
     
     protected int state;
 
-    private final StringBuffer sb = new StringBuffer();
-    
-    private int pos, start;
-    private int lineNumber, startLineNumber;
-    
+    private final ByteArrayBuffer linebuf;
+    private final CharArrayBuffer fieldbuf;
+
+    private int lineCount;
     private String field, fieldName, fieldValue;
+    private boolean endOfHeader;
 
     private static final BitSet fieldChars = new BitSet();
 
@@ -71,14 +70,12 @@
     private static final int T_IN_MESSAGE = -3;
 
     AbstractEntity(
-            RootInputStream rootStream,
             BodyDescriptor parent,
             int startState, 
             int endState,
             boolean maximalBodyDescriptor,
             boolean strictParsing) {
         this.log = LogFactory.getLog(getClass());        
-        this.rootStream = rootStream;
         this.parent = parent;
         this.state = startState;
         this.startState = startState; 
@@ -86,6 +83,10 @@
         this.maximalBodyDescriptor = maximalBodyDescriptor;
         this.strictParsing = strictParsing;
         this.body = newBodyDescriptor(parent);
+        this.linebuf = new ByteArrayBuffer(64);
+        this.fieldbuf = new CharArrayBuffer(64);
+        this.lineCount = 0;
+        this.endOfHeader = false;
     }
 
     public int getState() {
@@ -106,83 +107,92 @@
         }
         return result;
     }
+
+    protected abstract int getLineNumber();
     
-    protected abstract InputStream getDataStream();
+    protected abstract BufferingInputStream getDataStream();
     
-    protected void initHeaderParsing() throws IOException, MimeException {
-        startLineNumber = lineNumber = rootStream.getLineNumber();
-
-        InputStream instream = getDataStream();
-        
-        int curr = 0;
-        int prev = 0;
-        while ((curr = instream.read()) != -1) {
-            if (curr == '\n' && (prev == '\n' || prev == 0)) {
-                /*
-                 * [\r]\n[\r]\n or an immediate \r\n have been seen.
-                 */
-                sb.deleteCharAt(sb.length() - 1);
-                break;
-            }
-            sb.append((char) curr);
-            prev = curr == '\r' ? prev : curr;
-        }
-        
-        if (curr == -1) {
-            monitor(Event.HEADERS_PREMATURE_END);
+    private void fillFieldBuffer() throws IOException, MimeException {
+        if (endOfHeader) {
+            return;
+        }
+        BufferingInputStream instream = getDataStream();
+        fieldbuf.clear();
+        for (;;) {
+            // If there's still data stuck in the line buffer
+            // copy it to the field buffer
+            int len = linebuf.length();
+            if (len > 0) {
+                fieldbuf.append(linebuf, 0, len);
+            }
+            linebuf.clear();
+            if (instream.readLine(linebuf) == -1) {
+                monitor(Event.HEADERS_PREMATURE_END);
+                endOfHeader = true;
+                break;
+            }
+            len = linebuf.length();
+            if (len > 0 && linebuf.byteAt(len - 1) == '\n') {
+                len--;
+            }
+            if (len > 0 && linebuf.byteAt(len - 1) == '\r') {
+                len--;
+            }
+            if (len == 0) {
+                // empty line detected 
+                endOfHeader = true;
+                break;
+            }
+            lineCount++;
+            if (lineCount > 1) {
+                int ch = linebuf.byteAt(0);
+                if (ch != MessageUtils.SP && ch != MessageUtils.HT) {
+                    // new header detected
+                    break;
+                }
+            }
         }
     }
 
-    protected boolean parseField() {
-        while (pos < sb.length()) {
-            while (pos < sb.length() && sb.charAt(pos) != '\r') {
-                pos++;
-            }
-            if (pos < sb.length() - 1 && sb.charAt(pos + 1) != '\n') {
-                pos++;
-                continue;
-            }
-            if (pos >= sb.length() - 2 || fieldChars.get(sb.charAt(pos + 2))) {
-                /*
-                 * field should be the complete field data excluding the 
-                 * trailing \r\n.
-                 */
-                field = sb.substring(start, pos);
-                start = pos + 2;
-                
-                /*
-                 * Check for a valid field.
-                 */
-                int index = field.indexOf(':');
-                boolean valid = false;
-                if (index != -1 && fieldChars.get(field.charAt(0))) {
-                    valid = true;
-                    fieldName = field.substring(0, index).trim();
-                    for (int i = 0; i < fieldName.length(); i++) {
-                        if (!fieldChars.get(fieldName.charAt(i))) {
-                            valid = false;
-                            break;
-                        }
-                    }
-                    if (valid) {
-                        fieldValue = field.substring(index + 1);
-                        body.addField(fieldName, fieldValue);
-                        startLineNumber = lineNumber;
-                        pos += 2;
-                        lineNumber++;
-                        return true;
+    protected boolean parseField() throws IOException {
+        for (;;) {
+            if (endOfHeader) {
+                return false;
+            }
+            fillFieldBuffer();
+            
+            // Strip away line delimiter
+            int len = fieldbuf.length();
+            if (len > 0 && fieldbuf.charAt(len - 1) == '\n') {
+                len--;
+            }
+            if (len > 0 && fieldbuf.charAt(len - 1) == '\r') {
+                len--;
+            }
+            fieldbuf.setLength(len);
+            
+            boolean valid = true;
+            field = fieldbuf.toString();
+            int pos = fieldbuf.indexOf(':');
+            if (pos == -1) {
+                monitor(Event.INALID_HEADER);
+                valid = false;
+            } else {
+                fieldName = fieldbuf.substring(0, pos);
+                for (int i = 0; i < fieldName.length(); i++) {
+                    if (!fieldChars.get(fieldName.charAt(i))) {
+                        monitor(Event.INALID_HEADER);
+                        valid = false;
+                        break;
                     }
                 }
-                if (log.isWarnEnabled()) {
-                    log.warn("Line " + startLineNumber 
-                            + ": Ignoring invalid field: '" + field.trim() + "'");
-                }
-                startLineNumber = lineNumber;
+                fieldValue = fieldbuf.substring(pos + 1, fieldbuf.length());
+            }
+            if (valid) {
+                body.addField(fieldName, fieldValue);            
+                return true;
             }
-            pos += 2;
-            lineNumber++;
         }
-        return false;
     }
 
     /**
@@ -278,7 +288,7 @@
      * or for logging
      */
     protected String message(Event event) {
-        String preamble = "Line " + rootStream.getLineNumber() + ": ";
+        String preamble = "Line " + getLineNumber() + ": ";
         final String message;
         if (event == null) {
             message = "Event is unexpectedly null.";

Added: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BasicBufferingInputStream.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BasicBufferingInputStream.java?rev=674944&view=auto
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BasicBufferingInputStream.java (added)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BasicBufferingInputStream.java Tue Jul  8 13:44:06 2008
@@ -0,0 +1,89 @@
+/****************************************************************
+ * 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;
+
+import java.io.IOException;
+
+/**
+ * Implementation of {@link BufferingInputStream} backed by an {@link InputBuffer} instance.
+ * 
+ * @version $Id$
+ */
+public class BasicBufferingInputStream extends BufferingInputStream {
+
+    private final InputBuffer buffer;
+
+    public BasicBufferingInputStream(final InputBuffer buffer) {
+        super();
+        this.buffer = buffer;
+    }
+
+    public void close() throws IOException {
+        this.buffer.closeStream();
+    }
+
+    public boolean markSupported() {
+        return false;
+    }
+
+    public int read() throws IOException {
+        return this.buffer.read();
+    }
+
+    public int read(byte[] b, int off, int len) throws IOException {
+        return this.buffer.read(b, off, len);
+    }
+    
+    public int readLine(final ByteArrayBuffer linebuf) throws IOException {
+        if (linebuf == null) {
+            throw new IllegalArgumentException("Buffer may not be null");
+        }
+        int total = 0;
+        boolean found = false;
+        int bytesRead = 0;
+        while (!found) {
+            if (!this.buffer.hasBufferedData()) {
+                bytesRead = this.buffer.fillBuffer();
+                if (bytesRead == -1) {
+                    break;
+                }
+            }
+            int i = this.buffer.indexOf((byte)'\n');
+            int chunk;
+            if (i != -1) {
+                found = true;
+                chunk = i + 1 - this.buffer.pos();
+            } else {
+                chunk = this.buffer.length();
+            }
+            if (chunk > 0) {
+                linebuf.append(this.buffer.buf(), this.buffer.pos(), chunk);
+                this.buffer.skip(chunk);
+                total += chunk;
+            }
+        }
+        if (total == 0 && bytesRead == -1) {
+            return -1;
+        } else {
+            return total;
+        }
+    }
+
+}

Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BufferingInputStream.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BufferingInputStream.java?rev=674944&r1=674943&r2=674944&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BufferingInputStream.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BufferingInputStream.java Tue Jul  8 13:44:06 2008
@@ -1,53 +1,41 @@
-/****************************************************************
- * 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;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Implementation of {@link InputStream} backed by an {@link InputBuffer} instance. 
- */
-public class BufferingInputStream extends InputStream {
-
-    private final InputBuffer buffer;
-    
-    public BufferingInputStream(final InputBuffer buffer) {
-        super();
-        this.buffer = buffer;
-    }
-    
-    public void close() throws IOException {
-        this.buffer.closeStream();
-    }
-
-    public boolean markSupported() {
-        return false;
-    }
-
-    public int read() throws IOException {
-        return this.buffer.read();
-    }
-
-    public int read(byte[] b, int off, int len) throws IOException {
-        return this.buffer.read(b, off, len);
-    }
-    
-}
+/****************************************************************
+ * 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;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Input stream capable of reading lines of text. 
+ */
+public abstract class BufferingInputStream extends InputStream {
+
+    /**
+     * Reads one line of text into the given {@link ByteArrayBuffer}.
+     *  
+     * @param dst Destination
+     * @return number of bytes copied or <code>-1</code> if the end of 
+     * the stream has been reached.
+     * 
+     * @throws IOException in case of an I/O error.
+     */
+    public abstract int readLine(final ByteArrayBuffer dst) throws IOException;
+    
+}

Added: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BufferingInputStreamAdaptor.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BufferingInputStreamAdaptor.java?rev=674944&view=auto
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BufferingInputStreamAdaptor.java (added)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BufferingInputStreamAdaptor.java Tue Jul  8 13:44:06 2008
@@ -0,0 +1,100 @@
+/****************************************************************
+ * 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;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * <code>InputStream</code> used by the MIME parser to detect whether the
+ * underlying data stream was used (read from) and whether the end of the 
+ * stream was reached.
+ * 
+ * @version $Id$
+ */
+class BufferingInputStreamAdaptor extends BufferingInputStream {
+
+    private final InputStream is;
+    private final BufferingInputStream bis;
+    private boolean used = false;
+    private boolean eof = false;
+
+    public BufferingInputStreamAdaptor(final InputStream is) {
+        super();
+        this.is = is;
+        if (is instanceof BufferingInputStream) {
+            this.bis = (BufferingInputStream) is;
+        } else {
+            this.bis = null;
+        }
+    }
+
+    public int read() throws IOException {
+        int i = this.is.read();
+        this.eof = i == -1;
+        this.used = true;
+        return i;
+    }
+
+    public int read(byte[] b, int off, int len) throws IOException {
+        int i = this.is.read(b, off, len);
+        this.eof = i == -1;
+        this.used = true;
+        return i;
+    }
+    
+    public int readLine(final ByteArrayBuffer dst) throws IOException {
+        int i;
+        if (this.bis != null) {
+             i = this.bis.readLine(dst);
+        } else {
+             i = doReadLine(dst);
+        }
+        this.eof = i == -1;
+        this.used = true;
+        return i;
+    }
+
+    private int doReadLine(final ByteArrayBuffer dst) throws IOException {
+        int total = 0;
+        int ch;
+        while ((ch = this.is.read()) != -1) {
+            dst.append(ch);
+            total++;
+            if (ch == '\n') {
+                break;
+            }
+        }
+        if (total == 0 && ch == -1) {
+            return -1;
+        } else {
+            return total;
+        }
+    }
+    
+    public boolean eof() {
+        return this.eof;
+    }
+
+    public boolean isUsed() {
+        return this.used;
+    }
+
+}

Added: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/ByteArrayBuffer.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/ByteArrayBuffer.java?rev=674944&view=auto
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/ByteArrayBuffer.java (added)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/ByteArrayBuffer.java Tue Jul  8 13:44:06 2008
@@ -0,0 +1,141 @@
+/****************************************************************
+ * 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;
+
+/**
+ * A resizable byte array.
+ */
+public final class ByteArrayBuffer  {
+    
+    private byte[] buffer;
+    private int len;
+
+    public ByteArrayBuffer(int capacity) {
+        super();
+        if (capacity < 0) {
+            throw new IllegalArgumentException("Buffer capacity may not be negative");
+        }
+        this.buffer = new byte[capacity]; 
+    }
+
+    private void expand(int newlen) {
+        byte newbuffer[] = new byte[Math.max(this.buffer.length << 1, newlen)];
+        System.arraycopy(this.buffer, 0, newbuffer, 0, this.len);
+        this.buffer = newbuffer;
+    }
+    
+    public void append(final byte[] b, int off, int len) {
+        if (b == null) {
+            return;
+        }
+        if ((off < 0) || (off > b.length) || (len < 0) ||
+                ((off + len) < 0) || ((off + len) > b.length)) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (len == 0) {
+            return;
+        }
+        int newlen = this.len + len;
+        if (newlen > this.buffer.length) {
+            expand(newlen);
+        }
+        System.arraycopy(b, off, this.buffer, this.len, len);
+        this.len = newlen;
+    }
+
+    public void append(int b) {
+        int newlen = this.len + 1;
+        if (newlen > this.buffer.length) {
+            expand(newlen);
+        }
+        this.buffer[this.len] = (byte)b;
+        this.len = newlen;
+    }
+
+    public void append(final char[] b, int off, int len) {
+        if (b == null) {
+            return;
+        }
+        if ((off < 0) || (off > b.length) || (len < 0) ||
+                ((off + len) < 0) || ((off + len) > b.length)) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (len == 0) {
+            return;
+        }
+        int oldlen = this.len;
+        int newlen = oldlen + len;
+        if (newlen > this.buffer.length) {
+            expand(newlen);
+        }
+        for (int i1 = off, i2 = oldlen; i2 < newlen; i1++, i2++) {
+            this.buffer[i2] = (byte) b[i1];
+        }
+        this.len = newlen;
+    }
+
+    public void clear() {
+        this.len = 0;
+    }
+    
+    public byte[] toByteArray() {
+        byte[] b = new byte[this.len]; 
+        if (this.len > 0) {
+            System.arraycopy(this.buffer, 0, b, 0, this.len);
+        }
+        return b;
+    }
+    
+    public int byteAt(int i) {
+        return this.buffer[i];
+    }
+    
+    public int capacity() {
+        return this.buffer.length;
+    }
+    
+    public int length() {
+        return this.len;
+    }
+
+    public byte[] buffer() {
+        return this.buffer;
+    }
+        
+    public void setLength(int len) {
+        if (len < 0 || len > this.buffer.length) {
+            throw new IndexOutOfBoundsException();
+        }
+        this.len = len;
+    }
+    
+    public boolean isEmpty() {
+        return this.len == 0; 
+    }
+    
+    public boolean isFull() {
+        return this.len == this.buffer.length; 
+    }
+
+    public String toString() {
+        return new String(toByteArray());
+    }
+    
+}

Added: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/CharArrayBuffer.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/CharArrayBuffer.java?rev=674944&view=auto
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/CharArrayBuffer.java (added)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/CharArrayBuffer.java Tue Jul  8 13:44:06 2008
@@ -0,0 +1,247 @@
+/****************************************************************
+ * 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;
+
+import org.apache.james.mime4j.util.MessageUtils;
+
+/**
+ * A resizable char array.
+ *
+ */
+public final class CharArrayBuffer  {
+    
+    private char[] buffer;
+    private int len;
+
+    public CharArrayBuffer(int capacity) {
+        super();
+        if (capacity < 0) {
+            throw new IllegalArgumentException("Buffer capacity may not be negative");
+        }
+        this.buffer = new char[capacity]; 
+    }
+
+    private void expand(int newlen) {
+        char newbuffer[] = new char[Math.max(this.buffer.length << 1, newlen)];
+        System.arraycopy(this.buffer, 0, newbuffer, 0, this.len);
+        this.buffer = newbuffer;
+    }
+    
+    public void append(final char[] b, int off, int len) {
+        if (b == null) {
+            return;
+        }
+        if ((off < 0) || (off > b.length) || (len < 0) ||
+                ((off + len) < 0) || ((off + len) > b.length)) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (len == 0) {
+            return;
+        }
+        int newlen = this.len + len;
+        if (newlen > this.buffer.length) {
+            expand(newlen);
+        }
+        System.arraycopy(b, off, this.buffer, this.len, len);
+        this.len = newlen;
+    }
+    
+    public void append(String str) {
+        if (str == null) {
+            str = "null";
+        }
+        int strlen = str.length();
+        int newlen = this.len + strlen;
+        if (newlen > this.buffer.length) {
+            expand(newlen);
+        }
+        str.getChars(0, strlen, this.buffer, this.len);
+        this.len = newlen;
+    }
+
+    public void append(final CharArrayBuffer b, int off, int len) {
+        if (b == null) {
+            return;
+        }
+        append(b.buffer, off, len);
+    }
+        
+    public void append(final CharArrayBuffer b) {
+        if (b == null) {
+            return;
+        }
+        append(b.buffer,0, b.len);
+    }
+        
+    public void append(char ch) {
+        int newlen = this.len + 1;
+        if (newlen > this.buffer.length) {
+            expand(newlen);
+        }
+        this.buffer[this.len] = ch;
+        this.len = newlen;
+    }
+
+    public void append(final byte[] b, int off, int len) {
+        if (b == null) {
+            return;
+        }
+        if ((off < 0) || (off > b.length) || (len < 0) ||
+                ((off + len) < 0) || ((off + len) > b.length)) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (len == 0) {
+            return;
+        }
+        int oldlen = this.len;
+        int newlen = oldlen + len;
+        if (newlen > this.buffer.length) {
+            expand(newlen);
+        }
+        for (int i1 = off, i2 = oldlen; i2 < newlen; i1++, i2++) {
+            int ch = b[i1]; 
+            if (ch < 0) {
+                ch = 256 + ch;
+            }
+            this.buffer[i2] = (char) ch;
+        }
+        this.len = newlen;
+    }
+    
+    public void append(final ByteArrayBuffer b, int off, int len) {
+        if (b == null) {
+            return;
+        }
+        append(b.buffer(), off, len);
+    }
+    
+    public void append(final Object obj) {
+        append(String.valueOf(obj));
+    }
+    
+    public void clear() {
+        this.len = 0;
+    }
+    
+    public char[] toCharArray() {
+        char[] b = new char[this.len]; 
+        if (this.len > 0) {
+            System.arraycopy(this.buffer, 0, b, 0, this.len);
+        }
+        return b;
+    }
+    
+    public char charAt(int i) {
+        return this.buffer[i];
+    }
+    
+    public char[] buffer() {
+        return this.buffer;
+    }
+    
+    public int capacity() {
+        return this.buffer.length;
+    }
+    
+    public int length() {
+        return this.len;
+    }
+
+    public void ensureCapacity(int required) {
+        int available = this.buffer.length - this.len;
+        if (required > available) {
+            expand(this.len + required);
+        }
+    }
+    
+    public void setLength(int len) {
+        if (len < 0 || len > this.buffer.length) {
+            throw new IndexOutOfBoundsException();
+        }
+        this.len = len;
+    }
+    
+    public boolean isEmpty() {
+        return this.len == 0; 
+    }
+    
+    public boolean isFull() {
+        return this.len == this.buffer.length; 
+    }
+    
+    public int indexOf(int ch, int beginIndex, int endIndex) {
+        if (beginIndex < 0) {
+            beginIndex = 0;
+        }
+        if (endIndex > this.len) {
+            endIndex = this.len;
+        }
+        if (beginIndex > endIndex) {
+            return -1;
+        }
+        for (int i = beginIndex; i < endIndex; i++) {
+            if (this.buffer[i] == ch) {
+                return i;
+            }
+        }
+        return -1;
+    }
+    
+    public int indexOf(int ch) {
+        return indexOf(ch, 0, this.len);
+    }
+
+    public String substring(int beginIndex, int endIndex) {
+        if (beginIndex < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (endIndex > this.len) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (beginIndex > endIndex) {
+            throw new IndexOutOfBoundsException();
+        }
+        return new String(this.buffer, beginIndex, endIndex - beginIndex);
+    }
+    
+    public String substringTrimmed(int beginIndex, int endIndex) {
+        if (beginIndex < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (endIndex > this.len) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (beginIndex > endIndex) {
+            throw new IndexOutOfBoundsException();
+        }
+        while (beginIndex < endIndex && MessageUtils.isWhitespace(this.buffer[beginIndex])) {
+            beginIndex++;
+        }
+        while (endIndex > beginIndex && MessageUtils.isWhitespace(this.buffer[endIndex - 1])) {
+            endIndex--;
+        }
+        return new String(this.buffer, beginIndex, endIndex - beginIndex);
+    }
+    
+    public String toString() {
+        return new String(this.buffer, 0, this.len);
+    }
+    
+}

Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/Event.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/Event.java?rev=674944&r1=674943&r2=674944&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/Event.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/Event.java Tue Jul  8 13:44:06 2008
@@ -13,6 +13,9 @@
     public static final Event HEADERS_PREMATURE_END 
         = new Event("Unexpected end of headers detected. " +
                 "Higher level boundary detected or EOF reached.");
+    /** Indicates that unexpected end of headers detected.*/
+    public static final Event INALID_HEADER 
+        = new Event("Invalid header encountered");
     
     private final String code;
     

Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/InputBuffer.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/InputBuffer.java?rev=674944&r1=674943&r2=674944&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/InputBuffer.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/InputBuffer.java Tue Jul  8 13:44:06 2008
@@ -124,8 +124,13 @@
      * Communications of the ACM . 33(8):132-142.
      * </p>
      */
-    public int indexOf(final byte[] pattern) {
-        int len = this.buflen - this.bufpos;
+    public int indexOf(final byte[] pattern, int off, int len) {
+        if (pattern == null) {
+            throw new IllegalArgumentException("Pattern may not be null");
+        }
+        if (off < this.bufpos || len < 0 || off + len > this.buflen) {
+            throw new IndexOutOfBoundsException();
+        }
         if (len < pattern.length) {
             return -1;
         }
@@ -163,18 +168,57 @@
         return -1;
     }
     
-    public int length() {
-        return this.buflen;
+    /**
+     * Implements quick search algorithm as published by
+     * <p> 
+     * SUNDAY D.M., 1990, 
+     * A very fast substring search algorithm, 
+     * Communications of the ACM . 33(8):132-142.
+     * </p>
+     */
+    public int indexOf(final byte[] pattern) {
+        return indexOf(pattern, this.bufpos, this.buflen - this.bufpos);
+    }
+
+    public int indexOf(byte b, int off, int len) {
+        if (off < this.bufpos || len < 0 || off + len > this.buflen) {
+            throw new IndexOutOfBoundsException();
+        }
+        for (int i = off; i < off + len; i++) {
+            if (this.buffer[i] == b) {
+                return i;
+            }
+        }
+        return -1;
+    }
+    
+    public int indexOf(byte b) {
+        return indexOf(b, this.bufpos, this.buflen - this.bufpos);
     }
     
     public byte charAt(int pos) {
+        if (pos < this.bufpos || pos > this.buflen) {
+            throw new IndexOutOfBoundsException();
+        }
         return this.buffer[pos];
     }
     
+    public byte[] buf() {
+        return this.buffer;        
+    }
+    
     public int pos() {
         return this.bufpos;
     }
     
+    public int limit() {
+        return this.buflen;
+    }
+    
+    public int length() {
+        return this.buflen - this.bufpos;
+    }
+    
     public int skip(int n) {
         int chunk = Math.min(n, this.buflen - this.bufpos);
         this.bufpos += chunk; 
@@ -191,7 +235,7 @@
         buffer.append("[pos: ");
         buffer.append(this.bufpos);
         buffer.append("]");
-        buffer.append("[len: ");
+        buffer.append("[limit: ");
         buffer.append(this.buflen);
         buffer.append("]");
         buffer.append("[");

Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeBoundaryInputStream.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeBoundaryInputStream.java?rev=674944&r1=674943&r2=674944&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeBoundaryInputStream.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeBoundaryInputStream.java Tue Jul  8 13:44:06 2008
@@ -20,7 +20,6 @@
 package org.apache.james.mime4j;
 
 import java.io.IOException;
-import java.io.InputStream;
 
 /**
  * Stream that constrains itself to a single MIME body part.
@@ -29,7 +28,7 @@
  * 
  * @version $Id: MimeBoundaryInputStream.java,v 1.2 2004/11/29 13:15:42 ntherning Exp $
  */
-public class MimeBoundaryInputStream extends InputStream {
+public class MimeBoundaryInputStream extends BufferingInputStream {
 
     private final InputBuffer buffer;
     private final byte[] boundary;
@@ -121,13 +120,59 @@
         int chunk = Math.min(len, limit - buffer.pos());
         return buffer.read(b, off, chunk);
     }
-
+
+    public int readLine(final ByteArrayBuffer dst) throws IOException {
+        if (dst == null) {
+            throw new IllegalArgumentException("Destination buffer may not be null");
+        }
+        if (completed) {
+            return -1;
+        }
+        if (endOfStream() && !hasData()) {
+            skipBoundary();            
+            return -1;
+        }
+
+        int total = 0;
+        boolean found = false;
+        int bytesRead = 0;
+        while (!found) {
+            if (!hasData()) {
+                bytesRead = fillBuffer();
+                if (!hasData() && endOfStream()) {
+                    skipBoundary();
+                    bytesRead = -1;
+                    break;
+                }
+            }
+            int len = this.limit - this.buffer.pos();
+            int i = this.buffer.indexOf((byte)'\n', this.buffer.pos(), len);
+            int chunk;
+            if (i != -1) {
+                found = true;
+                chunk = i + 1 - this.buffer.pos();
+            } else {
+                chunk = len;
+            }
+            if (chunk > 0) {
+                dst.append(this.buffer.buf(), this.buffer.pos(), chunk);
+                this.buffer.skip(chunk);
+                total += chunk;
+            }
+        }
+        if (total == 0 && bytesRead == -1) {
+            return -1;
+        } else {
+            return total;
+        }
+    }
+    
     private boolean endOfStream() {
         return eof || atBoundary;
     }
     
     private boolean hasData() {
-        return limit > buffer.pos() && limit < buffer.length();
+        return limit > buffer.pos() && limit < buffer.limit();
     }
     
     private int fillBuffer() throws IOException {
@@ -149,9 +194,9 @@
             calculateBoundaryLen();
         } else {
             if (eof) {
-                limit = buffer.length();
+                limit = buffer.limit();
             } else {
-                limit = buffer.length() - (boundary.length + 1); 
+                limit = buffer.limit() - (boundary.length + 1); 
                                           // \r\n + (boundary - one char)
             }
         }
@@ -179,23 +224,15 @@
         if (!completed) {
             completed = true;
             buffer.skip(boundaryLen);
-            for (;;) {
+            for (;;) {
                 if (buffer.length() > 1) {
                     int ch1 = buffer.charAt(buffer.pos());
                     int ch2 = buffer.charAt(buffer.pos() + 1);
                     if (ch1 == '-' && ch2 == '-') {
                         this.lastPart = true;
                         buffer.skip(2);
-                        if (buffer.length() > 1) {
-                            ch1 = buffer.charAt(buffer.pos());
-                            ch2 = buffer.charAt(buffer.pos() + 1);
-                            if (ch1 == '\r' && ch2 == '\n') {
-                                buffer.skip(2);
-                            }
-                        }
-                    } else if (ch1 == '\r' && ch2 == '\n') {
-                        buffer.skip(2);
-                    }
+                    }
+                    skipLineDelimiter();                    
                     break;
                 } else {
                     fillBuffer();
@@ -206,6 +243,23 @@
             }
         }
     }
+    
+    private void skipLineDelimiter() {
+        int ch1 = 0;
+        int ch2 = 0;
+        int len = buffer.length(); 
+        if (len > 0) {
+            ch1 = buffer.charAt(buffer.pos());
+        }
+        if (len > 1) {
+            ch2 = buffer.charAt(buffer.pos() + 1);
+        }
+        if (ch1 == '\r' && ch2 == '\n') {
+            buffer.skip(2);
+        } else if (ch1 == '\n') {
+            buffer.skip(1);
+        }
+    }
     
     public boolean isLastPart() {
         return lastPart;        

Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeEntity.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeEntity.java?rev=674944&r1=674943&r2=674944&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeEntity.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeEntity.java Tue Jul  8 13:44:06 2008
@@ -18,12 +18,13 @@
      */
     private static final int T_IN_MESSAGE = -3;
 
-    private final InputBuffer inbuffer;
+    private final RootInputStream rootStream;
     private final InputStream rawStream;
+    private final InputBuffer inbuffer;
     
     private int recursionMode;
     private MimeBoundaryInputStream mimeStream;
-    private EOFSensitiveInputStream dataStream;
+    private BufferingInputStreamAdaptor dataStream;
     private boolean skipHeader;
     
     public MimeEntity(
@@ -35,10 +36,11 @@
             int endState,
             boolean maximalBodyDescriptor,
             boolean strictParsing) {
-        super(rootStream, parent, startState, endState, maximalBodyDescriptor, strictParsing);
+        super(parent, startState, endState, maximalBodyDescriptor, strictParsing);
+        this.rootStream = rootStream;
         this.inbuffer = inbuffer;
         this.rawStream = rawStream;
-        this.dataStream = new EOFSensitiveInputStream(rawStream);
+        this.dataStream = new BufferingInputStreamAdaptor(rawStream);
         this.skipHeader = false;
     }
 
@@ -68,7 +70,11 @@
         body.addField("Content-Type", contentType);
     }
     
-    protected InputStream getDataStream() {
+    protected int getLineNumber() {
+        return rootStream.getLineNumber();
+    }
+    
+    protected BufferingInputStream getDataStream() {
         return dataStream;
     }
     
@@ -85,9 +91,6 @@
             state = EntityStates.T_START_HEADER;
             break;
         case EntityStates.T_START_HEADER:
-            initHeaderParsing();
-            state = parseField() ? EntityStates.T_FIELD : EntityStates.T_END_HEADER;
-            break;
         case EntityStates.T_FIELD:
             state = parseField() ? EntityStates.T_FIELD : EntityStates.T_END_HEADER;
             break;
@@ -160,12 +163,12 @@
 
     private void createMimeStream() throws IOException {
         mimeStream = new MimeBoundaryInputStream(inbuffer, body.getBoundary());
-        dataStream = new EOFSensitiveInputStream(mimeStream); 
+        dataStream = new BufferingInputStreamAdaptor(mimeStream); 
     }
     
     private void clearMimeStream() {
         mimeStream = null;
-        dataStream = new EOFSensitiveInputStream(rawStream); 
+        dataStream = new BufferingInputStreamAdaptor(rawStream); 
     }
     
     private void advanceToBoundary() throws IOException {
@@ -181,12 +184,10 @@
         InputStream instream;
         if (MimeUtil.isBase64Encoding(transferEncoding)) {
             log.debug("base64 encoded message/rfc822 detected");
-            instream = new EOLConvertingInputStream(
-                    new Base64InputStream(mimeStream));                    
+            instream = new Base64InputStream(mimeStream);                    
         } else if (MimeUtil.isQuotedPrintableEncoded(transferEncoding)) {
             log.debug("quoted-printable encoded message/rfc822 detected");
-            instream = new EOLConvertingInputStream(
-                    new QuotedPrintableInputStream(mimeStream));                    
+            instream = new QuotedPrintableInputStream(mimeStream);                    
         } else {
             instream = dataStream;
         }

Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeTokenStream.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeTokenStream.java?rev=674944&r1=674943&r2=674944&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeTokenStream.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeTokenStream.java Tue Jul  8 13:44:06 2008
@@ -152,7 +152,7 @@
         inbuffer = new InputBuffer(rootInputStream, 4 * 1024);
         switch (recursionMode) {
         case M_RAW:
-            RawEntity rawentity = new RawEntity(new BufferingInputStream(inbuffer));
+            RawEntity rawentity = new RawEntity(new BasicBufferingInputStream(inbuffer));
             currentStateMachine = rawentity;
             break;
         case M_NO_RECURSE:
@@ -161,7 +161,7 @@
         case M_RECURSE:
             MimeEntity mimeentity = new MimeEntity(
                     rootInputStream,
-                    new BufferingInputStream(inbuffer),
+                    new BasicBufferingInputStream(inbuffer),
                     inbuffer,
                     null, 
                     T_START_MESSAGE, 

Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/MessageUtils.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/MessageUtils.java?rev=674944&r1=674943&r2=674944&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/MessageUtils.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/MessageUtils.java Tue Jul  8 13:44:06 2008
@@ -41,6 +41,11 @@
     
     public static final String CRLF = "\r\n";
     
+    public static final int CR = 13; // <US-ASCII CR, carriage return (13)>
+    public static final int LF = 10; // <US-ASCII LF, linefeed (10)>
+    public static final int SP = 32; // <US-ASCII SP, space (32)>
+    public static final int HT = 9;  // <US-ASCII HT, horizontal-tab (9)>
+
     public static boolean isASCII(char ch) {
         return (0xFF80 & ch) == 0;
     }
@@ -57,4 +62,9 @@
         }
         return true;
     }
+    
+    public static boolean isWhitespace(char ch) {
+        return ch == SP || ch == HT || ch == CR || ch == LF; 
+    }
+    
 }

Added: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/BasicBufferingInputStreamTest.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/test/java/org/apache/james/mime4j/BasicBufferingInputStreamTest.java?rev=674944&view=auto
==============================================================================
--- james/mime4j/trunk/src/test/java/org/apache/james/mime4j/BasicBufferingInputStreamTest.java (added)
+++ james/mime4j/trunk/src/test/java/org/apache/james/mime4j/BasicBufferingInputStreamTest.java Tue Jul  8 13:44:06 2008
@@ -0,0 +1,144 @@
+/****************************************************************
+ * 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;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+import junit.framework.TestCase;
+
+public class BasicBufferingInputStreamTest extends TestCase {
+
+    public void testBasicOperations() throws Exception {
+        String text = "ah blahblah";
+        byte[] b1 = text.getBytes("US-ASCII");
+        InputBuffer inbuffer = new InputBuffer(new ByteArrayInputStream(b1), 4096);
+        
+        BasicBufferingInputStream instream = new BasicBufferingInputStream(inbuffer); 
+        
+        assertEquals((byte)'a', instream.read());
+        assertEquals((byte)'h', instream.read());
+        assertEquals((byte)' ', instream.read());
+
+        byte[] tmp1 = new byte[4];
+        assertEquals(4, instream.read(tmp1));
+        assertEquals(4, instream.read(tmp1));
+
+        assertEquals(-1, instream.read(tmp1));
+        assertEquals(-1, instream.read(tmp1));
+        assertEquals(-1, instream.read());
+        assertEquals(-1, instream.read());
+    }
+
+    public void testBasicReadLine() throws Exception {
+        
+        String[] teststrs = new String[5];
+        teststrs[0] = "Hello\r\n";
+        teststrs[1] = "This string should be much longer than the size of the input buffer " +
+                "which is only 16 bytes for this test\r\n";
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < 15; i++) {
+            sb.append("123456789 ");
+        }
+        sb.append("and stuff like that\r\n");
+        teststrs[2] = sb.toString();
+        teststrs[3] = "\r\n";
+        teststrs[4] = "And goodbye\r\n";
+
+        ByteArrayOutputStream outstream = new ByteArrayOutputStream();
+        
+        for (int i = 0; i < teststrs.length; i++) {
+            outstream.write(teststrs[i].getBytes("US-ASCII"));
+        }
+        byte[] raw = outstream.toByteArray();
+        
+        InputBuffer inbuffer = new InputBuffer(new ByteArrayInputStream(raw), 16); 
+        BasicBufferingInputStream instream = new BasicBufferingInputStream(inbuffer); 
+        
+        ByteArrayBuffer linebuf = new ByteArrayBuffer(8); 
+        for (int i = 0; i < teststrs.length; i++) {
+            linebuf.clear();
+            instream.readLine(linebuf);
+            String s = new String(linebuf.toByteArray(), "US-ASCII");
+            assertEquals(teststrs[i], s);
+        }
+        assertEquals(-1, instream.readLine(linebuf));
+        assertEquals(-1, instream.readLine(linebuf));
+    }
+    
+    public void testReadEmptyLine() throws Exception {
+        
+        String teststr = "\n\n\r\n\r\r\n\n\n\n\n\n";
+        byte[] raw = teststr.getBytes("US-ASCII");
+        
+        InputBuffer inbuffer = new InputBuffer(new ByteArrayInputStream(raw), 4); 
+        BufferingInputStream instream = new BasicBufferingInputStream(inbuffer); 
+        
+        ByteArrayBuffer linebuf = new ByteArrayBuffer(8); 
+        linebuf.clear();
+        instream.readLine(linebuf);
+        String s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\n", s);
+        
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\n", s);
+        
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\r\n", s);
+
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\r\r\n", s);
+
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\n", s);
+
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\n", s);
+
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\n", s);
+
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\n", s);
+
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\n", s);
+
+        assertEquals(-1, instream.readLine(linebuf));
+        assertEquals(-1, instream.readLine(linebuf));
+    }
+
+}

Added: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/BufferingInputStreamAdaptorTest.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/test/java/org/apache/james/mime4j/BufferingInputStreamAdaptorTest.java?rev=674944&view=auto
==============================================================================
--- james/mime4j/trunk/src/test/java/org/apache/james/mime4j/BufferingInputStreamAdaptorTest.java (added)
+++ james/mime4j/trunk/src/test/java/org/apache/james/mime4j/BufferingInputStreamAdaptorTest.java Tue Jul  8 13:44:06 2008
@@ -0,0 +1,144 @@
+/****************************************************************
+ * 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;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+import junit.framework.TestCase;
+
+public class BufferingInputStreamAdaptorTest extends TestCase {
+
+    public void testBasicOperations() throws Exception {
+        String text = "ah blahblah";
+        byte[] b1 = text.getBytes("US-ASCII");
+        
+        BufferingInputStreamAdaptor instream = new BufferingInputStreamAdaptor(
+                new ByteArrayInputStream(b1)); 
+        
+        assertEquals((byte)'a', instream.read());
+        assertEquals((byte)'h', instream.read());
+        assertEquals((byte)' ', instream.read());
+
+        byte[] tmp1 = new byte[4];
+        assertEquals(4, instream.read(tmp1));
+        assertEquals(4, instream.read(tmp1));
+
+        assertEquals(-1, instream.read(tmp1));
+        assertEquals(-1, instream.read(tmp1));
+        assertEquals(-1, instream.read());
+        assertEquals(-1, instream.read());
+    }
+
+    public void testBasicReadLine() throws Exception {
+        
+        String[] teststrs = new String[5];
+        teststrs[0] = "Hello\r\n";
+        teststrs[1] = "This string should be much longer than the size of the input buffer " +
+                "which is only 16 bytes for this test\r\n";
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < 15; i++) {
+            sb.append("123456789 ");
+        }
+        sb.append("and stuff like that\r\n");
+        teststrs[2] = sb.toString();
+        teststrs[3] = "\r\n";
+        teststrs[4] = "And goodbye\r\n";
+
+        ByteArrayOutputStream outstream = new ByteArrayOutputStream();
+        
+        for (int i = 0; i < teststrs.length; i++) {
+            outstream.write(teststrs[i].getBytes("US-ASCII"));
+        }
+        byte[] raw = outstream.toByteArray();
+        
+        BufferingInputStreamAdaptor instream = new BufferingInputStreamAdaptor(
+                new ByteArrayInputStream(raw)); 
+        
+        ByteArrayBuffer linebuf = new ByteArrayBuffer(8); 
+        for (int i = 0; i < teststrs.length; i++) {
+            linebuf.clear();
+            instream.readLine(linebuf);
+            String s = new String(linebuf.toByteArray(), "US-ASCII");
+            assertEquals(teststrs[i], s);
+        }
+        assertEquals(-1, instream.readLine(linebuf));
+        assertEquals(-1, instream.readLine(linebuf));
+    }
+    
+    public void testReadEmptyLine() throws Exception {
+        
+        String teststr = "\n\n\r\n\r\r\n\n\n\n\n\n";
+        byte[] raw = teststr.getBytes("US-ASCII");
+        
+        BufferingInputStreamAdaptor instream = new BufferingInputStreamAdaptor(
+                new ByteArrayInputStream(raw)); 
+        
+        ByteArrayBuffer linebuf = new ByteArrayBuffer(8); 
+        linebuf.clear();
+        instream.readLine(linebuf);
+        String s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\n", s);
+        
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\n", s);
+        
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\r\n", s);
+
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\r\r\n", s);
+
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\n", s);
+
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\n", s);
+
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\n", s);
+
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\n", s);
+
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\n", s);
+
+        assertEquals(-1, instream.readLine(linebuf));
+        assertEquals(-1, instream.readLine(linebuf));
+    }
+
+}

Modified: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/InputBufferTest.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/test/java/org/apache/james/mime4j/InputBufferTest.java?rev=674944&r1=674943&r2=674944&view=diff
==============================================================================
--- james/mime4j/trunk/src/test/java/org/apache/james/mime4j/InputBufferTest.java (original)
+++ james/mime4j/trunk/src/test/java/org/apache/james/mime4j/InputBufferTest.java Tue Jul  8 13:44:06 2008
@@ -25,6 +25,107 @@
 
 public class InputBufferTest extends TestCase {
 
+    public void testInvalidInput() throws Exception {
+        String text = "blah blah yada yada";
+        byte[] b1 = text.getBytes("US-ASCII");
+        String pattern = "blah";
+        byte[] b2 = pattern.getBytes("US-ASCII");
+        InputBuffer inbuffer = new InputBuffer(new ByteArrayInputStream(b1), 4096);
+        inbuffer.fillBuffer();
+        
+        assertEquals((int)'b', inbuffer.read());
+        assertEquals((int)'l', inbuffer.read());
+        
+        try {
+            inbuffer.charAt(1);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException expected) {
+        }
+        try {
+            inbuffer.charAt(20);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException expected) {
+        }
+        try {
+            inbuffer.indexOf(b2, -1, 3);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException expected) {
+        }
+        try {
+            inbuffer.indexOf(b2, 1, 3);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException expected) {
+        }
+        try {
+            inbuffer.indexOf(b2, 2, -1);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException expected) {
+        }
+        try {
+            inbuffer.indexOf(b2, 2, 18);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException expected) {
+        }
+        assertEquals(5, inbuffer.indexOf(b2, 2, 17));
+        try {
+            inbuffer.indexOf((byte)' ', -1, 3);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException expected) {
+        }
+        try {
+            inbuffer.indexOf((byte)' ', 1, 3);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException expected) {
+        }
+        try {
+            inbuffer.indexOf((byte)' ', 2, -1);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException expected) {
+        }
+        try {
+            inbuffer.indexOf((byte)' ', 2, 18);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException expected) {
+        }
+        assertEquals(10, inbuffer.indexOf((byte)'y', 2, 17));
+    }
+      
+    public void testBasicOperations() throws Exception {
+        String text = "bla bla yada yada haha haha";
+        byte[] b1 = text.getBytes("US-ASCII");
+        InputBuffer inbuffer = new InputBuffer(new ByteArrayInputStream(b1), 4096);
+        inbuffer.fillBuffer();
+        assertEquals(0, inbuffer.pos());
+        assertEquals(27, inbuffer.limit());
+        assertEquals(27, inbuffer.length());
+
+        inbuffer.read();
+        inbuffer.read();
+
+        assertEquals(2, inbuffer.pos());
+        assertEquals(27, inbuffer.limit());
+        assertEquals(25, inbuffer.length());
+        
+        byte[] tmp1 = new byte[3];
+        assertEquals(3, inbuffer.read(tmp1));
+
+        assertEquals(5, inbuffer.pos());
+        assertEquals(27, inbuffer.limit());
+        assertEquals(22, inbuffer.length());
+        
+        byte[] tmp2 = new byte[22];
+        assertEquals(22, inbuffer.read(tmp2));
+
+        assertEquals(27, inbuffer.pos());
+        assertEquals(27, inbuffer.limit());
+        assertEquals(0, inbuffer.length());
+
+        assertEquals(-1, inbuffer.read(tmp1));
+        assertEquals(-1, inbuffer.read(tmp1));
+        assertEquals(-1, inbuffer.read());
+        assertEquals(-1, inbuffer.read());
+    }
+
     public void testPatternMatching1() throws Exception {
         String text = "blabla d is the word";
         String pattern = "d";
@@ -68,4 +169,32 @@
         int i = inbuffer.indexOf(b2);
         assertEquals(0, i);
     }
+
+    public void testPatternOutOfBound() throws Exception {
+        String text = "bla bla yada yada haha haha";
+        String pattern1 = "bla bla";
+        byte[] b1 = text.getBytes("US-ASCII");
+        byte[] b2 = pattern1.getBytes("US-ASCII");
+        InputBuffer inbuffer = new InputBuffer(new ByteArrayInputStream(b1), 4096);
+        inbuffer.fillBuffer();
+        byte[] tmp = new byte[3];
+        inbuffer.read(tmp);
+        int i = inbuffer.indexOf(b2, inbuffer.pos(), inbuffer.length());
+        assertEquals(-1, i);
+        i = inbuffer.indexOf(b2, inbuffer.pos(), inbuffer.length() - 1);
+        assertEquals(-1, i);
+    }
+
+    public void testCharOutOfBound() throws Exception {
+        String text = "zzz blah blah blah ggg";
+        byte[] b1 = text.getBytes("US-ASCII");
+        InputBuffer inbuffer = new InputBuffer(new ByteArrayInputStream(b1), 4096);
+        inbuffer.fillBuffer();
+        byte[] tmp = new byte[3];
+        inbuffer.read(tmp);
+        int i = inbuffer.indexOf((byte)'z', inbuffer.pos(), inbuffer.length());
+        assertEquals(-1, i);
+        i = inbuffer.indexOf((byte)'g', inbuffer.pos(), inbuffer.length() - 3);
+        assertEquals(-1, i);
+    }
 }

Modified: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/MimeBoundaryInputStreamTest.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/test/java/org/apache/james/mime4j/MimeBoundaryInputStreamTest.java?rev=674944&r1=674943&r2=674944&view=diff
==============================================================================
--- james/mime4j/trunk/src/test/java/org/apache/james/mime4j/MimeBoundaryInputStreamTest.java (original)
+++ james/mime4j/trunk/src/test/java/org/apache/james/mime4j/MimeBoundaryInputStreamTest.java Tue Jul  8 13:44:06 2008
@@ -20,6 +20,7 @@
 package org.apache.james.mime4j;
 
 import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
@@ -55,6 +56,25 @@
         assertTrue(mime2.isLastPart());
     }
     
+    public void testLenientLineDelimiterReading() throws IOException {
+        String text = "Line 1\r\nLine 2\n--boundary\n" +
+                "Line 3\r\nLine 4\n--boundary--\n";
+        
+        ByteArrayInputStream bis = new ByteArrayInputStream(text.getBytes("US-ASCII"));
+        
+        InputBuffer buffer = new InputBuffer(bis, 4096); 
+        
+        MimeBoundaryInputStream mime1 = new MimeBoundaryInputStream(buffer, "boundary");
+        assertEquals("Line 1\r\nLine 2", read(mime1, 5));
+        
+        assertFalse(mime1.isLastPart());
+        
+        MimeBoundaryInputStream mime2 = new MimeBoundaryInputStream(buffer, "boundary");
+        assertEquals("Line 3\r\nLine 4", read(mime2, 5));
+
+        assertTrue(mime2.isLastPart());
+    }
+    
     public void testBasicReadingSmallBuffer1() throws IOException {
         String text = "yadayadayadayadayadayadayadayadayadayadayadayadayadayadayadayada\r\n--boundary\r\n" +
                 "blahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblah\r\n--boundary--";
@@ -215,5 +235,99 @@
         buffer = new InputBuffer(bis, 4096); 
         stream = new MimeBoundaryInputStream(buffer, "boundary");
         assertEquals(-1, stream.read());
-    }    
+    }
+    
+    
+    public void testBasicReadLine() throws Exception {
+        
+        String[] teststrs = new String[5];
+        teststrs[0] = "Hello\r\n";
+        teststrs[1] = "This string should be much longer than the size of the input buffer " +
+                "which is only 20 bytes for this test\r\n";
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < 15; i++) {
+            sb.append("123456789 ");
+        }
+        sb.append("and stuff like that\r\n");
+        teststrs[2] = sb.toString();
+        teststrs[3] = "\r\n";
+        teststrs[4] = "And goodbye\r\n";
+
+        String term = "\r\n--1234\r\n";
+        
+        ByteArrayOutputStream outstream = new ByteArrayOutputStream();
+        
+        for (int i = 0; i < teststrs.length; i++) {
+            outstream.write(teststrs[i].getBytes("US-ASCII"));
+        }
+        outstream.write(term.getBytes("US-ASCII"));
+        byte[] raw = outstream.toByteArray();
+        
+        InputBuffer inbuffer = new InputBuffer(new ByteArrayInputStream(raw), 20); 
+        BufferingInputStream instream = new MimeBoundaryInputStream(inbuffer, "1234"); 
+        
+        ByteArrayBuffer linebuf = new ByteArrayBuffer(8); 
+        for (int i = 0; i < teststrs.length; i++) {
+            linebuf.clear();
+            instream.readLine(linebuf);
+            String s = new String(linebuf.toByteArray(), "US-ASCII");
+            assertEquals(teststrs[i], s);
+        }
+        assertEquals(-1, instream.readLine(linebuf));
+        assertEquals(-1, instream.readLine(linebuf));
+    }
+    
+    public void testReadEmptyLine() throws Exception {
+        
+        String teststr = "01234567890123456789\n\n\r\n\r\r\n\n\n\n\n\n--1234\r\n";
+        byte[] raw = teststr.getBytes("US-ASCII");
+        
+        InputBuffer inbuffer = new InputBuffer(new ByteArrayInputStream(raw), 20); 
+        BufferingInputStream instream = new MimeBoundaryInputStream(inbuffer, "1234"); 
+        
+        ByteArrayBuffer linebuf = new ByteArrayBuffer(8); 
+        linebuf.clear();
+        instream.readLine(linebuf);
+        String s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("01234567890123456789\n", s);
+        
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\n", s);
+        
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\r\n", s);
+
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\r\r\n", s);
+
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\n", s);
+
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\n", s);
+
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\n", s);
+
+        linebuf.clear();
+        instream.readLine(linebuf);
+        s = new String(linebuf.toByteArray(), "US-ASCII");
+        assertEquals("\n", s);
+
+        assertEquals(-1, instream.readLine(linebuf));
+        assertEquals(-1, instream.readLine(linebuf));
+    }
+    
 }

Modified: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/MimeEntityTest.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/test/java/org/apache/james/mime4j/MimeEntityTest.java?rev=674944&r1=674943&r2=674944&view=diff
==============================================================================
--- james/mime4j/trunk/src/test/java/org/apache/james/mime4j/MimeEntityTest.java (original)
+++ james/mime4j/trunk/src/test/java/org/apache/james/mime4j/MimeEntityTest.java Tue Jul  8 13:44:06 2008
@@ -40,7 +40,7 @@
         ByteArrayInputStream instream = new ByteArrayInputStream(raw);
         RootInputStream rootStream = new RootInputStream(instream); 
         InputBuffer inbuffer = new InputBuffer(rootStream, 12); 
-        BufferingInputStream rawstream = new BufferingInputStream(inbuffer); 
+        BasicBufferingInputStream rawstream = new BasicBufferingInputStream(inbuffer); 
         
         MimeEntity entity = new MimeEntity(
                 rootStream,
@@ -129,7 +129,7 @@
         ByteArrayInputStream instream = new ByteArrayInputStream(raw);
         RootInputStream rootStream = new RootInputStream(instream); 
         InputBuffer inbuffer = new InputBuffer(rootStream, 24); 
-        BufferingInputStream rawstream = new BufferingInputStream(inbuffer); 
+        BasicBufferingInputStream rawstream = new BasicBufferingInputStream(inbuffer); 
         
         MimeEntity entity = new MimeEntity(
                 rootStream,
@@ -244,7 +244,7 @@
         ByteArrayInputStream instream = new ByteArrayInputStream(raw);
         RootInputStream rootStream = new RootInputStream(instream); 
         InputBuffer inbuffer = new InputBuffer(rootStream, 24); 
-        BufferingInputStream rawstream = new BufferingInputStream(inbuffer); 
+        BasicBufferingInputStream rawstream = new BasicBufferingInputStream(inbuffer); 
         
         MimeEntity entity = new MimeEntity(
                 rootStream,

Modified: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/StrictMimeTokenStreamTest.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/test/java/org/apache/james/mime4j/StrictMimeTokenStreamTest.java?rev=674944&r1=674943&r2=674944&view=diff
==============================================================================
--- james/mime4j/trunk/src/test/java/org/apache/james/mime4j/StrictMimeTokenStreamTest.java (original)
+++ james/mime4j/trunk/src/test/java/org/apache/james/mime4j/StrictMimeTokenStreamTest.java Tue Jul  8 13:44:06 2008
@@ -35,6 +35,7 @@
         parser.parse(new ByteArrayInputStream(HEADER_ONLY.getBytes()));
         
         assertEquals("Headers start", MimeTokenStream.T_START_HEADER, parser.next());
+        assertEquals("Field", MimeTokenStream.T_FIELD, parser.next());
         try {
             parser.next();
             fail("Expected exception to be thrown");

Added: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/TestByteArrayBuffer.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/test/java/org/apache/james/mime4j/TestByteArrayBuffer.java?rev=674944&view=auto
==============================================================================
--- james/mime4j/trunk/src/test/java/org/apache/james/mime4j/TestByteArrayBuffer.java (added)
+++ james/mime4j/trunk/src/test/java/org/apache/james/mime4j/TestByteArrayBuffer.java Tue Jul  8 13:44:06 2008
@@ -0,0 +1,229 @@
+/****************************************************************
+ * 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;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link ByteArrayBuffer}.
+ */
+public class TestByteArrayBuffer extends TestCase {
+
+    public void testConstructor() throws Exception {
+        ByteArrayBuffer buffer = new ByteArrayBuffer(16);
+        assertEquals(16, buffer.capacity()); 
+        assertEquals(0, buffer.length());
+        assertNotNull(buffer.buffer());
+        assertEquals(16, buffer.buffer().length);
+        try {
+            new ByteArrayBuffer(-1);
+            fail("IllegalArgumentException should have been thrown");
+        } catch (IllegalArgumentException ex) {
+            // expected
+        }
+    }
+    
+    public void testSimpleAppend() throws Exception {
+        ByteArrayBuffer buffer = new ByteArrayBuffer(16);
+        assertEquals(16, buffer.capacity()); 
+        assertEquals(0, buffer.length());
+        byte[] b1 = buffer.toByteArray();
+        assertNotNull(b1);
+        assertEquals(0, b1.length);
+        assertTrue(buffer.isEmpty());
+        assertFalse(buffer.isFull());
+        
+        byte[] tmp = new byte[] { 1, 2, 3, 4};
+        buffer.append(tmp, 0, tmp.length);
+        assertEquals(16, buffer.capacity()); 
+        assertEquals(4, buffer.length());
+        assertFalse(buffer.isEmpty());
+        assertFalse(buffer.isFull());
+        
+        byte[] b2 = buffer.toByteArray();
+        assertNotNull(b2);
+        assertEquals(4, b2.length);
+        for (int i = 0; i < tmp.length; i++) {
+            assertEquals(tmp[i], b2[i]);
+            assertEquals(tmp[i], buffer.byteAt(i));
+        }
+        buffer.clear();
+        assertEquals(16, buffer.capacity()); 
+        assertEquals(0, buffer.length());
+        assertTrue(buffer.isEmpty());
+        assertFalse(buffer.isFull());
+    }
+    
+    public void testExpandAppend() throws Exception {
+        ByteArrayBuffer buffer = new ByteArrayBuffer(4);
+        assertEquals(4, buffer.capacity()); 
+        
+        byte[] tmp = new byte[] { 1, 2, 3, 4};
+        buffer.append(tmp, 0, 2);
+        buffer.append(tmp, 0, 4);
+        buffer.append(tmp, 0, 0);
+
+        assertEquals(8, buffer.capacity()); 
+        assertEquals(6, buffer.length());
+        
+        buffer.append(tmp, 0, 4);
+        
+        assertEquals(16, buffer.capacity()); 
+        assertEquals(10, buffer.length());
+    }
+    
+    public void testInvalidAppend() throws Exception {
+        ByteArrayBuffer buffer = new ByteArrayBuffer(4);
+        buffer.append((byte[])null, 0, 0);
+
+        byte[] tmp = new byte[] { 1, 2, 3, 4};
+        try {
+            buffer.append(tmp, -1, 0);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException ex) {
+            // expected
+        }
+        try {
+            buffer.append(tmp, 0, -1);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException ex) {
+            // expected
+        }
+        try {
+            buffer.append(tmp, 0, 8);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException ex) {
+            // expected
+        }
+        try {
+            buffer.append(tmp, 10, Integer.MAX_VALUE);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException ex) {
+            // expected
+        }
+        try {
+            buffer.append(tmp, 2, 4);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException ex) {
+            // expected
+        }
+    }
+
+    public void testAppendOneByte() throws Exception {
+        ByteArrayBuffer buffer = new ByteArrayBuffer(4);
+        assertEquals(4, buffer.capacity()); 
+        
+        byte[] tmp = new byte[] { 1, 127, -1, -128, 1, -2};
+        for (int i = 0; i < tmp.length; i++) {
+            buffer.append(tmp[i]);
+        }
+        assertEquals(8, buffer.capacity()); 
+        assertEquals(6, buffer.length());
+        
+        for (int i = 0; i < tmp.length; i++) {
+            assertEquals(tmp[i], buffer.byteAt(i));
+        }
+    }
+    
+    public void testSetLength() throws Exception {
+        ByteArrayBuffer buffer = new ByteArrayBuffer(4);
+        buffer.setLength(2);
+        assertEquals(2, buffer.length());
+    }
+    
+    public void testSetInvalidLength() throws Exception {
+        ByteArrayBuffer buffer = new ByteArrayBuffer(4);
+        try {
+            buffer.setLength(-2);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException ex) {
+            // expected
+        }
+        try {
+            buffer.setLength(200);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException ex) {
+            // expected
+        }
+    }
+    
+    public void testAppendCharArrayAsAscii() throws Exception {
+        String s1 = "stuff";
+        String s2 = " and more stuff";
+        char[] b1 = s1.toCharArray();
+        char[] b2 = s2.toCharArray();
+        
+        ByteArrayBuffer buffer = new ByteArrayBuffer(8);
+        buffer.append(b1, 0, b1.length);
+        buffer.append(b2, 0, b2.length);
+         
+        assertEquals(s1 + s2, new String(buffer.toByteArray(), "US-ASCII"));
+    }
+    
+    public void testAppendNullCharArray() throws Exception {
+        ByteArrayBuffer buffer = new ByteArrayBuffer(8);
+        buffer.append((char[])null, 0, 0);
+        assertEquals(0, buffer.length());
+    }
+
+    public void testAppendEmptyCharArray() throws Exception {
+        ByteArrayBuffer buffer = new ByteArrayBuffer(8);
+        buffer.append(new char[] {}, 0, 0);
+        assertEquals(0, buffer.length());
+    }
+
+    public void testInvalidAppendCharArrayAsAscii() throws Exception {
+        ByteArrayBuffer buffer = new ByteArrayBuffer(4);
+        buffer.append((char[])null, 0, 0);
+
+        char[] tmp = new char[] { '1', '2', '3', '4'};
+        try {
+            buffer.append(tmp, -1, 0);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException ex) {
+            // expected
+        }
+        try {
+            buffer.append(tmp, 0, -1);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException ex) {
+            // expected
+        }
+        try {
+            buffer.append(tmp, 0, 8);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException ex) {
+            // expected
+        }
+        try {
+            buffer.append(tmp, 10, Integer.MAX_VALUE);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException ex) {
+            // expected
+        }
+        try {
+            buffer.append(tmp, 2, 4);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException ex) {
+            // expected
+        }
+    }
+    
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org