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 * 36 * 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 * 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 * 70 * @param ch The character to write. 71 * @throws 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 * 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 * @throws 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 * 132 * @param buffer The character array to write. 133 * @throws 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 * 146 * @param string The String to write. 147 * @throws 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 * 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 * @throws 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 * 176 * @throws 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 * 194 * @throws 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 }