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