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 * https://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.IOException; 021import java.io.InputStream; 022import java.io.PushbackInputStream; 023import java.nio.charset.StandardCharsets; 024 025import org.apache.commons.net.util.NetConstants; 026 027/** 028 * This class wraps an input stream, replacing all occurrences of <CR><LF> (carriage return followed by a linefeed), which is the NETASCII standard 029 * for representing a newline, with the local line separator representation. You would use this class to implement ASCII file transfers requiring conversion 030 * from NETASCII. 031 */ 032public final class FromNetASCIIInputStream extends PushbackInputStream { 033 034 static final boolean NO_CONVERSION_REQUIRED = System.lineSeparator().equals("\r\n"); 035 static final byte[] LINE_SEPARATOR_BYTES = System.lineSeparator().getBytes(StandardCharsets.US_ASCII); 036 037 /** 038 * 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 039 * whether or not you need to instantiate a FromNetASCIIInputStream object. 040 * 041 * @return True if the NETASCII line separator differs from the local system line separator, false if they are the same. 042 */ 043 public static boolean isConversionRequired() { 044 return !NO_CONVERSION_REQUIRED; 045 } 046 047 private int length; 048 049 /** 050 * Creates a FromNetASCIIInputStream instance that wraps an existing InputStream. 051 * 052 * @param input the stream to wrap 053 */ 054 public FromNetASCIIInputStream(final InputStream input) { 055 super(input, LINE_SEPARATOR_BYTES.length + 1); 056 } 057 058 // PushbackInputStream in JDK 1.1.3 returns the wrong thing 059 // TODO - can we delete this override now? 060 /** 061 * 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 062 * bytes. In other words, you really should not rely on the value returned by this method if you are trying to avoid blocking. 063 */ 064 @Override 065 public int available() throws IOException { 066 if (in == null) { 067 throw new IOException("Stream closed"); 068 } 069 return buf.length - pos + in.available(); 070 } 071 072 /** 073 * 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 074 * multiple reads from the underlying input stream in order to convert NETASCII line separators to the local line separator format. This is transparent to 075 * the programmer and is only mentioned for completeness. 076 * 077 * @return The next character in the stream. Returns -1 if the end of the stream has been reached. 078 * @throws IOException If an error occurs while reading the underlying stream. 079 */ 080 @Override 081 public int read() throws IOException { 082 if (NO_CONVERSION_REQUIRED) { 083 return super.read(); 084 } 085 return readInt(); 086 } 087 088 /** 089 * 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. 090 * 091 * @param buffer The byte array in which to store the data. 092 * @return The number of bytes read. Returns -1 if the end of the message has been reached. 093 * @throws IOException If an error occurs in reading the underlying stream. 094 */ 095 @Override 096 public int read(final byte buffer[]) throws IOException { 097 return read(buffer, 0, buffer.length); 098 } 099 100 /** 101 * 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. 102 * The characters are stored in the array starting from the given offset and up to the length specified. 103 * 104 * @param buffer The byte array in which to store the data. 105 * @param offset The offset into the array at which to start storing data. 106 * @param length The number of bytes to read. 107 * @return The number of bytes read. Returns -1 if the end of the stream has been reached. 108 * @throws IOException If an error occurs while reading the underlying stream. 109 */ 110 @Override 111 public int read(final byte buffer[], int offset, final int length) throws IOException { 112 if (NO_CONVERSION_REQUIRED) { 113 return super.read(buffer, offset, length); 114 } 115 if (length < 1) { 116 return 0; 117 } 118 int ch; 119 final int off; 120 ch = available(); 121 this.length = Math.min(length, ch); 122 // If nothing is available, block to read only one character 123 if (this.length < 1) { 124 this.length = 1; 125 } 126 if ((ch = readInt()) == -1) { 127 return NetConstants.EOS; 128 } 129 off = offset; 130 do { 131 buffer[offset++] = (byte) ch; 132 } while (--this.length > 0 && (ch = readInt()) != -1); 133 return offset - off; 134 } 135 136 private int readInt() throws IOException { 137 int ch; 138 ch = super.read(); 139 if (ch == '\r') { 140 ch = super.read(); 141 if (ch != '\n') { 142 if (ch != -1) { 143 unread(ch); 144 } 145 return '\r'; 146 } 147 unread(LINE_SEPARATOR_BYTES); 148 ch = super.read(); 149 // This is a kluge for read(byte[], ...) to read the right amount 150 --length; 151 } 152 return ch; 153 } 154 155}