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 * <p> 032 * <p> 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 * <p> 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 ***/ 071 public FromNetASCIIInputStream(InputStream input) 072 { 073 super(input, _lineSeparatorBytes.length + 1); 074 } 075 076 077 private int __read() throws IOException 078 { 079 int ch; 080 081 ch = super.read(); 082 083 if (ch == '\r') 084 { 085 ch = super.read(); 086 if (ch == '\n') 087 { 088 unread(_lineSeparatorBytes); 089 ch = super.read(); 090 // This is a kluge for read(byte[], ...) to read the right amount 091 --__length; 092 } 093 else 094 { 095 if (ch != -1) { 096 unread(ch); 097 } 098 return '\r'; 099 } 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}