001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.commons.utils; 018 019import static org.apache.juneau.commons.utils.SystemUtils.*; 020import static org.apache.juneau.commons.utils.ThrowableUtils.*; 021import static org.apache.juneau.commons.utils.Utils.*; 022 023import java.io.*; 024import java.nio.charset.*; 025import java.nio.file.*; 026import java.util.*; 027import java.util.concurrent.atomic.*; 028import java.util.function.*; 029 030import org.apache.juneau.commons.io.*; 031 032/** 033 * Various I/O related utility methods. 034 */ 035public class IoUtils { 036 037 /** UTF-8 charset */ 038 public static final Charset UTF8 = StandardCharsets.UTF_8; 039 040 /** Reusable empty input stream. */ 041 public static final InputStream EMPTY_INPUT_STREAM = new InputStream() { 042 @Override 043 public int read() { 044 return -1; // end of stream 045 } 046 }; 047 048 private static final int BUFF_SIZE = 1024; 049 050 private static final ThreadLocal<byte[]> BYTE_BUFFER_CACHE = (Boolean.getBoolean("juneau.disableIoBufferReuse") ? null : new ThreadLocal<>()); // NOSONAR 051 private static final ThreadLocal<char[]> CHAR_BUFFER_CACHE = (Boolean.getBoolean("juneau.disableIoBufferReuse") ? null : new ThreadLocal<>()); // NOSONAR 052 static final AtomicInteger BYTE_BUFFER_CACHE_HITS = new AtomicInteger(); 053 054 static final AtomicInteger BYTE_BUFFER_CACHE_MISSES = new AtomicInteger(); 055 static final AtomicInteger CHAR_BUFFER_CACHE_HITS = new AtomicInteger(); 056 static final AtomicInteger CHAR_BUFFER_CACHE_MISSES = new AtomicInteger(); 057 static { 058 shutdownMessage(() -> "Byte buffer cache: hits=" + BYTE_BUFFER_CACHE_HITS.get() + ", misses=" + BYTE_BUFFER_CACHE_MISSES); 059 shutdownMessage(() -> "Char buffer cache: hits=" + CHAR_BUFFER_CACHE_HITS.get() + ", misses=" + CHAR_BUFFER_CACHE_MISSES); 060 } 061 062 /** Reusable empty reader. */ 063 public static final Reader EMPTY_READER = new Reader() { 064 @Override 065 public void close() throws IOException { /* no-op */ } 066 067 @Override 068 public int read() { 069 return -1; // end of stream 070 } 071 072 @Override 073 public int read(char[] cbuf, int off, int len) throws IOException { 074 return -1; // end of stream 075 } 076 }; 077 078 /** 079 * Close all specified input streams, output streams, readers, and writers. 080 * 081 * @param o 082 * The list of all objects to close. 083 * <jk>null</jk> entries are ignored. 084 * @throws IOException Thrown by underlying stream. 085 */ 086 @SuppressWarnings("null") 087 public static void close(Object...o) throws IOException { 088 var ex = (IOException)null; 089 for (var o2 : o) { 090 try { 091 if (o2 instanceof InputStream o3) 092 o3.close(); 093 if (o2 instanceof OutputStream o3) 094 o3.close(); 095 if (o2 instanceof Reader o3) 096 o3.close(); 097 if (o2 instanceof Writer o3) 098 o3.close(); 099 } catch (IOException e) { 100 ex = e; 101 } 102 } 103 if (nn(ex)) 104 throw ex; 105 } 106 107 /** 108 * Close input stream and ignore any exceptions. 109 * 110 * <p> 111 * No-op if input stream is <jk>null</jk>. 112 * 113 * @param is The input stream to close. 114 */ 115 public static void closeQuietly(InputStream is) { 116 if (nn(is)) 117 safe(() -> is.close()); 118 } 119 120 /** 121 * Quietly close all specified input streams, output streams, readers, and writers. 122 * 123 * @param o The list of all objects to quietly close. 124 */ 125 public static void closeQuietly(Object...o) { 126 for (var o2 : o) { 127 if (o2 instanceof InputStream o3) 128 closeQuietly(o3); 129 if (o2 instanceof OutputStream o3) 130 closeQuietly(o3); 131 if (o2 instanceof Reader o3) 132 closeQuietly(o3); 133 if (o2 instanceof Writer o3) 134 closeQuietly(o3); 135 } 136 } 137 138 /** 139 * Close output stream and ignore any exceptions. 140 * 141 * <p> 142 * No-op if output stream is <jk>null</jk>. 143 * 144 * @param os The output stream to close. 145 */ 146 public static void closeQuietly(OutputStream os) { 147 if (nn(os)) 148 quiet(() -> os.close()); 149 } 150 151 /** 152 * Close reader and ignore any exceptions. 153 * 154 * <p> 155 * No-op if reader is <jk>null</jk>. 156 * 157 * @param r The reader to close. 158 */ 159 public static void closeQuietly(Reader r) { 160 if (nn(r)) 161 quiet(() -> r.close()); 162 } 163 164 /** 165 * Close writer and ignore any exceptions. 166 * 167 * <p> 168 * No-op if writer is <jk>null</jk>. 169 * 170 * @param w The writer to close. 171 */ 172 public static void closeQuietly(Writer w) { 173 if (nn(w)) 174 quiet(() -> w.close()); 175 } 176 177 /** 178 * Counts the number of bytes in the input stream and then closes the stream. 179 * 180 * @param is The input stream to read from. 181 * @return The number of bytes read. 182 * @throws IOException Thrown by underlying stream. 183 */ 184 public static long count(InputStream is) throws IOException { 185 if (is == null) 186 return 0; 187 var c = 0l; 188 var i = 0l; 189 try { 190 while ((i = is.skip(1024)) != 0) 191 c += i; 192 } finally { 193 is.close(); 194 } 195 return c; 196 } 197 198 /** 199 * Counts the number of characters in the reader and then closes the reader. 200 * 201 * @param r The reader to read from. 202 * @return The number of characters read. 203 * @throws IOException Thrown by underlying stream. 204 */ 205 public static long count(Reader r) throws IOException { 206 if (r == null) 207 return 0; 208 var c = 0l; 209 var i = 0l; 210 try { 211 while ((i = r.skip(1024)) != 0) 212 c += i; 213 } finally { 214 r.close(); 215 } 216 return c; 217 } 218 219 /** 220 * Flushes multiple output streams and writers in a single call. 221 * 222 * @param o 223 * The objects to flush. 224 * <jk>null</jk> entries are ignored. 225 * @throws IOException Thrown by underlying stream. 226 */ 227 @SuppressWarnings("null") 228 public static void flush(Object...o) throws IOException { 229 var ex = (IOException)null; 230 for (var o2 : o) { 231 try { 232 if (o2 instanceof OutputStream o3) 233 o3.flush(); 234 if (o2 instanceof Writer o3) 235 o3.flush(); 236 } catch (IOException e) { 237 ex = e; 238 } 239 } 240 if (nn(ex)) 241 throw ex; 242 } 243 244 /** 245 * Loads a text file from either the file system or classpath. 246 * 247 * @param name The file name. 248 * @param paths The paths to search. 249 * @return The file contents, or <jk>null</jk> if not found. 250 * @throws IOException Thrown by underlying stream. 251 */ 252 public static String loadSystemResourceAsString(String name, String...paths) throws IOException { 253 for (var path : paths) { 254 var p = new File(path); 255 if (p.exists()) { 256 var f = new File(p, name); 257 if (f.exists() && f.canRead()) 258 return read(f); 259 } 260 } 261 var cl = Thread.currentThread().getContextClassLoader(); 262 if (cl == null) 263 cl = ClassLoader.getSystemClassLoader(); 264 for (var path : paths) { 265 var n = ".".equals(path) ? name : path + '/' + name; 266 try (var is = cl.getResourceAsStream(n)) { 267 if (nn(is)) 268 return read(is); 269 } 270 try (var is = ClassLoader.getSystemResourceAsStream(n)) { 271 if (nn(is)) 272 return read(is); 273 } 274 } 275 return null; 276 } 277 278 /** 279 * Pipes the specified byte array to the specified output stream. 280 * 281 * @param in 282 * The input byte array. 283 * <br>Can be <jk>null</jk>. 284 * @param out 285 * The output stream. 286 * <br>Can be <jk>null</jk>. 287 * <br>Stream is not automatically closed. 288 * @param maxBytes 289 * The maximum number of bytes or <c>-1</c> to read the entire byte array. 290 * @return The number of bytes written. 291 * @throws IOException If thrown from output stream. 292 */ 293 public static long pipe(byte[] in, OutputStream out, int maxBytes) throws IOException { 294 if (in == null || out == null) 295 return 0; 296 var length = (maxBytes < 0 || maxBytes > in.length) ? in.length : maxBytes; 297 out.write(in, 0, length); 298 return length; 299 } 300 301 /** 302 * Pipes the specified input stream to the specified output stream. 303 * 304 * <p> 305 * Either stream is not automatically closed. 306 * 307 * @param in 308 * The input stream. 309 * <br>Can be <jk>null</jk>. 310 * <br>Stream is automatically closed. 311 * @param out 312 * The output stream. 313 * <br>Can be <jk>null</jk>. 314 * <br>Stream is not automatically closed. 315 * @return The number of bytes written. 316 * @throws IOException If thrown from either stream. 317 */ 318 public static long pipe(InputStream in, OutputStream out) throws IOException { 319 try (var in2 = in) { 320 return pipe(in, out, -1); 321 } 322 } 323 324 /** 325 * Pipes the specified input stream to the specified output stream. 326 * 327 * <p> 328 * Either stream is not automatically closed. 329 * 330 * @param in 331 * The input stream. 332 * <br>Can be <jk>null</jk>. 333 * <br>Stream is automatically closed. 334 * @param out 335 * The output stream. 336 * <br>Can be <jk>null</jk>. 337 * <br>Stream is not automatically closed. 338 * @param onException Consumer of any {@link IOException I/O exceptions}. 339 * @return The number of bytes written. 340 */ 341 public static long pipe(InputStream in, OutputStream out, Consumer<IOException> onException) { 342 try { 343 try (var in2 = in) { 344 return pipe(in, out, -1); 345 } 346 } catch (IOException e) { 347 onException.accept(e); 348 return -1; 349 } 350 } 351 352 /** 353 * Pipes the specified input stream to the specified output stream. 354 * 355 * <p> 356 * Either stream is not automatically closed. 357 * 358 * @param in 359 * The input stream. 360 * <br>Can be <jk>null</jk>. 361 * <br>Stream is not automatically closed. 362 * @param out 363 * The output stream. 364 * <br>Can be <jk>null</jk>. 365 * <br>Stream is not automatically closed. 366 * @param maxBytes 367 * The maximum number of bytes or <c>-1</c> to read the entire input stream. 368 * @return The number of bytes written. 369 * @throws IOException If thrown from either stream. 370 */ 371 public static long pipe(InputStream in, OutputStream out, long maxBytes) throws IOException { 372 if (in == null || out == null) 373 return 0; 374 var buffer = byteBuffer((int)maxBytes); 375 int readLen; 376 var total = 0l; 377 if (maxBytes < 0) { 378 while ((readLen = in.read(buffer)) != -1) { 379 out.write(buffer, 0, readLen); 380 total += readLen; 381 } 382 } else { 383 var remaining = maxBytes; 384 while (remaining > 0) { 385 readLen = in.read(buffer, 0, buffSize(remaining)); 386 if (readLen == -1) 387 break; 388 out.write(buffer, 0, readLen); 389 total += readLen; 390 remaining -= readLen; 391 } 392 } 393 out.flush(); 394 return total; 395 } 396 397 /** 398 * Pipes the contents of the specified input stream to the writer. 399 * 400 * @param in 401 * The stream to pipe from. 402 * <br>Can be <jk>null</jk>. 403 * <br>Streams is automatically closed. 404 * @param out 405 * The writer to pipe to. 406 * <br>Can be <jk>null</jk>. 407 * <br>Stream is not automatically closed. 408 * @return 409 * The number of bytes written. 410 * @throws IOException Thrown by underlying stream. 411 */ 412 public static long pipe(InputStream in, Writer out) throws IOException { 413 if (in == null || out == null) 414 return 0; 415 return pipe(new InputStreamReader(in, UTF8), out); 416 } 417 418 /** 419 * Pipes the contents of the specified input stream to the writer. 420 * 421 * @param in 422 * The stream to pipe from. 423 * <br>Can be <jk>null</jk>. 424 * <br>Streams is automatically closed. 425 * @param out 426 * The writer to pipe to. 427 * <br>Can be <jk>null</jk>. 428 * <br>Stream is not automatically closed. 429 * @param onException Consumer of any {@link IOException I/O exceptions}. 430 * @return 431 * The number of bytes written. 432 */ 433 public static long pipe(InputStream in, Writer out, Consumer<IOException> onException) { 434 try { 435 if (in == null || out == null) 436 return 0; 437 return pipe(new InputStreamReader(in, UTF8), out); 438 } catch (IOException e) { 439 onException.accept(e); 440 return -2; 441 } 442 } 443 444 /** 445 * Pipes the contents of the specified <c>Reader</c> to the specified file. 446 * 447 * @param in 448 * The reader to pipe from. 449 * <br>Can be <jk>null</jk>. 450 * <br>Reader is automatically closed. 451 * @param out 452 * The file to write the output to. 453 * <br>Can be <jk>null</jk>. 454 * @return 455 * The number of characters piped. 456 * @throws IOException Thrown by underlying stream. 457 */ 458 public static long pipe(Reader in, File out) throws IOException { 459 if (out == null || in == null) 460 return 0; 461 try (var w = FileWriterBuilder.create(out).buffered().build()) { 462 return pipe(in, w); 463 } 464 } 465 466 /** 467 * Pipes the specified reader to the specified output stream. 468 * 469 * @param in 470 * The input reader. 471 * <br>Can be <jk>null</jk>. 472 * <br>Stream is automatically closed. 473 * @param out 474 * The output stream. 475 * <br>Can be <jk>null</jk>. 476 * <br>Stream is not automatically closed. 477 * @return The number of bytes written. 478 * @throws IOException If thrown from output stream. 479 */ 480 public static long pipe(Reader in, OutputStream out) throws IOException { 481 if (in == null || out == null) 482 return 0; 483 var total = 0l; 484 try (var in2 = in) { 485 var osw = new OutputStreamWriter(out, UTF8); 486 var i = 0; 487 var b = charBuffer(-1); 488 while ((i = in.read(b)) > 0) { 489 total += i; 490 osw.write(b, 0, i); 491 } 492 osw.flush(); 493 } 494 return total; 495 } 496 497 /** 498 * Pipes the specified reader to the specified output stream. 499 * 500 * @param in 501 * The input reader. 502 * <br>Can be <jk>null</jk>. 503 * <br>Stream is automatically closed. 504 * @param out 505 * The output stream. 506 * <br>Can be <jk>null</jk>. 507 * <br>Stream is not automatically closed. 508 * @param onException Consumer of any {@link IOException I/O exceptions}. 509 * @return The number of bytes written. 510 */ 511 public static long pipe(Reader in, OutputStream out, Consumer<IOException> onException) { 512 try { 513 return pipe(in, out); 514 } catch (IOException e) { 515 onException.accept(e); 516 return -1; 517 } 518 } 519 520 /** 521 * Pipes the contents of the specified <c>Reader</c> to the specified <c>Writer</c>. 522 * 523 * @param in 524 * The reader to pipe from. 525 * <br>Can be <jk>null</jk>. 526 * <br>Reader is automatically closed. 527 * @param out 528 * The file to write the output to. 529 * <br>Can be <jk>null</jk>. 530 * <br>Writer is flushed but not automatically closed. 531 * @return 532 * The number of characters piped. 533 * @throws IOException Thrown by underlying stream. 534 */ 535 public static long pipe(Reader in, Writer out) throws IOException { 536 if (out == null || in == null) 537 return 0; 538 var total = 0l; 539 try (var in2 = in) { 540 var buffer = charBuffer(-1); 541 int readLen; 542 while ((readLen = in.read(buffer)) != -1) { 543 out.write(buffer, 0, readLen); 544 total += readLen; 545 } 546 } 547 out.flush(); 548 return total; 549 } 550 551 /** 552 * Pipes the contents of the specified <c>Reader</c> to the specified <c>Writer</c>. 553 * 554 * @param in 555 * The reader to pipe from. 556 * <br>Can be <jk>null</jk>. 557 * <br>Reader is automatically closed. 558 * @param out 559 * The file to write the output to. 560 * <br>Can be <jk>null</jk>. 561 * <br>Writer is flushed but not automatically closed. 562 * @param onException Consumer of any {@link IOException I/O exceptions}. 563 * @return 564 * The number of characters piped. 565 */ 566 public static long pipe(Reader in, Writer out, Consumer<IOException> onException) { 567 try { 568 return pipe(in, out); 569 } catch (IOException e) { 570 onException.accept(e); 571 return -1; 572 } 573 } 574 575 /** 576 * Pipes the contents of the specified <c>Reader</c> to the specified <c>Writer</c> a line at a time. 577 * 578 * <p> 579 * Writer is flushed after every line. Typically useful when writing to consoles. 580 * 581 * @param in 582 * The reader to pipe from. 583 * <br>Can be <jk>null</jk>. 584 * <br>Reader is automatically closed. 585 * @param out 586 * The file to write the output to. 587 * <br>Can be <jk>null</jk>. 588 * <br>Writer is flushed but not automatically closed. 589 * @return 590 * The number of characters piped. 591 * @throws IOException Thrown by underlying stream. 592 */ 593 public static long pipeLines(Reader in, Writer out) throws IOException { 594 if (in == null || out == null) 595 return 0; 596 var total = 0l; 597 try (var in2 = in) { 598 try (var s = new Scanner(in2)) { 599 while (s.hasNextLine()) { 600 var l = s.nextLine(); 601 if (nn(l)) { 602 out.write(l); 603 out.write("\n"); 604 out.flush(); 605 total += l.length() + 1; 606 } 607 } 608 } 609 } 610 return total; 611 } 612 613 /** 614 * Reads the specified byte array containing UTF-8 into a string. 615 * 616 * @param in 617 * The input. 618 * <br>Can be <jk>null</jk>. 619 * @return The new string, or <jk>null</jk> if the input was null. 620 */ 621 public static String read(byte[] in) { 622 return read(in, UTF8); 623 } 624 625 /** 626 * Reads the specified byte array into a string. 627 * 628 * @param in 629 * The input. 630 * <br>Can be <jk>null</jk>. 631 * @param charset The character set to use for decoding. 632 * @return The new string, or <jk>null</jk> if the input was null. 633 */ 634 public static String read(byte[] in, Charset charset) { 635 if (in == null) 636 return null; 637 return new String(in, charset); 638 } 639 640 /** 641 * Reads the contents of a file into a string. 642 * 643 * <p> 644 * Assumes default character encoding. 645 * 646 * @param in 647 * The file to read. 648 * <br>Can be <jk>null</jk>. 649 * @return 650 * The contents of the reader as a string, or <jk>null</jk> if file does not exist. 651 * @throws IOException If a problem occurred trying to read from the reader. 652 */ 653 public static String read(File in) throws IOException { 654 if (in == null || ! in.exists()) 655 return null; 656 try (var r = FileReaderBuilder.create(in).build()) { 657 return read(r, in.length()); 658 } 659 } 660 661 /** 662 * Reads the contents of an input stream into a string. 663 * 664 * <p> 665 * Assumes UTF-8 encoding. 666 * 667 * @param in 668 * The input stream. 669 * <br>Can be <jk>null</jk>. 670 * <br>Stream is automatically closed. 671 * @return 672 * The contents of the reader as a string, or <jk>null</jk> if the input stream was <jk>null</jk>. 673 * @throws IOException If a problem occurred trying to read from the input stream. 674 */ 675 public static String read(InputStream in) throws IOException { 676 return read(in, UTF8); 677 } 678 679 /** 680 * Reads the contents of an input stream into a string using the specified charset. 681 * 682 * @param in 683 * The input stream. 684 * <br>Can be <jk>null</jk>. 685 * <br>Stream is automatically closed. 686 * @param cs 687 * The charset of the contents of the input stream. 688 * @return 689 * The contents of the reader as a string or <jk>null</jk> if input stream was <jk>null</jk>. 690 * @throws IOException If a problem occurred trying to read from the input stream. 691 */ 692 public static String read(InputStream in, Charset cs) throws IOException { 693 if (in == null) 694 return null; 695 try (var isr = new InputStreamReader(in, cs)) { 696 return read(isr); 697 } 698 } 699 700 /** 701 * Reads the contents of an input stream into a string using the specified charset. 702 * 703 * @param in 704 * The input stream. 705 * <br>Can be <jk>null</jk>. 706 * <br>Stream is automatically closed. 707 * @param cs 708 * The charset of the contents of the input stream. 709 * @param onException Consumer of any {@link IOException I/O exceptions}. 710 * @return 711 * The contents of the reader as a string or <jk>null</jk> if input stream was <jk>null</jk>. 712 */ 713 public static String read(InputStream in, Charset cs, Consumer<IOException> onException) { 714 if (in == null) 715 return null; 716 try (var isr = new InputStreamReader(in, cs)) { 717 return read(isr); 718 } catch (IOException e) { 719 onException.accept(e); 720 return null; 721 } 722 } 723 724 /** 725 * Reads the contents of an input stream into a string. 726 * 727 * <p> 728 * Assumes UTF-8 encoding. 729 * 730 * @param in 731 * The input stream. 732 * <br>Can be <jk>null</jk>. 733 * <br>Stream is automatically closed. 734 * @param onException Consumer of any {@link IOException I/O exceptions}. 735 * @return 736 * The contents of the reader as a string, or <jk>null</jk> if the input stream was <jk>null</jk>. 737 */ 738 public static String read(InputStream in, Consumer<IOException> onException) { 739 return read(in, UTF8, onException); 740 } 741 742 /** 743 * Reads the contents of an input stream into a string, reading up to the specified maximum number of bytes. 744 * 745 * <p> 746 * Assumes UTF-8 encoding. 747 * 748 * @param in 749 * The input stream. 750 * <br>Can be <jk>null</jk>. 751 * <br>Stream is automatically closed. 752 * @param maxBytes 753 * The maximum number of bytes to read, or <c>-1</c> to read all bytes. 754 * @return 755 * The contents of the input stream as a string, or <jk>null</jk> if the input stream was <jk>null</jk>. 756 * @throws IOException If a problem occurred trying to read from the input stream. 757 */ 758 public static String read(InputStream in, int maxBytes) throws IOException { 759 return read(in, maxBytes, UTF8); 760 } 761 762 /** 763 * Reads the contents of an input stream into a string using the specified charset, reading up to the specified maximum number of bytes. 764 * 765 * @param in 766 * The input stream. 767 * <br>Can be <jk>null</jk>. 768 * <br>Stream is automatically closed. 769 * @param maxBytes 770 * The maximum number of bytes to read, or <c>-1</c> to read all bytes. 771 * @param cs 772 * The charset of the contents of the input stream. 773 * @return 774 * The contents of the input stream as a string, or <jk>null</jk> if the input stream was <jk>null</jk>. 775 * @throws IOException If a problem occurred trying to read from the input stream. 776 */ 777 public static String read(InputStream in, int maxBytes, Charset cs) throws IOException { 778 if (in == null) 779 return null; 780 try (var in2 = in) { 781 var bytes = readBytes(in2, maxBytes); 782 return new String(bytes, cs); 783 } 784 } 785 786 /** 787 * Reads the contents of an input stream into a string, reading up to the specified maximum number of bytes. 788 * 789 * <p> 790 * Assumes UTF-8 encoding. 791 * 792 * @param in 793 * The input stream. 794 * <br>Can be <jk>null</jk>. 795 * <br>Stream is automatically closed. 796 * @param maxBytes 797 * The maximum number of bytes to read, or <c>-1</c> to read all bytes. 798 * @return 799 * The contents of the input stream as a string, or <jk>null</jk> if the input stream was <jk>null</jk>. 800 * @throws IOException If a problem occurred trying to read from the input stream. 801 */ 802 public static String read(InputStream in, long maxBytes) throws IOException { 803 if (maxBytes > Integer.MAX_VALUE) 804 maxBytes = Integer.MAX_VALUE; 805 return read(in, (int)maxBytes, UTF8); 806 } 807 808 /** 809 * Pipes the specified object to the specified output stream. 810 * 811 * @param in 812 * The input byte array. 813 * <br>Can be <jk>null</jk> or any of the following types: 814 * <ul> 815 * <li>{@link Reader} 816 * <li>{@link InputStream} 817 * <li>{@link File} 818 * <li>byte array. 819 * </ul> 820 * @return The input converted to a string. 821 * @throws IOException If thrown from output stream. 822 */ 823 public static String read(Object in) throws IOException { 824 if (in == null) 825 return null; 826 if (in instanceof Reader in2) 827 return read(in2); 828 if (in instanceof InputStream in2) 829 return read(in2); 830 if (in instanceof File in2) 831 return read(in2); 832 if (in instanceof byte[] in2) 833 return read(in2); 834 throw illegalArg("Invalid type passed to read: {0}", cn(in)); 835 } 836 837 /** 838 * Reads the contents of a path into a string. 839 * 840 * <p> 841 * Assumes default character encoding. 842 * 843 * @param in 844 * The path to read. 845 * <br>Can be <jk>null</jk>. 846 * @return 847 * The contents of the reader as a string, or <jk>null</jk> if path does not exist. 848 * @throws IOException If a problem occurred trying to read from the reader. 849 * @since 9.1.0 850 */ 851 public static String read(Path in) throws IOException { 852 if (in == null || ! Files.exists(in)) { 853 return null; 854 } 855 try (var r = PathReaderBuilder.create(in).build()) { 856 return read(r, Files.size(in)); 857 } 858 } 859 860 /** 861 * Reads the contents of a reader into a string. 862 * 863 * @param in 864 * The input reader. 865 * <br>Can be <jk>null</jk>. 866 * <br>Stream is automatically closed. 867 * @return 868 * The contents of the reader as a string, or <jk>null</jk> if the reader was <jk>null</jk>. 869 * @throws IOException If a problem occurred trying to read from the reader. 870 */ 871 public static String read(Reader in) throws IOException { 872 try (var in2 = in) { 873 return read(in, -1); 874 } 875 } 876 877 /** 878 * Reads the contents of a reader into a string. 879 * 880 * @param in 881 * The input reader. 882 * <br>Can be <jk>null</jk>. 883 * <br>Stream is automatically closed. 884 * @param onException Consumer of any {@link IOException I/O exceptions}. 885 * @return 886 * The contents of the reader as a string, or <jk>null</jk> if the reader was <jk>null</jk>. 887 */ 888 public static String read(Reader in, Consumer<IOException> onException) { 889 try (var in2 = in) { 890 return read(in, -1); 891 } catch (IOException e) { 892 onException.accept(e); 893 return null; 894 } 895 } 896 897 /** 898 * Reads the specified input into a {@link String} until the end of the input is reached. 899 * 900 * @param in 901 * The input reader. 902 * <br>Can be <jk>null</jk>. 903 * <br>String is automatically closed. 904 * @param expectedLength 905 * Specify a positive number if the length of the input is known, or <c>-1</c> if unknown. 906 * @return 907 * The contents of the reader as a string, or <jk>null</jk> if the reader was <jk>null</jk>. 908 * @throws IOException If a problem occurred trying to read from the reader. 909 */ 910 public static String read(Reader in, long expectedLength) throws IOException { 911 if (in == null) 912 return null; 913 try (var in2 = in) { 914 var sb = new StringBuilder(buffSize(expectedLength)); // Assume they're ASCII characters. 915 var buf = charBuffer((int)expectedLength); 916 var i = 0; 917 while ((i = in2.read(buf)) != -1) 918 sb.append(buf, 0, i); 919 return sb.toString(); 920 } 921 } 922 923 /** 924 * Read the specified file into a byte array. 925 * 926 * @param in 927 * The file to read into a byte array. 928 * @return The contents of the file as a byte array. 929 * @throws IOException Thrown by underlying stream. 930 */ 931 public static byte[] readBytes(File in) throws IOException { 932 return readBytes(in, -1); 933 } 934 935 /** 936 * Read the specified file into a byte array. 937 * 938 * @param in 939 * The file to read into a byte array. 940 * @param maxBytes 941 * The maximum number of bytes to read, or <jk>-1</jk> to read all bytes. 942 * @return The contents of the file as a byte array. 943 * @throws IOException Thrown by underlying stream. 944 */ 945 public static byte[] readBytes(File in, int maxBytes) throws IOException { 946 if (in == null || ! (in.exists() && in.canRead())) 947 return new byte[0]; 948 try (var is = new FileInputStream(in)) { 949 return readBytes(is, maxBytes); 950 } 951 } 952 953 /** 954 * Reads the specified input stream into the specified byte array. 955 * 956 * @param in 957 * The input stream to read. 958 * <br>Can be <jk>null</jk>. 959 * <br>Stream is automatically closed. 960 * @return A byte array containing the contents. Never <jk>null</jk>. 961 * @throws IOException Thrown by underlying stream. 962 */ 963 public static byte[] readBytes(InputStream in) throws IOException { 964 try (var in2 = in) { 965 return readBytes(in2, -1); 966 } 967 } 968 969 /** 970 * Reads the specified input stream into the specified byte array. 971 * 972 * @param in 973 * The input stream to read. 974 * <br>Can be <jk>null</jk>. 975 * <br>Stream is not automatically closed. 976 * @param maxBytes 977 * The maximum number of bytes or <c>-1</c> to read the entire stream. 978 * @return A byte array containing the contents. Never <jk>null</jk>. 979 * @throws IOException Thrown by underlying stream. 980 */ 981 public static byte[] readBytes(InputStream in, int maxBytes) throws IOException { 982 if (in == null) 983 return new byte[0]; 984 var buff = new ByteArrayOutputStream(buffSize(maxBytes)); 985 var nRead = 0; 986 var b = byteBuffer(maxBytes); 987 while ((nRead = in.read(b, 0, b.length)) != -1) 988 buff.write(b, 0, nRead); 989 buff.flush(); 990 return buff.toByteArray(); 991 } 992 993 /** 994 * Reads the specified input stream into the specified byte array. 995 * 996 * @param in 997 * The input stream to read. 998 * <br>Can be <jk>null</jk>. 999 * <br>Stream is automatically closed. 1000 * @return A byte array containing the contents. Never <jk>null</jk>. 1001 * @throws IOException Thrown by underlying stream. 1002 */ 1003 public static byte[] readBytes(Reader in) throws IOException { 1004 if (in == null) 1005 return new byte[0]; 1006 try (var in2 = in) { 1007 return read(in2, -1).getBytes(); 1008 } 1009 } 1010 1011 /** 1012 * Wraps the specified reader in a buffered reader. 1013 * 1014 * @param r The reader being wrapped. 1015 * @return 1016 * The reader wrapped in a {@link BufferedReader}, or the original {@link Reader} if it's already a buffered 1017 * reader. 1018 */ 1019 public static Reader toBufferedReader(Reader r) { 1020 if (r == null || r instanceof BufferedReader || r instanceof StringReader) 1021 return r; 1022 return new BufferedReader(r); 1023 } 1024 1025 private static int buffSize(long max) { 1026 return (max > 0 && max < BUFF_SIZE) ? (int)max : BUFF_SIZE; 1027 } 1028 1029 private static byte[] byteBuffer(int maxBytes) { 1030 if (nn(BYTE_BUFFER_CACHE)) { 1031 var x = BYTE_BUFFER_CACHE.get(); 1032 if (x == null) { 1033 x = new byte[BUFF_SIZE]; 1034 BYTE_BUFFER_CACHE.set(x); 1035 BYTE_BUFFER_CACHE_MISSES.incrementAndGet(); 1036 } else { 1037 BYTE_BUFFER_CACHE_HITS.incrementAndGet(); 1038 } 1039 return x; 1040 } 1041 return new byte[buffSize(maxBytes)]; 1042 } 1043 1044 private static char[] charBuffer(int maxChars) { 1045 if (nn(CHAR_BUFFER_CACHE)) { 1046 var x = CHAR_BUFFER_CACHE.get(); 1047 if (x == null) { 1048 x = new char[BUFF_SIZE]; 1049 CHAR_BUFFER_CACHE.set(x); 1050 CHAR_BUFFER_CACHE_MISSES.incrementAndGet(); 1051 } else { 1052 CHAR_BUFFER_CACHE_HITS.incrementAndGet(); 1053 } 1054 return x; 1055 } 1056 return new char[buffSize(maxChars)]; 1057 } 1058 1059 /** 1060 * Constructor. 1061 */ 1062 protected IoUtils() {} 1063}