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 }
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 }
097 //$FALL-THROUGH$
098 default:
099 __state = __NOTHING_SPECIAL_STATE;
100 __output.write(ch);
101 return ;
102 }
103 }
104 }
105
106
107 /***
108 * Writes a number of characters from a character array to the output
109 * starting from a given offset.
110 * <p>
111 * @param buffer The character array to write.
112 * @param offset The offset into the array at which to start copying data.
113 * @param length The number of characters to write.
114 * @exception IOException If an error occurs while writing to the underlying
115 * output.
116 ***/
117 @Override
118 public void write(char[] buffer, int offset, int length) throws IOException
119 {
120 synchronized (lock)
121 {
122 while (length-- > 0) {
123 write(buffer[offset++]);
124 }
125 }
126 }
127
128
129 /***
130 * Writes a character array to the output.
131 * <p>
132 * @param buffer The character array to write.
133 * @exception IOException If an error occurs while writing to the underlying
134 * output.
135 ***/
136 @Override
137 public void write(char[] buffer) throws IOException
138 {
139 write(buffer, 0, buffer.length);
140 }
141
142
143 /***
144 * Writes a String to the output.
145 * <p>
146 * @param string The String to write.
147 * @exception IOException If an error occurs while writing to the underlying
148 * output.
149 ***/
150 @Override
151 public void write(String string) throws IOException
152 {
153 write(string.toCharArray());
154 }
155
156
157 /***
158 * Writes part of a String to the output starting from a given offset.
159 * <p>
160 * @param string The String to write.
161 * @param offset The offset into the String at which to start copying data.
162 * @param length The number of characters to write.
163 * @exception IOException If an error occurs while writing to the underlying
164 * output.
165 ***/
166 @Override
167 public void write(String string, int offset, int length) throws IOException
168 {
169 write(string.toCharArray(), offset, length);
170 }
171
172
173 /***
174 * Flushes the underlying output, writing all buffered output.
175 * <p>
176 * @exception IOException If an error occurs while writing to the underlying
177 * output.
178 ***/
179 @Override
180 public void flush() throws IOException
181 {
182 synchronized (lock)
183 {
184 __output.flush();
185 }
186 }
187
188
189 /***
190 * Flushes the underlying output, writing all buffered output, but doesn't
191 * actually close the underlying stream. The underlying stream may still
192 * be used for communicating with the server and therefore is not closed.
193 * <p>
194 * @exception IOException If an error occurs while writing to the underlying
195 * output or closing the Writer.
196 ***/
197 @Override
198 public void close() throws IOException
199 {
200 synchronized (lock)
201 {
202 if (__output == null) {
203 return ;
204 }
205
206 if (__state == __LAST_WAS_CR_STATE) {
207 __output.write('\n');
208 } else if (__state != __LAST_WAS_NL_STATE) {
209 __output.write("\r\n");
210 }
211
212 __output.write(".\r\n");
213
214 __output.flush();
215 __output = null;
216 }
217 }
218
219 }