View Javadoc

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 }