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: 1670518 $ $Date: 2015-04-01 01:38:42 +0200 (Wed, 01 Apr 2015) $
87 *
88 */
89 public final class IOUtil
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 private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
122
123 /**
124 * Private constructor to prevent instantiation.
125 */
126 private IOUtil()
127 {
128 }
129
130 ///////////////////////////////////////////////////////////////
131 // Core copy methods
132 ///////////////////////////////////////////////////////////////
133
134 /**
135 * Copy bytes from an <code>InputStream</code> to an <code>OutputStream</code>.
136 */
137 public static void copy( @Nonnull final InputStream input, @Nonnull final OutputStream output )
138 throws IOException
139 {
140 copy( input, output, DEFAULT_BUFFER_SIZE );
141 }
142
143 /**
144 * Copy bytes from an <code>InputStream</code> to an <code>OutputStream</code>.
145 *
146 * @param bufferSize Size of internal buffer to use.
147 */
148 public static void copy( @Nonnull final InputStream input, @Nonnull final OutputStream output,
149 final int bufferSize )
150 throws IOException
151 {
152 final byte[] buffer = new byte[bufferSize];
153 int n;
154 while ( -1 != ( n = input.read( buffer ) ) )
155 {
156 output.write( buffer, 0, n );
157 }
158 }
159
160 /**
161 * Copy chars from a <code>Reader</code> to a <code>Writer</code>.
162 */
163 public static void copy( @Nonnull final Reader input, @Nonnull final Writer output )
164 throws IOException
165 {
166 copy( input, output, DEFAULT_BUFFER_SIZE );
167 }
168
169 /**
170 * Copy chars from a <code>Reader</code> to a <code>Writer</code>.
171 *
172 * @param bufferSize Size of internal buffer to use.
173 */
174 public static void copy( @Nonnull final Reader input, @Nonnull final Writer output, final int bufferSize )
175 throws IOException
176 {
177 final char[] buffer = new char[bufferSize];
178 int n;
179 while ( -1 != ( n = input.read( buffer ) ) )
180 {
181 output.write( buffer, 0, n );
182 }
183 output.flush();
184 }
185
186 ///////////////////////////////////////////////////////////////
187 // Derived copy methods
188 // InputStream -> *
189 ///////////////////////////////////////////////////////////////
190
191 ///////////////////////////////////////////////////////////////
192 // InputStream -> Writer
193
194 /**
195 * Copy and convert bytes from an <code>InputStream</code> to chars on a
196 * <code>Writer</code>.
197 * The platform's default encoding is used for the byte-to-char conversion.
198 */
199 public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output )
200 throws IOException
201 {
202 copy( input, output, DEFAULT_BUFFER_SIZE );
203 }
204
205 /**
206 * Copy and convert bytes from an <code>InputStream</code> to chars on a
207 * <code>Writer</code>.
208 * The platform's default encoding is used for the byte-to-char conversion.
209 *
210 * @param bufferSize Size of internal buffer to use.
211 */
212 public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output, final int bufferSize )
213 throws IOException
214 {
215 final InputStreamReader in = new InputStreamReader( input );
216 copy( in, output, bufferSize );
217 }
218
219 /**
220 * Copy and convert bytes from an <code>InputStream</code> to chars on a
221 * <code>Writer</code>, using the specified encoding.
222 *
223 * @param encoding The name of a supported character encoding. See the
224 * <a href="http://www.iana.org/assignments/character-sets">IANA
225 * Charset Registry</a> for a list of valid encoding types.
226 */
227 public static void copy( @Nonnull final InputStream input, @Nonnull final Writer output,
228 @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,
245 @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 @Nonnull public 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 @Nonnull public 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 @Nonnull public 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 @Nonnull public static String toString( @Nonnull final InputStream input, @Nonnull final String encoding,
301 final int bufferSize )
302 throws IOException
303 {
304 final StringWriter sw = new StringWriter();
305 copy( input, sw, encoding, bufferSize );
306 return sw.toString();
307 }
308
309 ///////////////////////////////////////////////////////////////
310 // InputStream -> byte[]
311
312 /**
313 * Get the contents of an <code>InputStream</code> as a <code>byte[]</code>.
314 */
315 @Nonnull public static byte[] toByteArray( @Nonnull final InputStream input )
316 throws IOException
317 {
318 return toByteArray( input, DEFAULT_BUFFER_SIZE );
319 }
320
321 /**
322 * Get the contents of an <code>InputStream</code> as a <code>byte[]</code>.
323 *
324 * @param bufferSize Size of internal buffer to use.
325 */
326 @Nonnull public static byte[] toByteArray( @Nonnull final InputStream input, final int bufferSize )
327 throws IOException
328 {
329 final ByteArrayOutputStream output = new ByteArrayOutputStream();
330 copy( input, output, bufferSize );
331 return output.toByteArray();
332 }
333
334 ///////////////////////////////////////////////////////////////
335 // Derived copy methods
336 // Reader -> *
337 ///////////////////////////////////////////////////////////////
338
339 ///////////////////////////////////////////////////////////////
340 // Reader -> OutputStream
341
342 /**
343 * Serialize chars from a <code>Reader</code> to bytes on an <code>OutputStream</code>, and
344 * flush the <code>OutputStream</code>.
345 */
346 public static void copy( @Nonnull final Reader input, @Nonnull final OutputStream output )
347 throws IOException
348 {
349 copy( input, output, DEFAULT_BUFFER_SIZE );
350 }
351
352 /**
353 * Serialize chars from a <code>Reader</code> to bytes on an <code>OutputStream</code>, and
354 * flush the <code>OutputStream</code>.
355 *
356 * @param bufferSize Size of internal buffer to use.
357 */
358 public static void copy( @Nonnull final Reader input, @Nonnull final OutputStream output, final int bufferSize )
359 throws IOException
360 {
361 final OutputStreamWriter out = new OutputStreamWriter( output );
362 copy( input, out, bufferSize );
363 // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush
364 // here.
365 out.flush();
366 }
367
368 ///////////////////////////////////////////////////////////////
369 // Reader -> String
370
371 /**
372 * Get the contents of a <code>Reader</code> as a String.
373 */
374 @Nonnull public static String toString( @Nonnull final Reader input )
375 throws IOException
376 {
377 return toString( input, DEFAULT_BUFFER_SIZE );
378 }
379
380 /**
381 * Get the contents of a <code>Reader</code> as a String.
382 *
383 * @param bufferSize Size of internal buffer to use.
384 */
385 @Nonnull public static String toString( @Nonnull final Reader input, final int bufferSize )
386 throws IOException
387 {
388 final StringWriter sw = new StringWriter();
389 copy( input, sw, bufferSize );
390 return sw.toString();
391 }
392
393 ///////////////////////////////////////////////////////////////
394 // Reader -> byte[]
395
396 /**
397 * Get the contents of a <code>Reader</code> as a <code>byte[]</code>.
398 */
399 @Nonnull public static byte[] toByteArray( @Nonnull final Reader input )
400 throws IOException
401 {
402 return toByteArray( input, DEFAULT_BUFFER_SIZE );
403 }
404
405 /**
406 * Get the contents of a <code>Reader</code> as a <code>byte[]</code>.
407 *
408 * @param bufferSize Size of internal buffer to use.
409 */
410 @Nonnull public static byte[] toByteArray( @Nonnull final Reader input, final int bufferSize )
411 throws IOException
412 {
413 ByteArrayOutputStream output = new ByteArrayOutputStream();
414 copy( input, output, bufferSize );
415 return output.toByteArray();
416 }
417
418 ///////////////////////////////////////////////////////////////
419 // Derived copy methods
420 // String -> *
421 ///////////////////////////////////////////////////////////////
422
423 ///////////////////////////////////////////////////////////////
424 // String -> OutputStream
425
426 /**
427 * Serialize chars from a <code>String</code> to bytes on an <code>OutputStream</code>, and
428 * flush the <code>OutputStream</code>.
429 */
430 public static void copy( @Nonnull final String input, @Nonnull final OutputStream output )
431 throws IOException
432 {
433 copy( input, output, DEFAULT_BUFFER_SIZE );
434 }
435
436 /**
437 * Serialize chars from a <code>String</code> to bytes on an <code>OutputStream</code>, and
438 * flush the <code>OutputStream</code>.
439 *
440 * @param bufferSize Size of internal buffer to use.
441 */
442 public static void copy( @Nonnull final String input, @Nonnull final OutputStream output, final int bufferSize )
443 throws IOException
444 {
445 final StringReader in = new StringReader( input );
446 final OutputStreamWriter out = new OutputStreamWriter( output );
447 copy( in, out, bufferSize );
448 // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush
449 // here.
450 out.flush();
451 }
452
453 ///////////////////////////////////////////////////////////////
454 // String -> Writer
455
456 /**
457 * Copy chars from a <code>String</code> to a <code>Writer</code>.
458 */
459 public static void copy( @Nonnull final String input, @Nonnull final Writer output )
460 throws IOException
461 {
462 output.write( input );
463 }
464
465 ///////////////////////////////////////////////////////////////
466 // String -> byte[]
467
468 /**
469 * Get the contents of a <code>String</code> as a <code>byte[]</code>.
470 */
471 @Nonnull public static byte[] toByteArray( @Nonnull final String input )
472 throws IOException
473 {
474 return toByteArray( input, DEFAULT_BUFFER_SIZE );
475 }
476
477 /**
478 * Get the contents of a <code>String</code> as a <code>byte[]</code>.
479 *
480 * @param bufferSize Size of internal buffer to use.
481 */
482 @Nonnull public static byte[] toByteArray( @Nonnull final String input, final int bufferSize )
483 throws IOException
484 {
485 ByteArrayOutputStream output = new ByteArrayOutputStream();
486 copy( input, output, bufferSize );
487 return output.toByteArray();
488 }
489
490 ///////////////////////////////////////////////////////////////
491 // Derived copy methods
492 // byte[] -> *
493 ///////////////////////////////////////////////////////////////
494
495 ///////////////////////////////////////////////////////////////
496 // byte[] -> Writer
497
498 /**
499 * Copy and convert bytes from a <code>byte[]</code> to chars on a
500 * <code>Writer</code>.
501 * The platform's default encoding is used for the byte-to-char conversion.
502 */
503 public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output )
504 throws IOException
505 {
506 copy( input, output, DEFAULT_BUFFER_SIZE );
507 }
508
509 /**
510 * Copy and convert bytes from a <code>byte[]</code> to chars on a
511 * <code>Writer</code>.
512 * The platform's default encoding is used for the byte-to-char conversion.
513 *
514 * @param bufferSize Size of internal buffer to use.
515 */
516 public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output, final int bufferSize )
517 throws IOException
518 {
519 final ByteArrayInputStream in = new ByteArrayInputStream( input );
520 copy( in, output, bufferSize );
521 }
522
523 /**
524 * Copy and convert bytes from a <code>byte[]</code> to chars on a
525 * <code>Writer</code>, using the specified encoding.
526 *
527 * @param encoding The name of a supported character encoding. See the
528 * <a href="http://www.iana.org/assignments/character-sets">IANA
529 * Charset Registry</a> for a list of valid encoding types.
530 */
531 public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output, final String encoding )
532 throws IOException
533 {
534 final ByteArrayInputStream in = new ByteArrayInputStream( input );
535 copy( in, output, encoding );
536 }
537
538 /**
539 * Copy and convert bytes from a <code>byte[]</code> to chars on a
540 * <code>Writer</code>, using the specified encoding.
541 *
542 * @param encoding The name of a supported character encoding. See the
543 * <a href="http://www.iana.org/assignments/character-sets">IANA
544 * Charset Registry</a> for a list of valid encoding types.
545 * @param bufferSize Size of internal buffer to use.
546 */
547 public static void copy( @Nonnull final byte[] input, @Nonnull final Writer output, @Nonnull final String encoding,
548 final int bufferSize )
549 throws IOException
550 {
551 final ByteArrayInputStream in = new ByteArrayInputStream( input );
552 copy( in, output, encoding, bufferSize );
553 }
554
555 ///////////////////////////////////////////////////////////////
556 // byte[] -> String
557
558 /**
559 * Get the contents of a <code>byte[]</code> as a String.
560 * The platform's default encoding is used for the byte-to-char conversion.
561 */
562 @Nonnull public static String toString( @Nonnull final byte[] input )
563 throws IOException
564 {
565 return toString( input, DEFAULT_BUFFER_SIZE );
566 }
567
568 /**
569 * Get the contents of a <code>byte[]</code> as a String.
570 * The platform's default encoding is used for the byte-to-char conversion.
571 *
572 * @param bufferSize Size of internal buffer to use.
573 */
574 @Nonnull public static String toString( @Nonnull final byte[] input, final int bufferSize )
575 throws IOException
576 {
577 final StringWriter sw = new StringWriter();
578 copy( input, sw, bufferSize );
579 return sw.toString();
580 }
581
582 /**
583 * Get the contents of a <code>byte[]</code> as a String.
584 *
585 * @param encoding The name of a supported character encoding. See the
586 * <a href="http://www.iana.org/assignments/character-sets">IANA
587 * Charset Registry</a> for a list of valid encoding types.
588 */
589 @Nonnull public static String toString( @Nonnull final byte[] input, @Nonnull final String encoding )
590 throws IOException
591 {
592 return toString( input, encoding, DEFAULT_BUFFER_SIZE );
593 }
594
595 /**
596 * Get the contents of a <code>byte[]</code> as a String.
597 *
598 * @param encoding The name of a supported character encoding. See the
599 * <a href="http://www.iana.org/assignments/character-sets">IANA
600 * Charset Registry</a> for a list of valid encoding types.
601 * @param bufferSize Size of internal buffer to use.
602 */
603 @Nonnull public static String toString( @Nonnull final byte[] input, @Nonnull final String encoding,
604 final int bufferSize )
605 throws IOException
606 {
607 final StringWriter sw = new StringWriter();
608 copy( input, sw, encoding, bufferSize );
609 return sw.toString();
610 }
611
612 ///////////////////////////////////////////////////////////////
613 // byte[] -> OutputStream
614
615 /**
616 * Copy bytes from a <code>byte[]</code> to an <code>OutputStream</code>.
617 */
618 public static void copy( @Nonnull final byte[] input, @Nonnull final OutputStream output )
619 throws IOException
620 {
621 output.write( input );
622 }
623
624 /**
625 * Compare the contents of two Streams to determine if they are equal or not.
626 *
627 * @param input1 the first stream
628 * @param input2 the second stream
629 * @return true if the content of the streams are equal or they both don't exist, false otherwise
630 */
631 public static boolean contentEquals( @Nonnull final InputStream input1, @Nonnull final InputStream input2 )
632 throws IOException
633 {
634 final InputStream bufferedInput1 = new BufferedInputStream( input1 );
635 final InputStream bufferedInput2 = new BufferedInputStream( input2 );
636
637 int ch = bufferedInput1.read();
638 while ( -1 != ch )
639 {
640 final int ch2 = bufferedInput2.read();
641 if ( ch != ch2 )
642 {
643 return false;
644 }
645 ch = bufferedInput1.read();
646 }
647
648 final int ch2 = bufferedInput2.read();
649 return -1 == ch2;
650 }
651
652 // ----------------------------------------------------------------------
653 // closeXXX()
654 // ----------------------------------------------------------------------
655
656 /**
657 * Closes a channel. Channel can be null and any IOException's will be swallowed.
658 *
659 * @param channel The stream to close.
660 */
661 public static void close( @Nullable Channel channel )
662 {
663 if ( channel == null )
664 {
665 return;
666 }
667
668 try
669 {
670 channel.close();
671 }
672 catch ( IOException ex )
673 {
674 // ignore
675 }
676 }
677
678 /**
679 * Closes the input stream. The input stream can be null and any IOException's will be swallowed.
680 *
681 * @param inputStream The stream to close.
682 */
683 public static void close( @Nullable InputStream inputStream )
684 {
685 if ( inputStream == null )
686 {
687 return;
688 }
689
690 try
691 {
692 inputStream.close();
693 }
694 catch ( IOException ex )
695 {
696 // ignore
697 }
698 }
699
700 /**
701 * Closes the output stream. The output stream can be null and any IOException's will be swallowed.
702 *
703 * @param outputStream The stream to close.
704 */
705 public static void close( @Nullable OutputStream outputStream )
706 {
707 if ( outputStream == null )
708 {
709 return;
710 }
711
712 try
713 {
714 outputStream.close();
715 }
716 catch ( IOException ex )
717 {
718 // ignore
719 }
720 }
721
722 /**
723 * Closes the reader. The reader can be null and any IOException's will be swallowed.
724 *
725 * @param reader The reader to close.
726 */
727 public static void close( @Nullable Reader reader )
728 {
729 if ( reader == null )
730 {
731 return;
732 }
733
734 try
735 {
736 reader.close();
737 }
738 catch ( IOException ex )
739 {
740 // ignore
741 }
742 }
743
744 /**
745 * Closes the writer. The writer can be null and any IOException's will be swallowed.
746 *
747 * @param writer The writer to close.
748 */
749 public static void close( @Nullable Writer writer )
750 {
751 if ( writer == null )
752 {
753 return;
754 }
755
756 try
757 {
758 writer.close();
759 }
760 catch ( IOException ex )
761 {
762 // ignore
763 }
764 }
765 }