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