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/01 22:31:51 UTC
svn commit: r673195 - in /james/mime4j/trunk/src:
main/java/org/apache/james/mime4j/ test/java/org/apache/james/mime4j/
Author: rdonkin
Date: Tue Jul 1 13:31:51 2008
New Revision: 673195
URL: http://svn.apache.org/viewvc?rev=673195&view=rev
Log:
MIME4J-5 Performance improvements. First of a sequence of patches. Contributed by Oleg Kalnichevski. See https://issues.apache.org/jira/browse/MIME4J-5.
Added:
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BufferingInputStream.java
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/InputBuffer.java
james/mime4j/trunk/src/test/java/org/apache/james/mime4j/InputBufferTest.java (with props)
Modified:
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/Cursor.java
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeBoundaryInputStream.java
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeException.java
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeTokenStream.java
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/StreamCursor.java
james/mime4j/trunk/src/test/java/org/apache/james/mime4j/MimeBoundaryInputStreamTest.java
james/mime4j/trunk/src/test/java/org/apache/james/mime4j/MimeTokenNoRecurseTest.java
Added: 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=673195&view=auto
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BufferingInputStream.java (added)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BufferingInputStream.java Tue Jul 1 13:31:51 2008
@@ -0,0 +1,53 @@
+/****************************************************************
+ * 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);
+ }
+
+}
Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/Cursor.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/Cursor.java?rev=673195&r1=673194&r2=673195&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/Cursor.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/Cursor.java Tue Jul 1 13:31:51 2008
@@ -112,4 +112,11 @@
* @return <code>InputStream</code>, not null
*/
public InputStream rest();
+
+ /**
+ * Gets root stream for this message.
+ * @return <code>InputStream</code>, not null
+ */
+ public InputStream root();
+
}
Added: 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=673195&view=auto
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/InputBuffer.java (added)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/InputBuffer.java Tue Jul 1 13:31:51 2008
@@ -0,0 +1,200 @@
+/****************************************************************
+ * 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 buffer that can be used to search for patterns using Quick Search
+ * algorithm in data read from an {@link InputStream}.
+ */
+public class InputBuffer {
+
+ private final InputStream instream;
+ private final byte[] buffer;
+
+ private int bufpos;
+ private int buflen;
+
+ public InputBuffer(final InputStream instream, int buffersize) {
+ super();
+ if (instream == null) {
+ throw new IllegalArgumentException("Input stream may not be null");
+ }
+ if (buffersize <= 0) {
+ throw new IllegalArgumentException("Buffer size may not be negative or zero");
+ }
+ this.instream = instream;
+ this.buffer = new byte[buffersize];
+ this.bufpos = 0;
+ this.buflen = 0;
+ }
+
+ public int fillBuffer() throws IOException {
+ // compact the buffer if necessary
+ if (this.bufpos > 0) {
+ int len = this.buflen - this.bufpos;
+ if (len > 0) {
+ System.arraycopy(this.buffer, this.bufpos, this.buffer, 0, len);
+ }
+ this.bufpos = 0;
+ this.buflen = len;
+ }
+ int l;
+ int off = this.buflen;
+ int len = this.buffer.length - off;
+ l = this.instream.read(this.buffer, off, len);
+ if (l == -1) {
+ return -1;
+ } else {
+ this.buflen = off + l;
+ return l;
+ }
+ }
+
+ public boolean hasBufferedData() {
+ return this.bufpos < this.buflen;
+ }
+
+ public void closeStream() throws IOException {
+ this.instream.close();
+ }
+
+ public int read() throws IOException {
+ int noRead = 0;
+ while (!hasBufferedData()) {
+ noRead = fillBuffer();
+ if (noRead == -1) {
+ return -1;
+ }
+ }
+ return this.buffer[this.bufpos++] & 0xff;
+ }
+
+ public int read(final byte[] b, int off, int len) throws IOException {
+ if (b == null) {
+ return 0;
+ }
+ int noRead = 0;
+ while (!hasBufferedData()) {
+ noRead = fillBuffer();
+ if (noRead == -1) {
+ return -1;
+ }
+ }
+ int chunk = this.buflen - this.bufpos;
+ if (chunk > len) {
+ chunk = len;
+ }
+ System.arraycopy(this.buffer, this.bufpos, b, off, chunk);
+ this.bufpos += chunk;
+ return chunk;
+ }
+
+ public int read(final byte[] b) throws IOException {
+ if (b == null) {
+ return 0;
+ }
+ return read(b, 0, b.length);
+ }
+
+ /**
+ * 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) {
+ int len = this.buflen - this.bufpos;
+ if (len < pattern.length) {
+ return -1;
+ }
+
+ int[] shiftTable = new int[255];
+ for (int i = 0; i < shiftTable.length; i++) {
+ shiftTable[i] = pattern.length + 1;
+ }
+ for (int i = 0; i < pattern.length; i++) {
+ int x = pattern[i] & 0xff;
+ shiftTable[x] = pattern.length - i;
+ }
+
+ int j = 0;
+ while (j <= len - pattern.length) {
+ int cur = this.bufpos + j;
+ boolean match = true;
+ for (int i = 0; i < pattern.length; i++) {
+ if (this.buffer[cur + i] != pattern[i]) {
+ match = false;
+ break;
+ }
+ }
+ if (match) {
+ return cur;
+ }
+
+ int pos = cur + pattern.length;
+ if (pos >= this.buffer.length) {
+ break;
+ }
+ int x = this.buffer[pos] & 0xff;
+ j += shiftTable[x];
+ }
+ return -1;
+ }
+
+ public int length() {
+ return this.buflen;
+ }
+
+ public byte charAt(int pos) {
+ return this.buffer[pos];
+ }
+
+ public int pos() {
+ return this.bufpos;
+ }
+
+ public int skip(int n) {
+ int chunk = Math.min(n, this.buflen - this.bufpos);
+ this.bufpos += chunk;
+ return chunk;
+ }
+
+ public String toString() {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("[pos: ");
+ buffer.append(this.bufpos);
+ buffer.append("]");
+ buffer.append("[len: ");
+ buffer.append(this.buflen);
+ buffer.append("]");
+ buffer.append("[");
+ for (int i = this.bufpos; i < this.buflen; i++) {
+ buffer.append((char) this.buffer[i]);
+ }
+ buffer.append("]");
+ return buffer.toString();
+ }
+
+}
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=673195&r1=673194&r2=673195&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 1 13:31:51 2008
@@ -21,52 +21,52 @@
import java.io.IOException;
import java.io.InputStream;
-import java.io.PushbackInputStream;
/**
* Stream that constrains itself to a single MIME body part.
- * After the stream ends (i.e. read() returns -1) {@link #hasMoreParts()}
+ * After the stream ends (i.e. read() returns -1) {@link #isLastPart()}
* can be used to determine if a final boundary has been seen or not.
- * If {@link #parentEOF()} is <code>true</code> an unexpected end of stream
- * has been detected in the parent stream.
- *
- *
*
* @version $Id: MimeBoundaryInputStream.java,v 1.2 2004/11/29 13:15:42 ntherning Exp $
*/
public class MimeBoundaryInputStream extends InputStream {
+
+ private final InputBuffer buffer;
+ private final byte[] boundary;
- private PushbackInputStream s;
- private byte[] boundary;
- private boolean first = true;
private boolean eof;
- private boolean parenteof;
- private boolean moreParts = true;
+ private int limit;
+ private boolean atBoundary;
+ private int boundaryLen;
+ private boolean lastPart;
+ private boolean completed;
/**
* Creates a new MimeBoundaryInputStream.
* @param s The underlying stream.
* @param boundary Boundary string (not including leading hyphens).
*/
- public MimeBoundaryInputStream(InputStream s, String boundary)
+ public MimeBoundaryInputStream(InputBuffer inbuffer, String boundary)
throws IOException {
-
- this.s = new PushbackInputStream(s, boundary.length() + 4);
-
- boundary = "--" + boundary;
- this.boundary = new byte[boundary.length()];
- for (int i = 0; i < this.boundary.length; i++) {
- this.boundary[i] = (byte) boundary.charAt(i);
- }
-
- /*
- * By reading one byte we will update moreParts to be as expected
- * before any bytes have been read.
- */
- int b = read();
- if (b != -1) {
- this.s.unread(b);
+ this.buffer = inbuffer;
+ this.eof = false;
+ this.limit = 0;
+ this.atBoundary = false;
+ this.boundaryLen = 0;
+ this.lastPart = false;
+ this.completed = false;
+
+ this.boundary = new byte[boundary.length() + 2];
+ this.boundary[0] = (byte) '-';
+ this.boundary[1] = (byte) '-';
+ for (int i = 0; i < boundary.length(); i++) {
+ byte ch = (byte) boundary.charAt(i);
+ if (ch == '\r' || ch == '\n') {
+ throw new IllegalArgumentException("Boundary may not contain CR or LF");
+ }
+ this.boundary[i + 2] = ch;
}
+ fillBuffer();
}
/**
@@ -75,110 +75,144 @@
* @throws IOException on I/O errors.
*/
public void close() throws IOException {
- s.close();
}
/**
- * Determines if the underlying stream has more parts (this stream has
- * not seen an end boundary).
- *
- * @return <code>true</code> if there are more parts in the underlying
- * stream, <code>false</code> otherwise.
+ * @see java.io.InputStream#markSupported()
*/
- public boolean hasMoreParts() {
- return moreParts;
+ public boolean markSupported() {
+ return false;
}
/**
- * Determines if the parent stream has reached EOF
- *
- * @return <code>true</code> if EOF has been reached for the parent stream,
- * <code>false</code> otherwise.
+ * @see java.io.InputStream#read()
*/
- public boolean parentEOF() {
- return parenteof;
+ public int read() throws IOException {
+ if (completed) {
+ return -1;
+ }
+ if (endOfStream() && !hasData()) {
+ skipBoundary();
+ return -1;
+ }
+ for (;;) {
+ if (hasData()) {
+ return buffer.read();
+ } else if (endOfStream()) {
+ skipBoundary();
+ return -1;
+ }
+ fillBuffer();
+ }
}
- /**
- * Consumes all unread bytes of this stream. After a call to this method
- * this stream will have reached EOF.
- *
- * @throws IOException on I/O errors.
- */
- public void consume() throws IOException {
- while (read() != -1) {
- }
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (completed) {
+ return -1;
+ }
+ if (endOfStream() && !hasData()) {
+ skipBoundary();
+ return -1;
+ }
+ fillBuffer();
+ if (!hasData()) {
+ return 0;
+ }
+ int chunk = Math.min(len, limit - buffer.pos());
+ return buffer.read(b, off, chunk);
+ }
+
+ private boolean endOfStream() {
+ return eof || atBoundary;
}
- /**
- * @see java.io.InputStream#read()
- */
- public int read() throws IOException {
+ private boolean hasData() {
+ return limit > buffer.pos() && limit < buffer.length();
+ }
+
+ private int fillBuffer() throws IOException {
if (eof) {
return -1;
}
-
- if (first) {
- first = false;
- if (matchBoundary()) {
- return -1;
+ int bytesRead;
+ if (!hasData()) {
+ bytesRead = buffer.fillBuffer();
+ } else {
+ bytesRead = 0;
+ }
+ eof = bytesRead == -1;
+
+ int i = buffer.indexOf(boundary);
+ if (i != -1) {
+ limit = i;
+ atBoundary = true;
+ calculateBoundaryLen();
+ } else {
+ if (eof) {
+ limit = buffer.length();
+ } else {
+ limit = buffer.length() - (boundary.length + 1);
+ // \r\n + (boundary - one char)
}
}
-
- int b1 = s.read();
- int b2 = s.read();
-
- if (b1 == '\r' && b2 == '\n') {
- if (matchBoundary()) {
- return -1;
+ return bytesRead;
+ }
+
+ private void calculateBoundaryLen() throws IOException {
+ boundaryLen = boundary.length;
+ int len = limit - buffer.pos();
+ if (len > 0) {
+ if (buffer.charAt(limit - 1) == '\n') {
+ boundaryLen++;
+ limit--;
}
}
-
- if (b2 != -1) {
- s.unread(b2);
+ if (len > 1) {
+ if (buffer.charAt(limit - 1) == '\r') {
+ boundaryLen++;
+ limit--;
+ }
}
-
- parenteof = b1 == -1;
- eof = parenteof;
-
- return b1;
}
- private boolean matchBoundary() throws IOException {
-
- for (int i = 0; i < boundary.length; i++) {
- int b = s.read();
- if (b != boundary[i]) {
- if (b != -1) {
- s.unread(b);
+ private void skipBoundary() throws IOException {
+ if (!completed) {
+ completed = true;
+ buffer.skip(boundaryLen);
+ 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);
+ }
+ break;
+ } else {
+ fillBuffer();
}
- for (int j = i - 1; j >= 0; j--) {
- s.unread(boundary[j]);
+ if (eof) {
+ break;
}
- return false;
}
}
-
- /*
- * We have a match. Is it an end boundary?
- */
- int prev = s.read();
- int curr = s.read();
- moreParts = !(prev == '-' && curr == '-');
- do {
- if (curr == '\n' && prev == '\r') {
- break;
- }
- prev = curr;
- } while ((curr = s.read()) != -1);
-
- if (curr == -1) {
- moreParts = false;
- parenteof = true;
- }
-
- eof = true;
-
- return true;
+ }
+
+ public boolean isLastPart() {
+ return lastPart;
+ }
+
+ public boolean eof() {
+ return eof && !buffer.hasBufferedData();
}
+
}
Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeException.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeException.java?rev=673195&r1=673194&r2=673195&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeException.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeException.java Tue Jul 1 13:31:51 2008
@@ -23,6 +23,8 @@
*/
public class MimeException extends IOException {
+ private static final long serialVersionUID = 8352821278714188542L;
+
/**
* Constructs a new MIME exception with the specified detail message.
*
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=673195&r1=673194&r2=673195&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 1 13:31:51 2008
@@ -341,6 +341,7 @@
private int pos, start;
private int lineNumber, startLineNumber;
private final int endState;
+ private boolean raw;
String field, fieldName, fieldValue;
@@ -357,7 +358,7 @@
private int setParseBodyPartState() throws IOException, MimeException {
cursor.advanceToBoundary();
- if (cursor.isEnded()) {
+ if (cursor.isEnded() && cursor.moreMimeParts()) {
monitor(Event.MIME_BODY_PREMATURE_END);
} else {
if (cursor.moreMimeParts()) {
@@ -414,7 +415,7 @@
break;
case T_START_MULTIPART:
cursor.boundary(body.getBoundary());
- if (cursor.isEnded()) {
+ if (cursor.isEnded() || raw) {
state = T_END_MULTIPART;
} else {
state = T_PREAMBLE;
@@ -449,6 +450,7 @@
case T_BODY:
return cursor.nextSection();
case T_START_MULTIPART:
+ raw = true;
return cursor.rest();
default:
throw new IllegalStateException("Expected state to be either of T_RAW_ENTITY, T_PREAMBLE, or T_EPILOGUE.");
@@ -535,6 +537,16 @@
Message(Cursor cursor, BodyDescriptor parent) {
super(cursor, parent, T_START_MESSAGE, T_END_MESSAGE);
}
+
+ InputStream read() {
+ switch (getState()) {
+ case T_EPILOGUE:
+ return cursor.root();
+ default:
+ return super.read();
+ }
+ }
+
}
private class BodyPart extends Entity {
Modified: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/StreamCursor.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/StreamCursor.java?rev=673195&r1=673194&r2=673195&view=diff
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/StreamCursor.java (original)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/StreamCursor.java Tue Jul 1 13:31:51 2008
@@ -30,19 +30,21 @@
*/
public class StreamCursor implements Cursor {
- final RootInputStream rootInputStream;
- final InputStream contents;
- MimeBoundaryInputStream mbis;
- InputStream stream;
+ protected final InputBuffer buffer;
+ protected final BufferingInputStream bufferingInputStream;
+ protected final RootInputStream rootInputStream;
+
+ protected MimeBoundaryInputStream mbis;
+ protected InputStream contentStream;
/**
* Constructs a child cursor.
* @param parent <code>Cursor</code>, not null
- * @param contents <code>InputStream</code> contents, not null
*/
- StreamCursor(StreamCursor parent, InputStream contents) {
+ StreamCursor(StreamCursor parent) {
+ this.buffer = parent.buffer;
+ this.bufferingInputStream = parent.bufferingInputStream;
this.rootInputStream = parent.rootInputStream;
- this.contents = contents;
}
/**
@@ -50,18 +52,22 @@
* @param stream <code>InputStream</code>, not null
*/
StreamCursor(InputStream stream) {
- rootInputStream = new RootInputStream(stream);
- contents = rootInputStream;
+ this.buffer = new InputBuffer(stream, 1024 * 4);
+ this.bufferingInputStream = new BufferingInputStream(this.buffer);
+ this.rootInputStream = new RootInputStream(this.bufferingInputStream);
+ this.contentStream = this.rootInputStream;
}
/**
- * Constructs a cursor with the given root and contents.
- * @param rootInputStream <code>RootInputStream</code>, not null
- * @param contents <code>InputStream</code>, not null
- */
- StreamCursor(RootInputStream rootInputStream, InputStream contents) {
- this.rootInputStream = rootInputStream;
- this.contents = contents;
+ * Constructs a cursor from the previous cursor.
+ * @param previous <code>StreamCursor</code>, not null
+ * @param contentStream <code>InputStream</code>, not null
+ */
+ StreamCursor(StreamCursor previous, InputStream contentStream) {
+ this.buffer = previous.buffer;
+ this.bufferingInputStream = previous.bufferingInputStream;
+ this.rootInputStream = previous.rootInputStream;
+ this.contentStream = contentStream;
}
/**
@@ -82,65 +88,61 @@
* @see Cursor#decodeBase64()
*/
public Cursor decodeBase64() throws IOException {
- InputStream is = new EOLConvertingInputStream(new Base64InputStream(contents));
- return new StreamCursor(rootInputStream, is);
+ return new StreamCursor(this,
+ new EOLConvertingInputStream(new Base64InputStream(mbis)));
}
/**
* @see Cursor#decodeQuotedPrintable()
*/
public Cursor decodeQuotedPrintable() throws IOException {
- InputStream is = new EOLConvertingInputStream(new QuotedPrintableInputStream(contents));
- return new StreamCursor(rootInputStream, is);
+ return new StreamCursor(this,
+ new EOLConvertingInputStream(new QuotedPrintableInputStream(mbis)));
}
/**
* @see Cursor#advanceToBoundary()
*/
public void advanceToBoundary() throws IOException {
- mbis.consume();
- stream = null;
+ byte[] tmp = new byte[2048];
+ while (mbis.read(tmp)!= -1) {
+ }
}
/**
* @see Cursor#isEnded()
*/
public boolean isEnded() throws IOException {
- return mbis.parentEOF();
+ return mbis.eof();
}
/**
* @see Cursor#moreMimeParts()
*/
public boolean moreMimeParts() throws IOException {
- return mbis.hasMoreParts();
+ return !mbis.isLastPart();
}
/**
* @see Cursor#boundary(String)
*/
public void boundary(String boundary) throws IOException {
- mbis = new MimeBoundaryInputStream(contents, boundary);
- stream = new CloseShieldInputStream(mbis);
+ mbis = new MimeBoundaryInputStream(buffer, boundary);
+ contentStream = new CloseShieldInputStream(mbis);
}
/**
* @see Cursor#nextMimePartCursor()
*/
public Cursor nextMimePartCursor() {
- return new StreamCursor(rootInputStream, mbis);
+ return new StreamCursor(this, mbis);
}
/**
* @see Cursor#nextSection()
*/
public InputStream nextSection() {
- InputStream result = stream;
- if (result == null)
- {
- result = contents;
- }
- return result;
+ return contentStream;
}
/**
@@ -151,8 +153,11 @@
}
public InputStream rest() {
- return contents;
+ return contentStream;
+ }
+
+ public InputStream root() {
+ return rootInputStream;
}
-
}
Added: 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=673195&view=auto
==============================================================================
--- james/mime4j/trunk/src/test/java/org/apache/james/mime4j/InputBufferTest.java (added)
+++ james/mime4j/trunk/src/test/java/org/apache/james/mime4j/InputBufferTest.java Tue Jul 1 13:31:51 2008
@@ -0,0 +1,61 @@
+/****************************************************************
+ * 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 junit.framework.TestCase;
+
+public class InputBufferTest extends TestCase {
+
+ public void testPatternMatching1() throws Exception {
+ String text = "blabla d is the word";
+ String pattern = "d";
+ byte[] b1 = text.getBytes("US-ASCII");
+ byte[] b2 = pattern.getBytes("US-ASCII");
+ InputBuffer inbuffer = new InputBuffer(new ByteArrayInputStream(b1), 4096);
+ inbuffer.fillBuffer();
+ int i = inbuffer.indexOf(b2);
+ assertEquals(7, i);
+ }
+
+ public void testPatternMatching2() throws Exception {
+ String text = "disddisdissdsidsidsiid";
+ String pattern = "siid";
+ byte[] b1 = text.getBytes("US-ASCII");
+ byte[] b2 = pattern.getBytes("US-ASCII");
+ InputBuffer inbuffer = new InputBuffer(new ByteArrayInputStream(b1), 4096);
+ inbuffer.fillBuffer();
+ int i = inbuffer.indexOf(b2);
+ assertEquals(18, i);
+ }
+
+ public void testPatternMatching3() throws Exception {
+ String text = "bla bla yada yada haha haha";
+ String pattern = "blah";
+ byte[] b1 = text.getBytes("US-ASCII");
+ byte[] b2 = pattern.getBytes("US-ASCII");
+ InputBuffer inbuffer = new InputBuffer(new ByteArrayInputStream(b1), 4096);
+ inbuffer.fillBuffer();
+ int i = inbuffer.indexOf(b2);
+ assertEquals(-1, i);
+ }
+
+}
Propchange: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/InputBufferTest.java
------------------------------------------------------------------------------
svn:mergeinfo =
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=673195&r1=673194&r2=673195&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 1 13:31:51 2008
@@ -35,42 +35,112 @@
* @version $Id: MimeBoundaryInputStreamTest.java,v 1.2 2004/10/02 12:41:11 ntherning Exp $
*/
public class MimeBoundaryInputStreamTest extends TestCase {
- /**
- * Tests that a CRLF immediately preceding a boundary isn't included in
- * the stream.
- */
- public void testCRLFPrecedingBoundary() throws IOException {
+
+ public void testBasicReading() throws IOException {
String text = "Line 1\r\nLine 2\r\n--boundary\r\n" +
- "Line 3\r\nLine 4\r\n\r\n--boundary\r\n";
-
- ByteArrayInputStream bis = new ByteArrayInputStream(text.getBytes());
-
- assertEquals("Line 1\r\nLine 2",
- read(new MimeBoundaryInputStream(bis, "boundary")));
-
- assertEquals("Line 3\r\nLine 4\r\n",
- read(new MimeBoundaryInputStream(bis, "boundary")));
- }
-
- public void testBigEnoughPushbackBuffer() throws IOException {
- String text = "Line 1\r\nLine 2\r\n--boundar\r\n" +
- "Line 3\r\nLine 4\r\n\r\n--boundary\r\n";
-
- ByteArrayInputStream bis = new ByteArrayInputStream(text.getBytes());
-
- assertEquals("Line 1\r\nLine 2\r\n--boundar\r\n" +
- "Line 3\r\nLine 4\r\n",
- read(new MimeBoundaryInputStream(bis, "boundary")));
+ "Line 3\r\nLine 4\r\n--boundary--";
+
+ 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--";
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(text.getBytes("US-ASCII"));
+
+ InputBuffer buffer = new InputBuffer(bis, 20);
+
+ MimeBoundaryInputStream mime1 = new MimeBoundaryInputStream(buffer, "boundary");
+ assertEquals("yadayadayadayadayadayadayadayadayadayadayadayadayadayadayadayada",
+ read(mime1, 10));
+
+ assertFalse(mime1.isLastPart());
+
+ MimeBoundaryInputStream mime2 = new MimeBoundaryInputStream(buffer, "boundary");
+ assertEquals("blahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblah",
+ read(mime2, 10));
+
+ assertTrue(mime2.isLastPart());
+ }
+
+ public void testBasicReadingSmallBuffer2() throws IOException {
+ String text = "yadayadayadayadayadayadayadayadayadayadayadayadayadayadayadayada\r\n--boundary\r\n" +
+ "blahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblah\r\n--boundary--";
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(text.getBytes("US-ASCII"));
+
+ InputBuffer buffer = new InputBuffer(bis, 20);
+
+ MimeBoundaryInputStream mime1 = new MimeBoundaryInputStream(buffer, "boundary");
+
+ assertEquals("yadayadayadayadayadayadayadayadayadayadayadayadayadayadayadayada",
+ read(mime1, 25));
+
+ assertFalse(mime1.isLastPart());
+
+ MimeBoundaryInputStream mime2 = new MimeBoundaryInputStream(buffer, "boundary");
+ assertEquals("blahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblah",
+ read(mime2, 25));
+
+ assertTrue(mime2.isLastPart());
+ }
+
+ public void testBasicReadingByOneByte() throws IOException {
+ String text = "Line 1\r\nLine 2\r\n--boundary\r\n" +
+ "Line 3\r\nLine 4\r\n--boundary--";
+
+ 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", readByOneByte(mime1));
+
+ assertFalse(mime1.isLastPart());
+
+ MimeBoundaryInputStream mime2 = new MimeBoundaryInputStream(buffer, "boundary");
+ assertEquals("Line 3\r\nLine 4", readByOneByte(mime2));
+
+ assertTrue(mime2.isLastPart());
}
/**
- * Tests that CR characters are ignored.
+ * Tests that a CRLF immediately preceding a boundary isn't included in
+ * the stream.
*/
- public void testCRIgnored() throws IOException {
-
+ public void testCRLFPrecedingBoundary() throws IOException {
+ String text = "Line 1\r\nLine 2\r\n--boundary\r\n" +
+ "Line 3\r\nLine 4\r\n\r\n--boundary\r\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\r\n", read(mime2, 5));
+
+ assertFalse(mime2.isLastPart());
}
- private String read(InputStream is) throws IOException {
+ private String readByOneByte(InputStream is) throws IOException {
StringBuffer sb = new StringBuffer();
int b = 0;
while ((b = is.read()) != -1) {
@@ -78,6 +148,18 @@
}
return sb.toString();
}
+
+ private String read(InputStream is, int bufsize) throws IOException {
+ StringBuffer sb = new StringBuffer();
+ int l;
+ byte[] tmp = new byte[bufsize];
+ while ((l = is.read(tmp)) != -1) {
+ for (int i = 0; i < l; i++) {
+ sb.append((char) tmp[i]);
+ }
+ }
+ return sb.toString();
+ }
/**
* Tests that a stream containing only a boundary is empty.
@@ -86,15 +168,18 @@
String text = "--boundary\r\n";
ByteArrayInputStream bis = new ByteArrayInputStream(text.getBytes());
+ InputBuffer buffer = new InputBuffer(bis, 4096);
+
MimeBoundaryInputStream stream =
- new MimeBoundaryInputStream(bis, "boundary");
+ new MimeBoundaryInputStream(buffer, "boundary");
assertEquals(-1, stream.read());
text = "\r\n--boundary\r\n";
bis = new ByteArrayInputStream(text.getBytes());
+ buffer = new InputBuffer(bis, 4096);
stream =
- new MimeBoundaryInputStream(bis, "boundary");
+ new MimeBoundaryInputStream(buffer, "boundary");
assertEquals(-1, stream.read());
}
@@ -105,11 +190,11 @@
String text = "--boundary--\r\n";
ByteArrayInputStream bis = new ByteArrayInputStream(text.getBytes());
+ InputBuffer buffer = new InputBuffer(bis, 4096);
MimeBoundaryInputStream stream =
- new MimeBoundaryInputStream(bis, "boundary");
- assertFalse(stream.hasMoreParts());
+ new MimeBoundaryInputStream(buffer, "boundary");
assertEquals(-1, stream.read());
-
+ assertTrue(stream.isLastPart());
}
/**
@@ -119,15 +204,16 @@
String text = "Line 1\r\n\r\n--boundaryyada\r\n";
ByteArrayInputStream bis = new ByteArrayInputStream(text.getBytes());
+ InputBuffer buffer = new InputBuffer(bis, 4096);
MimeBoundaryInputStream stream =
- new MimeBoundaryInputStream(bis, "boundary");
- assertEquals("Line 1\r\n", read(stream));
+ new MimeBoundaryInputStream(buffer, "boundary");
+ assertEquals("Line 1\r\n", read(stream, 100));
text = "--boundaryyada\r\n";
bis = new ByteArrayInputStream(text.getBytes());
- stream =
- new MimeBoundaryInputStream(bis, "boundary");
+ buffer = new InputBuffer(bis, 4096);
+ stream = new MimeBoundaryInputStream(buffer, "boundary");
assertEquals(-1, stream.read());
}
}
Modified: james/mime4j/trunk/src/test/java/org/apache/james/mime4j/MimeTokenNoRecurseTest.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/test/java/org/apache/james/mime4j/MimeTokenNoRecurseTest.java?rev=673195&r1=673194&r2=673195&view=diff
==============================================================================
--- james/mime4j/trunk/src/test/java/org/apache/james/mime4j/MimeTokenNoRecurseTest.java (original)
+++ james/mime4j/trunk/src/test/java/org/apache/james/mime4j/MimeTokenNoRecurseTest.java Tue Jul 1 13:31:51 2008
@@ -38,7 +38,7 @@
"--42\r\n" +
"Content-Type:text/plain; charset=US-ASCII\r\n\r\n" +
"Second part of this mail\r\n" +
- "--42\r\n";
+ "--42--\r\n";
private static final String MAIL_WITH_RFC822_PART = "MIME-Version: 1.0\r\n" +
"From: Timothy Tayler <ti...@example.org>\r\n" +
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org