    View Javadoc
    1   package org.apache.commons.net.io;
    2
    3   /* ====================================================================
    4    * The Apache Software License, Version 1.1
    5    *
    6    * Copyright (c) 2001 The Apache Software Foundation.  All rights
    7    * reserved.
    8    *
    9    * Redistribution and use in source and binary forms, with or without
    10   * modification, are permitted provided that the following conditions
    11   * are met:
    12   *
    13   * 1. Redistributions of source code must retain the above copyright
    14   *    notice, this list of conditions and the following disclaimer.
    15   *
    16   * 2. Redistributions in binary form must reproduce the above copyright
    17   *    notice, this list of conditions and the following disclaimer in
    18   *    the documentation and/or other materials provided with the
    19   *    distribution.
    20   *
    21   * 3. The end-user documentation included with the redistribution,
    22   *    if any, must include the following acknowledgment:
    23   *       "This product includes software developed by the
    24   *        Apache Software Foundation (http://www.apache.org/)."
    25   *    Alternately, this acknowledgment may appear in the software itself,
    26   *    if and wherever such third-party acknowledgments normally appear.
    27   *
    28   * 4. The names "Apache" and "Apache Software Foundation" and
    29   *    "Apache Commons" must not be used to endorse or promote products
    30   *    derived from this software without prior written permission. For
    31   *    written permission, please contact apache@apache.org.
    32   *
    33   * 5. Products derived from this software may not be called "Apache",
    34   *    nor may "Apache" appear in their name, without
    35   *    prior written permission of the Apache Software Foundation.
    36   *
    37   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
    38   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
    39   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    40   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
    41   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    42   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    43   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
    44   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    45   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    46   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
    47   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    48   * SUCH DAMAGE.
    49   * ====================================================================
    50   *
    51   * This software consists of voluntary contributions made by many
    52   * individuals on behalf of the Apache Software Foundation.  For more
    53   * information on the Apache Software Foundation, please see
    54   * <http://www.apache.org/>;.
    55   */
    56
    57  import java.io.IOException;
    58  import java.io.PushbackReader;
    59  import java.io.Reader;
    60
    61  /***
    62   * DotTerminatedMessageReader is a class used to read messages from a
    63   * server that are terminated by a single dot followed by a
    64   * <CR><LF>
    65   * sequence and with double dots appearing at the begining of lines which
    66   * do not signal end of message yet start with a dot.  Various Internet
    67   * protocols such as NNTP and POP3 produce messages of this type.
    68   * <p>
    69   * This class handles stripping of the duplicate period at the beginning
    70   * of lines starting with a period, converts NETASCII newlines to the
    71   * local line separator format, truncates the end of message indicator,
    72   * and ensures you cannot read past the end of the message.
    73   * @author <a href="mailto:savarese@apache.org">Daniel F. Savarese</a>
    74   * @version $Id: DotTerminatedMessageReader.java,v 1.6 2003/01/26 04:33:32 brekke Exp $
    75   */
    76  public final class DotTerminatedMessageReader extends Reader
    77  {
    78      private static final String LS;
    79      private static final char[] LS_CHARS;
    80
    81      static
    82      {
    83          LS = System.getProperty("line.separator");
    84          LS_CHARS = LS.toCharArray();
    85      }
    86
    87      private boolean atBeginning;
    88      private boolean eof;
    89      private int pos;
    90      private char[] internalBuffer;
    91      private PushbackReader internalReader;
    92
    93      /***
    94       * Creates a DotTerminatedMessageReader that wraps an existing Reader
    95       * input source.
    96       * @param reader  The Reader input source containing the message.
    97       */
    98      public DotTerminatedMessageReader(Reader reader)
    99      {
    100         super(reader);
    101         internalBuffer = new char[LS_CHARS.length + 3];
    102         pos = internalBuffer.length;
    103         // Assumes input is at start of message
    104         atBeginning = true;
    105         eof = false;
    106         internalReader = new PushbackReader(reader);
    107     }
    108
    109     /***
    110      * Reads and returns the next character in the message.  If the end of the
    111      * message has been reached, returns -1.  Note that a call to this method
    112      * may result in multiple reads from the underlying input stream to decode
    113      * the message properly (removing doubled dots and so on).  All of
    114      * this is transparent to the programmer and is only mentioned for
    115      * completeness.
    116      * @return The next character in the message. Returns -1 if the end of the
    117      *          message has been reached.
    118      * @exception IOException If an error occurs while reading the underlying
    119      *            stream.
    120      */
    121     public int read() throws IOException
    122     {
    123         int ch;
    124
    125         synchronized (lock)
    126         {
    127             if (pos < internalBuffer.length)
    128             {
    129                 return internalBuffer[pos++];
    130             }
    131
    132             if (eof)
    133             {
    134                 return -1;
    135             }
    136
    137             if ((ch = internalReader.read()) == -1)
    138             {
    139                 eof = true;
    140                 return -1;
    141             }
    142
    143             if (atBeginning)
    144             {
    145                 atBeginning = false;
    146                 if (ch == '.')
    147                 {
    148                     ch = internalReader.read();
    149
    150                     if (ch != '.')
    151                     {
    152                         // read newline
    153                         eof = true;
    154                         internalReader.read();
    155                         return -1;
    156                     }
    157                     else
    158                     {
    159                         return '.';
    160                     }
    161                 }
    162             }
    163
    164             if (ch == '\r')
    165             {
    166                 ch = internalReader.read();
    167
    168                 if (ch == '\n')
    169                 {
    170                     ch = internalReader.read();
    171
    172                     if (ch == '.')
    173                     {
    174                         ch = internalReader.read();
    175
    176                         if (ch != '.')
    177                         {
    178                             // read newline and indicate end of file
    179                             internalReader.read();
    180                             eof = true;
    181                         }
    182                         else
    183                         {
    184                             internalBuffer[--pos] = (char) ch;
    185                         }
    186                     }
    187                     else
    188                     {
    189                         internalReader.unread(ch);
    190                     }
    191
    192                     pos -= LS_CHARS.length;
    193                     System.arraycopy(LS_CHARS, 0, internalBuffer, pos,
    194                                      LS_CHARS.length);
    195                     ch = internalBuffer[pos++];
    196                 }
    197                 else
    198                 {
    199                     internalBuffer[--pos] = (char) ch;
    200                     return '\r';
    201                 }
    202             }
    203
    204             return ch;
    205         }
    206     }
    207
    208     /***
    209      * Reads the next characters from the message into an array and
    210      * returns the number of characters read.  Returns -1 if the end of the
    211      * message has been reached.
    212      * @param buffer  The character array in which to store the characters.
    213      * @return The number of characters read. Returns -1 if the
    214      *          end of the message has been reached.
    215      * @exception IOException If an error occurs in reading the underlying
    216      *            stream.
    217      */
    218     public int read(char[] buffer) throws IOException
    219     {
    220         return read(buffer, 0, buffer.length);
    221     }
    222
    223     /***
    224      * Reads the next characters from the message into an array and
    225      * returns the number of characters read.  Returns -1 if the end of the
    226      * message has been reached.  The characters are stored in the array
    227      * starting from the given offset and up to the length specified.
    228      * @param buffer  The character array in which to store the characters.
    229      * @param offset   The offset into the array at which to start storing
    230      *              characters.
    231      * @param length   The number of characters to read.
    232      * @return The number of characters read. Returns -1 if the
    233      *          end of the message has been reached.
    234      * @exception IOException If an error occurs in reading the underlying
    235      *            stream.
    236      */
    237     public int read(char[] buffer, int offset, int length) throws IOException
    238     {
    239         int ch, off;
    240         synchronized (lock)
    241         {
    242             if (length < 1)
    243             {
    244                 return 0;
    245             }
    246             if ((ch = read()) == -1)
    247             {
    248                 return -1;
    249             }
    250             off = offset;
    251
    252             do
    253             {
    254                 buffer[offset++] = (char) ch;
    255             }
    256             while (--length > 0 && (ch = read()) != -1);
    257
    258             return (offset - off);
    259         }
    260     }
    261
    262     /***
    263      * Determines if the message is ready to be read.
    264      * @return True if the message is ready to be read, false if not.
    265      * @exception IOException If an error occurs while checking the underlying
    266      *            stream.
    267      */
    268     public boolean ready() throws IOException
    269     {
    270         synchronized (lock)
    271         {
    272             return (pos < internalBuffer.length || internalReader.ready());
    273         }
    274     }
    275
    276     /***
    277      * Closes the message for reading.  This doesn't actually close the
    278      * underlying stream.  The underlying stream may still be used for
    279      * communicating with the server and therefore is not closed.
    280      * <p>
    281      * If the end of the message has not yet been reached, this method
    282      * will read the remainder of the message until it reaches the end,
    283      * so that the underlying stream may continue to be used properly
    284      * for communicating with the server.  If you do not fully read
    285      * a message, you MUST close it, otherwise your program will likely
    286      * hang or behave improperly.
    287      * @exception IOException  If an error occurs while reading the
    288      *            underlying stream.
    289      */
    290     public void close() throws IOException
    291     {
    292         synchronized (lock)
    293         {
    294             if (internalReader == null)
    295             {
    296                 return;
    297             }
    298
    299             if (!eof)
    300             {
    301                 while (read() != -1)
    302                 {
    303                     ;
    304                 }
    305             }
    306             eof = true;
    307             atBeginning = false;
    308             pos = internalBuffer.length;
    309             internalReader = null;
    310         }
    311     }
    312 }

This page was automatically generated by Maven