View Javadoc

1   /*
2    * Copyright 2001-2005 The Apache Software Foundation
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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                  // Double the dot at the beginning of a line
91                  if (__state == __LAST_WAS_NL_STATE)
92                      __output.write('.');
93                  // Fall through
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 }