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