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.io.UnsupportedEncodingException; 024 025/*** 026 * This class wraps an input stream, replacing all occurrences 027 * of <CR><LF> (carriage return followed by a linefeed), 028 * which is the NETASCII standard for representing a newline, with the 029 * local line separator representation. You would use this class to 030 * implement ASCII file transfers requiring conversion from NETASCII. 031 * 032 * 033 ***/ 034 035public final class FromNetASCIIInputStream extends PushbackInputStream 036{ 037 static final boolean _noConversionRequired; 038 static final String _lineSeparator; 039 static final byte[] _lineSeparatorBytes; 040 041 static { 042 _lineSeparator = System.getProperty("line.separator"); 043 _noConversionRequired = _lineSeparator.equals("\r\n"); 044 try { 045 _lineSeparatorBytes = _lineSeparator.getBytes("US-ASCII"); 046 } catch (UnsupportedEncodingException e) { 047 throw new RuntimeException("Broken JVM - cannot find US-ASCII charset!",e); 048 } 049 } 050 051 private int __length = 0; 052 053 /*** 054 * Returns true if the NetASCII line separator differs from the system 055 * line separator, false if they are the same. This method is useful 056 * to determine whether or not you need to instantiate a 057 * FromNetASCIIInputStream object. 058 * 059 * @return True if the NETASCII line separator differs from the local 060 * system line separator, false if they are the same. 061 ***/ 062 public static final boolean isConversionRequired() 063 { 064 return !_noConversionRequired; 065 } 066 067 /*** 068 * Creates a FromNetASCIIInputStream instance that wraps an existing 069 * InputStream. 070 * @param input the stream to wrap 071 ***/ 072 public FromNetASCIIInputStream(InputStream input) 073 { 074 super(input, _lineSeparatorBytes.length + 1); 075 } 076 077 078 private int __read() throws IOException 079 { 080 int ch; 081 082 ch = super.read(); 083 084 if (ch == '\r') 085 { 086 ch = super.read(); 087 if (ch == '\n') 088 { 089 unread(_lineSeparatorBytes); 090 ch = super.read(); 091 // This is a kluge for read(byte[], ...) to read the right amount 092 --__length; 093 } 094 else 095 { 096 if (ch != -1) { 097 unread(ch); 098 } 099 return '\r'; 100 } 101 } 102 103 return ch; 104 } 105 106 107 /*** 108 * Reads and returns the next byte in the stream. If the end of the 109 * message has been reached, returns -1. Note that a call to this method 110 * may result in multiple reads from the underlying input stream in order 111 * to convert NETASCII line separators to the local line separator format. 112 * This is transparent to the programmer and is only mentioned for 113 * completeness. 114 * 115 * @return The next character in the stream. Returns -1 if the end of the 116 * stream has been reached. 117 * @exception IOException If an error occurs while reading the underlying 118 * stream. 119 ***/ 120 @Override 121 public int read() throws IOException 122 { 123 if (_noConversionRequired) { 124 return super.read(); 125 } 126 127 return __read(); 128 } 129 130 131 /*** 132 * Reads the next number of bytes from the stream into an array and 133 * returns the number of bytes read. Returns -1 if the end of the 134 * stream has been reached. 135 * 136 * @param buffer The byte array in which to store the data. 137 * @return The number of bytes read. Returns -1 if the 138 * end of the message has been reached. 139 * @exception IOException If an error occurs in reading the underlying 140 * stream. 141 ***/ 142 @Override 143 public int read(byte buffer[]) throws IOException 144 { 145 return read(buffer, 0, buffer.length); 146 } 147 148 149 /*** 150 * Reads the next number of bytes from the stream into an array and returns 151 * the number of bytes read. Returns -1 if the end of the 152 * message has been reached. The characters are stored in the array 153 * starting from the given offset and up to the length specified. 154 * 155 * @param buffer The byte array in which to store the data. 156 * @param offset The offset into the array at which to start storing data. 157 * @param length The number of bytes to read. 158 * @return The number of bytes read. Returns -1 if the 159 * end of the stream has been reached. 160 * @exception IOException If an error occurs while reading the underlying 161 * stream. 162 ***/ 163 @Override 164 public int read(byte buffer[], int offset, int length) throws IOException 165 { 166 if (_noConversionRequired) { 167 return super.read(buffer, offset, length); 168 } 169 170 if (length < 1) { 171 return 0; 172 } 173 174 int ch, off; 175 176 ch = available(); 177 178 __length = (length > ch ? ch : length); 179 180 // If nothing is available, block to read only one character 181 if (__length < 1) { 182 __length = 1; 183 } 184 185 186 if ((ch = __read()) == -1) { 187 return -1; 188 } 189 190 off = offset; 191 192 do 193 { 194 buffer[offset++] = (byte)ch; 195 } 196 while (--__length > 0 && (ch = __read()) != -1); 197 198 199 return (offset - off); 200 } 201 202 203 // PushbackInputStream in JDK 1.1.3 returns the wrong thing 204 // TODO - can we delete this override now? 205 /*** 206 * Returns the number of bytes that can be read without blocking EXCEPT 207 * when newline conversions have to be made somewhere within the 208 * available block of bytes. In other words, you really should not 209 * rely on the value returned by this method if you are trying to avoid 210 * blocking. 211 ***/ 212 @Override 213 public int available() throws IOException 214 { 215 if (in == null) { 216 throw new IOException("Stream closed"); 217 } 218 return (buf.length - pos) + in.available(); 219 } 220 221}