1 /* |
|
2 * Copyright (c) 2002-2016, the original author or authors. |
|
3 * |
|
4 * This software is distributable under the BSD license. See the terms of the |
|
5 * BSD license in the documentation provided with this software. |
|
6 * |
|
7 * http://www.opensource.org/licenses/bsd-license.php |
|
8 */ |
|
9 package jdk.internal.jline.internal; |
|
10 |
|
11 import java.io.IOException; |
|
12 import java.io.InputStream; |
|
13 import java.io.OutputStreamWriter; |
|
14 import java.io.Reader; |
|
15 import java.io.UnsupportedEncodingException; |
|
16 import java.nio.ByteBuffer; |
|
17 import java.nio.CharBuffer; |
|
18 import java.nio.charset.Charset; |
|
19 import java.nio.charset.CharsetDecoder; |
|
20 import java.nio.charset.CoderResult; |
|
21 import java.nio.charset.CodingErrorAction; |
|
22 import java.nio.charset.MalformedInputException; |
|
23 import java.nio.charset.UnmappableCharacterException; |
|
24 |
|
25 |
|
26 /** |
|
27 * |
|
28 * NOTE for JLine: the default InputStreamReader that comes from the JRE |
|
29 * usually read more bytes than needed from the input stream, which |
|
30 * is not usable in a character per character model used in the console. |
|
31 * We thus use the harmony code which only reads the minimal number of bytes, |
|
32 * with a modification to ensure we can read larger characters (UTF-16 has |
|
33 * up to 4 bytes, and UTF-32, rare as it is, may have up to 8). |
|
34 */ |
|
35 /** |
|
36 * A class for turning a byte stream into a character stream. Data read from the |
|
37 * source input stream is converted into characters by either a default or a |
|
38 * provided character converter. The default encoding is taken from the |
|
39 * "file.encoding" system property. {@code InputStreamReader} contains a buffer |
|
40 * of bytes read from the source stream and converts these into characters as |
|
41 * needed. The buffer size is 8K. |
|
42 * |
|
43 * @see OutputStreamWriter |
|
44 */ |
|
45 public class InputStreamReader extends Reader { |
|
46 private InputStream in; |
|
47 |
|
48 private static final int BUFFER_SIZE = 8192; |
|
49 |
|
50 private boolean endOfInput = false; |
|
51 |
|
52 CharsetDecoder decoder; |
|
53 |
|
54 ByteBuffer bytes = ByteBuffer.allocate(BUFFER_SIZE); |
|
55 |
|
56 /** |
|
57 * Constructs a new {@code InputStreamReader} on the {@link InputStream} |
|
58 * {@code in}. This constructor sets the character converter to the encoding |
|
59 * specified in the "file.encoding" property and falls back to ISO 8859_1 |
|
60 * (ISO-Latin-1) if the property doesn't exist. |
|
61 * |
|
62 * @param in |
|
63 * the input stream from which to read characters. |
|
64 */ |
|
65 public InputStreamReader(InputStream in) { |
|
66 super(in); |
|
67 this.in = in; |
|
68 decoder = Charset.defaultCharset().newDecoder().onMalformedInput( |
|
69 CodingErrorAction.REPLACE).onUnmappableCharacter( |
|
70 CodingErrorAction.REPLACE); |
|
71 bytes.limit(0); |
|
72 } |
|
73 |
|
74 /** |
|
75 * Constructs a new InputStreamReader on the InputStream {@code in}. The |
|
76 * character converter that is used to decode bytes into characters is |
|
77 * identified by name by {@code enc}. If the encoding cannot be found, an |
|
78 * UnsupportedEncodingException error is thrown. |
|
79 * |
|
80 * @param in |
|
81 * the InputStream from which to read characters. |
|
82 * @param enc |
|
83 * identifies the character converter to use. |
|
84 * @throws NullPointerException |
|
85 * if {@code enc} is {@code null}. |
|
86 * @throws UnsupportedEncodingException |
|
87 * if the encoding specified by {@code enc} cannot be found. |
|
88 */ |
|
89 public InputStreamReader(InputStream in, final String enc) |
|
90 throws UnsupportedEncodingException { |
|
91 super(in); |
|
92 if (enc == null) { |
|
93 throw new NullPointerException(); |
|
94 } |
|
95 this.in = in; |
|
96 try { |
|
97 decoder = Charset.forName(enc).newDecoder().onMalformedInput( |
|
98 CodingErrorAction.REPLACE).onUnmappableCharacter( |
|
99 CodingErrorAction.REPLACE); |
|
100 } catch (IllegalArgumentException e) { |
|
101 throw (UnsupportedEncodingException) |
|
102 new UnsupportedEncodingException(enc).initCause(e); |
|
103 } |
|
104 bytes.limit(0); |
|
105 } |
|
106 |
|
107 /** |
|
108 * Constructs a new InputStreamReader on the InputStream {@code in} and |
|
109 * CharsetDecoder {@code dec}. |
|
110 * |
|
111 * @param in |
|
112 * the source InputStream from which to read characters. |
|
113 * @param dec |
|
114 * the CharsetDecoder used by the character conversion. |
|
115 */ |
|
116 public InputStreamReader(InputStream in, CharsetDecoder dec) { |
|
117 super(in); |
|
118 dec.averageCharsPerByte(); |
|
119 this.in = in; |
|
120 decoder = dec; |
|
121 bytes.limit(0); |
|
122 } |
|
123 |
|
124 /** |
|
125 * Constructs a new InputStreamReader on the InputStream {@code in} and |
|
126 * Charset {@code charset}. |
|
127 * |
|
128 * @param in |
|
129 * the source InputStream from which to read characters. |
|
130 * @param charset |
|
131 * the Charset that defines the character converter |
|
132 */ |
|
133 public InputStreamReader(InputStream in, Charset charset) { |
|
134 super(in); |
|
135 this.in = in; |
|
136 decoder = charset.newDecoder().onMalformedInput( |
|
137 CodingErrorAction.REPLACE).onUnmappableCharacter( |
|
138 CodingErrorAction.REPLACE); |
|
139 bytes.limit(0); |
|
140 } |
|
141 |
|
142 /** |
|
143 * Closes this reader. This implementation closes the source InputStream and |
|
144 * releases all local storage. |
|
145 * |
|
146 * @throws IOException |
|
147 * if an error occurs attempting to close this reader. |
|
148 */ |
|
149 @Override |
|
150 public void close() throws IOException { |
|
151 synchronized (lock) { |
|
152 decoder = null; |
|
153 if (in != null) { |
|
154 in.close(); |
|
155 in = null; |
|
156 } |
|
157 } |
|
158 } |
|
159 |
|
160 /** |
|
161 * Returns the name of the encoding used to convert bytes into characters. |
|
162 * The value {@code null} is returned if this reader has been closed. |
|
163 * |
|
164 * @return the name of the character converter or {@code null} if this |
|
165 * reader is closed. |
|
166 */ |
|
167 public String getEncoding() { |
|
168 if (!isOpen()) { |
|
169 return null; |
|
170 } |
|
171 return decoder.charset().name(); |
|
172 } |
|
173 |
|
174 /** |
|
175 * Reads a single character from this reader and returns it as an integer |
|
176 * with the two higher-order bytes set to 0. Returns -1 if the end of the |
|
177 * reader has been reached. The byte value is either obtained from |
|
178 * converting bytes in this reader's buffer or by first filling the buffer |
|
179 * from the source InputStream and then reading from the buffer. |
|
180 * |
|
181 * @return the character read or -1 if the end of the reader has been |
|
182 * reached. |
|
183 * @throws IOException |
|
184 * if this reader is closed or some other I/O error occurs. |
|
185 */ |
|
186 @Override |
|
187 public int read() throws IOException { |
|
188 synchronized (lock) { |
|
189 if (!isOpen()) { |
|
190 throw new IOException("InputStreamReader is closed."); |
|
191 } |
|
192 |
|
193 char buf[] = new char[4]; |
|
194 return read(buf, 0, 4) != -1 ? Character.codePointAt(buf, 0) : -1; |
|
195 } |
|
196 } |
|
197 |
|
198 /** |
|
199 * Reads at most {@code length} characters from this reader and stores them |
|
200 * at position {@code offset} in the character array {@code buf}. Returns |
|
201 * the number of characters actually read or -1 if the end of the reader has |
|
202 * been reached. The bytes are either obtained from converting bytes in this |
|
203 * reader's buffer or by first filling the buffer from the source |
|
204 * InputStream and then reading from the buffer. |
|
205 * |
|
206 * @param buf |
|
207 * the array to store the characters read. |
|
208 * @param offset |
|
209 * the initial position in {@code buf} to store the characters |
|
210 * read from this reader. |
|
211 * @param length |
|
212 * the maximum number of characters to read. |
|
213 * @return the number of characters read or -1 if the end of the reader has |
|
214 * been reached. |
|
215 * @throws IndexOutOfBoundsException |
|
216 * if {@code offset < 0} or {@code length < 0}, or if |
|
217 * {@code offset + length} is greater than the length of |
|
218 * {@code buf}. |
|
219 * @throws IOException |
|
220 * if this reader is closed or some other I/O error occurs. |
|
221 */ |
|
222 @Override |
|
223 public int read(char[] buf, int offset, int length) throws IOException { |
|
224 synchronized (lock) { |
|
225 if (!isOpen()) { |
|
226 throw new IOException("InputStreamReader is closed."); |
|
227 } |
|
228 if (offset < 0 || offset > buf.length - length || length < 0) { |
|
229 throw new IndexOutOfBoundsException(); |
|
230 } |
|
231 if (length == 0) { |
|
232 return 0; |
|
233 } |
|
234 |
|
235 CharBuffer out = CharBuffer.wrap(buf, offset, length); |
|
236 CoderResult result = CoderResult.UNDERFLOW; |
|
237 |
|
238 // bytes.remaining() indicates number of bytes in buffer |
|
239 // when 1-st time entered, it'll be equal to zero |
|
240 boolean needInput = !bytes.hasRemaining(); |
|
241 |
|
242 while (out.hasRemaining()) { |
|
243 // fill the buffer if needed |
|
244 if (needInput) { |
|
245 try { |
|
246 if ((in.available() == 0) |
|
247 && (out.position() > offset)) { |
|
248 // we could return the result without blocking read |
|
249 break; |
|
250 } |
|
251 } catch (IOException e) { |
|
252 // available didn't work so just try the read |
|
253 } |
|
254 |
|
255 int to_read = bytes.capacity() - bytes.limit(); |
|
256 int off = bytes.arrayOffset() + bytes.limit(); |
|
257 int was_red = in.read(bytes.array(), off, to_read); |
|
258 |
|
259 if (was_red == -1) { |
|
260 endOfInput = true; |
|
261 break; |
|
262 } else if (was_red == 0) { |
|
263 break; |
|
264 } |
|
265 bytes.limit(bytes.limit() + was_red); |
|
266 needInput = false; |
|
267 } |
|
268 |
|
269 // decode bytes |
|
270 result = decoder.decode(bytes, out, false); |
|
271 |
|
272 if (result.isUnderflow()) { |
|
273 // compact the buffer if no space left |
|
274 if (bytes.limit() == bytes.capacity()) { |
|
275 bytes.compact(); |
|
276 bytes.limit(bytes.position()); |
|
277 bytes.position(0); |
|
278 } |
|
279 needInput = true; |
|
280 } else { |
|
281 break; |
|
282 } |
|
283 } |
|
284 |
|
285 if (result == CoderResult.UNDERFLOW && endOfInput) { |
|
286 result = decoder.decode(bytes, out, true); |
|
287 decoder.flush(out); |
|
288 decoder.reset(); |
|
289 } |
|
290 if (result.isMalformed()) { |
|
291 throw new MalformedInputException(result.length()); |
|
292 } else if (result.isUnmappable()) { |
|
293 throw new UnmappableCharacterException(result.length()); |
|
294 } |
|
295 |
|
296 return out.position() - offset == 0 ? -1 : out.position() - offset; |
|
297 } |
|
298 } |
|
299 |
|
300 /* |
|
301 * Answer a boolean indicating whether or not this InputStreamReader is |
|
302 * open. |
|
303 */ |
|
304 private boolean isOpen() { |
|
305 return in != null; |
|
306 } |
|
307 |
|
308 /** |
|
309 * Indicates whether this reader is ready to be read without blocking. If |
|
310 * the result is {@code true}, the next {@code read()} will not block. If |
|
311 * the result is {@code false} then this reader may or may not block when |
|
312 * {@code read()} is called. This implementation returns {@code true} if |
|
313 * there are bytes available in the buffer or the source stream has bytes |
|
314 * available. |
|
315 * |
|
316 * @return {@code true} if the receiver will not block when {@code read()} |
|
317 * is called, {@code false} if unknown or blocking will occur. |
|
318 * @throws IOException |
|
319 * if this reader is closed or some other I/O error occurs. |
|
320 */ |
|
321 @Override |
|
322 public boolean ready() throws IOException { |
|
323 synchronized (lock) { |
|
324 if (in == null) { |
|
325 throw new IOException("InputStreamReader is closed."); |
|
326 } |
|
327 try { |
|
328 return bytes.hasRemaining() || in.available() > 0; |
|
329 } catch (IOException e) { |
|
330 return false; |
|
331 } |
|
332 } |
|
333 } |
|
334 } |
|