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   * @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 }