1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.net.io;
19
20 import java.io.IOException;
21 import java.io.Writer;
22
23 /***
24 * DotTerminatedMessageWriter is a class used to write messages to a
25 * server that are terminated by a single dot followed by a
26 * <CR><LF>
27 * sequence and with double dots appearing at the begining of lines which
28 * do not signal end of message yet start with a dot. Various Internet
29 * protocols such as NNTP and POP3 produce messages of this type.
30 * <p>
31 * This class handles the doubling of line-starting periods,
32 * converts single linefeeds to NETASCII newlines, and on closing
33 * will send the final message terminator dot and NETASCII newline
34 * sequence.
35 * <p>
36 * <p>
37 * @author Daniel F. Savarese
38 ***/
39
40 public final class DotTerminatedMessageWriter extends Writer
41 {
42 private static final int __NOTHING_SPECIAL_STATE = 0;
43 private static final int __LAST_WAS_CR_STATE = 1;
44 private static final int __LAST_WAS_NL_STATE = 2;
45
46 private int __state;
47 private Writer __output;
48
49
50 /***
51 * Creates a DotTerminatedMessageWriter that wraps an existing Writer
52 * output destination.
53 * <p>
54 * @param output The Writer output destination to write the message.
55 ***/
56 public DotTerminatedMessageWriter(Writer output)
57 {
58 super(output);
59 __output = output;
60 __state = __NOTHING_SPECIAL_STATE;
61 }
62
63
64 /***
65 * Writes a character to the output. Note that a call to this method
66 * may result in multiple writes to the underling Writer in order to
67 * convert naked linefeeds to NETASCII line separators and to double
68 * line-leading periods. This is transparent to the programmer and
69 * is only mentioned for completeness.
70 * <p>
71 * @param ch The character to write.
72 * @exception IOException If an error occurs while writing to the
73 * underlying output.
74 ***/
75 @Override
76 public void write(int ch) throws IOException
77 {
78 synchronized (lock)
79 {
80 switch (ch)
81 {
82 case '\r':
83 __state = __LAST_WAS_CR_STATE;
84 __output.write('\r');
85 return ;
86 case '\n':
87 if (__state != __LAST_WAS_CR_STATE)
88 __output.write('\r');
89 __output.write('\n');
90 __state = __LAST_WAS_NL_STATE;
91 return ;
92 case '.':
93 // Double the dot at the beginning of a line
94 if (__state == __LAST_WAS_NL_STATE)
95 __output.write('.');
96 // Fall through
97 default:
98 __state = __NOTHING_SPECIAL_STATE;
99 __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 }