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