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