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;
023import java.nio.charset.StandardCharsets;
024
025/**
026 * This class wraps an input stream, replacing all occurrences
027 * of <CR><LF> (carriage return followed by a linefeed),
028 * which is the NETASCII standard for representing a newline, with the
029 * local line separator representation.  You would use this class to
030 * implement ASCII file transfers requiring conversion from NETASCII.
031 *
032 *
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(StandardCharsets.US_ASCII);
045    }
046
047    private int length;
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     *
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 boolean isConversionRequired()
059    {
060        return !_noConversionRequired;
061    }
062
063    /**
064     * Creates a FromNetASCIIInputStream instance that wraps an existing
065     * InputStream.
066     * @param input the stream to wrap
067     */
068    public FromNetASCIIInputStream(final InputStream input)
069    {
070        super(input, _lineSeparatorBytes.length + 1);
071    }
072
073
074    private int readInt() throws IOException
075    {
076        int ch;
077
078        ch = super.read();
079
080        if (ch == '\r')
081        {
082            ch = super.read();
083            if (ch == '\n')
084            {
085                unread(_lineSeparatorBytes);
086                ch = super.read();
087                // This is a kluge for read(byte[], ...) to read the right amount
088                --length;
089            }
090            else
091            {
092                if (ch != -1) {
093                    unread(ch);
094                }
095                return '\r';
096            }
097        }
098
099        return ch;
100    }
101
102
103    /**
104     * Reads and returns the next byte in the stream.  If the end of the
105     * message has been reached, returns -1.  Note that a call to this method
106     * may result in multiple reads from the underlying input stream in order
107     * to convert NETASCII line separators to the local line separator format.
108     * This is transparent to the programmer and is only mentioned for
109     * completeness.
110     *
111     * @return The next character in the stream. Returns -1 if the end of the
112     *          stream has been reached.
113     * @throws IOException If an error occurs while reading the underlying
114     *            stream.
115     */
116    @Override
117    public int read() throws IOException
118    {
119        if (_noConversionRequired) {
120            return super.read();
121        }
122
123        return readInt();
124    }
125
126
127    /**
128     * Reads the next number of bytes from the stream into an array and
129     * returns the number of bytes read.  Returns -1 if the end of the
130     * stream has been reached.
131     *
132     * @param buffer  The byte array in which to store the data.
133     * @return The number of bytes read. Returns -1 if the
134     *          end of the message has been reached.
135     * @throws IOException If an error occurs in reading the underlying
136     *            stream.
137     */
138    @Override
139    public int read(final byte buffer[]) throws IOException
140    {
141        return read(buffer, 0, buffer.length);
142    }
143
144
145    /**
146     * Reads the next number of bytes from the stream into an array and returns
147     * the number of bytes read.  Returns -1 if the end of the
148     * message has been reached.  The characters are stored in the array
149     * starting from the given offset and up to the length specified.
150     *
151     * @param buffer The byte array in which to store the data.
152     * @param offset  The offset into the array at which to start storing data.
153     * @param length   The number of bytes to read.
154     * @return The number of bytes read. Returns -1 if the
155     *          end of the stream has been reached.
156     * @throws IOException If an error occurs while reading the underlying
157     *            stream.
158     */
159    @Override
160    public int read(final byte buffer[], int offset, final int length) throws IOException
161    {
162        if (_noConversionRequired) {
163            return super.read(buffer, offset, length);
164        }
165
166        if (length < 1) {
167            return 0;
168        }
169
170        int ch;
171        final int off;
172
173        ch = available();
174
175        this.length = length > ch ? ch : length;
176
177        // If nothing is available, block to read only one character
178        if (this.length < 1) {
179            this.length = 1;
180        }
181
182
183        if ((ch = readInt()) == -1) {
184            return -1;
185        }
186
187        off = offset;
188
189        do
190        {
191            buffer[offset++] = (byte)ch;
192        }
193        while (--this.length > 0 && (ch = readInt()) != -1);
194
195
196        return offset - off;
197    }
198
199
200    // PushbackInputStream in JDK 1.1.3 returns the wrong thing
201    // TODO - can we delete this override now?
202    /**
203     * Returns the number of bytes that can be read without blocking EXCEPT
204     * when newline conversions have to be made somewhere within the
205     * available block of bytes.  In other words, you really should not
206     * rely on the value returned by this method if you are trying to avoid
207     * blocking.
208     */
209    @Override
210    public int available() throws IOException
211    {
212        if (in == null) {
213            throw new IOException("Stream closed");
214        }
215        return buf.length - pos + in.available();
216    }
217
218}