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 ***/
38
39 public final class DotTerminatedMessageWriter extends Writer
40 {
41 private static final int __NOTHING_SPECIAL_STATE = 0;
42 private static final int __LAST_WAS_CR_STATE = 1;
43 private static final int __LAST_WAS_NL_STATE = 2;
44
45 private int __state;
46 private Writer __output;
47
48
49 /***
50 * Creates a DotTerminatedMessageWriter that wraps an existing Writer
51 * output destination.
52 * <p>
53 * @param output The Writer output destination to write the message.
54 ***/
55 public DotTerminatedMessageWriter(Writer output)
56 {
57 super(output);
58 __output = output;
59 __state = __NOTHING_SPECIAL_STATE;
60 }
61
62
63 /***
64 * Writes a character to the output. Note that a call to this method
65 * may result in multiple writes to the underling Writer in order to
66 * convert naked linefeeds to NETASCII line separators and to double
67 * line-leading periods. This is transparent to the programmer and
68 * is only mentioned for completeness.
69 * <p>
70 * @param ch The character to write.
71 * @exception IOException If an error occurs while writing to the
72 * underlying output.
73 ***/
74 @Override
75 public void write(int ch) throws IOException
76 {
77 synchronized (lock)
78 {
79 switch (ch)
80 {
81 case '\r':
82 __state = __LAST_WAS_CR_STATE;
83 __output.write('\r');
84 return ;
85 case '\n':
86 if (__state != __LAST_WAS_CR_STATE) {
87 __output.write('\r');
88 }
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 }
97 //$FALL-THROUGH$
98 default:
99 __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 }