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