FromNetASCIIInputStream.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.net.io;

  18. import java.io.IOException;
  19. import java.io.InputStream;
  20. import java.io.PushbackInputStream;
  21. import java.nio.charset.StandardCharsets;

  22. import org.apache.commons.net.util.NetConstants;

  23. /**
  24.  * This class wraps an input stream, replacing all occurrences of <CR><LF> (carriage return followed by a linefeed), which is the NETASCII standard
  25.  * for representing a newline, with the local line separator representation. You would use this class to implement ASCII file transfers requiring conversion
  26.  * from NETASCII.
  27.  */

  28. public final class FromNetASCIIInputStream extends PushbackInputStream {
  29.     static final boolean _noConversionRequired;
  30.     static final String _lineSeparator;
  31.     static final byte[] _lineSeparatorBytes;

  32.     static {
  33.         _lineSeparator = System.lineSeparator();
  34.         _noConversionRequired = _lineSeparator.equals("\r\n");
  35.         _lineSeparatorBytes = _lineSeparator.getBytes(StandardCharsets.US_ASCII);
  36.     }

  37.     /**
  38.      * Returns true if the NetASCII line separator differs from the system line separator, false if they are the same. This method is useful to determine
  39.      * whether or not you need to instantiate a FromNetASCIIInputStream object.
  40.      *
  41.      * @return True if the NETASCII line separator differs from the local system line separator, false if they are the same.
  42.      */
  43.     public static boolean isConversionRequired() {
  44.         return !_noConversionRequired;
  45.     }

  46.     private int length;

  47.     /**
  48.      * Creates a FromNetASCIIInputStream instance that wraps an existing InputStream.
  49.      *
  50.      * @param input the stream to wrap
  51.      */
  52.     public FromNetASCIIInputStream(final InputStream input) {
  53.         super(input, _lineSeparatorBytes.length + 1);
  54.     }

  55.     // PushbackInputStream in JDK 1.1.3 returns the wrong thing
  56.     // TODO - can we delete this override now?
  57.     /**
  58.      * Returns the number of bytes that can be read without blocking EXCEPT when newline conversions have to be made somewhere within the available block of
  59.      * bytes. In other words, you really should not rely on the value returned by this method if you are trying to avoid blocking.
  60.      */
  61.     @Override
  62.     public int available() throws IOException {
  63.         if (in == null) {
  64.             throw new IOException("Stream closed");
  65.         }
  66.         return buf.length - pos + in.available();
  67.     }

  68.     /**
  69.      * Reads and returns the next byte in the stream. If the end of the message has been reached, returns -1. Note that a call to this method may result in
  70.      * multiple reads from the underlying input stream in order to convert NETASCII line separators to the local line separator format. This is transparent to
  71.      * the programmer and is only mentioned for completeness.
  72.      *
  73.      * @return The next character in the stream. Returns -1 if the end of the stream has been reached.
  74.      * @throws IOException If an error occurs while reading the underlying stream.
  75.      */
  76.     @Override
  77.     public int read() throws IOException {
  78.         if (_noConversionRequired) {
  79.             return super.read();
  80.         }

  81.         return readInt();
  82.     }

  83.     /**
  84.      * 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.
  85.      *
  86.      * @param buffer The byte array in which to store the data.
  87.      * @return The number of bytes read. Returns -1 if the end of the message has been reached.
  88.      * @throws IOException If an error occurs in reading the underlying stream.
  89.      */
  90.     @Override
  91.     public int read(final byte buffer[]) throws IOException {
  92.         return read(buffer, 0, buffer.length);
  93.     }

  94.     /**
  95.      * 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.
  96.      * The characters are stored in the array starting from the given offset and up to the length specified.
  97.      *
  98.      * @param buffer The byte array in which to store the data.
  99.      * @param offset The offset into the array at which to start storing data.
  100.      * @param length The number of bytes to read.
  101.      * @return The number of bytes read. Returns -1 if the end of the stream has been reached.
  102.      * @throws IOException If an error occurs while reading the underlying stream.
  103.      */
  104.     @Override
  105.     public int read(final byte buffer[], int offset, final int length) throws IOException {
  106.         if (_noConversionRequired) {
  107.             return super.read(buffer, offset, length);
  108.         }

  109.         if (length < 1) {
  110.             return 0;
  111.         }

  112.         int ch;
  113.         final int off;

  114.         ch = available();

  115.         this.length = Math.min(length, ch);

  116.         // If nothing is available, block to read only one character
  117.         if (this.length < 1) {
  118.             this.length = 1;
  119.         }

  120.         if ((ch = readInt()) == -1) {
  121.             return NetConstants.EOS;
  122.         }

  123.         off = offset;

  124.         do {
  125.             buffer[offset++] = (byte) ch;
  126.         } while (--this.length > 0 && (ch = readInt()) != -1);

  127.         return offset - off;
  128.     }

  129.     private int readInt() throws IOException {
  130.         int ch;

  131.         ch = super.read();

  132.         if (ch == '\r') {
  133.             ch = super.read();
  134.             if (ch != '\n') {
  135.                 if (ch != -1) {
  136.                     unread(ch);
  137.                 }
  138.                 return '\r';
  139.             }
  140.             unread(_lineSeparatorBytes);
  141.             ch = super.read();
  142.             // This is a kluge for read(byte[], ...) to read the right amount
  143.             --length;
  144.         }

  145.         return ch;
  146.     }

  147. }