Author: bago
Date: Sat Jun 21 07:39:55 2008
New Revision: 670206

URL: http://svn.apache.org/viewvc?rev=670206&view=rev
Log:
Rewritten CRLFOutputStream and ExtraDotOutputStream to support buffered 
operations.
The new algorythm also take care of streams ending with "\r" (previosly they 
was not replaced unless we called a specific method).

Modified:
    
james/server/trunk/core-library/src/main/java/org/apache/james/util/CRLFOutputStream.java
    
james/server/trunk/core-library/src/main/java/org/apache/james/util/ExtraDotOutputStream.java
    
james/server/trunk/phoenix-deployment/src/test/org/apache/james/util/CRLFOutputStreamTest.java
    
james/server/trunk/phoenix-deployment/src/test/org/apache/james/util/ExtraDotOutputStreamTest.java

Modified: 
james/server/trunk/core-library/src/main/java/org/apache/james/util/CRLFOutputStream.java
URL: 
http://svn.apache.org/viewvc/james/server/trunk/core-library/src/main/java/org/apache/james/util/CRLFOutputStream.java?rev=670206&r1=670205&r2=670206&view=diff
==============================================================================
--- 
james/server/trunk/core-library/src/main/java/org/apache/james/util/CRLFOutputStream.java
 (original)
+++ 
james/server/trunk/core-library/src/main/java/org/apache/james/util/CRLFOutputStream.java
 Sat Jun 21 07:39:55 2008
@@ -17,8 +17,6 @@
  * under the License.                                           *
  ****************************************************************/
 
-
-
 package org.apache.james.util;
 
 import java.io.FilterOutputStream;
@@ -28,64 +26,136 @@
 /**
  * A Filter for use with SMTP or other protocols in which lines must end with
  * CRLF. Converts every "isolated" occourency of \r or \n with \r\n
+ * 
+ * RFC 2821 #2.3.7 mandates that line termination is CRLF, and that CR and LF
+ * must not be transmitted except in that pairing. If we get a naked LF, 
convert
+ * to CRLF.
+ * 
  */
 public class CRLFOutputStream extends FilterOutputStream {
 
     /**
      * Counter for number of last (0A or 0D).
      */
-    protected int countLast0A0D;
+    protected int statusLast;
+
+    protected final static int LAST_WAS_OTHER = 0;
+
+    protected final static int LAST_WAS_CR = 1;
+
+    protected final static int LAST_WAS_LF = 2;
+    
+    protected boolean startOfLine = true;
 
     /**
      * Constructor that wraps an OutputStream.
-     *
-     * @param out the OutputStream to be wrapped
+     * 
+     * @param out
+     *                the OutputStream to be wrapped
      */
     public CRLFOutputStream(OutputStream out) {
         super(out);
-        countLast0A0D = 2; // we already assume a CRLF at beginning (otherwise 
TOP would not work correctly !)
+        statusLast = LAST_WAS_LF; // we already assume a CRLF at beginning
+                                    // (otherwise TOP would not work correctly
+                                    // !)
     }
 
     /**
-     * Writes a byte to the stream
-     * Fixes any naked CR or LF to the RFC 2821 mandated CFLF
-     * pairing.
-     *
-     * @param b the byte to write
-     *
-     * @throws IOException if an error occurs writing the byte
+     * Writes a byte to the stream Fixes any naked CR or LF to the RFC 2821
+     * mandated CFLF pairing.
+     * 
+     * @param b
+     *                the byte to write
+     * 
+     * @throws IOException
+     *                 if an error occurs writing the byte
      */
     public void write(int b) throws IOException {
         switch (b) {
             case '\r':
-                if (countLast0A0D == 1) out.write('\n'); // two CR in a row, 
so insert an LF first
-                countLast0A0D = 1;
+                out.write('\r');
+                out.write('\n');
+                startOfLine = true;
+                statusLast = LAST_WAS_CR;
                 break;
             case '\n':
-                /* RFC 2821 #2.3.7 mandates that line termination is
-                 * CRLF, and that CR and LF must not be transmitted
-                 * except in that pairing.  If we get a naked LF,
-                 * convert to CRLF.
-                 */
-                if (countLast0A0D != 1) out.write('\r');
-                countLast0A0D = 2;
+                if (statusLast != LAST_WAS_CR) {
+                    out.write('\r');
+                    out.write('\n');
+                    startOfLine = true;
+                }
+                statusLast = LAST_WAS_LF;
                 break;
             default:
-                // we're  no longer at the start of a line
-                countLast0A0D = 0;
+                // we're no longer at the start of a line
+                out.write(b);
+                startOfLine = false;
+                statusLast = LAST_WAS_OTHER;
                 break;
         }
-        out.write(b);
     }
     
     /**
+     * Provides an extension point for ExtraDotOutputStream to be able to add 
dots
+     * at the beginning of new lines.
+     * 
+     * @see java.io.FilterOutputStream#write(byte[], int, int)
+     */
+    protected void writeChunk(byte buffer[], int offset, int length) throws 
IOException {
+        out.write(buffer, offset, length);
+    }
+
+    /**
+     * @see java.io.FilterOutputStream#write(byte[], int, int)
+     */
+    public synchronized void write(byte buffer[], int offset, int length)
+            throws IOException {
+        /* optimized */
+        int lineStart = offset;
+        for (int i = offset; i < length + offset; i++) {
+            switch(buffer[i]) {
+            case '\r':
+                // CR case. Write down the last line
+                // and position the new lineStart at the next char
+                writeChunk(buffer, lineStart, i - lineStart);
+                out.write('\r');
+                out.write('\n');
+                startOfLine = true;
+                lineStart = i + 1;
+                statusLast = LAST_WAS_CR;
+                break;
+            case '\n':
+                if (statusLast != LAST_WAS_CR) {
+                    writeChunk(buffer, lineStart, i - lineStart);
+                    out.write('\r');
+                    out.write('\n');
+                    startOfLine = true;
+                }
+                lineStart = i + 1;
+                statusLast = LAST_WAS_LF;
+                break;
+            default:
+                statusLast = LAST_WAS_OTHER;
+            }
+        }
+        if (length + offset > lineStart) {
+            writeChunk(buffer, lineStart, length + offset - lineStart);
+            startOfLine = false;
+        }
+    }
+
+
+    /**
      * Ensure that the stream is CRLF terminated.
      * 
-     * @throws IOException  if an error occurs writing the byte
+     * @throws IOException
+     *                 if an error occurs writing the byte
      */
     public void checkCRLFTerminator() throws IOException {
-        if (countLast0A0D != 2) {
-            write('\n');
+        if (statusLast == LAST_WAS_OTHER) {
+            out.write('\r');
+            out.write('\n');
+            statusLast = LAST_WAS_CR;
         }
     }
 }

Modified: 
james/server/trunk/core-library/src/main/java/org/apache/james/util/ExtraDotOutputStream.java
URL: 
http://svn.apache.org/viewvc/james/server/trunk/core-library/src/main/java/org/apache/james/util/ExtraDotOutputStream.java?rev=670206&r1=670205&r2=670206&view=diff
==============================================================================
--- 
james/server/trunk/core-library/src/main/java/org/apache/james/util/ExtraDotOutputStream.java
 (original)
+++ 
james/server/trunk/core-library/src/main/java/org/apache/james/util/ExtraDotOutputStream.java
 Sat Jun 21 07:39:55 2008
@@ -40,6 +40,20 @@
     }
 
     /**
+     * Overrides super writeChunk in order to add a "." if the previous chunk 
ended with
+     * a new line and a new chunk starts with "."
+     * 
+     * @see org.apache.james.util.CRLFOutputStream#writeChunk(byte[], int, int)
+     */
+    protected void writeChunk(byte buffer[], int offset, int length) throws 
IOException {
+        if (length > 0 && buffer[offset] == '.' && startOfLine) {
+            // add extra dot (the first of the pair)
+            out.write('.');
+        }
+        super.writeChunk(buffer, offset, length);
+    }
+
+    /**
      * Writes a byte to the stream, adding dots where appropriate.
      * Also fixes any naked CR or LF to the RFC 2821 mandated CRLF
      * pairing.
@@ -49,7 +63,7 @@
      * @throws IOException if an error occurs writing the byte
      */
     public void write(int b) throws IOException {
-        if (b == '.' && countLast0A0D == 2) {
+        if (b == '.' && statusLast != LAST_WAS_OTHER) {
             // add extra dot (the first of the pair)
             out.write('.');
         }

Modified: 
james/server/trunk/phoenix-deployment/src/test/org/apache/james/util/CRLFOutputStreamTest.java
URL: 
http://svn.apache.org/viewvc/james/server/trunk/phoenix-deployment/src/test/org/apache/james/util/CRLFOutputStreamTest.java?rev=670206&r1=670205&r2=670206&view=diff
==============================================================================
--- 
james/server/trunk/phoenix-deployment/src/test/org/apache/james/util/CRLFOutputStreamTest.java
 (original)
+++ 
james/server/trunk/phoenix-deployment/src/test/org/apache/james/util/CRLFOutputStreamTest.java
 Sat Jun 21 07:39:55 2008
@@ -58,8 +58,6 @@
         assertEquals("Test\r\n",bOut.toString());
         // if the stream ends with \r we should simply add the \n
         os.write("A line with incomplete ending\r".getBytes());
-        os.flush();
-        assertEquals("Test\r\nA line with incomplete 
ending\r",bOut.toString());
         os.checkCRLFTerminator();
         os.flush();
         assertEquals("Test\r\nA line with incomplete 
ending\r\n",bOut.toString());
@@ -68,5 +66,31 @@
         os.flush();
         assertEquals("Test\r\nA line with incomplete 
ending\r\n",bOut.toString());
     }
+    
+    public void testMixedSizeChunks() throws IOException {
+        String[] data = new String[] {".","This is a test","","\r\n","of the 
thing.\r","\nWe should not have much trouble.\r","\n",".","doubled?\r\n","or 
not?\n.","double","d\nor not?\r","\n\r\n","\n\n\r","\r\r\n"};
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        OutputStream os = new CRLFOutputStream(bOut);
+        for(int i = 0; i < data.length; i++) {
+            os.write(data[i].getBytes());
+            os.flush();
+        }
+        String expected = ".This is a test\r\nof the thing.\r\nWe should not 
have much trouble.\r\n.doubled?\r\nor not?\r\n.doubled\r\nor 
not?\r\n\r\n\r\n\r\n\r\n\r\n\r\n";
+        assertEquals(expected,bOut.toString());
+    }
+
+
+    public void testBytePerByte() throws IOException {
+        String data = ".This is a test\r\nof the thing.\r\nWe should not have 
much trouble.\r\n.doubled?\r\nor not?\n.doubled\nor not?\r\n\r\n\n\n\r\r\r\n";
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        OutputStream os = new CRLFOutputStream(bOut);
+        byte[] buffer = data.getBytes();
+        for (int i = 0; i < buffer.length; i++) {
+            os.write(buffer[i]);
+        }
+        os.flush();
+        String expected = ".This is a test\r\nof the thing.\r\nWe should not 
have much trouble.\r\n.doubled?\r\nor not?\r\n.doubled\r\nor 
not?\r\n\r\n\r\n\r\n\r\n\r\n\r\n";
+        assertEquals(expected,bOut.toString());
+    }
 
 }

Modified: 
james/server/trunk/phoenix-deployment/src/test/org/apache/james/util/ExtraDotOutputStreamTest.java
URL: 
http://svn.apache.org/viewvc/james/server/trunk/phoenix-deployment/src/test/org/apache/james/util/ExtraDotOutputStreamTest.java?rev=670206&r1=670205&r2=670206&view=diff
==============================================================================
--- 
james/server/trunk/phoenix-deployment/src/test/org/apache/james/util/ExtraDotOutputStreamTest.java
 (original)
+++ 
james/server/trunk/phoenix-deployment/src/test/org/apache/james/util/ExtraDotOutputStreamTest.java
 Sat Jun 21 07:39:55 2008
@@ -59,7 +59,8 @@
         // if the stream ends with \r we should simply add the \n
         os.write("A line with incomplete ending\r".getBytes());
         os.flush();
-        assertEquals("Test\r\nA line with incomplete 
ending\r",bOut.toString());
+        // no need to check this: this is an implementation detail
+        // assertEquals("Test\r\nA line with incomplete 
ending\r",bOut.toString());
         os.checkCRLFTerminator();
         os.flush();
         assertEquals("Test\r\nA line with incomplete 
ending\r\n",bOut.toString());
@@ -69,4 +70,31 @@
         assertEquals("Test\r\nA line with incomplete 
ending\r\n",bOut.toString());
     }
 
+    
+    public void testMixedSizeChunks() throws IOException {
+        String[] data = new String[] {".","This is a test","","\r\n","of the 
thing.\r","\nWe should not have much trouble.\r","\n",".","doubled?\r\n","or 
not?\n.","double","d\nor not?\r","\n\r\n","\n\n\r","\r\r\n"};
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        OutputStream os = new ExtraDotOutputStream(bOut);
+        for(int i = 0; i < data.length; i++) {
+            os.write(data[i].getBytes());
+            os.flush();
+        }
+        String expected = "..This is a test\r\nof the thing.\r\nWe should not 
have much trouble.\r\n..doubled?\r\nor not?\r\n..doubled\r\nor 
not?\r\n\r\n\r\n\r\n\r\n\r\n\r\n";
+        assertEquals(expected,bOut.toString());
+    }
+
+
+    public void testBytePerByte() throws IOException {
+        String data = ".This is a test\r\nof the thing.\r\nWe should not have 
much trouble.\r\n.doubled?\r\nor not?\n.doubled\nor not?\r\n\r\n\n\n\r\r\r\n";
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        OutputStream os = new ExtraDotOutputStream(bOut);
+        byte[] buffer = data.getBytes();
+        for (int i = 0; i < buffer.length; i++) {
+            os.write(buffer[i]);
+        }
+        os.flush();
+        String expected = "..This is a test\r\nof the thing.\r\nWe should not 
have much trouble.\r\n..doubled?\r\nor not?\r\n..doubled\r\nor 
not?\r\n\r\n\r\n\r\n\r\n\r\n\r\n";
+        assertEquals(expected,bOut.toString());
+    }
+
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to