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