001/* 002 * ============================================================================ 003 * Copyright © 2002-2024 by Thomas Thrien. 004 * All Rights Reserved. 005 * ============================================================================ 006 * Licensed to the public under the agreements of the GNU Lesser General Public 007 * License, version 3.0 (the "License"). You may obtain a copy of the License at 008 * 009 * http://www.gnu.org/licenses/lgpl.html 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, WITHOUT 013 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 014 * License for the specific language governing permissions and limitations 015 * under the License. 016 */ 017 018package org.tquadrat.foundation.util; 019 020import static java.lang.System.getProperty; 021import static java.lang.System.out; 022import static java.nio.file.FileVisitResult.CONTINUE; 023import static java.nio.file.Files.delete; 024import static java.nio.file.Files.walkFileTree; 025import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE; 026import static java.nio.file.attribute.PosixFilePermission.OWNER_READ; 027import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; 028import static org.apiguardian.api.API.Status.INTERNAL; 029import static org.apiguardian.api.API.Status.STABLE; 030import static org.tquadrat.foundation.lang.CommonConstants.PROPERTY_TEMPFOLDER; 031import static org.tquadrat.foundation.lang.CommonConstants.PROPERTY_USER_NAME; 032import static org.tquadrat.foundation.lang.Objects.nonNull; 033import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument; 034import static org.tquadrat.foundation.lang.Objects.requireNotEmptyArgument; 035 036import java.io.File; 037import java.io.IOException; 038import java.io.OutputStream; 039import java.io.PrintStream; 040import java.io.Reader; 041import java.nio.file.FileSystems; 042import java.nio.file.FileVisitResult; 043import java.nio.file.Files; 044import java.nio.file.Path; 045import java.nio.file.SimpleFileVisitor; 046import java.nio.file.attribute.BasicFileAttributes; 047import java.nio.file.attribute.FileAttribute; 048import java.nio.file.attribute.PosixFilePermission; 049import java.nio.file.attribute.PosixFilePermissions; 050import java.security.MessageDigest; 051import java.security.NoSuchAlgorithmException; 052import java.util.EnumSet; 053import java.util.Set; 054import java.util.zip.Adler32; 055import java.util.zip.CRC32; 056import java.util.zip.Checksum; 057 058import org.apiguardian.api.API; 059import org.tquadrat.foundation.annotation.ClassVersion; 060import org.tquadrat.foundation.annotation.UtilityClass; 061import org.tquadrat.foundation.exception.PrivateConstructorForStaticClassCalledError; 062import org.tquadrat.foundation.lang.CommonConstants; 063 064/** 065 * Some I/O, file, file system and network related helper and convenience 066 * methods. 067 * 068 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 069 * @version $Id: IOUtils.java 1092 2024-02-01 22:49:38Z tquadrat $ 070 * @since 0.0.5 071 * 072 * @UMLGraph.link 073 */ 074@ClassVersion( sourceVersion = "$Id: IOUtils.java 1092 2024-02-01 22:49:38Z tquadrat $" ) 075@UtilityClass 076public final class IOUtils 077{ 078 /*---------------*\ 079 ====** Inner Classes **==================================================== 080 \*---------------*/ 081 /** 082 * This implementation of an 083 * {@link Appendable} 084 * just swallows any data that is written to it, like the {@code /dev/null} 085 * device of a Unix or Linux machine, or {@code NUL:} on Windows. This class 086 * might be useful if an {@code Appendable} is required from the API, but 087 * not applicable from the application logic. 088 * 089 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 090 * @version $Id: IOUtils.java 1092 2024-02-01 22:49:38Z tquadrat $ 091 * @since 0.0.5 092 * 093 * @UMLGraph.link 094 */ 095 @SuppressWarnings( "PublicInnerClass" ) 096 @ClassVersion( sourceVersion = "$Id: IOUtils.java 1092 2024-02-01 22:49:38Z tquadrat $" ) 097 @API( status = STABLE, since = "0.1.0" ) 098 public static class NullAppendable implements Appendable 099 { 100 /*--------------*\ 101 ====** Constructors **===================================================== 102 \*--------------*/ 103 /** 104 * Creates a new instance of {@code NullAppendable}. 105 */ 106 public NullAppendable() { super(); } 107 108 /*---------*\ 109 ====** Methods **========================================================== 110 \*---------*/ 111 /** 112 * Appends the specified character sequence to this Appendable. In fact, 113 * it does nothing. 114 * 115 * @param csq {@inheritDoc} 116 */ 117 @Override 118 public final Appendable append( final CharSequence csq ) throws IOException { return this; } 119 120 /** 121 * Appends a subsequence of the specified character sequence to this 122 * Appendable. In fact, it does nothing. 123 * 124 * @param csq {@inheritDoc} 125 */ 126 @Override 127 public final Appendable append( final CharSequence csq, final int start, final int end ) throws IOException { return this; } 128 129 /** 130 * Appends the specified character to this Appendable; in fact, it does 131 * nothing. 132 * 133 * @param c {@inheritDoc} 134 */ 135 @Override 136 public final Appendable append( final char c ) throws IOException { return this; } 137 } 138 // class NullAppendable 139 140 /** 141 * The default file attributes. 142 * 143 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 144 * @version $Id: IOUtils.java 1092 2024-02-01 22:49:38Z tquadrat $ 145 * @since 0.0.6 146 * 147 * @UMLGraph.link 148 */ 149 @ClassVersion( sourceVersion = "$Id: IOUtils.java 1092 2024-02-01 22:49:38Z tquadrat $" ) 150 @UtilityClass 151 private static final class PosixPermissions 152 { 153 /*------------------------*\ 154 ====** Static Initialisations **======================================= 155 \*------------------------*/ 156 /** 157 * The default attributes for a temporary file. 158 * 159 * @since 0.0.6 160 */ 161 @API( status = INTERNAL, since = "0.0.6" ) 162 static final FileAttribute<Set<PosixFilePermission>> tempFilePermissions; 163 164 /** 165 * The default attributes for a temporary folder. 166 * 167 * @since 0.0.6 168 */ 169 @API( status = INTERNAL, since = "0.0.6" ) 170 static final FileAttribute<Set<PosixFilePermission>> tempDirPermissions; 171 172 static 173 { 174 tempFilePermissions = PosixFilePermissions.asFileAttribute( EnumSet.of( OWNER_READ, OWNER_WRITE ) ); 175 tempDirPermissions = PosixFilePermissions.asFileAttribute( EnumSet.of( OWNER_READ, OWNER_WRITE, OWNER_EXECUTE ) ); 176 } 177 178 /*--------------*\ 179 ====** Constructors **================================================= 180 \*--------------*/ 181 /** 182 * No instance allowed for this class! 183 */ 184 private PosixPermissions() { throw new PrivateConstructorForStaticClassCalledError( PosixPermissions.class ); } 185 } 186 // class PosixPermissions 187 188 /*-----------*\ 189 ====** Constants **======================================================== 190 \*-----------*/ 191 /** 192 * Some methods in this class need a buffer; the size of this buffer is 193 * defined here: {@value}. 194 */ 195 @API( status = STABLE, since = "0.0.5" ) 196 public static final int DEFAULT_BUFFER_SIZE = 8192; 197 198 /** 199 * The flag that indicates if the default file systems is POSIX compliant. 200 * 201 * @since 0.0.6 202 */ 203 @API( status = STABLE, since = "0.0.6" ) 204 public static final boolean DEFAULT_FILESYSTEM_IS_POSIX_COMPLIANT = FileSystems.getDefault().supportedFileAttributeViews().contains("posix"); 205 206 /*--------------*\ 207 ====** Constructors **===================================================== 208 \*--------------*/ 209 /** 210 * No instance allowed for this class. 211 */ 212 private IOUtils() { throw new PrivateConstructorForStaticClassCalledError( IOUtils.class ); } 213 214 /*---------*\ 215 ====** Methods **========================================================== 216 \*---------*/ 217 /** 218 * Unconditionally closes an object instance of a class that implements 219 * the interface 220 * {@link AutoCloseable}.<br> 221 * Equivalent to 222 * {@link AutoCloseable#close()}, 223 * except any exceptions will be ignored. This is typically used in 224 * {@code finally} blocks.<br> 225 * <br>Even after the introduction of {@code try-with-resources} with 226 * Java 7, this method can be still helpful. 227 * 228 * @param closeable The {@code AutoCloseable} instance to close, can be 229 * {@code null} or already closed. 230 */ 231 @API( status = STABLE, since = "0.0.5" ) 232 public static final void closeQuietly( final AutoCloseable closeable ) 233 { 234 try 235 { 236 if( nonNull( closeable ) ) closeable.close(); 237 } 238 catch( final Exception ignored ) { /* Deliberately ignored! */ } 239 } // closeQuietly() 240 241 /** 242 * <p>{@summary Creates a new directory by creating all nonexistent parent 243 * directories first.} Unlike the 244 * {@link #createDirectory(File,FileAttribute...)} 245 * method, an exception is not thrown if the directory could not be 246 * created because it already exists.</p> 247 * <p>The {@code attributes} parameter is optional to set 248 * {@linkplain FileAttribute file-attributes} 249 * atomically when creating the non-existent directories. Each file 250 * attribute is identified by its 251 * {@linkplain FileAttribute#name name}. 252 * If more than one attribute of the same name is included in the array 253 * then all but the last occurrence is ignored.</p> 254 * <p>If this method fails, then it may do so after creating some, but 255 * not all, of the parent directories.</p> 256 * 257 * @param dir The directory to create. 258 * @param attributes An optional list of file attributes to set 259 * atomically when creating the directory. 260 * @return The directory. 261 * @throws UnsupportedOperationException The attributes array contains 262 * an attribute that cannot be set atomically when creating the 263 * directory. 264 * @throws java.nio.file.FileAlreadyExistsException The {@code dir} 265 * exists but is not a directory <i>(optional specific exception)</i>. 266 * @throws IOException An I/O error occurred. 267 * @throws SecurityException In the case a security manager is 268 * installed, the 269 * {@link SecurityManager#checkWrite(String) checkWrite()} 270 * method is invoked prior to attempting to create a directory and its 271 * {@link SecurityManager#checkRead(String) checkRead()} 272 * is invoked for each parent directory that is checked. If 273 * {@code dir} is not an absolute path then its 274 * {@link File#getAbsoluteFile()} 275 * method may need to be invoked to get its absolute path. This may 276 * invoke the security manager's 277 * {@link SecurityManager#checkPropertyAccess(String) checkPropertyAccess()} 278 * method to check access to the system property {@code user.dir}. 279 * 280 * @see Files#createDirectories(Path, FileAttribute...) 281 * @see File#mkdirs() 282 * 283 * @since 0.0.6 284 */ 285 @API( status = STABLE, since = "0.0.6" ) 286 public static final File createDirectories( final File dir, final FileAttribute<?>... attributes ) throws IOException 287 { 288 final var path = Files.createDirectories( requireNonNullArgument( dir, "dir" ).toPath(), requireNonNullArgument( attributes, "attributes" ) ); 289 final var retValue = path.toFile(); 290 291 //---* Done *---------------------------------------------------------- 292 return retValue; 293 } // createDirectories() 294 295 /** 296 * <p>{@summary Creates a new directory.} The check for the existence of the file and 297 * the creation of the directory if it does not exist are a single 298 * operation that is atomic with respect to all other filesystem 299 * activities that might affect the directory. The 300 * {@link #createDirectories(File, FileAttribute...) createDirectories()} 301 * method should be used where it is required to create all nonexistent 302 * parent directories first.</p> 303 * <p>The {@code attributes} parameter is optional to set 304 * {@linkplain FileAttribute file-attributes} 305 * atomically when creating the directory. Each attribute is identified by 306 * its 307 * {@linkplain FileAttribute#name name}. 308 * If more than one attribute of the same name is included in the array 309 * then all but the last occurrence is ignored.</p> 310 * 311 * @param dir The directory to create. 312 * @param attributes An optional list of file attributes to set 313 * atomically when creating the directory 314 * @return The directory. 315 * @throws UnsupportedOperationException The attributes array contains 316 * an attribute that cannot be set atomically when creating the 317 * directory. 318 * @throws java.nio.file.FileAlreadyExistsException A directory could 319 * not otherwise be created because a file of that name already exists 320 * <i>(optional specific exception)</i>. 321 * @throws IOException An I/O error occurred or the parent directory does 322 * not exist. 323 * @throws SecurityException In the case a security manager is 324 * installed, the 325 * {@link SecurityManager#checkWrite(String) checkWrite()} 326 * method is invoked to check write access to the new directory. 327 * 328 * @since 0.0.6 329 */ 330 @API( status = STABLE, since = "0.0.6" ) 331 public static File createDirectory( final File dir, final FileAttribute<?>... attributes ) throws IOException 332 { 333 final var path = Files.createDirectory( requireNonNullArgument( dir, "dir" ).toPath(), requireNonNullArgument( attributes, "attributes" ) ); 334 final var retValue = path.toFile(); 335 336 //---* Done *---------------------------------------------------------- 337 return retValue; 338 } // createDirectory() 339 340 /** 341 * <p>{@summary Creates a directory named after the account name of the 342 * current user (determined by the system property 343 * {@value CommonConstants#PROPERTY_USER_NAME}) 344 * in the default {@code temp} folder, determined by the system property 345 * {@value CommonConstants#PROPERTY_TEMPFOLDER}.}</p> 346 * <p>The access rights are set for the current user only (for UNIX, it 347 * would be 700).</p> 348 * <p>The new directory will <i>not</i> be removed automatically after 349 * program termination.</p> 350 * <p>The class 351 * {@link File} 352 * provides a static method 353 * {@link File#createTempFile(String, String) createTempFile()} 354 * that creates a temporary file in the default temporary folder. As this 355 * may be not a problem on a single-user Windows system with default 356 * configuration, it will cause security problems on UNIX-like systems. 357 * Therefore, it is recommended, to use 358 * {@link File#createTempFile(String, String, File)} 359 * instead, like this:</p> 360 * <pre><code>File tempFile = File.createTempFile( "PREFIX", "EXT", createTempDirectory() );</code></pre> 361 * <p>This will guarantee that the temporary files cannot be read by 362 * other users.</p> 363 * 364 * @return The new temporary directory; it is guaranteed that the 365 * directory exists after the call to this method returned. 366 * @throws IOException Something has gone wrong. 367 * 368 * @since 0.0.6 369 */ 370 @API( status = STABLE, since = "0.0.6" ) 371 public static final File createTempDirectory() throws IOException 372 { 373 //---* Create the file object for the temp directory *----------------- 374 final var tempDir = getSystemTempFolder(); 375 final var userTempDir = tempDir.resolve( getProperty( PROPERTY_USER_NAME ) ); 376 377 //---* Get the file attributes *--------------------------------------- 378 @SuppressWarnings( "ZeroLengthArrayAllocation" ) 379 final FileAttribute<?> [] attributes = DEFAULT_FILESYSTEM_IS_POSIX_COMPLIANT ? new FileAttribute [] {PosixPermissions.tempDirPermissions} : new FileAttribute [0]; 380 381 //---* Create the directory *------------------------------------------ 382 final var retValue = Files.createDirectories( userTempDir, attributes ).toFile(); 383 384 //---* Done *---------------------------------------------------------- 385 return retValue; 386 } // createTempDirectory() 387 388 /** 389 * <p>{@summary Creates a new temporary directory in the specified 390 * directory, using the given prefix to generate its name.}</p> 391 * <p>The details as to how the name of the directory is constructed is 392 * implementation dependent and therefore not specified. Where possible 393 * the {@code prefix} is used to construct candidate names.</p> 394 * <p>The {@code attributes} parameter is optional to set 395 * {@linkplain FileAttribute file-attributes} 396 * atomically when creating the directory. Each attribute is identified by 397 * its 398 * {@linkplain FileAttribute#name() name}. 399 * If more than one attribute of the same name is included in the array 400 * then all but the last occurrence is ignored.</p> 401 * 402 * @param dir The directory in which to create the temporary directory. 403 * @param prefix The prefix string to be used in generating the 404 * directory's name; may be {@code null}. 405 * @param attributes An optional list of file attributes to set 406 * atomically when creating the directory. 407 * @return The path to the newly created directory that did not exist 408 * before this method was invoked. 409 * @throws IllegalArgumentException The prefix cannot be used to 410 * generate a candidate directory name. 411 * @throws UnsupportedOperationException The attributes array contains 412 * an attribute that cannot be set atomically when creating the 413 * directory. 414 * @throws IOException An I/O error occurs or {@code dir} does not exist. 415 * @throws SecurityException In the case a security manager is 416 * installed, the 417 * {@link SecurityManager#checkWrite(String) checkWrite()} 418 * method is invoked to check write access when creating the 419 * directory. 420 * 421 * @see Files#createTempDirectory(Path,String,FileAttribute...) 422 * 423 * @since 0.0.6 424 */ 425 @API( status = STABLE, since = "0.0.6" ) 426 public static Path createTempDirectory( final File dir, final String prefix, final FileAttribute<?>... attributes ) throws IOException 427 { 428 return Files.createTempDirectory( requireNonNullArgument( dir, "dir" ).toPath(), prefix, requireNonNullArgument( attributes, "attributes" ) ); 429 } // createTempDirectory() 430 431 /** 432 * <p>{@summary Creates a new directory in the default temporary-file 433 * directory, using the given prefix to generate its name.}</p> 434 * <p>This method works in exactly the manner specified by 435 * {@link #createTempDirectory(File,String,FileAttribute[])} 436 * method for the case that the {@code dir} parameter is the 437 * temporary-file directory.</p> 438 * 439 * @param prefix The prefix string to be used in generating the 440 * directory's name; may be {@code null}. 441 * @param attributes An optional list of file attributes to set 442 * atomically when creating the directory. 443 * @return The 444 * {@link File} 445 * instance to the newly created directory that did not exist before 446 * this method was invoked. 447 * @throws IllegalArgumentException The prefix cannot be used to 448 * generate a candidate directory name. 449 * @throws UnsupportedOperationException The {@code attributes} array 450 * contains an attribute that cannot be set atomically when creating 451 * the directory. 452 * @throws IOException An I/O error occurred or the temporary-file 453 * directory does not exist. 454 * @throws SecurityException In the case a security manager is 455 * installed, the 456 * {@link SecurityManager#checkWrite(String) checkWrite()} 457 * method is invoked to check write access when creating the 458 * directory. 459 * 460 * @see Files#createTempDirectory(String, FileAttribute...) 461 * 462 * @since 0.0.6 463 */ 464 @API( status = STABLE, since = "0.0.6" ) 465 public static File createTempDirectory( final String prefix, final FileAttribute<?>... attributes ) throws IOException 466 { 467 final var path = Files.createTempDirectory( prefix, requireNonNullArgument( attributes, "attributes" ) ); 468 final var retValue = path.toFile(); 469 470 //---* Done *---------------------------------------------------------- 471 return retValue; 472 } // createTempDirectory() 473 474 /** 475 * <p>{@summary Deletes the folder (or file) that is determined by the 476 * given 477 * {@link Path} 478 * instance.} If the argument denotes a directory, the method will remove 479 * its contents first, recursively.</p> 480 * 481 * @param folder The folder to remove; despite the name of the argument 482 * and the method, this can be also a plain file. 483 * @throws IOException A problem occurred when deleting the {@code Path}. 484 */ 485 public static final void deleteFolder( final Path folder ) throws IOException 486 { 487 //noinspection AnonymousInnerClass,OverlyComplexAnonymousInnerClass 488 walkFileTree( requireNonNullArgument( folder, "folder" ), new SimpleFileVisitor<>() 489 { 490 /** 491 * {@inheritDoc} 492 */ 493 @Override 494 public final FileVisitResult postVisitDirectory( final Path directory, final IOException exception ) throws IOException 495 { 496 try 497 { 498 delete( directory ); 499 } 500 catch( final IOException e ) 501 { 502 if( nonNull( exception ) ) e.addSuppressed( exception ); 503 throw e; 504 } 505 506 //---* Done *-------------------------------------------------- 507 return CONTINUE; 508 } // postVisitDirectory() 509 510 /** 511 * {@inheritDoc} 512 */ 513 @Override 514 public final FileVisitResult visitFile( final Path file, final BasicFileAttributes attributes ) throws IOException 515 { 516 delete( file ); 517 518 //---* Done *-------------------------------------------------- 519 return CONTINUE; 520 } // visitFile() 521 } ); 522 } // deleteFolder() 523 524 /** 525 * <p>{@summary Deletes the folder (or file) that is determined by the 526 * given 527 * {@link File}. 528 * instance.} If the argument denotes a directory, the method will remove 529 * its contents first, recursively.</p> 530 * <p>Calls 531 * {@link #deleteFolder(Path)} 532 * internally.</p> 533 * 534 * @param folder The folder to remove; despite the name of the argument 535 * and the method, this can be also a plain file. 536 * @throws IOException A problem occurred when deleting the {@code Path}. 537 */ 538 public static final void deleteFolder( final File folder ) throws IOException 539 { 540 deleteFolder( requireNonNullArgument( folder, "folder" ).toPath() ); 541 } // deleteFolder() 542 543 /** 544 * <p>{@summary Calculates the check sum for the given file, using the 545 * algorithm with the given name.}</p> 546 * <p>If the name is one of</p> 547 * <ul> 548 * <li>CRC32</li> 549 * <li>Adler32</li> 550 * </ul> 551 * <p>the method uses 552 * {@link java.util.zip.CRC32} 553 * or 554 * {@link java.util.zip.Adler32} 555 * for the calculation, any other name is taken as the name for a 556 * {@linkplain MessageDigest}; 557 * all JVMs know</p> 558 * <ul> 559 * <li>MD5</li> 560 * <li>SHA1</li> 561 * </ul> 562 * <p>others can be added by installing additional 563 * {@linkplain java.security.Provider security providers}.</p> 564 * <p>This method calls 565 * {@link #determineCheckSum(Path, String)} 566 * internally.</p> 567 * 568 * @param file The file to process. 569 * @param algorithm The name for the algorithm to use for the check sum 570 * calculation. 571 * @return The check sum as a hex string. 572 * @throws IOException Problems to process the file. 573 * @throws NoSuchAlgorithmException The provided algorithm does not 574 * exist or the provider for it is not installed properly. 575 */ 576 @API( status = STABLE, since = "0.0.5" ) 577 public static final String determineCheckSum( final File file, final String algorithm ) throws IOException, NoSuchAlgorithmException 578 { 579 final var retValue = determineCheckSum( requireNonNullArgument( file, "file" ).toPath(), algorithm ); 580 581 //---* Done *---------------------------------------------------------- 582 return retValue; 583 } // determineCheckSum() 584 585 /** 586 * <p>{@summary Calculates the check sum for the given file, using the 587 * algorithm with the given name.}</p> 588 * <p>If the name is one of</p> 589 * <ul> 590 * <li>CRC32</li> 591 * <li>Adler32</li> 592 * </ul> 593 * <p>the method uses 594 * {@link java.util.zip.CRC32} 595 * or 596 * {@link java.util.zip.Adler32} 597 * for the calculation, any other name is taken as the name for a 598 * {@linkplain MessageDigest}; 599 * all JVMs know</p> 600 * <ul> 601 * <li>MD5</li> 602 * <li>SHA1</li> 603 * </ul> 604 * <p>others can be added by installing additional 605 * {@linkplain java.security.Provider security providers}.</p> 606 * 607 * @param file The file to process. 608 * @param algorithm The name for the algorithm to use for the check sum 609 * calculation. 610 * @return The check sum as a hex string. 611 * @throws IOException Problems to process the file. 612 * @throws NoSuchAlgorithmException The provided algorithm does not 613 * exist or the provider for it is not installed properly. 614 */ 615 @API( status = STABLE, since = "0.0.5" ) 616 public static final String determineCheckSum( final Path file, final String algorithm ) throws IOException, NoSuchAlgorithmException 617 { 618 final var retValue = switch( requireNotEmptyArgument( algorithm, "algorithm" ) ) 619 { 620 case "Adler32" -> Hash.create( file, new Adler32() ).toString(); 621 case "CRC32" -> Hash.create( file, new CRC32() ).toString(); 622 default -> Hash.create( file, MessageDigest.getInstance( algorithm ) ).toString(); 623 }; 624 625 //---* Done *---------------------------------------------------------- 626 return retValue; 627 } // determineCheckSum() 628 629 /** 630 * Calculates the check sum for the given file, using provided check sum 631 * algorithm implementation. 632 * 633 * @param file The file to process. 634 * @param algorithm The check sum algorithm to use for the check sum 635 * calculation. 636 * @return The check sum. 637 * @throws IOException Problems to process the file. 638 */ 639 @API( status = STABLE, since = "0.0.5" ) 640 public static final long determineCheckSum( final File file, final Checksum algorithm ) throws IOException 641 { 642 final var retValue = determineCheckSum( requireNonNullArgument( file, "file" ).toPath(), algorithm ); 643 644 //---* Done *---------------------------------------------------------- 645 return retValue; 646 } // determineCheckSum() 647 648 /** 649 * Calculates the check sum for the given file, using provided check sum 650 * algorithm implementation. 651 * 652 * @param file The file to process. 653 * @param algorithm The check sum algorithm to use for the check sum 654 * calculation. 655 * @return The check sum. 656 * @throws IOException Problems to process the file. 657 */ 658 @API( status = STABLE, since = "0.0.5" ) 659 public static final long determineCheckSum( final Path file, final Checksum algorithm ) throws IOException 660 { 661 final var retValue = Hash.create( file, algorithm ) 662 .number() 663 .longValue(); 664 665 //---* Done *---------------------------------------------------------- 666 return retValue; 667 } // determineCheckSum() 668 669 /** 670 * Calculates the check sum for the given file, using the provided 671 * {@link MessageDigest}. 672 * 673 * @param file The file to process. 674 * @param algorithm The {@code MessageDigest} to use for the check sum 675 * calculation. 676 * @return The check sum as a byte array. 677 * @throws IOException Problems to process the file. 678 */ 679 @API( status = STABLE, since = "0.0.5" ) 680 public static final byte [] determineCheckSum( final File file, final MessageDigest algorithm ) throws IOException 681 { 682 final var retValue = determineCheckSum( requireNonNullArgument( file, "file" ).toPath(), algorithm ); 683 684 //---* Done *---------------------------------------------------------- 685 return retValue; 686 } // determineCheckSum() 687 688 /** 689 * Calculates the check sum for the given file, using the provided 690 * {@link MessageDigest}. 691 * 692 * @param file The file to process. 693 * @param algorithm The {@code MessageDigest} to use for the check sum 694 * calculation. 695 * @return The check sum as a byte array. 696 * @throws IOException Problems to process the file. 697 */ 698 @API( status = STABLE, since = "0.0.5" ) 699 public static final byte [] determineCheckSum( final Path file, final MessageDigest algorithm ) throws IOException 700 { 701 final var retValue = Hash.create( file, algorithm ).bytes(); 702 703 //---* Done *---------------------------------------------------------- 704 return retValue; 705 } // determineCheckSum() 706 707 /** 708 * Returns an 709 * {@link Appendable} 710 * that just swallows any data that is written to it, like the 711 * {@code /dev/null} device of a Unix or Linux machine, or {@code NUL:} 712 * on Windows. 713 * 714 * @return A null appendable. 715 * 716 * @see NullAppendable 717 */ 718 @API( status = STABLE, since = "0.0.5" ) 719 public static final Appendable getNullAppendable() { return new NullAppendable(); } 720 721 /** 722 * Returns the 723 * {@link Path} 724 * object that represents the system's default temporary folder as 725 * specified in 726 * {@value CommonConstants#PROPERTY_TEMPFOLDER}. 727 * 728 * @return The system temp folder. 729 * 730 * @since 0.4.0 731 */ 732 @API( status = STABLE, since = "0.4.0" ) 733 public static final Path getSystemTempFolder() { return Path.of( getProperty( PROPERTY_TEMPFOLDER ) ); } 734 735 /** 736 * <p>{@summary Returns 737 * {@link System#out} 738 * with a non-functional 739 * {@link OutputStream#close()} 740 * method.}</p> 741 * <p>Assume the following scenario:</p> 742 * <pre><code> … 743 * Optional<File> outputFile = … 744 * … 745 * PrintStream outputStream = outputFile.isPresent() ? new PrintStream( new FileOutputStream( outputFile.get() ) ) : IOUtils.getUncloseableOut(); 746 * try( outputStream ) 747 * { 748 * /** 749 * * Print something ... 750 * */ 751 * … 752 * } 753 * …</code></pre> 754 * <p>The output stream will be close at the end of the {@code try} block; 755 * this is desired in case of a 756 * {@link java.io.FileOutputStream FileOutputStream}, 757 * but totally not wanted if the output stream is {@code System.out}.</p> 758 * 759 * @return {@code System.out} without the {@code close()} method. 760 */ 761 @SuppressWarnings( "ImplicitDefaultCharsetUsage" ) 762 @API( status = STABLE, since = "0.0.7" ) 763 public static final PrintStream getUncloseableOut() 764 { 765 @SuppressWarnings( {"AnonymousInnerClass", "UseOfSystemOutOrSystemErr"} ) 766 final var retValue = new PrintStream( out ) 767 { 768 /** 769 * {@inheritDoc} 770 */ 771 @Override 772 public final void close() { /* Does nothing */ } 773 }; 774 775 //---* Done *---------------------------------------------------------- 776 return retValue; 777 } // getUncloseableOut() 778 779 /** 780 * <p>{@summary Reads the complete content of the provided 781 * {@link Reader} 782 * into a 783 * {@link String}.}</p> 784 * <p>Obviously this method is feasible only for files with a limited 785 * size.</p> 786 * 787 * @param reader The {@code Reader} instance. 788 * @return The content of the provided {@code Reader}. 789 * @throws IOException Problems on reading from the {@code Reader}. 790 */ 791 @API( status = STABLE, since = "0.0.5" ) 792 public static final String loadToString( final Reader reader ) throws IOException 793 { 794 final var builder = new StringBuilder( DEFAULT_BUFFER_SIZE ); 795 final var buffer = new char [DEFAULT_BUFFER_SIZE]; 796 var bytesRead = requireNonNullArgument( reader, "reader" ).read( buffer ); 797 while( bytesRead > 0 ) 798 { 799 builder.append( buffer, 0, bytesRead ); 800 bytesRead = reader.read( buffer ); 801 } 802 final var retValue = builder.toString(); 803 804 //---* Done *---------------------------------------------------------- 805 return retValue; 806 } // loadToString() 807} 808// class IOUtils 809 810/* 811 * End of File 812 */