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