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
018package org.apache.commons.net.io;
019
020import java.io.IOException;
021import java.io.InputStream;
022import java.io.PushbackInputStream;
023
024/***
025 * This class wraps an input stream, replacing all occurrences
026 * of <CR><LF> (carriage return followed by a linefeed),
027 * which is the NETASCII standard for representing a newline, with the
028 * local line separator representation.  You would use this class to
029 * implement ASCII file transfers requiring conversion from NETASCII.
030 * <p>
031 * <p>
032 * @author Daniel F. Savarese
033 ***/
034
035public final class FromNetASCIIInputStream extends PushbackInputStream
036{
037    static final boolean _noConversionRequired;
038    static final String _lineSeparator;
039    static final byte[] _lineSeparatorBytes;
040
041    static {
042        _lineSeparator = System.getProperty("line.separator");
043        _noConversionRequired = _lineSeparator.equals("\r\n");
044        _lineSeparatorBytes = _lineSeparator.getBytes();
045    }
046
047    private int __length = 0;
048
049    /***
050     * Returns true if the NetASCII line separator differs from the system
051     * line separator, false if they are the same.  This method is useful
052     * to determine whether or not you need to instantiate a
053     * FromNetASCIIInputStream object.
054     * <p>
055     * @return True if the NETASCII line separator differs from the local
056     *   system line separator, false if they are the same.
057     ***/
058    public static final boolean isConversionRequired()
059    {
060        return !_noConversionRequired;
061    }
062
063    /***
064     * Creates a FromNetASCIIInputStream instance that wraps an existing
065     * InputStream.
066     ***/
067    public FromNetASCIIInputStream(InputStream input)
068    {
069        super(input, _lineSeparatorBytes.length + 1);
070    }
071
072
073    private int __read() throws IOException
074    {
075        int ch;
076
077        ch = super.read();
078
079        if (ch == '\r')
080        {
081            ch = super.read();
082            if (ch == '\n')
083            {
084                unread(_lineSeparatorBytes);
085                ch = super.read();
086                // This is a kluge for read(byte[], ...) to read the right amount
087                --__length;
088            }
089            else
090            {
091                if (ch != -1)
092                    unread(ch);
093                return '\r';
094            }
095        }
096
097        return ch;
098    }
099
100
101    /***
102     * Reads and returns the next byte in the stream.  If the end of the
103     * message has been reached, returns -1.  Note that a call to this method
104     * may result in multiple reads from the underlying input stream in order
105     * to convert NETASCII line separators to the local line separator format.
106     * This is transparent to the programmer and is only mentioned for
107     * completeness.
108     * <p>
109     * @return The next character in the stream. Returns -1 if the end of the
110     *          stream has been reached.
111     * @exception IOException If an error occurs while reading the underlying
112     *            stream.
113     ***/
114    @Override
115    public int read() throws IOException
116    {
117        if (_noConversionRequired)
118            return super.read();
119
120        return __read();
121    }
122
123
124    /***
125     * Reads the next number of bytes from the stream into an array and
126     * returns the number of bytes read.  Returns -1 if the end of the
127     * stream has been reached.
128     * <p>
129     * @param buffer  The byte array in which to store the data.
130     * @return The number of bytes read. Returns -1 if the
131     *          end of the message has been reached.
132     * @exception IOException If an error occurs in reading the underlying
133     *            stream.
134     ***/
135    @Override
136    public int read(byte buffer[]) throws IOException
137    {
138        return read(buffer, 0, buffer.length);
139    }
140
141
142    /***
143     * Reads the next number of bytes from the stream into an array and returns
144     * the number of bytes read.  Returns -1 if the end of the
145     * message has been reached.  The characters are stored in the array
146     * starting from the given offset and up to the length specified.
147     * <p>
148     * @param buffer The byte array in which to store the data.
149     * @param offset  The offset into the array at which to start storing data.
150     * @param length   The number of bytes to read.
151     * @return The number of bytes read. Returns -1 if the
152     *          end of the stream has been reached.
153     * @exception IOException If an error occurs while reading the underlying
154     *            stream.
155     ***/
156    @Override
157    public int read(byte buffer[], int offset, int length) throws IOException
158    {
159        int ch, off;
160
161        if (length < 1)
162            return 0;
163
164        ch = available();
165
166        __length = (length > ch ? ch : length);
167
168        // If nothing is available, block to read only one character
169        if (__length < 1)
170            __length = 1;
171
172        if (_noConversionRequired)
173            return super.read(buffer, offset, __length);
174
175        if ((ch = __read()) == -1)
176            return -1;
177
178        off = offset;
179
180        do
181        {
182            buffer[offset++] = (byte)ch;
183        }
184        while (--__length > 0 && (ch = __read()) != -1);
185
186
187        return (offset - off);
188    }
189
190
191    // PushbackInputStream in JDK 1.1.3 returns the wrong thing
192    /***
193     * Returns the number of bytes that can be read without blocking EXCEPT
194     * when newline conversions have to be made somewhere within the
195     * available block of bytes.  In other words, you really should not
196     * rely on the value returned by this method if you are trying to avoid
197     * blocking.
198     ***/
199    @Override
200    public int available() throws IOException
201    {
202        return (buf.length - pos) + in.available();
203    }
204
205}