001: /*
002: * Copyright 2001-2005 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.net.io;
017:
018: import java.io.IOException;
019: import java.io.Writer;
020:
021: /***
022: * DotTerminatedMessageWriter is a class used to write messages to a
023: * server that are terminated by a single dot followed by a
024: * <CR><LF>
025: * sequence and with double dots appearing at the begining of lines which
026: * do not signal end of message yet start with a dot. Various Internet
027: * protocols such as NNTP and POP3 produce messages of this type.
028: * <p>
029: * This class handles the doubling of line-starting periods,
030: * converts single linefeeds to NETASCII newlines, and on closing
031: * will send the final message terminator dot and NETASCII newline
032: * sequence.
033: * <p>
034: * <p>
035: * @author Daniel F. Savarese
036: ***/
037:
038: public final class DotTerminatedMessageWriter extends Writer {
039: private static final int __NOTHING_SPECIAL_STATE = 0;
040: private static final int __LAST_WAS_CR_STATE = 1;
041: private static final int __LAST_WAS_NL_STATE = 2;
042:
043: private int __state;
044: private Writer __output;
045:
046: /***
047: * Creates a DotTerminatedMessageWriter that wraps an existing Writer
048: * output destination.
049: * <p>
050: * @param output The Writer output destination to write the message.
051: ***/
052: public DotTerminatedMessageWriter(Writer output) {
053: super (output);
054: __output = output;
055: __state = __NOTHING_SPECIAL_STATE;
056: }
057:
058: /***
059: * Writes a character to the output. Note that a call to this method
060: * may result in multiple writes to the underling Writer in order to
061: * convert naked linefeeds to NETASCII line separators and to double
062: * line-leading periods. This is transparent to the programmer and
063: * is only mentioned for completeness.
064: * <p>
065: * @param ch The character to write.
066: * @exception IOException If an error occurs while writing to the
067: * underlying output.
068: ***/
069: public void write(int ch) throws IOException {
070: synchronized (lock) {
071: switch (ch) {
072: case '\r':
073: __state = __LAST_WAS_CR_STATE;
074: __output.write('\r');
075: return;
076: case '\n':
077: if (__state != __LAST_WAS_CR_STATE)
078: __output.write('\r');
079: __output.write('\n');
080: __state = __LAST_WAS_NL_STATE;
081: return;
082: case '.':
083: // Double the dot at the beginning of a line
084: if (__state == __LAST_WAS_NL_STATE)
085: __output.write('.');
086: // Fall through
087: default:
088: __state = __NOTHING_SPECIAL_STATE;
089: __output.write(ch);
090: return;
091: }
092: }
093: }
094:
095: /***
096: * Writes a number of characters from a character array to the output
097: * starting from a given offset.
098: * <p>
099: * @param buffer The character array to write.
100: * @param offset The offset into the array at which to start copying data.
101: * @param length The number of characters to write.
102: * @exception IOException If an error occurs while writing to the underlying
103: * output.
104: ***/
105: public void write(char[] buffer, int offset, int length)
106: throws IOException {
107: synchronized (lock) {
108: while (length-- > 0)
109: write(buffer[offset++]);
110: }
111: }
112:
113: /***
114: * Writes a character array to the output.
115: * <p>
116: * @param buffer The character array to write.
117: * @exception IOException If an error occurs while writing to the underlying
118: * output.
119: ***/
120: public void write(char[] buffer) throws IOException {
121: write(buffer, 0, buffer.length);
122: }
123:
124: /***
125: * Writes a String to the output.
126: * <p>
127: * @param string The String to write.
128: * @exception IOException If an error occurs while writing to the underlying
129: * output.
130: ***/
131: public void write(String string) throws IOException {
132: write(string.toCharArray());
133: }
134:
135: /***
136: * Writes part of a String to the output starting from a given offset.
137: * <p>
138: * @param string The String to write.
139: * @param offset The offset into the String at which to start copying data.
140: * @param length The number of characters to write.
141: * @exception IOException If an error occurs while writing to the underlying
142: * output.
143: ***/
144: public void write(String string, int offset, int length)
145: throws IOException {
146: write(string.toCharArray(), offset, length);
147: }
148:
149: /***
150: * Flushes the underlying output, writing all buffered output.
151: * <p>
152: * @exception IOException If an error occurs while writing to the underlying
153: * output.
154: ***/
155: public void flush() throws IOException {
156: synchronized (lock) {
157: __output.flush();
158: }
159: }
160:
161: /***
162: * Flushes the underlying output, writing all buffered output, but doesn't
163: * actually close the underlying stream. The underlying stream may still
164: * be used for communicating with the server and therefore is not closed.
165: * <p>
166: * @exception IOException If an error occurs while writing to the underlying
167: * output or closing the Writer.
168: ***/
169: public void close() throws IOException {
170: synchronized (lock) {
171: if (__output == null)
172: return;
173:
174: if (__state == __LAST_WAS_CR_STATE)
175: __output.write('\n');
176: else if (__state != __LAST_WAS_NL_STATE)
177: __output.write("\r\n");
178:
179: __output.write(".\r\n");
180:
181: __output.flush();
182: __output = null;
183: }
184: }
185:
186: }
|