blob: c063bb98b93e810e6853fe01f7f9f12ae5d9df0f [file]
/***
* $Id: DotTerminatedMessageReader.java,v 1.1 2002/04/03 01:04:41 brekke Exp $
*
* NetComponents Internet Protocol Library
* Copyright (C) 1997-2002 Daniel F. Savarese
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library in the LICENSE file; if not, write
* to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
***/
package com.oroinc.io;
import java.io.*;
import java.util.*;
/***
* DotTerminatedMessageReader is a class used to read messages from a
* server that are terminated by a single dot followed by a
* <CR><LF>
* sequence and with double dots appearing at the begining of lines which
* do not signal end of message yet start with a dot. Various Internet
* 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.
* <p>
* <p>
* @author Daniel F. Savarese
***/
public final class DotTerminatedMessageReader extends Reader {
private static final String __lineSeparator;
private static final char[] __lineSeparatorChars;
static {
__lineSeparator = System.getProperty("line.separator");
__lineSeparatorChars = __lineSeparator.toCharArray();
}
private boolean __atBeginning, __eof;
private int __pos;
private char[] __buffer;
private PushbackReader __in;
/***
* Creates a DotTerminatedMessageReader that wraps an existing Reader
* input source.
* <p>
* @param reader The Reader input source containing the message.
***/
public DotTerminatedMessageReader(Reader reader) {
super(reader);
__buffer = new char[__lineSeparatorChars.length + 3];
__pos = __buffer.length;
// Assumes input is at start of message
__atBeginning = true;
__eof = false;
__in = new PushbackReader(reader);
}
/***
* Reads and returns the next character in the message. If the end of the
* message has been reached, returns -1. Note that a call to this method
* may result in multiple reads from the underlying input stream to decode
* the message properly (removing doubled dots and so on). All of
* this is transparent to the programmer and is only mentioned for
* completeness.
* <p>
* @return The next character in the message. Returns -1 if the end of the
* message has been reached.
* @exception IOException If an error occurs while reading the underlying
* stream.
***/
public int read() throws IOException {
int ch;
synchronized(lock) {
if(__pos < __buffer.length)
return __buffer[__pos++];
if(__eof)
return -1;
if((ch = __in.read()) == -1) {
__eof = true;
return -1;
}
if(__atBeginning) {
__atBeginning = false;
if(ch == '.') {
ch = __in.read();
if(ch != '.') {
// read newline
__eof = true;
__in.read();
return -1;
} else
return '.';
}
}
if(ch == '\r') {
ch = __in.read();
if(ch == '\n') {
ch = __in.read();
if(ch == '.') {
ch = __in.read();
if(ch != '.') {
// read newline and indicate end of file
__in.read();
__eof = true;
} else
__buffer[--__pos] = (char)ch;
} else
__in.unread(ch);
__pos-=__lineSeparatorChars.length;
System.arraycopy(__lineSeparatorChars, 0, __buffer, __pos,
__lineSeparatorChars.length);
ch = __buffer[__pos++];
} else {
__buffer[--__pos] = (char)ch;
return '\r';
}
}
return ch;
}
}
/***
* Reads the next characters from the message into an array and
* returns the number of characters read. Returns -1 if the end of the
* message has been reached.
* <p>
* @param buffer The character array in which to store the characters.
* @return The number of characters read. Returns -1 if the
* end of the message has been reached.
* @exception IOException If an error occurs in reading the underlying
* stream.
***/
public int read(char[] buffer) throws IOException {
return read(buffer, 0, buffer.length);
}
/***
* Reads the next characters from the message into an array and
* returns the number of characters read. Returns -1 if the end of the
* message has been reached. The characters are stored in the array
* starting from the given offset and up to the length specified.
* <p>
* @param bufffer The character array in which to store the characters.
* @param offset The offset into the array at which to start storing
* characters.
* @param length The number of characters to read.
* @return The number of characters read. Returns -1 if the
* end of the message has been reached.
* @exception IOException If an error occurs in reading the underlying
* stream.
***/
public int read(char[] buffer, int offset, int length) throws IOException {
int ch, off;
synchronized(lock) {
if(length < 1)
return 0;
if((ch = read()) == -1)
return -1;
off = offset;
do {
buffer[offset++] = (char)ch;
} while(--length > 0 && (ch = read()) != -1);
return (offset - off);
}
}
/***
* Determines if the message is ready to be read.
* <p>
* @return True if the message is ready to be read, false if not.
* @exception IOException If an error occurs while checking the underlying
* stream.
***/
public boolean ready() throws IOException {
synchronized(lock) {
return (__pos < __buffer.length || __in.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.
* <p>
* If the end of the message has not yet been reached, this method
* will read the remainder of the message until it reaches the end,
* so that the underlying stream may continue to be used properly
* for communicating with the server. If you do not fully read
* a message, you MUST close it, otherwise your program will likely
* hang or behave improperly.
* <p>
* @exception IOException If an error occurs while reading the
* underlying stream.
***/
public void close() throws IOException {
synchronized(lock) {
if(__in == null)
return;
if(!__eof)
while(read() != -1);
__eof = true;
__atBeginning = false;
__pos = __buffer.length;
__in = null;
}
}
}