• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

devonfw / IDEasy / 19534528277

20 Nov 2025 10:59AM UTC coverage: 68.924% (+0.02%) from 68.905%
19534528277

push

github

web-flow
add FileAccess getBin[Parent]Path() methods (#1606)

3537 of 5627 branches covered (62.86%)

Branch coverage included in aggregate %.

9227 of 12892 relevant lines covered (71.57%)

3.14 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.36
cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java
1
package com.devonfw.tools.ide.io;
2

3
import java.io.OutputStream;
4
import java.io.Reader;
5
import java.nio.file.Files;
6
import java.nio.file.Path;
7
import java.nio.file.StandardCopyOption;
8
import java.nio.file.attribute.PosixFilePermission;
9
import java.time.Duration;
10
import java.util.List;
11
import java.util.Properties;
12
import java.util.Set;
13
import java.util.function.Consumer;
14
import java.util.function.Function;
15
import java.util.function.Predicate;
16

17
import com.devonfw.tools.ide.context.IdeContext;
18
import com.devonfw.tools.ide.io.ini.IniFile;
19
import com.devonfw.tools.ide.io.ini.IniFileImpl;
20

21
/**
22
 * Interface that gives access to various operations on files.
23
 */
24
public interface FileAccess {
25

26
  /** {@link PosixFilePermission}s for "rwxr-xr-x" or 0755. */
27
  Set<PosixFilePermission> RWX_RX_RX = Set.of(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE,
10✔
28
      PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_EXECUTE, PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_EXECUTE);
29

30
  /**
31
   * Downloads a file from an arbitrary location.
32
   *
33
   * @param url the location of the binary file to download. May also be a local or remote path to copy from.
34
   * @param targetFile the {@link Path} to the target file to download to. Should not already exist. Missing parent directories will be created
35
   *     automatically.
36
   */
37
  void download(String url, Path targetFile);
38

39
  /**
40
   * @param url the URL of the text to download.
41
   * @return the downloaded body as {@link String} (e.g. JSON or XML).
42
   */
43
  String download(String url);
44

45
  /**
46
   * Creates the entire {@link Path} as directories if not already existing.
47
   *
48
   * @param directory the {@link Path} to {@link java.nio.file.Files#createDirectories(Path, java.nio.file.attribute.FileAttribute...) create}.
49
   */
50
  void mkdirs(Path directory);
51

52
  /**
53
   * @param file the {@link Path} to check.
54
   * @return {@code true} if the given {@code file} points to an existing file, {@code false} otherwise (the given {@link Path} does not exist or is a
55
   *     directory).
56
   */
57
  boolean isFile(Path file);
58

59
  /**
60
   * @param folder the {@link Path} to check.
61
   * @return {@code true} if the given {@code folder} points to an existing directory, {@code false} otherwise (a warning is logged in this case).
62
   */
63
  boolean isExpectedFolder(Path folder);
64

65
  /**
66
   * @param file the {@link Path} to compute the checksum of.
67
   * @param hashAlgorithm the hash algorithm (e.g. SHA-266).
68
   * @return the computed hash checksum as hex {@link String}.
69
   */
70
  String checksum(Path file, String hashAlgorithm);
71

72
  /**
73
   * Moves the given {@link Path} to the backup.
74
   *
75
   * @param fileOrFolder the {@link Path} to move to the backup (soft-deletion).
76
   * @return the {@link Path} in the backup where the given {@link Path} was moved to.
77
   */
78
  Path backup(Path fileOrFolder);
79

80
  /**
81
   * @param source the source {@link Path file or folder} to move.
82
   * @param targetDir the {@link Path} with the directory to move {@code source} into.
83
   * @param copyOptions the {@link java.nio.file.CopyOption} which specify how the move should be done
84
   */
85
  void move(Path source, Path targetDir, StandardCopyOption... copyOptions);
86

87
  /**
88
   * Creates a relative symbolic link. If the given {@code targetLink} already exists and is a symbolic link or a Windows junction, it will be replaced. In case
89
   * of missing privileges, Windows Junctions may be used as fallback, which must point to absolute paths. Therefore, the created link will be absolute instead
90
   * of relative.
91
   *
92
   * @param source the source {@link Path} to link to, may be relative or absolute.
93
   * @param targetLink the {@link Path} where the symbolic link shall be created pointing to {@code source}.
94
   */
95
  default void symlink(Path source, Path targetLink) {
96

97
    symlink(source, targetLink, true);
5✔
98
  }
1✔
99

100
  /**
101
   * Creates a {@link PathLinkType#SYMBOLIC_LINK symbolic link}. If the given {@code link} already exists and is a symbolic link or a Windows junction, it will
102
   * be replaced. In case of missing privileges, Windows mklink may be used as fallback, which must point to absolute paths. In such case the {@code relative}
103
   * flag will be ignored.
104
   *
105
   * @param target the target {@link Path} to link to, may be relative or absolute.
106
   * @param link the {@link Path} where the symbolic link shall be created pointing to {@code target}.
107
   * @param relative - {@code true} if the symbolic link shall be relative, {@code false} if it shall be absolute.
108
   */
109
  default void symlink(Path target, Path link, boolean relative) {
110

111
    link(target, link, relative, PathLinkType.SYMBOLIC_LINK);
6✔
112
  }
1✔
113

114
  /**
115
   * Creates a {@link PathLinkType#HARD_LINK hard link}. If the given {@code link} already exists and is a symbolic link or a Windows junction, it will be
116
   * replaced. In case of missing privileges, Windows mklink may be used as fallback.
117
   *
118
   * @param target the target {@link Path} to link to, may be relative or absolute.
119
   * @param link the {@link Path} where the symbolic link shall be created pointing to {@code target}.
120
   */
121
  default void hardlink(Path target, Path link) {
122

123
    link(target, link, false, PathLinkType.HARD_LINK);
×
124
  }
×
125

126
  /**
127
   * Creates a link. If the given {@code link} already exists and is a symbolic link or a Windows junction, it will be replaced. In case of missing privileges,
128
   * Windows mklink may be used as fallback, which must point to absolute paths. In such case the {@code relative} flag will be ignored.
129
   *
130
   * @param target the target {@link Path} to link to, may be relative or absolute.
131
   * @param link the {@link Path} where the symbolic link shall be created pointing to {@code target}.
132
   * @param relative - {@code true} if the symbolic link shall be relative, {@code false} if it shall be absolute.
133
   * @param type the {@link PathLinkType}.
134
   */
135
  void link(Path target, Path link, boolean relative, PathLinkType type);
136

137
  /**
138
   * @param link the {@link PathLink} to {@link #link(Path, Path, boolean, PathLinkType) create}.
139
   */
140
  default void link(PathLink link) {
141
    link(link.target(), link.link(), true, link.type());
×
142
  }
×
143

144
  /**
145
   * @param source the source {@link Path file or folder} to copy.
146
   * @param target the {@link Path} to copy {@code source} to. See {@link #copy(Path, Path, FileCopyMode)} for details. Will always ensure that in the end
147
   *     you will find the same content of {@code source} in {@code target}.
148
   */
149
  default void copy(Path source, Path target) {
150

151
    copy(source, target, FileCopyMode.COPY_TREE_FAIL_IF_EXISTS);
5✔
152
  }
1✔
153

154
  /**
155
   * @param source the source {@link Path file or folder} to copy.
156
   * @param target the {@link Path} to copy {@code source} to. Unlike the Linux {@code cp} command this method will not take the filename of {@code source}
157
   *     and copy that to {@code target} in case that is an existing folder. Instead it will always be simple and stupid and just copy from {@code source} to
158
   *     {@code target}. Therefore the result is always clear and easy to predict and understand. Also you can easily rename a file to copy. While
159
   *     {@code cp my-file target} may lead to a different result than {@code cp my-file target/} this method will always ensure that in the end you will find
160
   *     the same content of {@code source} in {@code target}.
161
   * @param mode the {@link FileCopyMode}.
162
   */
163
  default void copy(Path source, Path target, FileCopyMode mode) {
164

165
    copy(source, target, mode, PathCopyListener.NONE);
6✔
166
  }
1✔
167

168
  /**
169
   * @param source the source {@link Path file or folder} to copy.
170
   * @param target the {@link Path} to copy {@code source} to. Unlike the Linux {@code cp} command this method will not take the filename of {@code source}
171
   *     and copy that to {@code target} in case that is an existing folder. Instead it will always be simple and stupid and just copy from {@code source} to
172
   *     {@code target}. Therefore the result is always clear and easy to predict and understand. Also you can easily rename a file to copy. While
173
   *     {@code cp my-file target} may lead to a different result than {@code cp my-file target/} this method will always ensure that in the end you will find
174
   *     the same content of {@code source} in {@code target}.
175
   * @param mode the {@link FileCopyMode}.
176
   * @param listener the {@link PathCopyListener} that will be called for each copied {@link Path}.
177
   */
178
  void copy(Path source, Path target, FileCopyMode mode, PathCopyListener listener);
179

180
  /**
181
   * @param archiveFile the {@link Path} to the file to extract.
182
   * @param targetDir the {@link Path} to the directory where to extract the {@code archiveFile} to.
183
   */
184
  default void extract(Path archiveFile, Path targetDir) {
185

186
    extract(archiveFile, targetDir, null);
5✔
187
  }
1✔
188

189
  /**
190
   * @param archiveFile the {@link Path} to the archive file to extract.
191
   * @param targetDir the {@link Path} to the directory where to extract the {@code archiveFile}.
192
   * @param postExtractHook the {@link Consumer} to be called after the extraction on the final folder before it is moved to {@code targetDir}.
193
   */
194
  default void extract(Path archiveFile, Path targetDir, Consumer<Path> postExtractHook) {
195

196
    extract(archiveFile, targetDir, postExtractHook, true);
6✔
197
  }
1✔
198

199
  /**
200
   * @param archiveFile the {@link Path} to the archive file to extract.
201
   * @param targetDir the {@link Path} to the directory where to extract the {@code archiveFile}.
202
   * @param postExtractHook the {@link Consumer} to be called after the extraction on the final folder before it is moved to {@code targetDir}.
203
   * @param extract {@code true} if the {@code archiveFile} should be extracted (default), {@code false} otherwise.
204
   */
205
  void extract(Path archiveFile, Path targetDir, Consumer<Path> postExtractHook, boolean extract);
206

207
  /**
208
   * Extracts a ZIP file what is the common archive format on Windows. Initially invented by PKZIP for MS-DOS and also famous from WinZIP software for Windows.
209
   *
210
   * @param file the ZIP file to extract.
211
   * @param targetDir the {@link Path} with the directory to unzip to.
212
   */
213
  void extractZip(Path file, Path targetDir);
214

215
  /**
216
   * @param file the ZIP file to extract.
217
   * @param targetDir the {@link Path} with the directory to unzip to.
218
   * @param compression the {@link TarCompression} to use.
219
   */
220
  void extractTar(Path file, Path targetDir, TarCompression compression);
221

222
  /**
223
   * @param file the JAR file to extract.
224
   * @param targetDir the {@link Path} with the directory to extract to.
225
   */
226
  void extractJar(Path file, Path targetDir);
227

228
  /**
229
   * Extracts an Apple DMG (Disk Image) file that is similar to an ISO image. DMG files are commonly used for software releases on MacOS. Double-clicking such
230
   * files on MacOS mounts them and show the application together with a symbolic link to the central applications folder and some help instructions. The user
231
   * then copies the application to the applications folder via drag and drop in order to perform the installation.
232
   *
233
   * @param file the DMG file to extract.
234
   * @param targetDir the target directory where to extract the contents to.
235
   */
236
  void extractDmg(Path file, Path targetDir);
237

238
  /**
239
   * Extracts an MSI (Microsoft Installer) file. MSI files are commonly used for software releases on Windows that allow an installation wizard and easy later
240
   * uninstallation.
241
   *
242
   * @param file the MSI file to extract.
243
   * @param targetDir the target directory where to extract the contents to.
244
   */
245
  void extractMsi(Path file, Path targetDir);
246

247
  /**
248
   * Extracts an Apple PKG (Package) file. PKG files are used instead of {@link #extractDmg(Path, Path) DMG files} if additional changes have to be performed
249
   * like drivers to be installed. Similar to what {@link #extractMsi(Path, Path) MSI} is on Windows. PKG files are internally a xar based archive with a
250
   * specific structure.
251
   *
252
   * @param file the PKG file to extract.
253
   * @param targetDir the target directory where to extract the contents to.
254
   */
255
  void extractPkg(Path file, Path targetDir);
256

257
  /**
258
   * @param dir the {@link Path directory} to compress.
259
   * @param out the {@link OutputStream} to write the compressed data to.
260
   * @param path the path or filename to derive the archive format from (e.g. "archive.tgz", "archive.tar.gz", "archive.zip", etc.).
261
   */
262
  void compress(Path dir, OutputStream out, String path);
263

264
  /**
265
   * @param dir the {@link Path directory} to compress as TAR with given {@link TarCompression}.
266
   * @param out the {@link OutputStream} to write the compressed data to.
267
   * @param tarCompression the {@link TarCompression} to use for the TAR archive.
268
   */
269
  void compressTar(Path dir, OutputStream out, TarCompression tarCompression);
270

271
  /**
272
   * @param dir the {@link Path directory} to compress as TAR.
273
   * @param out the {@link OutputStream} to write the compressed data to.
274
   */
275
  void compressTar(Path dir, OutputStream out);
276

277
  /**
278
   * @param dir the {@link Path directory} to compress as TGZ.
279
   * @param out the {@link OutputStream} to write the compressed data to.
280
   */
281
  void compressTarGz(Path dir, OutputStream out);
282

283
  /**
284
   * @param dir the {@link Path directory} to compress as TBZ2.
285
   * @param out the {@link OutputStream} to write the compressed data to.
286
   */
287
  void compressTarBzip2(Path dir, OutputStream out);
288

289
  /**
290
   * @param dir the {@link Path directory} to compress as ZIP.
291
   * @param out the {@link OutputStream} to write the compressed data to.
292
   */
293
  void compressZip(Path dir, OutputStream out);
294

295
  /**
296
   * @param path the {@link Path} to convert.
297
   * @return the absolute and physical {@link Path} (without symbolic links).
298
   */
299
  Path toRealPath(Path path);
300

301
  /**
302
   * @param path the {@link Path} to convert.
303
   * @return the absolute and physical {@link Path}.
304
   */
305
  Path toCanonicalPath(Path path);
306

307
  /**
308
   * Deletes the given {@link Path} idempotent and recursive.
309
   * <p>
310
   * ATTENTION: In most cases we want to use {@link #backup(Path)} instead to prevent the user from data loss.
311
   * </p>
312
   *
313
   * @param path the {@link Path} to delete.
314
   */
315
  void delete(Path path);
316

317
  /**
318
   * Creates a new temporary directory. ATTENTION: The user of this method is responsible to do house-keeping and {@link #delete(Path) delete} it after the work
319
   * is done.
320
   *
321
   * @param name the default name of the temporary directory to create. A prefix or suffix may be added to ensure uniqueness.
322
   * @return the {@link Path} to the newly created and unique temporary directory.
323
   */
324
  Path createTempDir(String name);
325

326
  /**
327
   * @param dir the folder to search.
328
   * @param filter the {@link Predicate} used to find the {@link Predicate#test(Object) match}.
329
   * @param recursive - {@code true} to search recursive in all sub-folders, {@code false} otherwise.
330
   * @return the first child {@link Path} matching the given {@link Predicate} or {@code null} if no match was found.
331
   */
332
  Path findFirst(Path dir, Predicate<Path> filter, boolean recursive);
333

334
  /**
335
   * @param dir the {@link Path} to the directory where to list the children.
336
   * @param filter the {@link Predicate} used to {@link Predicate#test(Object) decide} which children to include (if {@code true} is returned).
337
   * @return all children of the given {@link Path} that match the given {@link Predicate}. Will be the empty list of the given {@link Path} is not an existing
338
   *     directory.
339
   */
340
  default List<Path> listChildren(Path dir, Predicate<Path> filter) {
341

342
    return listChildrenMapped(dir, child -> (filter.test(child)) ? child : null);
13!
343
  }
344

345
  /**
346
   * @param dir the {@link Path} to the directory where to list the children.
347
   * @param filter the filter {@link Function} used to {@link Function#apply(Object) filter and transform} children to include. If the {@link Function}
348
   *     returns  {@code null}, the child will be filtered, otherwise the returned {@link Path} will be included in the resulting {@link List}.
349
   * @return all children of the given {@link Path} returned by the given {@link Function}. Will be the empty list if the given {@link Path} is not an existing
350
   *     directory.
351
   */
352
  List<Path> listChildrenMapped(Path dir, Function<Path, Path> filter);
353

354
  /**
355
   * Finds the existing file with the specified name in the given list of directories.
356
   *
357
   * @param fileName The name of the file to find.
358
   * @param searchDirs The list of directories to search for the file.
359
   * @return The {@code Path} of the existing file, or {@code null} if the file is not found.
360
   */
361
  Path findExistingFile(String fileName, List<Path> searchDirs);
362

363
  /**
364
   * Checks if the given directory is empty.
365
   *
366
   * @param dir The {@link Path} object representing the directory to check.
367
   * @return {@code true} if the directory is empty, {@code false} otherwise.
368
   */
369
  boolean isEmptyDir(Path dir);
370

371
  /**
372
   * Sets or unsets the writable permission for the specified file path.
373
   *
374
   * @param file {@link Path} to the file.
375
   * @param writable {@code true} to make the file writable, {@code false} to make it read-only
376
   * @return {@code true} if the operation was successful or supported, {@code false} otherwise
377
   */
378
  boolean setWritable(Path file, boolean writable);
379

380
  /**
381
   * Makes a path executable (analog to 'chmod a+x').
382
   *
383
   * @param path the {@link Path} to the file or directory.
384
   */
385
  default void makeExecutable(Path path) {
386

387
    makeExecutable(path, false);
4✔
388
  }
1✔
389

390
  /**
391
   * Makes a path executable (analog to 'chmod a+x').
392
   *
393
   * @param path the {@link Path} to the file or directory.
394
   * @param confirm - {@code true} to get user confirmation before adding missing executable flags, {@code false} otherwise (always set missing flags).
395
   */
396
  void makeExecutable(Path path, boolean confirm);
397

398
  /**
399
   * Sets the given {@link PathPermissions} for the specified {@link Path}.
400
   *
401
   * @param path the {@link Path} to the file or directory.
402
   * @param permissions the {@link PathPermissions} to set.
403
   * @param logErrorAndContinue - {@code true} to only log errors and continue, {@code false} to fail with an exception on error.
404
   */
405
  void setFilePermissions(Path path, PathPermissions permissions, boolean logErrorAndContinue);
406

407
  /**
408
   * Gets the {@link PathPermissions} from the specified {@link Path}.
409
   *
410
   * @param path the {@link Path} to the file or directory.
411
   * @return the {@link PathPermissions} of the specified {@link Path}.
412
   */
413
  PathPermissions getFilePermissions(Path path);
414

415
  /**
416
   * Like the linux touch command this method will update the modification time of the given {@link Path} to the current
417
   * {@link System#currentTimeMillis() system time}. In case the file does not exist, it will be created as empty file. If already the
418
   * {@link Path#getParent() parent folder} does not exist, the operation will fail.
419
   *
420
   * @param file the {@link Path} to the file or folder.
421
   */
422
  void touch(Path file);
423

424
  /**
425
   * @param file the {@link Path} to the file to read.
426
   * @return the content of the specified file (in UTF-8 encoding), or {@code null} if the file doesn't exist.
427
   * @see java.nio.file.Files#readString(Path)
428
   */
429
  String readFileContent(Path file);
430

431
  /**
432
   * @param content the {@link String} with the text to write to a file.
433
   * @param file the {@link Path} to the file where to save.
434
   */
435
  default void writeFileContent(String content, Path file) {
436

437
    writeFileContent(content, file, false);
5✔
438
  }
1✔
439

440
  /**
441
   * @param content the {@link String} with the text to write to a file.
442
   * @param file the {@link Path} to the file where to save.
443
   * @param createParentDir if {@code true}, the parent directory will be created if it does not already exist, {@code false} otherwise (fail if parent does
444
   *     not exist).
445
   */
446
  void writeFileContent(String content, Path file, boolean createParentDir);
447

448
  /**
449
   * Like {@link #readFileContent(Path)} but giving one {@link String} per line of text. It will not allow to preserve line endings (CRLF vs. LF).
450
   *
451
   * @param file the {@link Path} to the file to read.
452
   * @return the content of the specified file (in UTF-8 encoding) as {@link List} of {@link String}s per line of text.
453
   */
454
  List<String> readFileLines(Path file);
455

456
  /**
457
   * Like {@link #writeFileContent(String, Path)} but taking a {@link List} with one {@link String} per line of text. It will always use LF as newline character
458
   * independent of the operating system.
459
   *
460
   * @param lines the {@link List} of {@link String}s per line of text.
461
   * @param file the {@link Path} to the file where to save.
462
   */
463
  default void writeFileLines(List<String> lines, Path file) {
464
    writeFileLines(lines, file, false);
5✔
465
  }
1✔
466

467
  /**
468
   * Like {@link #writeFileContent(String, Path, boolean)} but taking a {@link List} with one {@link String} per line of text. It will always use LF as newline
469
   * character independent of the operating system.
470
   *
471
   * @param lines the {@link List} of {@link String}s per line of text.
472
   * @param file the {@link Path} to the file where to save.
473
   * @param createParentDir if {@code true}, the parent directory will be created if it does not already exist, {@code false} otherwise (fail if parent does
474
   *     not exist).
475
   */
476
  void writeFileLines(List<String> lines, Path file, boolean createParentDir);
477

478
  /**
479
   * @param path the {@link Path} to check.
480
   * @return {@code true} if the given {@link Path} is a junction, false otherwise.
481
   */
482
  boolean isJunction(Path path);
483

484
  /**
485
   * @param file the {@link Path} to the {@link Properties} file to read.
486
   * @return the parsed {@link Properties}.
487
   */
488
  default Properties readProperties(Path file) {
489
    Properties properties = new Properties();
4✔
490
    readProperties(file, properties);
4✔
491
    return properties;
2✔
492
  }
493

494
  /**
495
   * @param file the {@link Path} to the {@link Properties} file to read.
496
   * @param properties the existing {@link Properties} to {@link Properties#load(Reader) load} into.
497
   */
498
  void readProperties(Path file, Properties properties);
499

500
  /**
501
   * @param properties the {@link Properties} to save.
502
   * @param file the {@link Path} to the file where to save the properties.
503
   */
504
  default void writeProperties(Properties properties, Path file) {
505

506
    writeProperties(properties, file, false);
5✔
507
  }
1✔
508

509

510
  /**
511
   * @param properties the {@link Properties} to save.
512
   * @param file the {@link Path} to the file where to save the properties.
513
   * @param createParentDir if {@code true}, the parent directory will created if it does not already exist, {@code false} otherwise (fail if parent does
514
   *     not exist).
515
   */
516
  void writeProperties(Properties properties, Path file, boolean createParentDir);
517

518
  /**
519
   * @param file the {@link Path} to read from
520
   * @return {@link com.devonfw.tools.ide.io.ini.IniFile}
521
   */
522
  default IniFile readIniFile(Path file) {
523
    IniFile iniFile = new IniFileImpl();
4✔
524
    readIniFile(file, iniFile);
4✔
525
    return iniFile;
2✔
526
  }
527

528
  /**
529
   * @param file the {@link Path} to read from
530
   * @param iniFile the {@link IniFile} object the data is loaded into
531
   */
532
  void readIniFile(Path file, IniFile iniFile);
533

534
  /**
535
   * @param iniFile the {@link IniFile} object
536
   * @param file the {@link Path} to write to
537
   */
538
  default void writeIniFile(IniFile iniFile, Path file) {
539
    writeIniFile(iniFile, file, false);
5✔
540
  }
1✔
541

542
  /**
543
   * @param iniFile the {@link IniFile} object
544
   * @param file the {@link Path} to write to
545
   * @param createParentDir whether to create missing parent directories
546
   */
547
  void writeIniFile(IniFile iniFile, Path file, boolean createParentDir);
548

549
  /**
550
   * @param path the {@link Path} to get the age from the modification time.
551
   * @return the age of the file as {@link Duration} from now to the modification time of the file.
552
   */
553
  public Duration getFileAge(Path path);
554

555
  /**
556
   * @param path the {@link Path} to check.
557
   * @param cacheDuration the {@link Duration} to consider as recent.
558
   * @return {@code true} if the given {@link Path} exists and is recent enough (its {@link #getFileAge(Path) age} is not greater than the given
559
   *     {@link Duration}), {@code false} otherwise.
560
   */
561
  boolean isFileAgeRecent(Path path, Duration cacheDuration);
562

563
  /**
564
   * @param path the tool {@link Path}.
565
   * @return a potential "bin" sub-folder or the given {@link Path} itself, if no such sub-folder was found.
566
   */
567
  default Path getBinPath(Path path) {
568

569
    Path binPath = path.resolve(IdeContext.FOLDER_BIN);
4✔
570
    if (Files.exists(binPath)) {
5✔
571
      return binPath;
2✔
572
    }
573
    return path;
2✔
574
  }
575

576
  /**
577
   * Reverse operation of {@link #getBinPath(Path)}.
578
   *
579
   * @param binPath the {@link Path} to a potential "bin" sub-folder of a tool {@link Path}.
580
   * @return the tool {@link Path} containing the "bin" sub-folder.
581
   */
582
  default Path getBinParentPath(Path binPath) {
583

584
    if (binPath.getFileName().toString().equals(IdeContext.FOLDER_BIN)) {
6✔
585
      return binPath.getParent();
3✔
586
    }
587
    return binPath;
2✔
588
  }
589
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc