1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.util; |
7 | |
8 | import java.io.BufferedReader; |
9 | import java.io.BufferedWriter; |
10 | import java.io.ByteArrayInputStream; |
11 | import java.io.ByteArrayOutputStream; |
12 | import java.io.Closeable; |
13 | import java.io.EOFException; |
14 | import java.io.IOException; |
15 | import java.io.InputStream; |
16 | import java.io.InputStreamReader; |
17 | import java.io.OutputStream; |
18 | import java.io.OutputStreamWriter; |
19 | import java.io.Reader; |
20 | import java.io.StringWriter; |
21 | import java.io.Writer; |
22 | import org.h2.engine.Constants; |
23 | import org.h2.engine.SysProperties; |
24 | import org.h2.message.DbException; |
25 | import org.h2.store.fs.FileUtils; |
26 | |
27 | /** |
28 | * This utility class contains input/output functions. |
29 | */ |
30 | public class IOUtils { |
31 | |
32 | private IOUtils() { |
33 | // utility class |
34 | } |
35 | |
36 | /** |
37 | * Close an output stream without throwing an exception. |
38 | * |
39 | * @param out the output stream or null |
40 | */ |
41 | public static void closeSilently(Closeable out) { |
42 | if (out != null) { |
43 | try { |
44 | trace("closeSilently", null, out); |
45 | out.close(); |
46 | } catch (Exception e) { |
47 | // ignore |
48 | } |
49 | } |
50 | } |
51 | |
52 | /** |
53 | * Skip a number of bytes in an input stream. |
54 | * |
55 | * @param in the input stream |
56 | * @param skip the number of bytes to skip |
57 | * @throws EOFException if the end of file has been reached before all bytes |
58 | * could be skipped |
59 | * @throws IOException if an IO exception occurred while skipping |
60 | */ |
61 | public static void skipFully(InputStream in, long skip) throws IOException { |
62 | try { |
63 | while (skip > 0) { |
64 | long skipped = in.skip(skip); |
65 | if (skipped <= 0) { |
66 | throw new EOFException(); |
67 | } |
68 | skip -= skipped; |
69 | } |
70 | } catch (Exception e) { |
71 | throw DbException.convertToIOException(e); |
72 | } |
73 | } |
74 | |
75 | /** |
76 | * Skip a number of characters in a reader. |
77 | * |
78 | * @param reader the reader |
79 | * @param skip the number of characters to skip |
80 | * @throws EOFException if the end of file has been reached before all |
81 | * characters could be skipped |
82 | * @throws IOException if an IO exception occurred while skipping |
83 | */ |
84 | public static void skipFully(Reader reader, long skip) throws IOException { |
85 | try { |
86 | while (skip > 0) { |
87 | long skipped = reader.skip(skip); |
88 | if (skipped <= 0) { |
89 | throw new EOFException(); |
90 | } |
91 | skip -= skipped; |
92 | } |
93 | } catch (Exception e) { |
94 | throw DbException.convertToIOException(e); |
95 | } |
96 | } |
97 | |
98 | /** |
99 | * Copy all data from the input stream to the output stream and close both |
100 | * streams. Exceptions while closing are ignored. |
101 | * |
102 | * @param in the input stream |
103 | * @param out the output stream |
104 | * @return the number of bytes copied |
105 | */ |
106 | public static long copyAndClose(InputStream in, OutputStream out) |
107 | throws IOException { |
108 | try { |
109 | long len = copyAndCloseInput(in, out); |
110 | out.close(); |
111 | return len; |
112 | } catch (Exception e) { |
113 | throw DbException.convertToIOException(e); |
114 | } finally { |
115 | closeSilently(out); |
116 | } |
117 | } |
118 | |
119 | /** |
120 | * Copy all data from the input stream to the output stream and close the |
121 | * input stream. Exceptions while closing are ignored. |
122 | * |
123 | * @param in the input stream |
124 | * @param out the output stream (null if writing is not required) |
125 | * @return the number of bytes copied |
126 | */ |
127 | public static long copyAndCloseInput(InputStream in, OutputStream out) |
128 | throws IOException { |
129 | try { |
130 | return copy(in, out); |
131 | } catch (Exception e) { |
132 | throw DbException.convertToIOException(e); |
133 | } finally { |
134 | closeSilently(in); |
135 | } |
136 | } |
137 | |
138 | /** |
139 | * Copy all data from the input stream to the output stream. Both streams |
140 | * are kept open. |
141 | * |
142 | * @param in the input stream |
143 | * @param out the output stream (null if writing is not required) |
144 | * @return the number of bytes copied |
145 | */ |
146 | public static long copy(InputStream in, OutputStream out) |
147 | throws IOException { |
148 | return copy(in, out, Long.MAX_VALUE); |
149 | } |
150 | |
151 | /** |
152 | * Copy all data from the input stream to the output stream. Both streams |
153 | * are kept open. |
154 | * |
155 | * @param in the input stream |
156 | * @param out the output stream (null if writing is not required) |
157 | * @param length the maximum number of bytes to copy |
158 | * @return the number of bytes copied |
159 | */ |
160 | public static long copy(InputStream in, OutputStream out, long length) |
161 | throws IOException { |
162 | try { |
163 | long copied = 0; |
164 | int len = (int) Math.min(length, Constants.IO_BUFFER_SIZE); |
165 | byte[] buffer = new byte[len]; |
166 | while (length > 0) { |
167 | len = in.read(buffer, 0, len); |
168 | if (len < 0) { |
169 | break; |
170 | } |
171 | if (out != null) { |
172 | out.write(buffer, 0, len); |
173 | } |
174 | copied += len; |
175 | length -= len; |
176 | len = (int) Math.min(length, Constants.IO_BUFFER_SIZE); |
177 | } |
178 | return copied; |
179 | } catch (Exception e) { |
180 | throw DbException.convertToIOException(e); |
181 | } |
182 | } |
183 | |
184 | /** |
185 | * Copy all data from the reader to the writer and close the reader. |
186 | * Exceptions while closing are ignored. |
187 | * |
188 | * @param in the reader |
189 | * @param out the writer (null if writing is not required) |
190 | * @param length the maximum number of bytes to copy |
191 | * @return the number of characters copied |
192 | */ |
193 | public static long copyAndCloseInput(Reader in, Writer out, long length) |
194 | throws IOException { |
195 | try { |
196 | long copied = 0; |
197 | int len = (int) Math.min(length, Constants.IO_BUFFER_SIZE); |
198 | char[] buffer = new char[len]; |
199 | while (length > 0) { |
200 | len = in.read(buffer, 0, len); |
201 | if (len < 0) { |
202 | break; |
203 | } |
204 | if (out != null) { |
205 | out.write(buffer, 0, len); |
206 | } |
207 | length -= len; |
208 | len = (int) Math.min(length, Constants.IO_BUFFER_SIZE); |
209 | copied += len; |
210 | } |
211 | return copied; |
212 | } catch (Exception e) { |
213 | throw DbException.convertToIOException(e); |
214 | } finally { |
215 | in.close(); |
216 | } |
217 | } |
218 | |
219 | /** |
220 | * Close an input stream without throwing an exception. |
221 | * |
222 | * @param in the input stream or null |
223 | */ |
224 | public static void closeSilently(InputStream in) { |
225 | if (in != null) { |
226 | try { |
227 | trace("closeSilently", null, in); |
228 | in.close(); |
229 | } catch (Exception e) { |
230 | // ignore |
231 | } |
232 | } |
233 | } |
234 | |
235 | /** |
236 | * Close a reader without throwing an exception. |
237 | * |
238 | * @param reader the reader or null |
239 | */ |
240 | public static void closeSilently(Reader reader) { |
241 | if (reader != null) { |
242 | try { |
243 | reader.close(); |
244 | } catch (Exception e) { |
245 | // ignore |
246 | } |
247 | } |
248 | } |
249 | |
250 | /** |
251 | * Close a writer without throwing an exception. |
252 | * |
253 | * @param writer the writer or null |
254 | */ |
255 | public static void closeSilently(Writer writer) { |
256 | if (writer != null) { |
257 | try { |
258 | writer.flush(); |
259 | writer.close(); |
260 | } catch (Exception e) { |
261 | // ignore |
262 | } |
263 | } |
264 | } |
265 | |
266 | /** |
267 | * Read a number of bytes from an input stream and close the stream. |
268 | * |
269 | * @param in the input stream |
270 | * @param length the maximum number of bytes to read, or -1 to read until |
271 | * the end of file |
272 | * @return the bytes read |
273 | */ |
274 | public static byte[] readBytesAndClose(InputStream in, int length) |
275 | throws IOException { |
276 | try { |
277 | if (length <= 0) { |
278 | length = Integer.MAX_VALUE; |
279 | } |
280 | int block = Math.min(Constants.IO_BUFFER_SIZE, length); |
281 | ByteArrayOutputStream out = new ByteArrayOutputStream(block); |
282 | copy(in, out, length); |
283 | return out.toByteArray(); |
284 | } catch (Exception e) { |
285 | throw DbException.convertToIOException(e); |
286 | } finally { |
287 | in.close(); |
288 | } |
289 | } |
290 | |
291 | /** |
292 | * Read a number of characters from a reader and close it. |
293 | * |
294 | * @param in the reader |
295 | * @param length the maximum number of characters to read, or -1 to read |
296 | * until the end of file |
297 | * @return the string read |
298 | */ |
299 | public static String readStringAndClose(Reader in, int length) |
300 | throws IOException { |
301 | try { |
302 | if (length <= 0) { |
303 | length = Integer.MAX_VALUE; |
304 | } |
305 | int block = Math.min(Constants.IO_BUFFER_SIZE, length); |
306 | StringWriter out = new StringWriter(block); |
307 | copyAndCloseInput(in, out, length); |
308 | return out.toString(); |
309 | } finally { |
310 | in.close(); |
311 | } |
312 | } |
313 | |
314 | /** |
315 | * Try to read the given number of bytes to the buffer. This method reads |
316 | * until the maximum number of bytes have been read or until the end of |
317 | * file. |
318 | * |
319 | * @param in the input stream |
320 | * @param buffer the output buffer |
321 | * @param max the number of bytes to read at most |
322 | * @return the number of bytes read, 0 meaning EOF |
323 | */ |
324 | public static int readFully(InputStream in, byte[] buffer, int max) |
325 | throws IOException { |
326 | try { |
327 | int result = 0, len = Math.min(max, buffer.length); |
328 | while (len > 0) { |
329 | int l = in.read(buffer, result, len); |
330 | if (l < 0) { |
331 | break; |
332 | } |
333 | result += l; |
334 | len -= l; |
335 | } |
336 | return result; |
337 | } catch (Exception e) { |
338 | throw DbException.convertToIOException(e); |
339 | } |
340 | } |
341 | |
342 | /** |
343 | * Try to read the given number of characters to the buffer. This method |
344 | * reads until the maximum number of characters have been read or until the |
345 | * end of file. |
346 | * |
347 | * @param in the reader |
348 | * @param buffer the output buffer |
349 | * @param max the number of characters to read at most |
350 | * @return the number of characters read, 0 meaning EOF |
351 | */ |
352 | public static int readFully(Reader in, char[] buffer, int max) |
353 | throws IOException { |
354 | try { |
355 | int result = 0, len = Math.min(max, buffer.length); |
356 | while (len > 0) { |
357 | int l = in.read(buffer, result, len); |
358 | if (l < 0) { |
359 | break; |
360 | } |
361 | result += l; |
362 | len -= l; |
363 | } |
364 | return result; |
365 | } catch (Exception e) { |
366 | throw DbException.convertToIOException(e); |
367 | } |
368 | } |
369 | |
370 | /** |
371 | * Create a buffered reader to read from an input stream using the UTF-8 |
372 | * format. If the input stream is null, this method returns null. The |
373 | * InputStreamReader that is used here is not exact, that means it may read |
374 | * some additional bytes when buffering. |
375 | * |
376 | * @param in the input stream or null |
377 | * @return the reader |
378 | */ |
379 | public static Reader getBufferedReader(InputStream in) { |
380 | return in == null ? null : new BufferedReader( |
381 | new InputStreamReader(in, Constants.UTF8)); |
382 | } |
383 | |
384 | /** |
385 | * Create a reader to read from an input stream using the UTF-8 format. If |
386 | * the input stream is null, this method returns null. The InputStreamReader |
387 | * that is used here is not exact, that means it may read some additional |
388 | * bytes when buffering. |
389 | * |
390 | * @param in the input stream or null |
391 | * @return the reader |
392 | */ |
393 | public static Reader getReader(InputStream in) { |
394 | // InputStreamReader may read some more bytes |
395 | return in == null ? null : new BufferedReader( |
396 | new InputStreamReader(in, Constants.UTF8)); |
397 | } |
398 | |
399 | /** |
400 | * Create a buffered writer to write to an output stream using the UTF-8 |
401 | * format. If the output stream is null, this method returns null. |
402 | * |
403 | * @param out the output stream or null |
404 | * @return the writer |
405 | */ |
406 | public static Writer getBufferedWriter(OutputStream out) { |
407 | return out == null ? null : new BufferedWriter( |
408 | new OutputStreamWriter(out, Constants.UTF8)); |
409 | } |
410 | |
411 | /** |
412 | * Wrap an input stream in a reader. The bytes are converted to characters |
413 | * using the US-ASCII character set. |
414 | * |
415 | * @param in the input stream |
416 | * @return the reader |
417 | */ |
418 | public static Reader getAsciiReader(InputStream in) { |
419 | try { |
420 | return in == null ? null : new InputStreamReader(in, "US-ASCII"); |
421 | } catch (Exception e) { |
422 | // UnsupportedEncodingException |
423 | throw DbException.convert(e); |
424 | } |
425 | } |
426 | |
427 | /** |
428 | * Trace input or output operations if enabled. |
429 | * |
430 | * @param method the method from where this method was called |
431 | * @param fileName the file name |
432 | * @param o the object to append to the message |
433 | */ |
434 | public static void trace(String method, String fileName, Object o) { |
435 | if (SysProperties.TRACE_IO) { |
436 | System.out.println("IOUtils." + method + " " + fileName + " " + o); |
437 | } |
438 | } |
439 | |
440 | /** |
441 | * Create an input stream to read from a string. The string is converted to |
442 | * a byte array using UTF-8 encoding. |
443 | * If the string is null, this method returns null. |
444 | * |
445 | * @param s the string |
446 | * @return the input stream |
447 | */ |
448 | public static InputStream getInputStreamFromString(String s) { |
449 | if (s == null) { |
450 | return null; |
451 | } |
452 | return new ByteArrayInputStream(s.getBytes(Constants.UTF8)); |
453 | } |
454 | |
455 | /** |
456 | * Copy a file from one directory to another, or to another file. |
457 | * |
458 | * @param original the original file name |
459 | * @param copy the file name of the copy |
460 | */ |
461 | public static void copyFiles(String original, String copy) throws IOException { |
462 | InputStream in = FileUtils.newInputStream(original); |
463 | OutputStream out = FileUtils.newOutputStream(copy, false); |
464 | copyAndClose(in, out); |
465 | } |
466 | |
467 | } |