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