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 18 package org.apache.commons.net.io; 19 20 import java.io.IOException; 21 import java.io.InputStream; 22 import java.io.PushbackInputStream; 23 import java.nio.charset.StandardCharsets; 24 25 import org.apache.commons.net.util.NetConstants; 26 27 /** 28 * This class wraps an input stream, replacing all occurrences of <CR><LF> (carriage return followed by a linefeed), which is the NETASCII standard 29 * for representing a newline, with the local line separator representation. You would use this class to implement ASCII file transfers requiring conversion 30 * from NETASCII. 31 */ 32 33 public final class FromNetASCIIInputStream extends PushbackInputStream { 34 static final boolean _noConversionRequired; 35 static final String _lineSeparator; 36 static final byte[] _lineSeparatorBytes; 37 38 static { 39 _lineSeparator = System.lineSeparator(); 40 _noConversionRequired = _lineSeparator.equals("\r\n"); 41 _lineSeparatorBytes = _lineSeparator.getBytes(StandardCharsets.US_ASCII); 42 } 43 44 /** 45 * 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 46 * whether or not you need to instantiate a FromNetASCIIInputStream object. 47 * 48 * @return True if the NETASCII line separator differs from the local system line separator, false if they are the same. 49 */ 50 public static boolean isConversionRequired() { 51 return !_noConversionRequired; 52 } 53 54 private int length; 55 56 /** 57 * Creates a FromNetASCIIInputStream instance that wraps an existing InputStream. 58 * 59 * @param input the stream to wrap 60 */ 61 public FromNetASCIIInputStream(final InputStream input) { 62 super(input, _lineSeparatorBytes.length + 1); 63 } 64 65 // PushbackInputStream in JDK 1.1.3 returns the wrong thing 66 // TODO - can we delete this override now? 67 /** 68 * 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 69 * bytes. In other words, you really should not rely on the value returned by this method if you are trying to avoid blocking. 70 */ 71 @Override 72 public int available() throws IOException { 73 if (in == null) { 74 throw new IOException("Stream closed"); 75 } 76 return buf.length - pos + in.available(); 77 } 78 79 /** 80 * 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 81 * multiple reads from the underlying input stream in order to convert NETASCII line separators to the local line separator format. This is transparent to 82 * the programmer and is only mentioned for completeness. 83 * 84 * @return The next character in the stream. Returns -1 if the end of the stream has been reached. 85 * @throws IOException If an error occurs while reading the underlying stream. 86 */ 87 @Override 88 public int read() throws IOException { 89 if (_noConversionRequired) { 90 return super.read(); 91 } 92 93 return readInt(); 94 } 95 96 /** 97 * 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. 98 * 99 * @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 }