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:[email protected]">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());
}
}