Author: sebb
Date: Wed Mar 16 23:12:34 2011
New Revision: 1082341

URL: http://svn.apache.org/viewvc?rev=1082341&view=rev
Log:
NET-375 DotTerminatedMessageReader should extend BufferedReader, rather than 
Reader

Modified:
    commons/proper/net/trunk/src/changes/changes.xml
    
commons/proper/net/trunk/src/main/java/org/apache/commons/net/io/DotTerminatedMessageReader.java
    
commons/proper/net/trunk/src/test/java/org/apache/commons/net/io/DotTerminatedMessageReaderTest.java

Modified: commons/proper/net/trunk/src/changes/changes.xml
URL: 
http://svn.apache.org/viewvc/commons/proper/net/trunk/src/changes/changes.xml?rev=1082341&r1=1082340&r2=1082341&view=diff
==============================================================================
--- commons/proper/net/trunk/src/changes/changes.xml (original)
+++ commons/proper/net/trunk/src/changes/changes.xml Wed Mar 16 23:12:34 2011
@@ -57,6 +57,9 @@ The <action> type attribute can be add,u
 
     <body>
         <release version="3.0" date="TBA" description="TBA">
+            <action issue="NET-375" dev="sebb" type="fix">
+            DotTerminatedMessageReader should extend BufferedReader, rather 
than Reader.
+            </action>
             <action issue="NET-374" dev="sebb" type="update">
             ParserInitializationException doesn't use standard JDK exception 
chaining
             </action>

Modified: 
commons/proper/net/trunk/src/main/java/org/apache/commons/net/io/DotTerminatedMessageReader.java
URL: 
http://svn.apache.org/viewvc/commons/proper/net/trunk/src/main/java/org/apache/commons/net/io/DotTerminatedMessageReader.java?rev=1082341&r1=1082340&r2=1082341&view=diff
==============================================================================
--- 
commons/proper/net/trunk/src/main/java/org/apache/commons/net/io/DotTerminatedMessageReader.java
 (original)
+++ 
commons/proper/net/trunk/src/main/java/org/apache/commons/net/io/DotTerminatedMessageReader.java
 Wed Mar 16 23:12:34 2011
@@ -17,8 +17,8 @@
 
 package org.apache.commons.net.io;
 
+import java.io.BufferedReader;
 import java.io.IOException;
-import java.io.PushbackReader;
 import java.io.Reader;
 
 /**
@@ -30,22 +30,23 @@ import java.io.Reader;
  * protocols such as NNTP and POP3 produce messages of this type.
  * <p>
  * This class handles stripping of the duplicate period at the beginning
- * of lines starting with a period, converts NETASCII newlines to the
- * local line separator format, truncates the end of message indicator,
- * and ensures you cannot read past the end of the message.
+ * of lines starting with a period, and ensures you cannot read past the end 
of the message.
+ * <p>
+ * Note: versions since 3.0 extend BufferedReader rather than Reader,
+ * and no longer change the CRLF into the local EOL. Also only DOT CR LF
+ * acts as EOF.
  * @author <a href="mailto:savar...@apache.org";>Daniel F. Savarese</a>
  * @version $Id$
  */
-public final class DotTerminatedMessageReader extends Reader
+public final class DotTerminatedMessageReader extends BufferedReader
 {
-    private static final String LS = System.getProperty("line.separator");
-    char[] LS_CHARS;
+    private static final char LF = '\n';
+    private static final char CR = '\r';
+    private static final int DOT = '.';
 
     private boolean atBeginning;
     private boolean eof;
-    private int pos;
-    private char[] internalBuffer;
-    private PushbackReader internalReader;
+    private boolean seenCR; // was last character CR?
 
     /**
      * Creates a DotTerminatedMessageReader that wraps an existing Reader
@@ -55,13 +56,9 @@ public final class DotTerminatedMessageR
     public DotTerminatedMessageReader(Reader reader)
     {
         super(reader);
-        LS_CHARS = LS.toCharArray();
-        internalBuffer = new char[LS_CHARS.length + 3];
-        pos = internalBuffer.length;
         // Assumes input is at start of message
         atBeginning = true;
         eof = false;
-        internalReader = new PushbackReader(reader);
     }
 
     /**
@@ -77,96 +74,66 @@ public final class DotTerminatedMessageR
      *            stream.
      */
     @Override
-    public int read() throws IOException
-    {
-        int ch;
-
-        synchronized (lock)
-        {
-            if (pos < internalBuffer.length)
-            {
-                return internalBuffer[pos++];
+    public int read() throws IOException {
+        synchronized (lock) {
+            if (eof) {
+                return -1; // Don't allow read past EOF
             }
-
-            if (eof)
-            {
-                return -1;
-            }
-
-            if ((ch = internalReader.read()) == -1)
-            {
+            int chint = super.read();
+            if (chint == -1) { // True EOF
                 eof = true;
                 return -1;
             }
-
-            if (atBeginning)
-            {
+            if (atBeginning) {
                 atBeginning = false;
-                if (ch == '.')
-                {
-                    ch = internalReader.read();
-
-                    if (ch != '.')
-                    {
-                        // read newline
+                if (chint == DOT) { // Have DOT
+                    mark(2); // need to check for CR LF or DOT
+                    chint = super.read();
+                    if (chint == -1) { // Should not happen
+                        // new Throwable("Trailing DOT").printStackTrace();
                         eof = true;
-                        internalReader.read();
-                        return -1;
-                    }
-                    else
-                    {
-                        return '.';
+                        return DOT; // return the trailing DOT
                     }
-                }
-            }
-
-            if (ch == '\r')
-            {
-                ch = internalReader.read();
-
-                if (ch == '\n')
-                {
-                    ch = internalReader.read();
-
-                    if (ch == '.')
-                    {
-                        ch = internalReader.read();
-
-                        if (ch != '.')
-                        {
-                            // read newline and indicate end of file
-                            internalReader.read();
-                            eof = true;
+                    if (chint == DOT) { // Have DOT DOT
+                        // no need to reset as we want to lose the first DOT
+                        return chint; // i.e. DOT
+                    } 
+                    if (chint == CR) { // Have DOT CR
+                        chint = super.read();
+                        if (chint == -1) { // Still only DOT CR - should not 
happen
+                            //new Throwable("Trailing DOT 
CR").printStackTrace();
+                            reset(); // So CR is picked up next time
+                            return DOT; // return the trailing DOT
                         }
-                        else
-                        {
-                            internalBuffer[--pos] = (char) ch;
+                        if (chint == LF) { // DOT CR LF
+                            atBeginning = true;
+                            eof = true;
+                            // Do we need to clear the mark somehow?
+                            return -1;
                         }
                     }
-                    else
-                    {
-                        internalReader.unread(ch);
-                    }
-
-                    pos -= LS_CHARS.length;
-                    System.arraycopy(LS_CHARS, 0, internalBuffer, pos,
-                                     LS_CHARS.length);
-                    ch = internalBuffer[pos++];
-                }
-                else if (ch == '\r') {
-                    internalReader.unread(ch);
-                }
-                else
-                {
-                    internalBuffer[--pos] = (char) ch;
-                    return '\r';
+                    // Should not happen - lone DOT at beginning
+                    //new Throwable("Lone DOT followed by 
"+(char)chint).printStackTrace();
+                    reset();
+                    return DOT;
+                } // have DOT
+            } // atBeginning
+
+            // Handle CRLF in normal flow
+            if (seenCR) {
+                seenCR = false;
+                if (chint == LF) {
+                    atBeginning = true;
                 }
             }
-
-            return ch;
+            if (chint == CR) {
+                seenCR = true;
+            }
+            return chint;
         }
     }
 
+
     /**
      * Reads the next characters from the message into an array and
      * returns the number of characters read.  Returns -1 if the end of the
@@ -224,21 +191,6 @@ public final class DotTerminatedMessageR
     }
 
     /**
-     * Determines if the message is ready to be read.
-     * @return True if the message is ready to be read, false if not.
-     * @exception IOException If an error occurs while checking the underlying
-     *            stream.
-     */
-    @Override
-    public boolean ready() throws IOException
-    {
-        synchronized (lock)
-        {
-            return (pos < internalBuffer.length || internalReader.ready());
-        }
-    }
-
-    /**
      * Closes the message for reading.  This doesn't actually close the
      * underlying stream.  The underlying stream may still be used for
      * communicating with the server and therefore is not closed.
@@ -257,11 +209,6 @@ public final class DotTerminatedMessageR
     {
         synchronized (lock)
         {
-            if (internalReader == null)
-            {
-                return;
-            }
-
             if (!eof)
             {
                 while (read() != -1)
@@ -271,8 +218,34 @@ public final class DotTerminatedMessageR
             }
             eof = true;
             atBeginning = false;
-            pos = internalBuffer.length;
-            internalReader = null;
         }
     }
+
+    /**
+     * Read a line of text. 
+     * A line is considered to be terminated by carriage return followed 
immediately by a linefeed.
+     * This contrasts with BufferedReader which also allows other combinations.
+     * @since 3.0
+     */
+    @Override
+    public String readLine() throws IOException {
+        StringBuilder sb = new StringBuilder();
+        int intch;
+        synchronized(lock) { // make thread-safe (hopefully!)
+            while((intch = read()) != -1) 
+            {
+                if (intch == LF && atBeginning) {
+                    return sb.substring(0, sb.length()-1);
+                }
+                sb.append((char) intch);
+            }
+        }
+        String string = sb.toString();
+        if (string.length() == 0) { // immediate EOF
+            return null;
+        }
+        // Should not happen - EOF without CRLF
+        //new Throwable(string).printStackTrace();
+        return string;
+    }
 }

Modified: 
commons/proper/net/trunk/src/test/java/org/apache/commons/net/io/DotTerminatedMessageReaderTest.java
URL: 
http://svn.apache.org/viewvc/commons/proper/net/trunk/src/test/java/org/apache/commons/net/io/DotTerminatedMessageReaderTest.java?rev=1082341&r1=1082340&r2=1082341&view=diff
==============================================================================
--- 
commons/proper/net/trunk/src/test/java/org/apache/commons/net/io/DotTerminatedMessageReaderTest.java
 (original)
+++ 
commons/proper/net/trunk/src/test/java/org/apache/commons/net/io/DotTerminatedMessageReaderTest.java
 Wed Mar 16 23:12:34 2011
@@ -27,36 +27,36 @@ public class DotTerminatedMessageReaderT
     private DotTerminatedMessageReader reader;
     private StringBuilder str = new StringBuilder();
     private char[] buf = new char[64];
-    final static String SEP = System.getProperty("line.separator");
+    private static final String CRLF = "\r\n";
+    private static final String DOT = ".";
+    private static final String EOM = CRLF+DOT+CRLF;
 
     public void testReadSimpleStringCrLfLineEnding() throws IOException {
-        final String test = "Hello World!\r\n.\r\n";
+        final String test = "Hello World!"+EOM;
         reader = new DotTerminatedMessageReader(new StringReader(test));
-        reader.LS_CHARS = new char[]{'\r','\n'};
 
         int read = 0;
         while ((read = reader.read(buf)) != -1) {
             str.append(buf, 0, read);
         }
 
-        assertEquals("Hello World!" + String.valueOf(reader.LS_CHARS), 
str.toString());
+        assertEquals("Hello World!"+CRLF, str.toString());
     }
 
     public void testReadSimpleStringLfLineEnding() throws IOException {
-        final String test = "Hello World!\r\n.\r\n";
+        final String test = "Hello World!"+EOM;
         reader = new DotTerminatedMessageReader(new StringReader(test));
-        reader.LS_CHARS = new char[]{'\n'};
 
         int read = 0;
         while ((read = reader.read(buf)) != -1) {
             str.append(buf, 0, read);
         }
 
-        assertEquals("Hello World!" + String.valueOf(reader.LS_CHARS), 
str.toString());
+        assertEquals("Hello World!"+CRLF, str.toString());
     }
 
     public void testEmbeddedNewlines() throws IOException {
-        final String test = "Hello\r\nWorld\nA\rB\r\n.\r\n";
+        final String test = "Hello"+CRLF+"World\nA\rB"+EOM;
         reader = new DotTerminatedMessageReader(new StringReader(test));
 
         int read = 0;
@@ -64,11 +64,11 @@ public class DotTerminatedMessageReaderT
             str.append(buf, 0, read);
         }
 
-        assertEquals(str.toString(), "Hello" + SEP  +"World\nA\rB" + SEP);
+        assertEquals(str.toString(), "Hello" + CRLF  +"World\nA\rB" + CRLF);
     }
 
     public void testDoubleCrBeforeDot() throws IOException {
-        final String test = "Hello World!\r\r\n.\r\n";
+        final String test = "Hello World!\r"+EOM;
         reader = new DotTerminatedMessageReader(new StringReader(test));
 
         int read = 0;
@@ -76,11 +76,11 @@ public class DotTerminatedMessageReaderT
             str.append(buf, 0, read);
         }
 
-        assertEquals("Hello World!\r" + SEP,str.toString());
+        assertEquals("Hello World!\r" + CRLF,str.toString());
     }
 
     public void testLeadingDot() throws IOException {
-        final String test = "Hello World!\r\n..text\r\n.\r\n";
+        final String test = "Hello World!"+CRLF+"..text"+EOM;
         reader = new DotTerminatedMessageReader(new StringReader(test));
 
         int read = 0;
@@ -88,11 +88,11 @@ public class DotTerminatedMessageReaderT
             str.append(buf, 0, read);
         }
 
-        assertEquals("Hello World!" + SEP+".text"+SEP,str.toString());
+        assertEquals("Hello World!" + CRLF+".text"+CRLF,str.toString());
     }
 
     public void testEmbeddedDot1() throws IOException {
-        final String test = "Hello . World!\r\n.\r\n";
+        final String test = "Hello . World!"+EOM;
         reader = new DotTerminatedMessageReader(new StringReader(test));
 
         int read = 0;
@@ -100,11 +100,11 @@ public class DotTerminatedMessageReaderT
             str.append(buf, 0, read);
         }
 
-        assertEquals("Hello . World!" + SEP,str.toString());
+        assertEquals("Hello . World!" + CRLF,str.toString());
     }
 
     public void testEmbeddedDot2() throws IOException {
-        final String test = "Hello .. World!\r\n.\r\n";
+        final String test = "Hello .. World!"+EOM;
         reader = new DotTerminatedMessageReader(new StringReader(test));
 
         int read = 0;
@@ -112,11 +112,11 @@ public class DotTerminatedMessageReaderT
             str.append(buf, 0, read);
         }
 
-        assertEquals("Hello .. World!" + SEP,str.toString());
+        assertEquals("Hello .. World!" + CRLF,str.toString());
     }
 
     public void testEmbeddedDot3() throws IOException {
-        final String test = "Hello World.\r\nmore\r\n.\r\n";
+        final String test = "Hello World."+CRLF+"more"+EOM;
         reader = new DotTerminatedMessageReader(new StringReader(test));
 
         int read = 0;
@@ -124,11 +124,11 @@ public class DotTerminatedMessageReaderT
             str.append(buf, 0, read);
         }
 
-        assertEquals("Hello World." + SEP+"more"+SEP,str.toString());
+        assertEquals("Hello World." + CRLF+"more"+CRLF,str.toString());
     }
 
     public void testEmbeddedDot4() throws IOException {
-        final String test = "Hello World\r.\nmore\r\n.\r\n";
+        final String test = "Hello World\r.\nmore"+EOM;
         reader = new DotTerminatedMessageReader(new StringReader(test));
 
         int read = 0;
@@ -136,13 +136,39 @@ public class DotTerminatedMessageReaderT
             str.append(buf, 0, read);
         }
 
-        assertEquals("Hello World\r.\nmore" + SEP,str.toString());
+        assertEquals("Hello World\r.\nmore" + CRLF,str.toString());
+    }
+
+    public void testReadLine1() throws Exception {
+        final String test = "Hello World"+CRLF+"more"+EOM;
+        reader = new DotTerminatedMessageReader(new StringReader(test));
+
+        String line;
+        while ((line = reader.readLine()) != null) {
+            str.append(line);
+            str.append("#");
+        }
+
+        assertEquals("Hello World#more#",str.toString());
+        
+    }
+
+    public void testReadLine2() throws Exception {
+        final String test = "Hello World\r.\nmore"+EOM;
+        reader = new DotTerminatedMessageReader(new StringReader(test));
+
+        String line;
+        while ((line = reader.readLine()) != null) {
+            str.append(line);
+            str.append("#");
+        }
+
+        assertEquals("Hello World\r.\nmore#",str.toString());
+        
     }
 
-    // This test agrees with the Javadoc.
-    // However the sequence should not happen for well-behaved NNTP and POP3 
servers
     public void testSingleDotWithTrailingText() throws IOException {
-        final String test = "Hello World!\r\n.text\r\n";
+        final String test = "Hello World!"+CRLF+".text"+EOM;
         reader = new DotTerminatedMessageReader(new StringReader(test));
 
         int read = 0;
@@ -150,10 +176,7 @@ public class DotTerminatedMessageReaderT
             str.append(buf, 0, read);
         }
 
-        assertEquals("Hello World!" + SEP,str.toString());
-        
-        // Note: the StringReader input will still contain "xt\r\n"
-        // because DTMR treats the "te" as CRLF
+        assertEquals("Hello World!"+CRLF+".text"+CRLF,str.toString());
     }
 
 }


Reply via email to