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.FilterInputStream;
021import java.io.IOException;
022import java.io.InputStream;
023
024/***
025 * This class wraps an input stream, replacing all singly occurring
026 * <LF> (linefeed) characters with <CR><LF> (carriage return
027 * followed by linefeed), which is the NETASCII standard for representing
028 * a newline.
029 * You would use this class to implement ASCII file transfers requiring
030 * conversion to NETASCII.
031 *
032 *
033 ***/
034
035public final class ToNetASCIIInputStream extends FilterInputStream
036{
037    private static final int __NOTHING_SPECIAL = 0;
038    private static final int __LAST_WAS_CR = 1;
039    private static final int __LAST_WAS_NL = 2;
040    private int __status;
041
042    /***
043     * Creates a ToNetASCIIInputStream instance that wraps an existing
044     * InputStream.
045     *
046     * @param input  The InputStream to wrap.
047     ***/
048    public ToNetASCIIInputStream(InputStream input)
049    {
050        super(input);
051        __status = __NOTHING_SPECIAL;
052    }
053
054
055    /***
056     * Reads and returns the next byte in the stream.  If the end of the
057     * message has been reached, returns -1.
058     *
059     * @return The next character in the stream. Returns -1 if the end of the
060     *          stream has been reached.
061     * @throws IOException If an error occurs while reading the underlying
062     *            stream.
063     ***/
064    @Override
065    public int read() throws IOException
066    {
067        int ch;
068
069        if (__status == __LAST_WAS_NL)
070        {
071            __status = __NOTHING_SPECIAL;
072            return '\n';
073        }
074
075        ch = in.read();
076
077        switch (ch)
078        {
079        case '\r':
080            __status = __LAST_WAS_CR;
081            return '\r';
082        case '\n':
083            if (__status != __LAST_WAS_CR)
084            {
085                __status = __LAST_WAS_NL;
086                return '\r';
087            }
088            //$FALL-THROUGH$
089        default:
090            __status = __NOTHING_SPECIAL;
091            return ch;
092        }
093        // statement not reached
094        //return ch;
095    }
096
097
098    /***
099     * Reads the next number of bytes from the stream into an array and
100     * returns the number of bytes read.  Returns -1 if the end of the
101     * stream has been reached.
102     *
103     * @param buffer  The byte array in which to store the data.
104     * @return The number of bytes read. Returns -1 if the
105     *          end of the message has been reached.
106     * @throws IOException If an error occurs in reading the underlying
107     *            stream.
108     ***/
109    @Override
110    public int read(byte buffer[]) throws IOException
111    {
112        return read(buffer, 0, buffer.length);
113    }
114
115
116    /***
117     * Reads the next number of bytes from the stream into an array and returns
118     * the number of bytes read.  Returns -1 if the end of the
119     * message has been reached.  The characters are stored in the array
120     * starting from the given offset and up to the length specified.
121     *
122     * @param buffer The byte array in which to store the data.
123     * @param offset  The offset into the array at which to start storing data.
124     * @param length   The number of bytes to read.
125     * @return The number of bytes read. Returns -1 if the
126     *          end of the stream has been reached.
127     * @throws IOException If an error occurs while reading the underlying
128     *            stream.
129     ***/
130    @Override
131    public int read(byte buffer[], int offset, int length) throws IOException
132    {
133        int ch, off;
134
135        if (length < 1) {
136            return 0;
137        }
138
139        ch = available();
140
141        if (length > ch) {
142            length = ch;
143        }
144
145        // If nothing is available, block to read only one character
146        if (length < 1) {
147            length = 1;
148        }
149
150        if ((ch = read()) == -1) {
151            return -1;
152        }
153
154        off = offset;
155
156        do
157        {
158            buffer[offset++] = (byte)ch;
159        }
160        while (--length > 0 && (ch = read()) != -1);
161
162        return (offset - off);
163    }
164
165    /*** Returns false.  Mark is not supported. ***/
166    @Override
167    public boolean markSupported()
168    {
169        return false;
170    }
171
172    @Override
173    public int available() throws IOException
174    {
175        int result;
176
177        result = in.available();
178
179        if (__status == __LAST_WAS_NL) {
180            return (result + 1);
181        }
182
183        return result;
184    }
185}