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    }