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 2007/07/24 23:01:36 UTC

svn commit: r559208 - in /james/mime4j/trunk/src/main/java/org/apache/james/mime4j: Cursor.java MimeTokenStream.java StreamCursor.java

Author: rdonkin
Date: Tue Jul 24 14:01:35 2007
New Revision: 559208

URL: http://svn.apache.org/viewvc?view=rev&rev=559208
Log:
Factored out cursor. This will allow an easy byte buffer implementation.

Added:
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/Cursor.java
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/StreamCursor.java
Modified:
    james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeTokenStream.java

Added: 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?view=auto&rev=559208
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/Cursor.java (added)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/Cursor.java Tue Jul 24 14:01:35 2007
@@ -0,0 +1,110 @@
+/****************************************************************
+ * 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;
+
+/**
+ * Manages parser input.
+ */
+public interface Cursor {
+    
+    /**
+     * Stops processing.
+     */
+    public void stop();
+
+    /**
+     * Gets the number of CR LF sequences passed.
+     * @return number of lines passed
+     * @throws IOException
+     */
+    public int getLineNumber() throws IOException;
+
+    /**
+     * Decodes the next MIME part as BASE 64 
+     * and returns a cursor for the contents.
+     * @return <code>Cursor</code>, not null
+     * @throws IOException TODO: subclass IOException with state exception
+     */
+    public Cursor decodeBase64() throws IOException;
+
+    /**
+     * Decodes the next MIME part as Quoted Printable
+     * and returns a cursor for the contents.
+     * @return <code>Cursor</code>, not null
+     * @throws IOException TODO: subclass IOException with state exception
+     */
+    public Cursor decodeQuotedPrintable() throws IOException;
+
+    /**
+     * Advances the cursor to the next boundary.
+     * @throws IOException TODO: subclass IOException with state exception
+     */
+    public void advanceToBoundary() throws IOException;
+
+    /**
+     * Is this cursor at the end of the input?
+     * @return true if the input is at the end,
+     * false otherwise
+     * @throws IOException
+     */
+    public boolean isEnded() throws IOException;
+
+    /**
+     * Are more parts available?
+     * @return true if this cursor is reading a MIME message and 
+     * there are more parts available,
+     * false otherwise
+     * @throws IOException TODO: subclass IOException with state exception
+     */
+    public boolean moreMimeParts() throws IOException;
+
+    /**
+     * Sets the MIME boundary.
+     * 
+     * @param boundary TODO: should this be a byte sequence?
+     * @throws IOException
+     */
+    public void boundary(String boundary) throws IOException;
+
+    /**
+     * Gets a cursor for the next mime part.
+     * @return <code>Cursor</code>, not null
+     * @throws IOException TODO: subclass IOException with state exception
+     */
+    public Cursor nextMimePartCursor() throws IOException;
+
+    /**
+     * Advances the cursor.
+     * @return the next byte
+     * @throws IOException
+     * @see #getStream()
+     */
+    public byte advance() throws IOException;
+    
+    /**
+     * Gets a stream to read the contents of the next section of the message.
+     * @return <code>InputStream</code>, not null
+     */
+    public InputStream getStream();
+
+}

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?view=diff&rev=559208&r1=559207&r2=559208
==============================================================================
--- 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 24 14:01:35 2007
@@ -27,8 +27,6 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.apache.james.mime4j.decoder.Base64InputStream;
-import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;
 
 
 /**
@@ -189,19 +187,18 @@
 
     private abstract class Entity extends StateMachine {
         private final BodyDescriptor parent;
-        private final InputStream contents;
+        private final Cursor cursor;
         private final StringBuffer sb = new StringBuffer();
         private BodyDescriptor body;
         private int pos, start;
         private int lineNumber, startLineNumber;
         private final int endState;
-        private MimeBoundaryInputStream mbis;
-        InputStream stream;
+        
         String field;
 
-        Entity(InputStream contents, BodyDescriptor parent, int startState, int endState) {
+        Entity(Cursor cursor, BodyDescriptor parent, int startState, int endState) {
             this.parent = parent;
-            this.contents = contents;
+            this.cursor = cursor;
             state = startState;
             this.endState = endState;
         }
@@ -211,21 +208,23 @@
         }
 
         private int setParseBodyPartState() throws IOException {
-            mbis.consume();
-            if (mbis.parentEOF()) {
+            cursor.advanceToBoundary();
+            if (cursor.isEnded()) {
                 if (log.isWarnEnabled()) {
-                    log.warn("Line " + rootInputStream.getLineNumber() 
+                    log.warn("Line " + cursor.getLineNumber() 
                             + ": Body part ended prematurely. "
                             + "Higher level boundary detected or "
                             + "EOF reached.");
                 }
             } else {
-                if (mbis.hasMoreParts()) {
-                    mbis = new MimeBoundaryInputStream(contents, body.getBoundary());
+                if (cursor.moreMimeParts()) {
+                    final String boundary = body.getBoundary();
+                    cursor.boundary(boundary);
+                   
                     if (isRaw()) {
-                        currentStateMachine = new RawEntity(mbis);
+                        currentStateMachine = new RawEntity(cursor.getStream());
                     } else {
-                        currentStateMachine = new BodyPart(mbis, body);
+                        currentStateMachine = new BodyPart(cursor.nextMimePartCursor(), body);
                     }
                     entities.add(currentStateMachine);
                     state = T_IN_BODYPART;
@@ -233,7 +232,6 @@
                 }
             }
             state = T_EPILOGUE;
-            stream = new CloseShieldInputStream(contents);
             return T_EPILOGUE;
         }
 
@@ -254,25 +252,23 @@
                     if (body.isMultipart()) {
                         state = T_START_MULTIPART;
                     } else if (body.isMessage()) {
-                        InputStream is = contents;
+                        Cursor nextCursor = cursor;
                         if (body.isBase64Encoded()) {
                             log.warn("base64 encoded message/rfc822 detected");
-                            is = new EOLConvertingInputStream(new Base64InputStream(contents));
+                            nextCursor = cursor.decodeBase64();
                         } else if (body.isQuotedPrintableEncoded()) {
                             log.warn("quoted-printable encoded message/rfc822 detected");
-                            is = new EOLConvertingInputStream(new QuotedPrintableInputStream(contents));
+                            nextCursor = cursor.decodeQuotedPrintable();
                         }
                         state = endState;
-                        return parseMessage(is, body);
+                        return parseMessage(nextCursor, body);
                     } else {
-                        stream = new CloseShieldInputStream(contents);
                         state = T_BODY;
                         break;
                     }
                     break;
                 case T_START_MULTIPART:
-                    mbis = new MimeBoundaryInputStream(contents, body.getBoundary());
-                    stream = new CloseShieldInputStream(mbis);
+                    cursor.boundary(body.getBoundary());
                     state = T_PREAMBLE;
                     break;
                 case T_PREAMBLE:
@@ -299,11 +295,11 @@
 
         private void initHeaderParsing() throws IOException {
             body = new BodyDescriptor(parent);
-            startLineNumber = lineNumber = rootInputStream.getLineNumber();
+            startLineNumber = lineNumber = cursor.getLineNumber();
 
             int curr = 0;
             int prev = 0;
-            while ((curr = contents.read()) != -1) {
+            while ((curr = cursor.advance()) != -1) {
                 if (curr == '\n' && (prev == '\n' || prev == 0)) {
                     /*
                      * [\r]\n[\r]\n or an immediate \r\n have been seen.
@@ -316,7 +312,7 @@
             }
             
             if (curr == -1 && log.isWarnEnabled()) {
-                log.warn("Line " + rootInputStream.getLineNumber()  
+                log.warn("Line " + cursor.getLineNumber()  
                         + ": Unexpected end of headers detected. "
                         + "Boundary detected in header or EOF reached.");
             }
@@ -375,19 +371,19 @@
     }
 
     private class Message extends Entity {
-        Message(InputStream contents, BodyDescriptor parent) {
-            super(contents, parent, T_START_MESSAGE, T_END_MESSAGE);
+        Message(Cursor cursor, BodyDescriptor parent) {
+            super(cursor, parent, T_START_MESSAGE, T_END_MESSAGE);
         }
     }
 
     private class BodyPart extends Entity {
-        BodyPart(InputStream contents, BodyDescriptor parent) {
-            super(contents, parent, T_START_BODYPART, T_END_BODYPART);
+        BodyPart(Cursor cursor, BodyDescriptor parent) {
+            super(cursor, parent, T_START_BODYPART, T_END_BODYPART);
         }
     }
     
     private int state = T_END_OF_STREAM;
-    private RootInputStream rootInputStream;
+    private Cursor cursor;
     private StateMachine currentStateMachine;
     private final List entities = new ArrayList();
     private boolean raw;
@@ -398,15 +394,15 @@
      */
     public void parse(InputStream stream) {
         entities.clear();
-        rootInputStream = new RootInputStream(stream);
-        state = parseMessage(rootInputStream, null);
+        cursor = new StreamCursor(stream);
+        state = parseMessage(cursor, null);
     }
 
-    private int parseMessage(InputStream pStream, BodyDescriptor parent) {
+    private int parseMessage(Cursor cursor, BodyDescriptor parent) {
         if (isRaw()) {
-            currentStateMachine = new RawEntity(pStream);
+            currentStateMachine = new RawEntity(cursor.getStream());
         } else {
-            currentStateMachine = new Message(pStream, parent);
+            currentStateMachine = new Message(cursor, parent);
         }
         entities.add(currentStateMachine);
         return currentStateMachine.state;
@@ -450,7 +446,7 @@
      * {@link ContentHandler#startMessage()}, etc.
      */
     public void stop() {
-        rootInputStream.truncate();
+        cursor.stop();
     }
 
     /**
@@ -490,7 +486,7 @@
             case T_PREAMBLE:
             case T_EPILOGUE:
             case T_BODY:
-                return ((Entity) currentStateMachine).stream;
+                return ((Entity) currentStateMachine).cursor.getStream();
             default:
                 throw new IllegalStateException("Expected state to be either of T_RAW_ENTITY, T_PREAMBLE, or T_EPILOGUE.");
         }

Added: 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?view=auto&rev=559208
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/StreamCursor.java (added)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/StreamCursor.java Tue Jul 24 14:01:35 2007
@@ -0,0 +1,152 @@
+/****************************************************************
+ * 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;
+
+import org.apache.james.mime4j.decoder.Base64InputStream;
+import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;
+
+/**
+ * Stream based cursor. 
+ */
+public class StreamCursor implements Cursor {
+
+    final RootInputStream rootInputStream;
+    final InputStream contents;
+    MimeBoundaryInputStream mbis;
+    InputStream stream;
+    
+    /**
+     * Constructs a child cursor.
+     * @param parent <code>Cursor</code>, not null 
+     * @param contents <code>InputStream</code> contents, not null
+     */
+    StreamCursor(StreamCursor parent, InputStream contents) {
+        this.rootInputStream = parent.rootInputStream;
+        this.contents = contents;
+    }
+    
+    /**
+     * Constructs a new cursor from the given root contents.
+     * @param stream <code>InputStream</code>, not null
+     */
+    StreamCursor(InputStream stream) {
+        rootInputStream = new RootInputStream(stream);
+        contents = 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;
+    }
+    
+    /**
+     * @see Cursor#stop()
+     */
+    public void stop() {
+        rootInputStream.truncate();
+    }
+    
+    /**
+     * @see Cursor#getLineNumber()
+     */
+    public int getLineNumber() {
+        return rootInputStream.getLineNumber();
+    }
+
+    /**
+     * @see Cursor#decodeBase64()
+     */
+    public Cursor decodeBase64() throws IOException {
+        InputStream is = new EOLConvertingInputStream(new Base64InputStream(contents));
+        return new StreamCursor(rootInputStream, is);
+    }
+    
+    /**
+     * @see Cursor#decodeQuotedPrintable()
+     */
+    public Cursor decodeQuotedPrintable() throws IOException {
+        InputStream is = new EOLConvertingInputStream(new QuotedPrintableInputStream(contents));
+        return new StreamCursor(rootInputStream, is);
+    }
+    
+    /**
+     * @see Cursor#advanceToBoundary()
+     */
+    public void advanceToBoundary() throws IOException {
+        mbis.consume();
+        stream = null;
+    }
+
+    /**
+     * @see Cursor#isEnded()
+     */
+    public boolean isEnded() throws IOException {
+        return mbis.parentEOF();
+    }
+
+    /**
+     * @see Cursor#moreMimeParts()
+     */
+    public boolean moreMimeParts() throws IOException {
+        return mbis.hasMoreParts();
+    }
+
+    /**
+     * @see Cursor#boundary(String)
+     */
+    public void boundary(String boundary) throws IOException {
+        mbis = new MimeBoundaryInputStream(contents, boundary);
+        stream = new CloseShieldInputStream(mbis);
+    }
+
+    /**
+     * @see Cursor#nextMimePartCursor()
+     */
+    public Cursor nextMimePartCursor() {
+        return new StreamCursor(rootInputStream, mbis);
+    }
+
+    /**
+     * @see Cursor#getStream()
+     */
+    public InputStream getStream() {
+        InputStream result = stream;
+        if (result == null)
+        {
+            result = contents;
+        }
+        return result;
+    }
+
+    /**
+     * @see Cursor#advance()
+     */
+    public byte advance() throws IOException {
+        return (byte) getStream().read();
+    }
+}



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