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