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