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.io.UnsupportedEncodingException; 24 25 /*** 26 * This class wraps an input stream, replacing all occurrences 27 * of <CR><LF> (carriage return followed by a linefeed), 28 * which is the NETASCII standard for representing a newline, with the 29 * local line separator representation. You would use this class to 30 * implement ASCII file transfers requiring conversion from NETASCII. 31 * <p> 32 * <p> 33 ***/ 34 35 public final class FromNetASCIIInputStream extends PushbackInputStream 36 { 37 static final boolean _noConversionRequired; 38 static final String _lineSeparator; 39 static final byte[] _lineSeparatorBytes; 40 41 static { 42 _lineSeparator = System.getProperty("line.separator"); 43 _noConversionRequired = _lineSeparator.equals("\r\n"); 44 try { 45 _lineSeparatorBytes = _lineSeparator.getBytes("US-ASCII"); 46 } catch (UnsupportedEncodingException e) { 47 throw new RuntimeException("Broken JVM - cannot find US-ASCII charset!",e); 48 } 49 } 50 51 private int __length = 0; 52 53 /*** 54 * Returns true if the NetASCII line separator differs from the system 55 * line separator, false if they are the same. This method is useful 56 * to determine whether or not you need to instantiate a 57 * FromNetASCIIInputStream object. 58 * <p> 59 * @return True if the NETASCII line separator differs from the local 60 * system line separator, false if they are the same. 61 ***/ 62 public static final boolean isConversionRequired() 63 { 64 return !_noConversionRequired; 65 } 66 67 /*** 68 * Creates a FromNetASCIIInputStream instance that wraps an existing 69 * InputStream. 70 ***/ 71 public FromNetASCIIInputStream(InputStream input) 72 { 73 super(input, _lineSeparatorBytes.length + 1); 74 } 75 76 77 private int __read() throws IOException 78 { 79 int ch; 80 81 ch = super.read(); 82 83 if (ch == '\r') 84 { 85 ch = super.read(); 86 if (ch == '\n') 87 { 88 unread(_lineSeparatorBytes); 89 ch = super.read(); 90 // This is a kluge for read(byte[], ...) to read the right amount 91 --__length; 92 } 93 else 94 { 95 if (ch != -1) { 96 unread(ch); 97 } 98 return '\r'; 99 } 100 } 101 102 return ch; 103 } 104 105 106 /*** 107 * Reads and returns the next byte in the stream. If the end of the 108 * message has been reached, returns -1. Note that a call to this method 109 * may result in multiple reads from the underlying input stream in order 110 * to convert NETASCII line separators to the local line separator format. 111 * This is transparent to the programmer and is only mentioned for 112 * completeness. 113 * <p> 114 * @return The next character in the stream. Returns -1 if the end of the 115 * stream has been reached. 116 * @exception IOException If an error occurs while reading the underlying 117 * stream. 118 ***/ 119 @Override 120 public int read() throws IOException 121 { 122 if (_noConversionRequired) { 123 return super.read(); 124 } 125 126 return __read(); 127 } 128 129 130 /*** 131 * Reads the next number of bytes from the stream into an array and 132 * returns the number of bytes read. Returns -1 if the end of the 133 * stream has been reached. 134 * <p> 135 * @param buffer The byte array in which to store the data. 136 * @return The number of bytes read. Returns -1 if the 137 * end of the message has been reached. 138 * @exception IOException If an error occurs in reading the underlying 139 * stream. 140 ***/ 141 @Override 142 public int read(byte buffer[]) throws IOException 143 { 144 return read(buffer, 0, buffer.length); 145 } 146 147 148 /*** 149 * Reads the next number of bytes from the stream into an array and returns 150 * the number of bytes read. Returns -1 if the end of the 151 * message has been reached. The characters are stored in the array 152 * starting from the given offset and up to the length specified. 153 * <p> 154 * @param buffer The byte array in which to store the data. 155 * @param offset The offset into the array at which to start storing data. 156 * @param length The number of bytes to read. 157 * @return The number of bytes read. Returns -1 if the 158 * end of the stream has been reached. 159 * @exception IOException If an error occurs while reading the underlying 160 * stream. 161 ***/ 162 @Override 163 public int read(byte buffer[], int offset, int length) throws IOException 164 { 165 if (_noConversionRequired) { 166 return super.read(buffer, offset, length); 167 } 168 169 if (length < 1) { 170 return 0; 171 } 172 173 int ch, off; 174 175 ch = available(); 176 177 __length = (length > ch ? ch : length); 178 179 // If nothing is available, block to read only one character 180 if (__length < 1) { 181 __length = 1; 182 } 183 184 185 if ((ch = __read()) == -1) { 186 return -1; 187 } 188 189 off = offset; 190 191 do 192 { 193 buffer[offset++] = (byte)ch; 194 } 195 while (--__length > 0 && (ch = __read()) != -1); 196 197 198 return (offset - off); 199 } 200 201 202 // PushbackInputStream in JDK 1.1.3 returns the wrong thing 203 // TODO - can we delete this override now? 204 /*** 205 * Returns the number of bytes that can be read without blocking EXCEPT 206 * when newline conversions have to be made somewhere within the 207 * available block of bytes. In other words, you really should not 208 * rely on the value returned by this method if you are trying to avoid 209 * blocking. 210 ***/ 211 @Override 212 public int available() throws IOException 213 { 214 if (in == null) { 215 throw new IOException("Stream closed"); 216 } 217 return (buf.length - pos) + in.available(); 218 } 219 220 }