1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.commons.net.io;
17
18 import java.io.IOException;
19 import java.io.Writer;
20
21 /***
22 * DotTerminatedMessageWriter is a class used to write messages to a
23 * server that are terminated by a single dot followed by a
24 * <CR><LF>
25 * sequence and with double dots appearing at the begining of lines which
26 * do not signal end of message yet start with a dot. Various Internet
27 * protocols such as NNTP and POP3 produce messages of this type.
28 * <p>
29 * This class handles the doubling of line-starting periods,
30 * converts single linefeeds to NETASCII newlines, and on closing
31 * will send the final message terminator dot and NETASCII newline
32 * sequence.
33 * <p>
34 * <p>
35 * @author Daniel F. Savarese
36 ***/
37
38 public final class DotTerminatedMessageWriter extends Writer
39 {
40 private static final int __NOTHING_SPECIAL_STATE = 0;
41 private static final int __LAST_WAS_CR_STATE = 1;
42 private static final int __LAST_WAS_NL_STATE = 2;
43
44 private int __state;
45 private Writer __output;
46
47
48 /***
49 * Creates a DotTerminatedMessageWriter that wraps an existing Writer
50 * output destination.
51 * <p>
52 * @param output The Writer output destination to write the message.
53 ***/
54 public DotTerminatedMessageWriter(Writer output)
55 {
56 super(output);
57 __output = output;
58 __state = __NOTHING_SPECIAL_STATE;
59 }
60
61
62 /***
63 * Writes a character to the output. Note that a call to this method
64 * may result in multiple writes to the underling Writer in order to
65 * convert naked linefeeds to NETASCII line separators and to double
66 * line-leading periods. This is transparent to the programmer and
67 * is only mentioned for completeness.
68 * <p>
69 * @param ch The character to write.
70 * @exception IOException If an error occurs while writing to the
71 * underlying output.
72 ***/
73 public void write(int ch) throws IOException
74 {
75 synchronized (lock)
76 {
77 switch (ch)
78 {
79 case '\r':
80 __state = __LAST_WAS_CR_STATE;
81 __output.write('\r');
82 return ;
83 case '\n':
84 if (__state != __LAST_WAS_CR_STATE)
85 __output.write('\r');
86 __output.write('\n');
87 __state = __LAST_WAS_NL_STATE;
88 return ;
89 case '.':
90
91 if (__state == __LAST_WAS_NL_STATE)
92 __output.write('.');
93
94 default:
95 __state = __NOTHING_SPECIAL_STATE;
96 __output.write(ch);
97 return ;
98 }
99 }
100 }
101
102
103 /***
104 * Writes a number of characters from a character array to the output
105 * starting from a given offset.
106 * <p>
107 * @param buffer The character array to write.
108 * @param offset The offset into the array at which to start copying data.
109 * @param length The number of characters to write.
110 * @exception IOException If an error occurs while writing to the underlying
111 * output.
112 ***/
113 public void write(char[] buffer, int offset, int length) throws IOException
114 {
115 synchronized (lock)
116 {
117 while (length-- > 0)
118 write(buffer[offset++]);
119 }
120 }
121
122
123 /***
124 * Writes a character array to the output.
125 * <p>
126 * @param buffer The character array to write.
127 * @exception IOException If an error occurs while writing to the underlying
128 * output.
129 ***/
130 public void write(char[] buffer) throws IOException
131 {
132 write(buffer, 0, buffer.length);
133 }
134
135
136 /***
137 * Writes a String to the output.
138 * <p>
139 * @param string The String to write.
140 * @exception IOException If an error occurs while writing to the underlying
141 * output.
142 ***/
143 public void write(String string) throws IOException
144 {
145 write(string.toCharArray());
146 }
147
148
149 /***
150 * Writes part of a String to the output starting from a given offset.
151 * <p>
152 * @param string The String to write.
153 * @param offset The offset into the String at which to start copying data.
154 * @param length The number of characters to write.
155 * @exception IOException If an error occurs while writing to the underlying
156 * output.
157 ***/
158 public void write(String string, int offset, int length) throws IOException
159 {
160 write(string.toCharArray(), offset, length);
161 }
162
163
164 /***
165 * Flushes the underlying output, writing all buffered output.
166 * <p>
167 * @exception IOException If an error occurs while writing to the underlying
168 * output.
169 ***/
170 public void flush() throws IOException
171 {
172 synchronized (lock)
173 {
174 __output.flush();
175 }
176 }
177
178
179 /***
180 * Flushes the underlying output, writing all buffered output, but doesn't
181 * actually close the underlying stream. The underlying stream may still
182 * be used for communicating with the server and therefore is not closed.
183 * <p>
184 * @exception IOException If an error occurs while writing to the underlying
185 * output or closing the Writer.
186 ***/
187 public void close() throws IOException
188 {
189 synchronized (lock)
190 {
191 if (__output == null)
192 return ;
193
194 if (__state == __LAST_WAS_CR_STATE)
195 __output.write('\n');
196 else if (__state != __LAST_WAS_NL_STATE)
197 __output.write("\r\n");
198
199 __output.write(".\r\n");
200
201 __output.flush();
202 __output = null;
203 }
204 }
205
206 }