1 package org.apache.maven.shared.utils.io;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import javax.annotation.Nonnull;
23 import javax.annotation.Nullable;
24
25 import java.io.BufferedInputStream;
26 import java.io.ByteArrayInputStream;
27 import java.io.ByteArrayOutputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.InputStreamReader;
31 import java.io.OutputStream;
32 import java.io.OutputStreamWriter;
33 import java.io.Reader;
34 import java.io.StringReader;
35 import java.io.StringWriter;
36 import java.io.Writer;
37 import java.nio.channels.Channel;
38
39 /**
40 * General IO Stream manipulation.
41 * <p>
42 * This class provides static utility methods for input/output operations, particularly buffered
43 * copying between sources (<code>InputStream</code>, <code>Reader</code>, <code>String</code> and
44 * <code>byte[]</code>) and destinations (<code>OutputStream</code>, <code>Writer</code>,
45 * <code>String</code> and <code>byte[]</code>).
46 * </p>
47 * <p/>
48 * <p>Unless otherwise noted, these <code>copy</code> methods do <em>not</em> flush or close the
49 * streams. Often, doing so would require making non-portable assumptions about the streams' origin
50 * and further use. This means that both streams' <code>close()</code> methods must be called after
51 * copying. if one omits this step, then the stream resources (sockets, file descriptors) are
52 * released when the associated Stream is garbage-collected. It is not a good idea to rely on this
53 * mechanism. For a good overview of the distinction between "memory management" and "resource
54 * management", see <a href="http://www.unixreview.com/articles/1998/9804/9804ja/ja.htm">this
55 * UnixReview article</a></p>
56 * <p/>
57 * <p>For each <code>copy</code> method, a variant is provided that allows the caller to specify the
58 * buffer size (the default is 4k). As the buffer size can have a fairly large impact on speed, this
59 * may be worth tweaking. Often "large buffer -> faster" does not hold, even for large data
60 * transfers.</p>
61 * <p/>
62 * <p>For byte-to-char methods, a <code>copy</code> variant allows the encoding to be selected
63 * (otherwise the platform default is used).</p>
64 * <p/>
65 * <p>The <code>copy</code> methods use an internal buffer when copying. It is therefore advisable
66 * <em>not</em> to deliberately wrap the stream arguments to the <code>copy</code> methods in
67 * <code>Buffered*</code> streams. For example, don't do the
68 * following:</p>
69 * <p/>
70 * <code>copy( new BufferedInputStream( in ), new BufferedOutputStream( out ) );</code>
71 * <p/>
72 * <p>The rationale is as follows:</p>
73 * <p/>
74 * <p>Imagine that an InputStream's read() is a very expensive operation, which would usually suggest
75 * wrapping in a BufferedInputStream. The BufferedInputStream works by issuing infrequent
76 * {@link java.io.InputStream#read(byte[] b, int off, int len)} requests on the underlying InputStream, to
77 * fill an internal buffer, from which further <code>read</code> requests can inexpensively get
78 * their data (until the buffer runs out).</p>
79 * <p>However, the <code>copy</code> methods do the same thing, keeping an internal buffer,
80 * populated by {@link InputStream#read(byte[] b, int off, int len)} requests. Having two buffers
81 * (or three if the destination stream is also buffered) is pointless, and the unnecessary buffer
82 * management hurts performance slightly (about 3%, according to some simple experiments).</p>
83 *
84 * @author <a href="mailto:peter@apache.org">Peter Donald</a>
85 * @author <a href="mailto:jefft@apache.org">Jeff Turner</a>
86 * @version CVS $Revision: 925654 $ $Date: 2014-10-13 20:12:40 +0000 (Mon, 13 Oct 2014) $
87 *
88 */
89
90 /*
91 * Behold, intrepid explorers; a map of this class:
92 *
93 * Method Input Output Dependency
94 * ------ ----- ------ -------
95 * 1 copy InputStream OutputStream (primitive)
96 * 2 copy Reader Writer (primitive)
97 *
98 * 3 copy InputStream Writer 2
99 * 4 toString InputStream String 3
100 * 5 toByteArray InputStream byte[] 1
101 *
102 * 6 copy Reader OutputStream 2
103 * 7 toString Reader String 2
104 * 8 toByteArray Reader byte[] 6
105 *
106 * 9 copy String OutputStream 2
107 * 10 copy String Writer (trivial)
108 * 11 toByteArray String byte[] 9
109 *
110 * 12 copy byte[] Writer 3
111 * 13 toString byte[] String 12
112 * 14 copy byte[] OutputStream (trivial)
113 *
114 *
115 * Note that only the first two methods shuffle bytes; the rest use these two, or (if possible) copy
116 * using native Java copy methods. As there are method variants to specify buffer size and encoding,
117 * each row may correspond to up to 4 methods.
118 *
119 */
120
121 public final class IOUtil
122 {
123 private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
124
125 /**
126 * Private constructor to prevent instantiation.
127 */
128 private IOUtil()
129 {
130 }
131
132 ///////////////////////////////////////////////////////////////
133 // Core copy methods
134 ///////////////////////////////////////////////////////////////
135
136 /**
137 * Copy bytes from an <code>InputStream</code> to an <code>OutputStream</code>.
138 */
139 public static void copy( @Nonnull final InputStream input, @Nonnull final OutputStream output )
140 throws IOException
141 {
142 copy( input, output, DEFAULT_BUFFER_SIZE );
143 }
144
145 /**
146 * Copy bytes from an <code>InputStream</code> to an <code>OutputStream</code>.
147 *
148 * @param bufferSize Size of internal buffer to use.
149 */
150 public static void copy( @Nonnull final InputStream input, @Nonnull final OutputStream output, final int bufferSize )
151 throws IOException
152 {
153 final byte[] buffer = new byte[bufferSize];
154 int n;
155 while ( -1 != ( n = input.read( buffer ) ) )
156 {
157 output.write( buffer, 0, n );
158 }
159 }
160
161 /**
162 * Copy chars from a <code>Reader</code> to a <code>Writer</code>.
163 */
164 public static void copy( @Nonnull final Reader input, @Nonnull final Writer output )
165 throws IOException
166 {
167 copy( input, output, DEFAULT_BUFFER_SIZE );
168 }
169
170 /**
171 * Copy chars from a <code>Reader</code> to a <code>Writer</code>.
172 *
173 * @param bufferSize Size of internal buffer to use.
174 */
175 public static void copy( @Nonnull final Reader input, @Nonnull final Writer output, final int bufferSize )
176 throws IOException
177 {
178 final char[] buffer = new char[bufferSize];
179 int n;
180 while ( -1 != ( n = input.read( buffer ) ) )
181 {
182 output.write( buffer, 0, n );
183 }
184 output.flush();
185 }
186
187 ///////////////////////////////////////////////////////////////
188 // Derived copy methods
189 // InputStream -> *
190 ///////////////////////////////////////////////////////////////
191
192 ///////////////////////////////////////////////////////////////
193 // InputStream -> Writer
194
195 /**
196 * Copy and convert bytes from an <code>InputStream</code> to chars on a
197 * <code>Writer</code>.
198 * The platform's default encoding is used for the byte-to-char conversion.
199 */
200 public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output )
201 throws IOException
202 {
203 copy( input, output, DEFAULT_BUFFER_SIZE );
204 }
205
206 /**
207 * Copy and convert bytes from an <code>InputStream</code> to chars on a
208 * <code>Writer</code>.
209 * The platform's default encoding is used for the byte-to-char conversion.
210 *
211 * @param bufferSize Size of internal buffer to use.
212 */
213 public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output, final int bufferSize )
214 throws IOException
215 {
216 final InputStreamReader in = new InputStreamReader( input );
217 copy( in, output, bufferSize );
218 }
219
220 /**
221 * Copy and convert bytes from an <code>InputStream</code> to chars on a
222 * <code>Writer</code>, using the specified encoding.
223 *
224 * @param encoding The name of a supported character encoding. See the
225 * <a href="http://www.iana.org/assignments/character-sets">IANA
226 * Charset Registry</a> for a list of valid encoding types.
227 */
228 public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output, @Nonnull final String encoding )
229 throws IOException
230 {
231 final InputStreamReader in = new InputStreamReader( input, encoding );
232 copy( in, output );
233 }
234
235 /**
236 * Copy and convert bytes from an <code>InputStream</code> to chars on a
237 * <code>Writer</code>, using the specified encoding.
238 *
239 * @param encoding The name of a supported character encoding. See the
240 * <a href="http://www.iana.org/assignments/character-sets">IANA
241 * Charset Registry</a> for a list of valid encoding types.
242 * @param bufferSize Size of internal buffer to use.
243 */
244 public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output, @Nonnull final String encoding, final int bufferSize )
245 throws IOException
246 {
247 final InputStreamReader in = new InputStreamReader( input, encoding );
248 copy( in, output, bufferSize );
249 }
250
251 ///////////////////////////////////////////////////////////////
252 // InputStream -> String
253
254 /**
255 * Get the contents of an <code>InputStream</code> as a String.
256 * The platform's default encoding is used for the byte-to-char conversion.
257 */
258 @Nonnull public static String toString( @Nonnull final InputStream input )
259 throws IOException
260 {
261 return toString( input, DEFAULT_BUFFER_SIZE );
262 }
263
264 /**
265 * Get the contents of an <code>InputStream</code> as a String.
266 * The platform's default encoding is used for the byte-to-char conversion.
267 *
268 * @param bufferSize Size of internal buffer to use.
269 */
270 @Nonnull public static String toString( @Nonnull final InputStream input, final int bufferSize )
271 throws IOException
272 {
273 final StringWriter sw = new StringWriter();
274 copy( input, sw, bufferSize );
275 return sw.toString();
276 }
277
278 /**
279 * Get the contents of an <code>InputStream</code> as a String.
280 *
281 * @param encoding The name of a supported character encoding. See the
282 * <a href="http://www.iana.org/assignments/character-sets">IANA
283 * Charset Registry</a> for a list of valid encoding types.
284 */
285 @Nonnull public static String toString( @Nonnull final InputStream input, @Nonnull final String encoding )
286 throws IOException
287 {
288 return toString( input, encoding, DEFAULT_BUFFER_SIZE );
289 }
290
291 /**
292 * Get the contents of an <code>InputStream</code> as a String.
293 *
294 * @param encoding The name of a supported character encoding. See the
295 * <a href="http://www.iana.org/assignments/character-sets">IANA
296 * Charset Registry</a> for a list of valid encoding types.
297 * @param bufferSize Size of internal buffer to use.
298 */
299 @Nonnull public static String toString( @Nonnull final InputStream input, @Nonnull final String encoding, final int bufferSize )
300 throws IOException
301 {
302 final StringWriter sw = new StringWriter();
303 copy( input, sw, encoding, bufferSize );
304 return sw.toString();
305 }
306
307 ///////////////////////////////////////////////////////////////
308 // InputStream -> byte[]
309
310 /**
311 * Get the contents of an <code>InputStream</code> as a <code>byte[]</code>.
312 */
313 @Nonnull public static byte[] toByteArray( @Nonnull final InputStream input )
314 throws IOException
315 {
316 return toByteArray( input, DEFAULT_BUFFER_SIZE );
317 }
318
319 /**
320 * Get the contents of an <code>InputStream</code> as a <code>byte[]</code>.
321 *
322 * @param bufferSize Size of internal buffer to use.
323 */
324 @Nonnull public static byte[] toByteArray( @Nonnull final InputStream input, final int bufferSize )
325 throws IOException
326 {
327 final ByteArrayOutputStream output = new ByteArrayOutputStream();
328 copy( input, output, bufferSize );
329 return output.toByteArray();
330 }
331
332 ///////////////////////////////////////////////////////////////
333 // Derived copy methods
334 // Reader -> *
335 ///////////////////////////////////////////////////////////////
336
337 ///////////////////////////////////////////////////////////////
338 // Reader -> OutputStream
339
340 /**
341 * Serialize chars from a <code>Reader</code> to bytes on an <code>OutputStream</code>, and
342 * flush the <code>OutputStream</code>.
343 */
344 public static void copy( @Nonnull final Reader input, @Nonnull final OutputStream output )
345 throws IOException
346 {
347 copy( input, output, DEFAULT_BUFFER_SIZE );
348 }
349
350 /**
351 * Serialize chars from a <code>Reader</code> to bytes on an <code>OutputStream</code>, and
352 * flush the <code>OutputStream</code>.
353 *
354 * @param bufferSize Size of internal buffer to use.
355 */
356 public static void copy( @Nonnull final Reader input, @Nonnull final OutputStream output, final int bufferSize )
357 throws IOException
358 {
359 final OutputStreamWriter out = new OutputStreamWriter( output );
360 copy( input, out, bufferSize );
361 // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush
362 // here.
363 out.flush();
364 }
365
366 ///////////////////////////////////////////////////////////////
367 // Reader -> String
368
369 /**
370 * Get the contents of a <code>Reader</code> as a String.
371 */
372 @Nonnull public static String toString( @Nonnull final Reader input )
373 throws IOException
374 {
375 return toString( input, DEFAULT_BUFFER_SIZE );
376 }
377
378 /**
379 * Get the contents of a <code>Reader</code> as a String.
380 *
381 * @param bufferSize Size of internal buffer to use.
382 */
383 @Nonnull public static String toString( @Nonnull final Reader input, final int bufferSize )
384 throws IOException
385 {
386 final StringWriter sw = new StringWriter();
387 copy( input, sw, bufferSize );
388 return sw.toString();
389 }
390
391 ///////////////////////////////////////////////////////////////
392 // Reader -> byte[]
393
394 /**
395 * Get the contents of a <code>Reader</code> as a <code>byte[]</code>.
396 */
397 @Nonnull public static byte[] toByteArray( @Nonnull final Reader input )
398 throws IOException
399 {
400 return toByteArray( input, DEFAULT_BUFFER_SIZE );
401 }
402
403 /**
404 * Get the contents of a <code>Reader</code> as a <code>byte[]</code>.
405 *
406 * @param bufferSize Size of internal buffer to use.
407 */
408 @Nonnull public static byte[] toByteArray( @Nonnull final Reader input, final int bufferSize )
409 throws IOException
410 {
411 ByteArrayOutputStream output = new ByteArrayOutputStream();
412 copy( input, output, bufferSize );
413 return output.toByteArray();
414 }
415
416 ///////////////////////////////////////////////////////////////
417 // Derived copy methods
418 // String -> *
419 ///////////////////////////////////////////////////////////////
420
421 ///////////////////////////////////////////////////////////////
422 // String -> OutputStream
423
424 /**
425 * Serialize chars from a <code>String</code> to bytes on an <code>OutputStream</code>, and
426 * flush the <code>OutputStream</code>.
427 */
428 public static void copy( @Nonnull final String input, @Nonnull final OutputStream output )
429 throws IOException
430 {
431 copy( input, output, DEFAULT_BUFFER_SIZE );
432 }
433
434 /**
435 * Serialize chars from a <code>String</code> to bytes on an <code>OutputStream</code>, and
436 * flush the <code>OutputStream</code>.
437 *
438 * @param bufferSize Size of internal buffer to use.
439 */
440 public static void copy( @Nonnull final String input, @Nonnull final OutputStream output, final int bufferSize )
441 throws IOException
442 {
443 final StringReader in = new StringReader( input );
444 final OutputStreamWriter out = new OutputStreamWriter( output );
445 copy( in, out, bufferSize );
446 // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush
447 // here.
448 out.flush();
449 }
450
451 ///////////////////////////////////////////////////////////////
452 // String -> Writer
453
454 /**
455 * Copy chars from a <code>String</code> to a <code>Writer</code>.
456 */
457 public static void copy( @Nonnull final String input, @Nonnull final Writer output )
458 throws IOException
459 {
460 output.write( input );
461 }
462
463 ///////////////////////////////////////////////////////////////
464 // String -> byte[]
465
466 /**
467 * Get the contents of a <code>String</code> as a <code>byte[]</code>.
468 */
469 @Nonnull public static byte[] toByteArray( @Nonnull final String input )
470 throws IOException
471 {
472 return toByteArray( input, DEFAULT_BUFFER_SIZE );
473 }
474
475 /**
476 * Get the contents of a <code>String</code> as a <code>byte[]</code>.
477 *
478 * @param bufferSize Size of internal buffer to use.
479 */
480 @Nonnull public static byte[] toByteArray( @Nonnull final String input, final int bufferSize )
481 throws IOException
482 {
483 ByteArrayOutputStream output = new ByteArrayOutputStream();
484 copy( input, output, bufferSize );
485 return output.toByteArray();
486 }
487
488 ///////////////////////////////////////////////////////////////
489 // Derived copy methods
490 // byte[] -> *
491 ///////////////////////////////////////////////////////////////
492
493 ///////////////////////////////////////////////////////////////
494 // byte[] -> Writer
495
496 /**
497 * Copy and convert bytes from a <code>byte[]</code> to chars on a
498 * <code>Writer</code>.
499 * The platform's default encoding is used for the byte-to-char conversion.
500 */
501 public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output )
502 throws IOException
503 {
504 copy( input, output, DEFAULT_BUFFER_SIZE );
505 }
506
507 /**
508 * Copy and convert bytes from a <code>byte[]</code> to chars on a
509 * <code>Writer</code>.
510 * The platform's default encoding is used for the byte-to-char conversion.
511 *
512 * @param bufferSize Size of internal buffer to use.
513 */
514 public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output, final int bufferSize )
515 throws IOException
516 {
517 final ByteArrayInputStream in = new ByteArrayInputStream( input );
518 copy( in, output, bufferSize );
519 }
520
521 /**
522 * Copy and convert bytes from a <code>byte[]</code> to chars on a
523 * <code>Writer</code>, using the specified encoding.
524 *
525 * @param encoding The name of a supported character encoding. See the
526 * <a href="http://www.iana.org/assignments/character-sets">IANA
527 * Charset Registry</a> for a list of valid encoding types.
528 */
529 public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output, final String encoding )
530 throws IOException
531 {
532 final ByteArrayInputStream in = new ByteArrayInputStream( input );
533 copy( in, output, encoding );
534 }
535
536 /**
537 * Copy and convert bytes from a <code>byte[]</code> to chars on a
538 * <code>Writer</code>, using the specified encoding.
539 *
540 * @param encoding The name of a supported character encoding. See the
541 * <a href="http://www.iana.org/assignments/character-sets">IANA
542 * Charset Registry</a> for a list of valid encoding types.
543 * @param bufferSize Size of internal buffer to use.
544 */
545 public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output, @Nonnull final String encoding, final int bufferSize )
546 throws IOException
547 {
548 final ByteArrayInputStream in = new ByteArrayInputStream( input );
549 copy( in, output, encoding, bufferSize );
550 }
551
552 ///////////////////////////////////////////////////////////////
553 // byte[] -> String
554
555 /**
556 * Get the contents of a <code>byte[]</code> as a String.
557 * The platform's default encoding is used for the byte-to-char conversion.
558 */
559 @Nonnull public static String toString( @Nonnull final byte[] input )
560 throws IOException
561 {
562 return toString( input, DEFAULT_BUFFER_SIZE );
563 }
564
565 /**
566 * Get the contents of a <code>byte[]</code> as a String.
567 * The platform's default encoding is used for the byte-to-char conversion.
568 *
569 * @param bufferSize Size of internal buffer to use.
570 */
571 @Nonnull public static String toString( @Nonnull final byte[] input, final int bufferSize )
572 throws IOException
573 {
574 final StringWriter sw = new StringWriter();
575 copy( input, sw, bufferSize );
576 return sw.toString();
577 }
578
579 /**
580 * Get the contents of a <code>byte[]</code> as a String.
581 *
582 * @param encoding The name of a supported character encoding. See the
583 * <a href="http://www.iana.org/assignments/character-sets">IANA
584 * Charset Registry</a> for a list of valid encoding types.
585 */
586 @Nonnull public static String toString( @Nonnull final byte[] input, @Nonnull final String encoding )
587 throws IOException
588 {
589 return toString( input, encoding, DEFAULT_BUFFER_SIZE );
590 }
591
592 /**
593 * Get the contents of a <code>byte[]</code> as a String.
594 *
595 * @param encoding The name of a supported character encoding. See the
596 * <a href="http://www.iana.org/assignments/character-sets">IANA
597 * Charset Registry</a> for a list of valid encoding types.
598 * @param bufferSize Size of internal buffer to use.
599 */
600 @Nonnull public static String toString( @Nonnull final byte[] input, @Nonnull final String encoding, final int bufferSize )
601 throws IOException
602 {
603 final StringWriter sw = new StringWriter();
604 copy( input, sw, encoding, bufferSize );
605 return sw.toString();
606 }
607
608 ///////////////////////////////////////////////////////////////
609 // byte[] -> OutputStream
610
611 /**
612 * Copy bytes from a <code>byte[]</code> to an <code>OutputStream</code>.
613 */
614 public static void copy( @Nonnull final byte[] input, @Nonnull final OutputStream output )
615 throws IOException
616 {
617 output.write( input );
618 }
619
620 /**
621 * Compare the contents of two Streams to determine if they are equal or not.
622 *
623 * @param input1 the first stream
624 * @param input2 the second stream
625 * @return true if the content of the streams are equal or they both don't exist, false otherwise
626 */
627 public static boolean contentEquals( @Nonnull final InputStream input1, @Nonnull final InputStream input2 )
628 throws IOException
629 {
630 final InputStream bufferedInput1 = new BufferedInputStream( input1 );
631 final InputStream bufferedInput2 = new BufferedInputStream( input2 );
632
633 int ch = bufferedInput1.read();
634 while ( -1 != ch )
635 {
636 final int ch2 = bufferedInput2.read();
637 if ( ch != ch2 )
638 {
639 return false;
640 }
641 ch = bufferedInput1.read();
642 }
643
644 final int ch2 = bufferedInput2.read();
645 return -1 == ch2;
646 }
647
648 // ----------------------------------------------------------------------
649 // closeXXX()
650 // ----------------------------------------------------------------------
651
652 /**
653 * Closes a channel. Channel can be null and any IOException's will be swallowed.
654 *
655 * @param channel The stream to close.
656 */
657 public static void close( @Nullable Channel channel )
658 {
659 if ( channel == null )
660 {
661 return;
662 }
663
664 try
665 {
666 channel.close();
667 }
668 catch ( IOException ex )
669 {
670 // ignore
671 }
672 }
673
674 /**
675 * Closes the input stream. The input stream can be null and any IOException's will be swallowed.
676 *
677 * @param inputStream The stream to close.
678 */
679 public static void close( @Nullable InputStream inputStream )
680 {
681 if ( inputStream == null )
682 {
683 return;
684 }
685
686 try
687 {
688 inputStream.close();
689 }
690 catch ( IOException ex )
691 {
692 // ignore
693 }
694 }
695
696 /**
697 * Closes the output stream. The output stream can be null and any IOException's will be swallowed.
698 *
699 * @param outputStream The stream to close.
700 */
701 public static void close( @Nullable OutputStream outputStream )
702 {
703 if ( outputStream == null )
704 {
705 return;
706 }
707
708 try
709 {
710 outputStream.close();
711 }
712 catch ( IOException ex )
713 {
714 // ignore
715 }
716 }
717
718 /**
719 * Closes the reader. The reader can be null and any IOException's will be swallowed.
720 *
721 * @param reader The reader to close.
722 */
723 public static void close( @Nullable Reader reader )
724 {
725 if ( reader == null )
726 {
727 return;
728 }
729
730 try
731 {
732 reader.close();
733 }
734 catch ( IOException ex )
735 {
736 // ignore
737 }
738 }
739
740 /**
741 * Closes the writer. The writer can be null and any IOException's will be swallowed.
742 *
743 * @param writer The writer to close.
744 */
745 public static void close( @Nullable Writer writer )
746 {
747 if ( writer == null )
748 {
749 return;
750 }
751
752 try
753 {
754 writer.close();
755 }
756 catch ( IOException ex )
757 {
758 // ignore
759 }
760 }
761 }