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