001 /* 002 * Copyright 2001-2005 The Apache Software Foundation 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.apache.commons.net.io; 017 018 import java.io.IOException; 019 import java.io.Writer; 020 021 /*** 022 * DotTerminatedMessageWriter is a class used to write messages to a 023 * server that are terminated by a single dot followed by a 024 * <CR><LF> 025 * sequence and with double dots appearing at the begining of lines which 026 * do not signal end of message yet start with a dot. Various Internet 027 * protocols such as NNTP and POP3 produce messages of this type. 028 * <p> 029 * This class handles the doubling of line-starting periods, 030 * converts single linefeeds to NETASCII newlines, and on closing 031 * will send the final message terminator dot and NETASCII newline 032 * sequence. 033 * <p> 034 * <p> 035 * @author Daniel F. Savarese 036 ***/ 037 038 public final class DotTerminatedMessageWriter extends Writer 039 { 040 private static final int __NOTHING_SPECIAL_STATE = 0; 041 private static final int __LAST_WAS_CR_STATE = 1; 042 private static final int __LAST_WAS_NL_STATE = 2; 043 044 private int __state; 045 private Writer __output; 046 047 048 /*** 049 * Creates a DotTerminatedMessageWriter that wraps an existing Writer 050 * output destination. 051 * <p> 052 * @param output The Writer output destination to write the message. 053 ***/ 054 public DotTerminatedMessageWriter(Writer output) 055 { 056 super(output); 057 __output = output; 058 __state = __NOTHING_SPECIAL_STATE; 059 } 060 061 062 /*** 063 * Writes a character to the output. Note that a call to this method 064 * may result in multiple writes to the underling Writer in order to 065 * convert naked linefeeds to NETASCII line separators and to double 066 * line-leading periods. This is transparent to the programmer and 067 * is only mentioned for completeness. 068 * <p> 069 * @param ch The character to write. 070 * @exception IOException If an error occurs while writing to the 071 * underlying output. 072 ***/ 073 public void write(int ch) throws IOException 074 { 075 synchronized (lock) 076 { 077 switch (ch) 078 { 079 case '\r': 080 __state = __LAST_WAS_CR_STATE; 081 __output.write('\r'); 082 return ; 083 case '\n': 084 if (__state != __LAST_WAS_CR_STATE) 085 __output.write('\r'); 086 __output.write('\n'); 087 __state = __LAST_WAS_NL_STATE; 088 return ; 089 case '.': 090 // Double the dot at the beginning of a line 091 if (__state == __LAST_WAS_NL_STATE) 092 __output.write('.'); 093 // Fall through 094 default: 095 __state = __NOTHING_SPECIAL_STATE; 096 __output.write(ch); 097 return ; 098 } 099 } 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 }