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