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 }