001package jmri.util; 002 003import java.io.File; 004import java.io.FileNotFoundException; 005import java.io.IOException; 006import java.io.InputStream; 007import java.net.URI; 008import java.net.URL; 009import java.util.Map; 010import java.util.Set; 011import java.util.jar.JarFile; 012import javax.annotation.CheckReturnValue; 013import javax.annotation.Nonnull; 014import javax.annotation.CheckForNull; 015 016import jmri.profile.Profile; 017import jmri.profile.ProfileManager; 018 019/** 020 * Common utility methods for working with Files. 021 * <p> 022 * All methods in this class call the identical method from the default instance 023 * of {@link FileUtilSupport}. 024 * 025 * @author Bob Jacobsen Copyright 2003, 2005, 2006 026 * @author Randall Wood Copyright 2012, 2013, 2014, 2016, 2019 027 * @see FileUtilSupport 028 */ 029public final class FileUtil { 030 031 /** 032 * Portable reference to items in the JMRI program directory. 033 */ 034 static public final String PROGRAM = "program:"; // NOI18N 035 /** 036 * Portable reference to the JMRI user's files and preferences directory. 037 */ 038 static public final String PREFERENCES = "preference:"; // NOI18N 039 /** 040 * Portable reference to the JMRI applications preferences directory. 041 */ 042 static public final String SETTINGS = "settings:"; // NOI18N 043 /** 044 * Portable reference to the user's home directory. 045 */ 046 static public final String HOME = "home:"; // NOI18N 047 /** 048 * Portable reference to the current profile directory. 049 */ 050 static public final String PROFILE = "profile:"; // NOI18N 051 /** 052 * Portable reference to the current scripts directory. 053 */ 054 static public final String SCRIPTS = "scripts:"; // NOI18N 055 /** 056 * The portable file path component separator. 057 */ 058 static public final char SEPARATOR = '/'; // NOI18N 059 060 /** 061 * The types of locations to use when falling back on default locations in 062 * {@link #findURI(java.lang.String, jmri.util.FileUtil.Location, java.lang.String...)}. 063 */ 064 static public enum Location { 065 INSTALLED, USER, ALL, NONE 066 } 067 068 /** 069 * Get the {@link java.io.File} that path refers to. Throws a 070 * {@link java.io.FileNotFoundException} if the file cannot be found instead 071 * of returning null (as File would). Use {@link #getURI(java.lang.String) } 072 * or {@link #getURL(java.lang.String) } instead of this method if possible. 073 * 074 * @param path the path to find 075 * @return {@link java.io.File} at path 076 * @throws java.io.FileNotFoundException if path cannot be found 077 * @see #getURI(java.lang.String) 078 * @see #getURL(java.lang.String) 079 */ 080 @Nonnull 081 @CheckReturnValue 082 static public File getFile(@Nonnull String path) throws FileNotFoundException { 083 return FileUtilSupport.getDefault().getFile(path); 084 } 085 086 /** 087 * Get the {@link java.io.File} that path refers to. Throws a 088 * {@link java.io.FileNotFoundException} if the file cannot be found instead 089 * of returning null (as File would). Use {@link #getURI(java.lang.String) } 090 * or {@link #getURL(java.lang.String) } instead of this method if possible. 091 * 092 * @param profile the profile to use as a base 093 * @param path the path to find 094 * @return {@link java.io.File} at path 095 * @throws java.io.FileNotFoundException if path cannot be found 096 * @see #getURI(java.lang.String) 097 * @see #getURL(java.lang.String) 098 */ 099 @Nonnull 100 @CheckReturnValue 101 static public File getFile(@CheckForNull Profile profile, @Nonnull String path) throws FileNotFoundException { 102 return FileUtilSupport.getDefault().getFile(profile, path); 103 } 104 105 /** 106 * Get the {@link java.io.File} that path refers to. Throws a 107 * {@link java.io.FileNotFoundException} if the file cannot be found instead 108 * of returning null (as File would). 109 * 110 * @param path the path to find 111 * @return {@link java.io.File} at path 112 * @throws java.io.FileNotFoundException if path cannot be found 113 * @see #getFile(java.lang.String) 114 * @see #getURL(java.lang.String) 115 */ 116 @Nonnull 117 @CheckReturnValue 118 static public URI getURI(@Nonnull String path) throws FileNotFoundException { 119 return FileUtilSupport.getDefault().getURI(path); 120 } 121 122 /** 123 * Get the {@link java.net.URL} that path refers to. Throws a 124 * {@link java.io.FileNotFoundException} if the URL cannot be found instead 125 * of returning null. 126 * 127 * @param path the path to find 128 * @return {@link java.net.URL} at path 129 * @throws java.io.FileNotFoundException if path cannot be found 130 * @see #getFile(java.lang.String) 131 * @see #getURI(java.lang.String) 132 */ 133 @Nonnull 134 @CheckReturnValue 135 static public URL getURL(@Nonnull String path) throws FileNotFoundException { 136 return FileUtilSupport.getDefault().getURL(path); 137 } 138 139 /** 140 * Convenience method to get the {@link java.net.URL} from a 141 * {@link java.net.URI}. Logs errors and returns null if any exceptions are 142 * thrown by the conversion. 143 * 144 * @param uri The URI to convert. 145 * @return URL or null if any errors exist. 146 */ 147 @CheckForNull 148 @CheckReturnValue 149 static public URL getURL(@Nonnull URI uri) { 150 return FileUtilSupport.getDefault().getURL(uri); 151 } 152 153 /** 154 * Find all files matching the given name under the given root directory 155 * within both the user and installed file locations. 156 * 157 * @param name the name of the file to find 158 * @param root the relative path to a directory in either or both of the 159 * user or installed file locations; use a single period 160 * character to refer to the root of the user or installed file 161 * locations 162 * @return a set of found files or an empty set if no matching files were 163 * found 164 * @throws IllegalArgumentException if the name is not a relative path, is 165 * empty, or contains path separators; or 166 * if the root is not a relative path, is 167 * empty, or contains a parent directory 168 * (..) 169 * @throws NullPointerException if any parameter is null 170 */ 171 @Nonnull 172 @CheckReturnValue 173 static public Set<File> findFiles(@Nonnull String name, @Nonnull String root) throws IllegalArgumentException { 174 return FileUtilSupport.getDefault().findFiles(name, root); 175 } 176 177 /** 178 * Find all files matching the given name under the given root directory 179 * within the specified location. 180 * 181 * @param name the name of the file to find 182 * @param root the relative path to a directory in either or both of the 183 * user or installed file locations; use a single period 184 * character to refer to the root of the location 185 * @param location the location to search within 186 * @return a set of found files or an empty set if no matching files were 187 * found 188 * @throws IllegalArgumentException if the name is not a relative path, is 189 * empty, or contains path separators; if 190 * the root is not a relative path, is 191 * empty, or contains a parent directory 192 * (..); or if the location is 193 * {@link Location#NONE} 194 * @throws NullPointerException if any parameter is null 195 */ 196 @Nonnull 197 @CheckReturnValue 198 static public Set<File> findFiles(@Nonnull String name, @Nonnull String root, @Nonnull Location location) { 199 return FileUtilSupport.getDefault().findFiles(name, root, location); 200 } 201 202 /** 203 * Get the resource file corresponding to a name. There are five cases: 204 * <ul> 205 * <li>Starts with "program:", treat the rest as a relative pathname below 206 * the program directory</li> 207 * <li>Starts with "preference:", treat the rest as a relative path below 208 * the user's files directory</li> 209 * <li>Starts with "settings:", treat the rest as a relative path below the 210 * JMRI system preferences directory</li> 211 * <li>Starts with "home:", treat the rest as a relative path below the 212 * user.home directory</li> 213 * <li>Starts with "profile:", treat the rest as a relative path below the 214 * profile directory as specified in the 215 * active{@link jmri.profile.Profile}</li> 216 * <li>Starts with "scripts:", treat the rest as a relative path below the 217 * scripts directory</li> 218 * <li>Otherwise, treat the name as a relative path below the program 219 * directory</li> 220 * </ul> 221 * In any case, absolute pathnames will work. Uses the Profile returned by 222 * {@link ProfileManager#getActiveProfile()} as the base. 223 * 224 * @param pName the name, possibly starting with home:, profile:, program:, 225 * preference:, scripts:, or settings: 226 * @return Absolute file name to use, or null. This will include 227 * system-specific file separators. 228 * @since 2.7.2 229 */ 230 @Nonnull 231 @CheckReturnValue 232 static public String getExternalFilename(@Nonnull String pName) { 233 return FileUtilSupport.getDefault().getExternalFilename(pName); 234 } 235 236 /** 237 * Get the resource file corresponding to a name. There are five cases: 238 * <ul> 239 * <li>Starts with "program:", treat the rest as a relative pathname below 240 * the program directory</li> 241 * <li>Starts with "preference:", treat the rest as a relative path below 242 * the user's files directory</li> 243 * <li>Starts with "settings:", treat the rest as a relative path below the 244 * JMRI system preferences directory</li> 245 * <li>Starts with "home:", treat the rest as a relative path below the 246 * user.home directory</li> 247 * <li>Starts with "profile:", treat the rest as a relative path below the 248 * profile directory as specified in the 249 * active{@link jmri.profile.Profile}</li> 250 * <li>Starts with "scripts:", treat the rest as a relative path below the 251 * scripts directory</li> 252 * <li>Otherwise, treat the name as a relative path below the program 253 * directory</li> 254 * </ul> 255 * In any case, absolute pathnames will work. 256 * 257 * @param profile the Profile to use as a base. 258 * @param pName the name, possibly starting with home:, profile:, 259 * program:, preference:, scripts:, or settings: 260 * @return Absolute file name to use, or null. This will include 261 * system-specific file separators. 262 * @since 4.17.3 263 */ 264 @Nonnull 265 @CheckReturnValue 266 static public String getExternalFilename(@CheckForNull Profile profile, @Nonnull String pName) { 267 return FileUtilSupport.getDefault().getExternalFilename(profile, pName); 268 } 269 270 /** 271 * Convert a portable filename into an absolute filename, using 272 * {@link ProfileManager#getActiveProfile()} as the base. 273 * 274 * @param path the portable filename 275 * @return An absolute filename 276 */ 277 @Nonnull 278 @CheckReturnValue 279 static public String getAbsoluteFilename(@Nonnull String path) { 280 return FileUtilSupport.getDefault().getAbsoluteFilename(path); 281 } 282 283 /** 284 * Convert a portable filename into an absolute filename. 285 * 286 * @param profile the profile to use the base 287 * @param path the portable filename 288 * @return An absolute filename 289 */ 290 @Nonnull 291 @CheckReturnValue 292 static public String getAbsoluteFilename(@CheckForNull Profile profile, @Nonnull String path) { 293 return FileUtilSupport.getDefault().getAbsoluteFilename(profile, path); 294 } 295 296 /** 297 * Convert a File object's path to our preferred storage form. 298 * <p> 299 * This is the inverse of {@link #getFile(String pName)}. Deprecated forms 300 * are not created. 301 * 302 * @param file File at path to be represented 303 * @return Filename for storage in a portable manner. This will include 304 * portable, not system-specific, file separators. 305 * @since 2.7.2 306 */ 307 @Nonnull 308 @CheckReturnValue 309 static public String getPortableFilename(@Nonnull File file) { 310 return FileUtilSupport.getDefault().getPortableFilename(file); 311 } 312 313 /** 314 * Convert a File object's path to our preferred storage form. 315 * <p> 316 * This is the inverse of {@link #getFile(String pName)}. Deprecated forms 317 * are not created. 318 * <p> 319 * This method supports a specific use case concerning profiles and other 320 * portable paths that are stored within the User files directory, which 321 * will cause the {@link jmri.profile.ProfileManager} to write an incorrect 322 * path for the current profile or 323 * {@link apps.configurexml.FileLocationPaneXml} to write an incorrect path 324 * for the Users file directory. In most cases, the use of 325 * {@link #getPortableFilename(java.io.File)} is preferable. 326 * 327 * @param file File at path to be represented 328 * @param ignoreUserFilesPath true if paths in the User files path should be 329 * stored as absolute paths, which is often not 330 * desirable. 331 * @param ignoreProfilePath true if paths in the profile should be stored 332 * as absolute paths, which is often not 333 * desirable. 334 * @return Storage format representation 335 * @since 3.5.5 336 */ 337 @Nonnull 338 @CheckReturnValue 339 static public String getPortableFilename(@Nonnull File file, boolean ignoreUserFilesPath, boolean ignoreProfilePath) { 340 return FileUtilSupport.getDefault().getPortableFilename(file, ignoreUserFilesPath, ignoreProfilePath); 341 } 342 343 /** 344 * Convert a filename string to our preferred storage form. 345 * <p> 346 * This is the inverse of {@link #getExternalFilename(String pName)}. 347 * Deprecated forms are not created. 348 * 349 * @param filename Filename to be represented 350 * @return Filename for storage in a portable manner 351 * @since 2.7.2 352 */ 353 @Nonnull 354 @CheckReturnValue 355 static public String getPortableFilename(@Nonnull String filename) { 356 return FileUtilSupport.getDefault().getPortableFilename(filename); 357 } 358 359 /** 360 * Convert a filename string to our preferred storage form. 361 * <p> 362 * This is the inverse of {@link #getExternalFilename(String pName)}. 363 * Deprecated forms are not created. 364 * <p> 365 * This method supports a specific use case concerning profiles and other 366 * portable paths that are stored within the User files directory, which 367 * will cause the {@link jmri.profile.ProfileManager} to write an incorrect 368 * path for the current profile or 369 * {@link apps.configurexml.FileLocationPaneXml} to write an incorrect path 370 * for the Users file directory. In most cases, the use of 371 * {@link #getPortableFilename(java.io.File)} is preferable. 372 * 373 * @param filename Filename to be represented 374 * @param ignoreUserFilesPath true if paths in the User files path should be 375 * stored as absolute paths, which is often not 376 * desirable. 377 * @param ignoreProfilePath true if paths in the profile path should be 378 * stored as absolute paths, which is often not 379 * desirable. 380 * @return Storage format representation 381 * @since 3.5.5 382 */ 383 @Nonnull 384 @CheckReturnValue 385 static public String getPortableFilename(@Nonnull String filename, boolean ignoreUserFilesPath, boolean ignoreProfilePath) { 386 return FileUtilSupport.getDefault().getPortableFilename(filename, ignoreUserFilesPath, ignoreProfilePath); 387 } 388 389 /** 390 * Convert a File object's path to our preferred storage form. 391 * <p> 392 * This is the inverse of {@link #getFile(String pName)}. Deprecated forms 393 * are not created. 394 * 395 * @param profile Profile to use as a base 396 * @param file File at path to be represented 397 * @return Filename for storage in a portable manner. This will include 398 * portable, not system-specific, file separators. 399 * @since 4.17.3 400 */ 401 @Nonnull 402 @CheckReturnValue 403 static public String getPortableFilename(@CheckForNull Profile profile, @Nonnull File file) { 404 return FileUtilSupport.getDefault().getPortableFilename(profile, file); 405 } 406 407 /** 408 * Convert a File object's path to our preferred storage form. 409 * <p> 410 * This is the inverse of {@link #getFile(String pName)}. Deprecated forms 411 * are not created. 412 * <p> 413 * This method supports a specific use case concerning profiles and other 414 * portable paths that are stored within the User files directory, which 415 * will cause the {@link jmri.profile.ProfileManager} to write an incorrect 416 * path for the current profile or 417 * {@link apps.configurexml.FileLocationPaneXml} to write an incorrect path 418 * for the Users file directory. In most cases, the use of 419 * {@link #getPortableFilename(java.io.File)} is preferable. 420 * 421 * @param profile Profile to use as a base 422 * @param file File at path to be represented 423 * @param ignoreUserFilesPath true if paths in the User files path should be 424 * stored as absolute paths, which is often not 425 * desirable. 426 * @param ignoreProfilePath true if paths in the profile should be stored 427 * as absolute paths, which is often not 428 * desirable. 429 * @return Storage format representation 430 * @since 4.17.3 431 */ 432 @Nonnull 433 @CheckReturnValue 434 static public String getPortableFilename(@CheckForNull Profile profile, @Nonnull File file, boolean ignoreUserFilesPath, 435 boolean ignoreProfilePath) { 436 return FileUtilSupport.getDefault().getPortableFilename(profile, file, ignoreUserFilesPath, ignoreProfilePath); 437 } 438 439 /** 440 * Convert a filename string to our preferred storage form. 441 * <p> 442 * This is the inverse of {@link #getExternalFilename(String pName)}. 443 * Deprecated forms are not created. 444 * 445 * @param profile the Profile to use as a base 446 * @param filename Filename to be represented 447 * @return Filename for storage in a portable manner 448 * @since 4.17.3 449 */ 450 @Nonnull 451 @CheckReturnValue 452 static public String getPortableFilename(@CheckForNull Profile profile, @Nonnull String filename) { 453 return FileUtilSupport.getDefault().getPortableFilename(profile, filename); 454 } 455 456 /** 457 * Convert a filename string to our preferred storage form. 458 * <p> 459 * This is the inverse of {@link #getExternalFilename(String pName)}. 460 * Deprecated forms are not created. 461 * <p> 462 * This method supports a specific use case concerning profiles and other 463 * portable paths that are stored within the User files directory, which 464 * will cause the {@link jmri.profile.ProfileManager} to write an incorrect 465 * path for the current profile or 466 * {@link apps.configurexml.FileLocationPaneXml} to write an incorrect path 467 * for the Users file directory. In most cases, the use of 468 * {@link #getPortableFilename(java.io.File)} is preferable. 469 * 470 * @param profile the profile to use as a base 471 * @param filename Filename to be represented 472 * @param ignoreUserFilesPath true if paths in the User files path should be 473 * stored as absolute paths, which is often not 474 * desirable. 475 * @param ignoreProfilePath true if paths in the profile path should be 476 * stored as absolute paths, which is often not 477 * desirable. 478 * @return Storage format representation 479 * @since 4.17.3 480 */ 481 @Nonnull 482 @CheckReturnValue 483 static public String getPortableFilename(@CheckForNull Profile profile, @Nonnull String filename, 484 boolean ignoreUserFilesPath, boolean ignoreProfilePath) { 485 return FileUtilSupport.getDefault().getPortableFilename(profile, filename, ignoreUserFilesPath, ignoreProfilePath); 486 } 487 488 /** 489 * Test if the given filename is a portable filename. 490 * 491 * @param filename the name to test 492 * @return true if filename is portable 493 */ 494 static public boolean isPortableFilename(@Nonnull String filename) { 495 return FileUtilSupport.getDefault().isPortableFilename(filename); 496 } 497 498 /** 499 * Get the user's home directory. 500 * 501 * @return User's home directory as a String 502 */ 503 @Nonnull 504 @CheckReturnValue 505 static public String getHomePath() { 506 return FileUtilSupport.getDefault().getHomePath(); 507 } 508 509 /** 510 * Get the user's files directory. If not set by the user, this is the same 511 * as the profile path for the Profile specified by 512 * {@link ProfileManager#getActiveProfile()}. 513 * 514 * @see #getProfilePath() 515 * @return User's files directory as a String 516 */ 517 @Nonnull 518 @CheckReturnValue 519 static public String getUserFilesPath() { 520 return FileUtilSupport.getDefault().getUserFilesPath(); 521 } 522 523 /** 524 * Get the user's files directory. If not set by the user, this is the same 525 * as the profile path. 526 * 527 * @param profile the profile to use as a base 528 * @see #getProfilePath() 529 * @return User's files directory as a String 530 */ 531 @Nonnull 532 @CheckReturnValue 533 static public String getUserFilesPath(@CheckForNull Profile profile) { 534 return FileUtilSupport.getDefault().getUserFilesPath(profile); 535 } 536 537 /** 538 * Set the user's files directory. 539 * 540 * @see #getUserFilesPath() 541 * @param profile The profile to use as a base 542 * @param path The path to the user's files directory 543 */ 544 static public void setUserFilesPath(@CheckForNull Profile profile, @Nonnull String path) { 545 FileUtilSupport.getDefault().setUserFilesPath(profile, path); 546 } 547 548 /** 549 * Get the profile directory. Uses the Profile returned by 550 * {@link ProfileManager#getActiveProfile()} as a base. If that is null, 551 * gets the preferences path. 552 * 553 * @see #getPreferencesPath() 554 * @return Profile directory 555 */ 556 @Nonnull 557 @CheckReturnValue 558 static public String getProfilePath() { 559 return FileUtilSupport.getDefault().getProfilePath(); 560 } 561 562 /** 563 * Get the profile directory. If the profile is null or has a null 564 * directory, this is the same as the preferences path. 565 * 566 * @param profile the profile to use as a base 567 * @see #getPreferencesPath() 568 * @return Profile directory 569 */ 570 @Nonnull 571 @CheckReturnValue 572 static public String getProfilePath(@CheckForNull Profile profile) { 573 return FileUtilSupport.getDefault().getProfilePath(profile); 574 } 575 576 /** 577 * Get the preferences directory. This directory is set based on the OS and 578 * is not normally settable by the user. 579 * <ul> 580 * <li>On Microsoft Windows systems, this is {@code JMRI} in the User's home 581 * directory.</li> 582 * <li>On OS X systems, this is {@code Library/Preferences/JMRI} in the 583 * User's home directory.</li> 584 * <li>On Linux, Solaris, and other UNIXes, this is {@code .jmri} in the 585 * User's home directory.</li> 586 * <li>This can be overridden with by setting the {@code jmri.prefsdir} Java 587 * property when starting JMRI.</li> 588 * </ul> 589 * Use {@link #getHomePath()} to get the User's home directory. 590 * 591 * @see #getHomePath() 592 * @return Path to the preferences directory. 593 */ 594 @Nonnull 595 @CheckReturnValue 596 static public String getPreferencesPath() { 597 return FileUtilSupport.getDefault().getPreferencesPath(); 598 } 599 600 /** 601 * Get the JMRI program directory. If the program directory has not been 602 * previously sets, first sets the program directory to the value specified 603 * in the Java System property <code>jmri.path.program</code>, or 604 * <code>.</code> if that property is not set. 605 * 606 * @return JMRI program directory as a String. 607 */ 608 @Nonnull 609 @CheckReturnValue 610 static public String getProgramPath() { 611 return FileUtilSupport.getDefault().getProgramPath(); 612 } 613 614 /** 615 * Set the JMRI program directory. 616 * <p> 617 * Convenience method that calls 618 * {@link FileUtil#setProgramPath(java.io.File)} with the passed in path. 619 * 620 * @param path the path to the JMRI installation 621 */ 622 static public void setProgramPath(@Nonnull String path) { 623 FileUtilSupport.getDefault().setProgramPath(new File(path)); 624 } 625 626 /** 627 * Set the JMRI program directory. 628 * <p> 629 * If set, allows JMRI to be loaded from locations other than the directory 630 * containing JMRI resources. This must be set very early in the process of 631 * loading JMRI (prior to loading any other JMRI code) to be meaningfully 632 * used. 633 * 634 * @param path the path to the JMRI installation 635 */ 636 static public void setProgramPath(@Nonnull File path) { 637 FileUtilSupport.getDefault().setProgramPath(path); 638 } 639 640 /** 641 * Get the URL of a portable filename if it can be located using 642 * {@link #findURL(java.lang.String)} 643 * 644 * @param path the path to find 645 * @return URL of portable or absolute path 646 */ 647 @Nonnull 648 @CheckReturnValue 649 static public URI findExternalFilename(@Nonnull String path) { 650 return FileUtilSupport.getDefault().findExternalFilename(path); 651 } 652 653 /** 654 * Search for a file or JAR resource by name and return the 655 * {@link java.io.InputStream} for that file. Search order is defined by 656 * {@link #findURL(java.lang.String, jmri.util.FileUtil.Location, java.lang.String...) }. 657 * No limits are placed on search locations. 658 * 659 * @param path The relative path of the file or resource 660 * @return InputStream or null. 661 * @see #findInputStream(java.lang.String, java.lang.String...) 662 * @see #findInputStream(java.lang.String, jmri.util.FileUtil.Location, 663 * java.lang.String...) 664 * @see #findURL(java.lang.String) 665 * @see #findURL(java.lang.String, java.lang.String...) 666 * @see #findURL(java.lang.String, jmri.util.FileUtil.Location, 667 * java.lang.String...) 668 */ 669 static public InputStream findInputStream(@Nonnull String path) { 670 return FileUtilSupport.getDefault().findInputStream(path); 671 } 672 673 /** 674 * Search for a file or JAR resource by name and return the 675 * {@link java.io.InputStream} for that file. Search order is defined by 676 * {@link #findURL(java.lang.String, jmri.util.FileUtil.Location, java.lang.String...) }. 677 * No limits are placed on search locations. 678 * 679 * @param path The relative path of the file or resource 680 * @param searchPaths a list of paths to search for the path in 681 * @return InputStream or null. 682 * @see #findInputStream(java.lang.String) 683 * @see #findInputStream(java.lang.String, jmri.util.FileUtil.Location, 684 * java.lang.String...) 685 */ 686 static public InputStream findInputStream(@Nonnull String path, @Nonnull String... searchPaths) { 687 return FileUtilSupport.getDefault().findInputStream(path, searchPaths); 688 } 689 690 /** 691 * Search for a file or JAR resource by name and return the 692 * {@link java.io.InputStream} for that file. Search order is defined by 693 * {@link #findURL(java.lang.String, jmri.util.FileUtil.Location, java.lang.String...) }. 694 * 695 * @param path The relative path of the file or resource 696 * @param locations The type of locations to limit the search to 697 * @return InputStream or null. 698 * @see #findInputStream(java.lang.String) 699 * @see #findInputStream(java.lang.String, jmri.util.FileUtil.Location, 700 * java.lang.String...) 701 */ 702 static public InputStream findInputStream(@Nonnull String path, Location locations) { 703 return FileUtilSupport.getDefault().findInputStream(path, locations); 704 } 705 706 /** 707 * Search for a file or JAR resource by name and return the 708 * {@link java.io.InputStream} for that file. Search order is defined by 709 * {@link #findURL(java.lang.String, jmri.util.FileUtil.Location, java.lang.String...) }. 710 * 711 * @param path The relative path of the file or resource 712 * @param locations The type of locations to limit the search to 713 * @param searchPaths a list of paths to search for the path in 714 * @return InputStream or null. 715 * @see #findInputStream(java.lang.String) 716 * @see #findInputStream(java.lang.String, java.lang.String...) 717 */ 718 static public InputStream findInputStream(@Nonnull String path, Location locations, @Nonnull String... searchPaths) { 719 return FileUtilSupport.getDefault().findInputStream(path, locations, searchPaths); 720 } 721 722 /** 723 * Get the resources directory within the user's files directory. 724 * 725 * @return path to [user's file]/resources/ 726 */ 727 @Nonnull 728 @CheckReturnValue 729 static public String getUserResourcePath() { 730 return FileUtilSupport.getDefault().getUserResourcePath(); 731 } 732 733 /** 734 * Search for a file or JAR resource by name and return the 735 * {@link java.net.URI} for that file. Search order is defined by 736 * {@link #findURI(java.lang.String, jmri.util.FileUtil.Location, java.lang.String...)}. 737 * No limits are placed on search locations. 738 * 739 * @param path The relative path of the file or resource. 740 * @return The URI or null. 741 * @see #findURI(java.lang.String, java.lang.String...) 742 * @see #findURI(java.lang.String, jmri.util.FileUtil.Location) 743 * @see #findURI(java.lang.String, jmri.util.FileUtil.Location, 744 * java.lang.String...) 745 */ 746 static public URI findURI(@Nonnull String path) { 747 return FileUtilSupport.getDefault().findURI(path); 748 } 749 750 /** 751 * Search for a file or JAR resource by name and return the 752 * {@link java.net.URI} for that file. Search order is defined by 753 * {@link #findURI(java.lang.String, jmri.util.FileUtil.Location, java.lang.String...)}. 754 * No limits are placed on search locations. 755 * <p> 756 * Note that if the file for path is not found in one of the searchPaths, 757 * all standard locations are also be searched through to find the file. If 758 * you need to limit the locations where the file can be found use 759 * {@link #findURI(java.lang.String, jmri.util.FileUtil.Location, java.lang.String...)}. 760 * 761 * @param path The relative path of the file or resource 762 * @param searchPaths a list of paths to search for the path in 763 * @return The URI or null 764 * @see #findURI(java.lang.String) 765 * @see #findURI(java.lang.String, jmri.util.FileUtil.Location) 766 * @see #findURI(java.lang.String, jmri.util.FileUtil.Location, 767 * java.lang.String...) 768 */ 769 static public URI findURI(@Nonnull String path, @Nonnull String... searchPaths) { 770 return FileUtilSupport.getDefault().findURI(path, searchPaths); 771 } 772 773 /** 774 * Search for a file or JAR resource by name and return the 775 * {@link java.net.URI} for that file. Search order is defined by 776 * {@link #findURI(java.lang.String, jmri.util.FileUtil.Location, java.lang.String...)}. 777 * 778 * @param path The relative path of the file or resource 779 * @param locations The types of locations to limit the search to 780 * @return The URI or null 781 * @see #findURI(java.lang.String) 782 * @see #findURI(java.lang.String, java.lang.String...) 783 * @see #findURI(java.lang.String, jmri.util.FileUtil.Location, 784 * java.lang.String...) 785 */ 786 static public URI findURI(@Nonnull String path, @Nonnull Location locations) { 787 return FileUtilSupport.getDefault().findURI(path, locations); 788 } 789 790 /** 791 * Search for a file or JAR resource by name and return the 792 * {@link java.net.URI} for that file. 793 * <p> 794 * Search order is: 795 * <ol> 796 * <li>For any provided searchPaths, iterate over the searchPaths by 797 * prepending each searchPath to the path and following the following search 798 * order:<ol> 799 * <li>As a {@link java.io.File} in the user preferences directory</li> 800 * <li>As a File in the current working directory (usually, but not always 801 * the JMRI distribution directory)</li> 802 * <li>As a File in the JMRI distribution directory</li> 803 * <li>As a resource in jmri.jar</li> 804 * </ol></li> 805 * <li>If the file or resource has not been found in the searchPaths, search 806 * in the four locations listed without prepending any path</li> 807 * <li>As a File with an absolute path</li> 808 * </ol> 809 * <p> 810 * The <code>locations</code> parameter limits the above logic by limiting 811 * the location searched. 812 * <ol> 813 * <li>{@link Location#ALL} will not place any limits on the search</li> 814 * <li>{@link Location#NONE} effectively requires that <code>path</code> be 815 * a portable pathname</li> 816 * <li>{@link Location#INSTALLED} limits the search to the 817 * {@link FileUtil#PROGRAM} directory and JARs in the class path</li> 818 * <li>{@link Location#USER} limits the search to the 819 * {@link FileUtil#PREFERENCES}, {@link FileUtil#PROFILE}, and 820 * {@link FileUtil#SETTINGS} directories (in that order)</li> 821 * </ol> 822 * 823 * @param path The relative path of the file or resource 824 * @param locations The types of locations to limit the search to 825 * @param searchPaths a list of paths to search for the path in 826 * @return The URI or null 827 * @see #findURI(java.lang.String) 828 * @see #findURI(java.lang.String, jmri.util.FileUtil.Location) 829 * @see #findURI(java.lang.String, java.lang.String...) 830 */ 831 static public URI findURI(@Nonnull String path, @Nonnull Location locations, @Nonnull String... searchPaths) { 832 return FileUtilSupport.getDefault().findURI(path, locations, searchPaths); 833 } 834 835 /** 836 * Search for a file or JAR resource by name and return the 837 * {@link java.net.URL} for that file. Search order is defined by 838 * {@link #findURL(java.lang.String, jmri.util.FileUtil.Location, java.lang.String...)}. 839 * No limits are placed on search locations. 840 * <p> 841 * TODO: add @CheckForNull annotation / fix Possible null pointers. 842 * 843 * @param path The relative path of the file or resource. 844 * @return The URL or null. 845 * @see #findURL(java.lang.String, java.lang.String...) 846 * @see #findURL(java.lang.String, jmri.util.FileUtil.Location) 847 * @see #findURL(java.lang.String, jmri.util.FileUtil.Location, 848 * java.lang.String...) 849 */ 850 static public URL findURL(@Nonnull String path) { 851 return FileUtilSupport.getDefault().findURL(path); 852 } 853 854 /** 855 * Search for a file or JAR resource by name and return the 856 * {@link java.net.URL} for that file. Search order is defined by 857 * {@link #findURL(java.lang.String, jmri.util.FileUtil.Location, java.lang.String...)}. 858 * No limits are placed on search locations. 859 * 860 * @param path The relative path of the file or resource 861 * @param searchPaths a list of paths to search for the path in 862 * @return The URL or null 863 * @see #findURL(java.lang.String) 864 * @see #findURL(java.lang.String, jmri.util.FileUtil.Location) 865 * @see #findURL(java.lang.String, jmri.util.FileUtil.Location, 866 * java.lang.String...) 867 */ 868 static public URL findURL(@Nonnull String path, @Nonnull String... searchPaths) { 869 return FileUtilSupport.getDefault().findURL(path, searchPaths); 870 } 871 872 /** 873 * Search for a file or JAR resource by name and return the 874 * {@link java.net.URL} for that file. Search order is defined by 875 * {@link #findURL(java.lang.String, jmri.util.FileUtil.Location, java.lang.String...)}. 876 * 877 * @param path The relative path of the file or resource 878 * @param locations The types of locations to limit the search to 879 * @return The URL or null 880 * @see #findURL(java.lang.String) 881 * @see #findURL(java.lang.String, java.lang.String...) 882 * @see #findURL(java.lang.String, jmri.util.FileUtil.Location, 883 * java.lang.String...) 884 */ 885 static public URL findURL(@Nonnull String path, @Nonnull Location locations) { 886 return FileUtilSupport.getDefault().findURL(path, locations); 887 } 888 889 /** 890 * Search for a file or JAR resource by name and return the 891 * {@link java.net.URL} for that file. 892 * <p> 893 * Search order is: 894 * <ol><li>For any provided searchPaths, iterate over the searchPaths by 895 * prepending each searchPath to the path and following the following search 896 * order: 897 * <ol><li>As a {@link java.io.File} in the user preferences directory</li> 898 * <li>As a File in the current working directory (usually, but not always 899 * the JMRI distribution directory)</li> <li>As a File in the JMRI 900 * distribution directory</li> <li>As a resource in jmri.jar</li></ol></li> 901 * <li>If the file or resource has not been found in the searchPaths, search 902 * in the four locations listed without prepending any path</li></ol> 903 * <p> 904 * The <code>locations</code> parameter limits the above logic by limiting 905 * the location searched. 906 * <ol><li>{@link Location#ALL} will not place any limits on the 907 * search</li><li>{@link Location#NONE} effectively requires that 908 * <code>path</code> be a portable 909 * pathname</li><li>{@link Location#INSTALLED} limits the search to the 910 * {@link #PROGRAM} directory and JARs in the class 911 * path</li><li>{@link Location#USER} limits the search to the 912 * {@link #PROFILE} directory</li></ol> 913 * 914 * @param path The relative path of the file or resource 915 * @param locations The types of locations to limit the search to 916 * @param searchPaths a list of paths to search for the path in 917 * @return The URL or null 918 * @see #findURL(java.lang.String) 919 * @see #findURL(java.lang.String, jmri.util.FileUtil.Location) 920 * @see #findURL(java.lang.String, java.lang.String...) 921 */ 922 static public URL findURL(@Nonnull String path, @Nonnull Location locations, @Nonnull String... searchPaths) { 923 return FileUtilSupport.getDefault().findURL(path, locations, searchPaths); 924 } 925 926 /** 927 * Return the {@link java.net.URI} for a given URL 928 * 929 * @param url the URL 930 * @return a URI or null if the conversion would have caused a 931 * {@link java.net.URISyntaxException} 932 */ 933 static public URI urlToURI(@Nonnull URL url) { 934 return FileUtilSupport.getDefault().urlToURI(url); 935 } 936 937 /** 938 * Return the {@link java.net.URL} for a given {@link java.io.File}. This 939 * method catches a {@link java.net.MalformedURLException} and returns null 940 * in its place, since we really do not expect a File object to ever give a 941 * malformed URL. This method exists solely so implementing classes do not 942 * need to catch that exception. 943 * 944 * @param file The File to convert. 945 * @return a URL or null if the conversion would have caused a 946 * MalformedURLException 947 */ 948 static public URL fileToURL(@Nonnull File file) { 949 return FileUtilSupport.getDefault().fileToURL(file); 950 } 951 952 /** 953 * Get the JMRI distribution jar file. 954 * 955 * @return the JAR file containing the JMRI library or null if not running 956 * from a JAR file 957 */ 958 static public JarFile jmriJarFile() { 959 return FileUtilSupport.getDefault().getJmriJarFile(); 960 } 961 962 /** 963 * Log all paths at the INFO level. 964 */ 965 static public void logFilePaths() { 966 FileUtilSupport.getDefault().logFilePaths(); 967 } 968 969 /** 970 * Get the path to the scripts directory using the Profile returned by 971 * {@link ProfileManager#getActiveProfile()} as the base. 972 * 973 * @return the scriptsPath 974 */ 975 @Nonnull 976 @CheckReturnValue 977 public static String getScriptsPath() { 978 return FileUtilSupport.getDefault().getScriptsPath(); 979 } 980 981 /** 982 * Get the path to the scripts directory. 983 * 984 * @param profile the Profile to use as the base 985 * @return the scriptsPath 986 */ 987 @Nonnull 988 @CheckReturnValue 989 public static String getScriptsPath(@CheckForNull Profile profile) { 990 return FileUtilSupport.getDefault().getScriptsPath(profile); 991 } 992 993 /** 994 * Set the path to python scripts. 995 * 996 * @param profile the profile to set the path for 997 * @param path the scriptsPath to set 998 */ 999 public static void setScriptsPath(@CheckForNull Profile profile, @CheckForNull String path) { 1000 FileUtilSupport.getDefault().setScriptsPath(profile, path); 1001 } 1002 1003 /** 1004 * Read a text file into a String. 1005 * 1006 * @param file The text file. 1007 * @return The contents of the file. 1008 * @throws java.io.IOException if the file cannot be read 1009 */ 1010 public static String readFile(@Nonnull File file) throws IOException { 1011 return FileUtil.readURL(FileUtil.fileToURL(file)); 1012 } 1013 1014 /** 1015 * Read a text URL into a String. Would be significantly simpler with Java 1016 * 7. File is assumed to be encoded using UTF-8 1017 * 1018 * @param url The text URL. 1019 * @return The contents of the file. 1020 * @throws java.io.IOException if the URL cannot be read 1021 */ 1022 public static String readURL(@Nonnull URL url) throws IOException { 1023 return FileUtilSupport.getDefault().readURL(url); 1024 } 1025 1026 /** 1027 * Replaces most non-alphanumeric characters in name with an underscore. 1028 * 1029 * @param name The filename to be sanitized. 1030 * @return The sanitized filename. 1031 */ 1032 @Nonnull 1033 public static String sanitizeFilename(@Nonnull String name) { 1034 return FileUtilSupport.getDefault().sanitizeFilename(name); 1035 } 1036 1037 /** 1038 * Create a directory if required. Any parent directories will also be 1039 * created. 1040 * 1041 * @param path directory to create 1042 */ 1043 public static void createDirectory(@Nonnull String path) { 1044 FileUtilSupport.getDefault().createDirectory(path); 1045 } 1046 1047 /** 1048 * Create a directory if required. Any parent directories will also be 1049 * created. 1050 * 1051 * @param dir directory to create 1052 */ 1053 public static void createDirectory(@Nonnull File dir) { 1054 FileUtilSupport.getDefault().createDirectory(dir); 1055 } 1056 1057 /** 1058 * Recursively delete a path. It is recommended to use 1059 * {@link java.nio.file.Files#delete(java.nio.file.Path)} or 1060 * {@link java.nio.file.Files#deleteIfExists(java.nio.file.Path)} for files. 1061 * 1062 * @param path path to delete 1063 * @return true if path was deleted, false otherwise 1064 */ 1065 public static boolean delete(@Nonnull File path) { 1066 return FileUtilSupport.getDefault().delete(path); 1067 } 1068 1069 /** 1070 * Copy a file or directory. It is recommended to use 1071 * {@link java.nio.file.Files#copy(java.nio.file.Path, java.io.OutputStream)} 1072 * for files. 1073 * 1074 * @param source the file or directory to copy 1075 * @param dest must be the file or directory, not the containing directory 1076 * @throws java.io.IOException if file cannot be copied 1077 */ 1078 public static void copy(@Nonnull File source, @Nonnull File dest) throws IOException { 1079 FileUtilSupport.getDefault().copy(source, dest); 1080 } 1081 1082 /** 1083 * Simple helper method to just append a text string to the end of the given 1084 * filename. The file will be created if it does not exist. 1085 * 1086 * @param file File to append text to 1087 * @param text Text to append 1088 * @throws java.io.IOException if file cannot be written to 1089 */ 1090 public static void appendTextToFile(@Nonnull File file, @Nonnull String text) throws IOException { 1091 FileUtilSupport.getDefault().appendTextToFile(file, text); 1092 } 1093 1094 /** 1095 * Backup a file. The backup is in the same location as the original file, 1096 * has the extension <code>.bak</code> appended to the file name, and up to 1097 * four revisions are retained. The lowest numbered revision is the most 1098 * recent. 1099 * 1100 * @param file the file to backup 1101 * @throws java.io.IOException if a backup cannot be created 1102 * @see jmri.util.FileUtilSupport#backup(java.io.File) 1103 */ 1104 public static void backup(@Nonnull File file) throws IOException { 1105 FileUtilSupport.getDefault().backup(file); 1106 } 1107 1108 /** 1109 * Rotate a file and its backups, retaining only a set number of backups. 1110 * 1111 * @param file the file to rotate 1112 * @param max maximum number of backups to retain 1113 * @param extension The extension to use for the rotations. If null or an 1114 * empty string, the rotation number is used as the 1115 * extension. 1116 * @throws java.io.IOException if a backup cannot be created 1117 * @throws IllegalArgumentException if max is less than one 1118 * @see jmri.util.FileUtilSupport#rotate(java.io.File, int, 1119 * java.lang.String) 1120 * @see jmri.util.FileUtilSupport#backup(java.io.File) 1121 */ 1122 public static void rotate(@Nonnull File file, int max, @CheckForNull String extension) throws IOException { 1123 FileUtilSupport.getDefault().rotate(file, max, extension); 1124 } 1125 1126 /** 1127 * Get the default instance of FileUtilSupport. 1128 * 1129 * @return the default instance of FileUtilSupport 1130 */ 1131 public static FileUtilSupport getDefault() { 1132 return FileUtilSupport.getDefault(); 1133 } 1134 1135 /** 1136 * PropertyChangeEvents for properties that are Profile-specific use a 1137 * Property to enclose both the Profile and the value of the property. 1138 */ 1139 public static class Property implements Map.Entry<Profile, String> { 1140 1141 private final Profile key; 1142 private final String value; 1143 1144 // package private 1145 Property(Profile key, String value) { 1146 this.key = key; 1147 this.value = value; 1148 } 1149 1150 @Override 1151 public Profile getKey() { 1152 return key; 1153 } 1154 1155 @Override 1156 public String getValue() { 1157 return value; 1158 } 1159 1160 @Override 1161 public String setValue(String value) { 1162 throw new UnsupportedOperationException("Immutable by design"); 1163 } 1164 1165 } 1166 1167 /* Private default constructor to ensure it's not documented. */ 1168 private FileUtil() { 1169 } 1170}