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