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 ***/ 038 039 public final class DotTerminatedMessageWriter extends Writer 040 { 041 private static final int __NOTHING_SPECIAL_STATE = 0; 042 private static final int __LAST_WAS_CR_STATE = 1; 043 private static final int __LAST_WAS_NL_STATE = 2; 044 045 private int __state; 046 private Writer __output; 047 048 049 /*** 050 * Creates a DotTerminatedMessageWriter that wraps an existing Writer 051 * output destination. 052 * <p> 053 * @param output The Writer output destination to write the message. 054 ***/ 055 public DotTerminatedMessageWriter(Writer output) 056 { 057 super(output); 058 __output = output; 059 __state = __NOTHING_SPECIAL_STATE; 060 } 061 062 063 /*** 064 * Writes a character to the output. Note that a call to this method 065 * may result in multiple writes to the underling Writer in order to 066 * convert naked linefeeds to NETASCII line separators and to double 067 * line-leading periods. This is transparent to the programmer and 068 * is only mentioned for completeness. 069 * <p> 070 * @param ch The character to write. 071 * @exception IOException If an error occurs while writing to the 072 * underlying output. 073 ***/ 074 @Override 075 public void write(int ch) throws IOException 076 { 077 synchronized (lock) 078 { 079 switch (ch) 080 { 081 case '\r': 082 __state = __LAST_WAS_CR_STATE; 083 __output.write('\r'); 084 return ; 085 case '\n': 086 if (__state != __LAST_WAS_CR_STATE) 087 __output.write('\r'); 088 __output.write('\n'); 089 __state = __LAST_WAS_NL_STATE; 090 return ; 091 case '.': 092 // Double the dot at the beginning of a line 093 if (__state == __LAST_WAS_NL_STATE) 094 __output.write('.'); 095 //$FALL-THROUGH$ 096 default: 097 __state = __NOTHING_SPECIAL_STATE; 098 __output.write(ch); 099 return ; 100 } 101 } 102 } 103 104 105 /*** 106 * Writes a number of characters from a character array to the output 107 * starting from a given offset. 108 * <p> 109 * @param buffer The character array to write. 110 * @param offset The offset into the array at which to start copying data. 111 * @param length The number of characters to write. 112 * @exception IOException If an error occurs while writing to the underlying 113 * output. 114 ***/ 115 @Override 116 public void write(char[] buffer, int offset, int length) throws IOException 117 { 118 synchronized (lock) 119 { 120 while (length-- > 0) 121 write(buffer[offset++]); 122 } 123 } 124 125 126 /*** 127 * Writes a character array to the output. 128 * <p> 129 * @param buffer The character array to write. 130 * @exception IOException If an error occurs while writing to the underlying 131 * output. 132 ***/ 133 @Override 134 public void write(char[] buffer) throws IOException 135 { 136 write(buffer, 0, buffer.length); 137 } 138 139 140 /*** 141 * Writes a String to the output. 142 * <p> 143 * @param string The String to write. 144 * @exception IOException If an error occurs while writing to the underlying 145 * output. 146 ***/ 147 @Override 148 public void write(String string) throws IOException 149 { 150 write(string.toCharArray()); 151 } 152 153 154 /*** 155 * Writes part of a String to the output starting from a given offset. 156 * <p> 157 * @param string The String to write. 158 * @param offset The offset into the String at which to start copying data. 159 * @param length The number of characters to write. 160 * @exception IOException If an error occurs while writing to the underlying 161 * output. 162 ***/ 163 @Override 164 public void write(String string, int offset, int length) throws IOException 165 { 166 write(string.toCharArray(), offset, length); 167 } 168 169 170 /*** 171 * Flushes the underlying output, writing all buffered output. 172 * <p> 173 * @exception IOException If an error occurs while writing to the underlying 174 * output. 175 ***/ 176 @Override 177 public void flush() throws IOException 178 { 179 synchronized (lock) 180 { 181 __output.flush(); 182 } 183 } 184 185 186 /*** 187 * Flushes the underlying output, writing all buffered output, but doesn't 188 * actually close the underlying stream. The underlying stream may still 189 * be used for communicating with the server and therefore is not closed. 190 * <p> 191 * @exception IOException If an error occurs while writing to the underlying 192 * output or closing the Writer. 193 ***/ 194 @Override 195 public void close() throws IOException 196 { 197 synchronized (lock) 198 { 199 if (__output == null) 200 return ; 201 202 if (__state == __LAST_WAS_CR_STATE) 203 __output.write('\n'); 204 else if (__state != __LAST_WAS_NL_STATE) 205 __output.write("\r\n"); 206 207 __output.write(".\r\n"); 208 209 __output.flush(); 210 __output = null; 211 } 212 } 213 214 }