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 ***/
033
034public final class FromNetASCIIInputStream extends PushbackInputStream
035{
036    static final boolean _noConversionRequired;
037    static final String _lineSeparator;
038    static final byte[] _lineSeparatorBytes;
039
040    static {
041        _lineSeparator = System.getProperty("line.separator");
042        _noConversionRequired = _lineSeparator.equals("\r\n");
043        _lineSeparatorBytes = _lineSeparator.getBytes();
044    }
045
046    private int __length = 0;
047
048    /***
049     * Returns true if the NetASCII line separator differs from the system
050     * line separator, false if they are the same.  This method is useful
051     * to determine whether or not you need to instantiate a
052     * FromNetASCIIInputStream object.
053     * <p>
054     * @return True if the NETASCII line separator differs from the local
055     *   system line separator, false if they are the same.
056     ***/
057    public static final boolean isConversionRequired()
058    {
059        return !_noConversionRequired;
060    }
061
062    /***
063     * Creates a FromNetASCIIInputStream instance that wraps an existing
064     * InputStream.
065     ***/
066    public FromNetASCIIInputStream(InputStream input)
067    {
068        super(input, _lineSeparatorBytes.length + 1);
069    }
070
071
072    private int __read() throws IOException
073    {
074        int ch;
075
076        ch = super.read();
077
078        if (ch == '\r')
079        {
080            ch = super.read();
081            if (ch == '\n')
082            {
083                unread(_lineSeparatorBytes);
084                ch = super.read();
085                // This is a kluge for read(byte[], ...) to read the right amount
086                --__length;
087            }
088            else
089            {
090                if (ch != -1) {
091                    unread(ch);
092                }
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
121        return __read();
122    }
123
124
125    /***
126     * Reads the next number of bytes from the stream into an array and
127     * returns the number of bytes read.  Returns -1 if the end of the
128     * stream has been reached.
129     * <p>
130     * @param buffer  The byte array in which to store the data.
131     * @return The number of bytes read. Returns -1 if the
132     *          end of the message has been reached.
133     * @exception IOException If an error occurs in reading the underlying
134     *            stream.
135     ***/
136    @Override
137    public int read(byte buffer[]) throws IOException
138    {
139        return read(buffer, 0, buffer.length);
140    }
141
142
143    /***
144     * Reads the next number of bytes from the stream into an array and returns
145     * the number of bytes read.  Returns -1 if the end of the
146     * message has been reached.  The characters are stored in the array
147     * starting from the given offset and up to the length specified.
148     * <p>
149     * @param buffer The byte array in which to store the data.
150     * @param offset  The offset into the array at which to start storing data.
151     * @param length   The number of bytes to read.
152     * @return The number of bytes read. Returns -1 if the
153     *          end of the stream has been reached.
154     * @exception IOException If an error occurs while reading the underlying
155     *            stream.
156     ***/
157    @Override
158    public int read(byte buffer[], int offset, int length) throws IOException
159    {
160        if (_noConversionRequired) {
161            return super.read(buffer, offset, length);
162        }
163
164        if (length < 1) {
165            return 0;
166        }
167
168        int ch, off;
169
170        ch = available();
171
172        __length = (length > ch ? ch : length);
173
174        // If nothing is available, block to read only one character
175        if (__length < 1) {
176            __length = 1;
177        }
178
179
180        if ((ch = __read()) == -1) {
181            return -1;
182        }
183
184        off = offset;
185
186        do
187        {
188            buffer[offset++] = (byte)ch;
189        }
190        while (--__length > 0 && (ch = __read()) != -1);
191
192
193        return (offset - off);
194    }
195
196
197    // PushbackInputStream in JDK 1.1.3 returns the wrong thing
198    // TODO - can we delete this override now?
199    /***
200     * Returns the number of bytes that can be read without blocking EXCEPT
201     * when newline conversions have to be made somewhere within the
202     * available block of bytes.  In other words, you really should not
203     * rely on the value returned by this method if you are trying to avoid
204     * blocking.
205     ***/
206    @Override
207    public int available() throws IOException
208    {
209        if (in == null) {
210            throw new IOException("Stream closed");
211        }
212        return (buf.length - pos) + in.available();
213    }
214
215}