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 {
040 private static final int __NOTHING_SPECIAL_STATE = 0;
041 private static final int __LAST_WAS_CR_STATE = 1;
042 private static final int __LAST_WAS_NL_STATE = 2;
043
044 private int __state;
045 private Writer __output;
046
047
048 /***
049 * Creates a DotTerminatedMessageWriter that wraps an existing Writer
050 * output destination.
051 * <p>
052 * @param output The Writer output destination to write the message.
053 ***/
054 public DotTerminatedMessageWriter(Writer output)
055 {
056 super(output);
057 __output = output;
058 __state = __NOTHING_SPECIAL_STATE;
059 }
060
061
062 /***
063 * Writes a character to the output. Note that a call to this method
064 * may result in multiple writes to the underling Writer in order to
065 * convert naked linefeeds to NETASCII line separators and to double
066 * line-leading periods. This is transparent to the programmer and
067 * is only mentioned for completeness.
068 * <p>
069 * @param ch The character to write.
070 * @exception IOException If an error occurs while writing to the
071 * underlying output.
072 ***/
073 public void write(int ch) throws IOException
074 {
075 synchronized (lock)
076 {
077 switch (ch)
078 {
079 case '\r':
080 __state = __LAST_WAS_CR_STATE;
081 __output.write('\r');
082 return ;
083 case '\n':
084 if (__state != __LAST_WAS_CR_STATE)
085 __output.write('\r');
086 __output.write('\n');
087 __state = __LAST_WAS_NL_STATE;
088 return ;
089 case '.':
090 // Double the dot at the beginning of a line
091 if (__state == __LAST_WAS_NL_STATE)
092 __output.write('.');
093 // Fall through
094 default:
095 __state = __NOTHING_SPECIAL_STATE;
096 __output.write(ch);
097 return ;
098 }
099 }
100 }
101
102
103 /***
104 * Writes a number of characters from a character array to the output
105 * starting from a given offset.
106 * <p>
107 * @param buffer The character array to write.
108 * @param offset The offset into the array at which to start copying data.
109 * @param length The number of characters to write.
110 * @exception IOException If an error occurs while writing to the underlying
111 * output.
112 ***/
113 public void write(char[] buffer, int offset, int length) throws IOException
114 {
115 synchronized (lock)
116 {
117 while (length-- > 0)
118 write(buffer[offset++]);
119 }
120 }
121
122
123 /***
124 * Writes a character array to the output.
125 * <p>
126 * @param buffer The character array to write.
127 * @exception IOException If an error occurs while writing to the underlying
128 * output.
129 ***/
130 public void write(char[] buffer) throws IOException
131 {
132 write(buffer, 0, buffer.length);
133 }
134
135
136 /***
137 * Writes a String to the output.
138 * <p>
139 * @param string The String to write.
140 * @exception IOException If an error occurs while writing to the underlying
141 * output.
142 ***/
143 public void write(String string) throws IOException
144 {
145 write(string.toCharArray());
146 }
147
148
149 /***
150 * Writes part of a String to the output starting from a given offset.
151 * <p>
152 * @param string The String to write.
153 * @param offset The offset into the String at which to start copying data.
154 * @param length The number of characters to write.
155 * @exception IOException If an error occurs while writing to the underlying
156 * output.
157 ***/
158 public void write(String string, int offset, int length) throws IOException
159 {
160 write(string.toCharArray(), offset, length);
161 }
162
163
164 /***
165 * Flushes the underlying output, writing all buffered output.
166 * <p>
167 * @exception IOException If an error occurs while writing to the underlying
168 * output.
169 ***/
170 public void flush() throws IOException
171 {
172 synchronized (lock)
173 {
174 __output.flush();
175 }
176 }
177
178
179 /***
180 * Flushes the underlying output, writing all buffered output, but doesn't
181 * actually close the underlying stream. The underlying stream may still
182 * be used for communicating with the server and therefore is not closed.
183 * <p>
184 * @exception IOException If an error occurs while writing to the underlying
185 * output or closing the Writer.
186 ***/
187 public void close() throws IOException
188 {
189 synchronized (lock)
190 {
191 if (__output == null)
192 return ;
193
194 if (__state == __LAST_WAS_CR_STATE)
195 __output.write('\n');
196 else if (__state != __LAST_WAS_NL_STATE)
197 __output.write("\r\n");
198
199 __output.write(".\r\n");
200
201 __output.flush();
202 __output = null;
203 }
204 }
205
206 }