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

devonfw / IDEasy / 19833443579

01 Dec 2025 06:36PM UTC coverage: 70.042% (+0.2%) from 69.854%
19833443579

Pull #1466

github

web-flow
Merge 8f5bcafad into 4921aa3d0
Pull Request #1466: #1166: import maven repos in intellij by editing misc.xml

3856 of 6039 branches covered (63.85%)

Branch coverage included in aggregate %.

9880 of 13572 relevant lines covered (72.8%)

3.16 hits per line

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

93.62
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.Optional;
12
import java.util.Properties;
13
import java.util.Set;
14
import java.util.function.Consumer;
15
import java.util.function.Function;
16
import java.util.function.Predicate;
17

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

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

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

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

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

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

53
  /**
54
   * @param file the {@link Path} to check.
55
   * @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
56
   *     directory).
57
   */
58
  boolean isFile(Path file);
59

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

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

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

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

88
  /**
89
   * 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
90
   * of missing privileges, Windows Junctions may be used as fallback, which must point to absolute paths. Therefore, the created link will be absolute instead
91
   * of relative.
92
   *
93
   * @param source the source {@link Path} to link to, may be relative or absolute.
94
   * @param link the destination {@link Path} where the symbolic link shall be created pointing to {@code source}.
95
   */
96
  default void symlink(Path source, Path link) {
97

98
    symlink(source, link, true);
5✔
99
  }
1✔
100

101
  /**
102
   * 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
103
   * 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}
104
   * flag will be ignored.
105
   *
106
   * @param source the source {@link Path} to link to, may be relative or absolute.
107
   * @param link the destination {@link Path} where the symbolic link shall be created pointing to {@code source}.
108
   * @param relative - {@code true} if the symbolic link shall be relative, {@code false} if it shall be absolute.
109
   */
110
  default void symlink(Path source, Path link, boolean relative) {
111

112
    link(source, link, relative, PathLinkType.SYMBOLIC_LINK);
6✔
113
  }
1✔
114

115
  /**
116
   * 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
117
   * replaced. In case of missing privileges, Windows mklink may be used as fallback.
118
   *
119
   * @param source the source {@link Path} to link to, may be relative or absolute.
120
   * @param link the destination {@link Path} where the hard link shall be created pointing to {@code source}.
121
   */
122
  default void hardlink(Path source, Path link) {
123

124
    link(source, link, false, PathLinkType.HARD_LINK);
×
125
  }
×
126

127
  /**
128
   * 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,
129
   * Windows mklink may be used as fallback, which must point to absolute paths. In such case the {@code relative} flag will be ignored.
130
   *
131
   * @param source the source {@link Path} to link to, may be relative or absolute.
132
   * @param link the destination {@link Path} where the link shall be created pointing to {@code source}.
133
   * @param relative - {@code true} if the link shall be relative, {@code false} if it shall be absolute.
134
   * @param type the {@link PathLinkType}.
135
   */
136
  void link(Path source, Path link, boolean relative, PathLinkType type);
137

138
  /**
139
   * @param link the {@link PathLink} to {@link #link(Path, Path, boolean, PathLinkType) create}.
140
   */
141
  default void link(PathLink link) {
142
    link(link.source(), link.link(), true, link.type());
9✔
143
  }
1✔
144

145
  /**
146
   * @param source the source {@link Path file or folder} to copy.
147
   * @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
148
   *     you will find the same content of {@code source} in {@code target}.
149
   */
150
  default void copy(Path source, Path target) {
151

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

155
  /**
156
   * @param source the source {@link Path file or folder} to copy.
157
   * @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}
158
   *     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
159
   *     {@code target}. Therefore, the result is always clear and easy to predict and understand. Also, you can easily rename a file to copy. While
160
   *     {@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
161
   *     the same content of {@code source} in {@code target}.
162
   * @param mode the {@link FileCopyMode}.
163
   */
164
  default void copy(Path source, Path target, FileCopyMode mode) {
165

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

169
  /**
170
   * @param source the source {@link Path file or folder} to copy.
171
   * @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}
172
   *     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
173
   *     {@code target}. Therefore, the result is always clear and easy to predict and understand. Also, you can easily rename a file to copy. While
174
   *     {@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
175
   *     the same content of {@code source} in {@code target}.
176
   * @param mode the {@link FileCopyMode}.
177
   * @param listener the {@link PathCopyListener} that will be called for each copied {@link Path}.
178
   */
179
  void copy(Path source, Path target, FileCopyMode mode, PathCopyListener listener);
180

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

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

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

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

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

208
  /**
209
   * 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.
210
   *
211
   * @param file the ZIP file to extract.
212
   * @param targetDir the {@link Path} with the directory to unzip to.
213
   */
214
  void extractZip(Path file, Path targetDir);
215

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

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

229
  /**
230
   * 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
231
   * 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
232
   * then copies the application to the applications folder via drag and drop in order to perform the installation.
233
   *
234
   * @param file the DMG file to extract.
235
   * @param targetDir the target directory where to extract the contents to.
236
   */
237
  void extractDmg(Path file, Path targetDir);
238

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

248
  /**
249
   * 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
250
   * 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
251
   * specific structure.
252
   *
253
   * @param file the PKG file to extract.
254
   * @param targetDir the target directory where to extract the contents to.
255
   */
256
  void extractPkg(Path file, Path targetDir);
257

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

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

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

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

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

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

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

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

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

318
  /**
319
   * 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
320
   * is done.
321
   *
322
   * @param name the default name of the temporary directory to create. A prefix or suffix may be added to ensure uniqueness.
323
   * @return the {@link Path} to the newly created and unique temporary directory.
324
   */
325
  Path createTempDir(String name);
326

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

335
  /**
336
   * Searches upward from the given starting path to find the nearest ancestor directory that contains a specific subfolder. The search stops before ascending
337
   * into any parent directory whose name matches the provided stop boundary.
338
   *
339
   * @param start the starting {@link Path} from which to begin the upward traversal. Must not be {@code null}.
340
   * @param folderName the name of the subfolder to look for in each ancestor directory (e.g., ".idea"). Must not be {@code null} or empty.
341
   * @param stopBeforeParentName the name of a parent directory at which the search should stop (case-insensitive). The method will not ascend into this
342
   *     directory. For example, if this is "workspaces", the search will stop at the child of "workspaces" and will not check inside "workspaces" itself.
343
   * @return {@link Path} of the ancestor directory that contains the specified subfolder, or {@link Optional#empty()} if no such ancestor is found before
344
   *     reaching the stop boundary.
345
   */
346
  Path findAncestorWithFolder(
347
      Path start,
348
      String folderName,
349
      String stopBeforeParentName
350
  );
351

352
  /**
353
   * @param dir the {@link Path} to the directory where to list the children.
354
   * @param filter the {@link Predicate} used to {@link Predicate#test(Object) decide} which children to include (if {@code true} is returned).
355
   * @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
356
   *     directory.
357
   */
358
  default List<Path> listChildren(Path dir, Predicate<Path> filter) {
359

360
    return listChildrenMapped(dir, child -> (filter.test(child)) ? child : null);
13!
361
  }
362

363
  /**
364
   * @param dir the {@link Path} to the directory where to list the children.
365
   * @param filter the filter {@link Function} used to {@link Function#apply(Object) filter and transform} children to include. If the {@link Function}
366
   *     returns  {@code null}, the child will be filtered, otherwise the returned {@link Path} will be included in the resulting {@link List}.
367
   * @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
368
   *     directory.
369
   */
370
  List<Path> listChildrenMapped(Path dir, Function<Path, Path> filter);
371

372
  /**
373
   * Finds the existing file with the specified name in the given list of directories.
374
   *
375
   * @param fileName The name of the file to find.
376
   * @param searchDirs The list of directories to search for the file.
377
   * @return The {@code Path} of the existing file, or {@code null} if the file is not found.
378
   */
379
  Path findExistingFile(String fileName, List<Path> searchDirs);
380

381
  /**
382
   * Checks if the given directory is empty.
383
   *
384
   * @param dir The {@link Path} object representing the directory to check.
385
   * @return {@code true} if the directory is empty, {@code false} otherwise.
386
   */
387
  boolean isEmptyDir(Path dir);
388

389
  /**
390
   * Sets or unsets the writable permission for the specified file path.
391
   *
392
   * @param file {@link Path} to the file.
393
   * @param writable {@code true} to make the file writable, {@code false} to make it read-only
394
   * @return {@code true} if the operation was successful or supported, {@code false} otherwise
395
   */
396
  boolean setWritable(Path file, boolean writable);
397

398
  /**
399
   * Makes a path executable (analog to 'chmod a+x').
400
   *
401
   * @param path the {@link Path} to the file or directory.
402
   */
403
  default void makeExecutable(Path path) {
404

405
    makeExecutable(path, false);
4✔
406
  }
1✔
407

408
  /**
409
   * Makes a path executable (analog to 'chmod a+x').
410
   *
411
   * @param path the {@link Path} to the file or directory.
412
   * @param confirm - {@code true} to get user confirmation before adding missing executable flags, {@code false} otherwise (always set missing flags).
413
   */
414
  void makeExecutable(Path path, boolean confirm);
415

416
  /**
417
   * Sets the given {@link PathPermissions} for the specified {@link Path}.
418
   *
419
   * @param path the {@link Path} to the file or directory.
420
   * @param permissions the {@link PathPermissions} to set.
421
   * @param logErrorAndContinue - {@code true} to only log errors and continue, {@code false} to fail with an exception on error.
422
   */
423
  void setFilePermissions(Path path, PathPermissions permissions, boolean logErrorAndContinue);
424

425
  /**
426
   * Gets the {@link PathPermissions} from the specified {@link Path}.
427
   *
428
   * @param path the {@link Path} to the file or directory.
429
   * @return the {@link PathPermissions} of the specified {@link Path}.
430
   */
431
  PathPermissions getFilePermissions(Path path);
432

433
  /**
434
   * Like the linux touch command this method will update the modification time of the given {@link Path} to the current
435
   * {@link System#currentTimeMillis() system time}. In case the file does not exist, it will be created as empty file. If already the
436
   * {@link Path#getParent() parent folder} does not exist, the operation will fail.
437
   *
438
   * @param file the {@link Path} to the file or folder.
439
   */
440
  void touch(Path file);
441

442
  /**
443
   * @param file the {@link Path} to the file to read.
444
   * @return the content of the specified file (in UTF-8 encoding), or {@code null} if the file doesn't exist.
445
   * @see java.nio.file.Files#readString(Path)
446
   */
447
  String readFileContent(Path file);
448

449
  /**
450
   * @param content the {@link String} with the text to write to a file.
451
   * @param file the {@link Path} to the file where to save.
452
   */
453
  default void writeFileContent(String content, Path file) {
454

455
    writeFileContent(content, file, false);
5✔
456
  }
1✔
457

458
  /**
459
   * @param content the {@link String} with the text to write to a file.
460
   * @param file the {@link Path} to the file where to save.
461
   * @param createParentDir if {@code true}, the parent directory will be created if it does not already exist, {@code false} otherwise (fail if parent does
462
   *     not exist).
463
   */
464
  void writeFileContent(String content, Path file, boolean createParentDir);
465

466
  /**
467
   * Like {@link #readFileContent(Path)} but giving one {@link String} per line of text. It will not allow to preserve line endings (CRLF vs. LF).
468
   *
469
   * @param file the {@link Path} to the file to read.
470
   * @return the content of the specified file (in UTF-8 encoding) as {@link List} of {@link String}s per line of text.
471
   */
472
  List<String> readFileLines(Path file);
473

474
  /**
475
   * 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
476
   * independent of the operating system.
477
   *
478
   * @param lines the {@link List} of {@link String}s per line of text.
479
   * @param file the {@link Path} to the file where to save.
480
   */
481
  default void writeFileLines(List<String> lines, Path file) {
482
    writeFileLines(lines, file, false);
5✔
483
  }
1✔
484

485
  /**
486
   * 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
487
   * character independent of the operating system.
488
   *
489
   * @param lines the {@link List} of {@link String}s per line of text.
490
   * @param file the {@link Path} to the file where to save.
491
   * @param createParentDir if {@code true}, the parent directory will be created if it does not already exist, {@code false} otherwise (fail if parent does
492
   *     not exist).
493
   */
494
  void writeFileLines(List<String> lines, Path file, boolean createParentDir);
495

496
  /**
497
   * @param path the {@link Path} to check.
498
   * @return {@code true} if the given {@link Path} is a junction, false otherwise.
499
   */
500
  boolean isJunction(Path path);
501

502
  /**
503
   * @param file the {@link Path} to the {@link Properties} file to read.
504
   * @return the parsed {@link Properties}.
505
   */
506
  default Properties readProperties(Path file) {
507
    Properties properties = new Properties();
4✔
508
    readProperties(file, properties);
4✔
509
    return properties;
2✔
510
  }
511

512
  /**
513
   * @param file the {@link Path} to the {@link Properties} file to read.
514
   * @param properties the existing {@link Properties} to {@link Properties#load(Reader) load} into.
515
   */
516
  void readProperties(Path file, Properties properties);
517

518
  /**
519
   * @param properties the {@link Properties} to save.
520
   * @param file the {@link Path} to the file where to save the properties.
521
   */
522
  default void writeProperties(Properties properties, Path file) {
523

524
    writeProperties(properties, file, false);
5✔
525
  }
1✔
526

527

528
  /**
529
   * @param properties the {@link Properties} to save.
530
   * @param file the {@link Path} to the file where to save the properties.
531
   * @param createParentDir if {@code true}, the parent directory will be created if it does not already exist, {@code false} otherwise (fail if parent does
532
   *     not exist).
533
   */
534
  void writeProperties(Properties properties, Path file, boolean createParentDir);
535

536
  /**
537
   * @param file the {@link Path} to read from
538
   * @return {@link com.devonfw.tools.ide.io.ini.IniFile}
539
   */
540
  default IniFile readIniFile(Path file) {
541
    IniFile iniFile = new IniFileImpl();
4✔
542
    readIniFile(file, iniFile);
4✔
543
    return iniFile;
2✔
544
  }
545

546
  /**
547
   * @param file the {@link Path} to read from
548
   * @param iniFile the {@link IniFile} object the data is loaded into
549
   */
550
  void readIniFile(Path file, IniFile iniFile);
551

552
  /**
553
   * @param iniFile the {@link IniFile} object
554
   * @param file the {@link Path} to write to
555
   */
556
  default void writeIniFile(IniFile iniFile, Path file) {
557
    writeIniFile(iniFile, file, false);
5✔
558
  }
1✔
559

560
  /**
561
   * @param iniFile the {@link IniFile} object
562
   * @param file the {@link Path} to write to
563
   * @param createParentDir whether to create missing parent directories
564
   */
565
  void writeIniFile(IniFile iniFile, Path file, boolean createParentDir);
566

567
  /**
568
   * @param path the {@link Path} to get the age from the modification time.
569
   * @return the age of the file as {@link Duration} from now to the modification time of the file.
570
   */
571
  public Duration getFileAge(Path path);
572

573
  /**
574
   * @param path the {@link Path} to check.
575
   * @param cacheDuration the {@link Duration} to consider as recent.
576
   * @return {@code true} if the given {@link Path} exists and is recent enough (its {@link #getFileAge(Path) age} is not greater than the given
577
   *     {@link Duration}), {@code false} otherwise.
578
   */
579
  boolean isFileAgeRecent(Path path, Duration cacheDuration);
580

581
  /**
582
   * @param path the tool {@link Path}.
583
   * @return a potential "bin" sub-folder or the given {@link Path} itself, if no such sub-folder was found.
584
   */
585
  default Path getBinPath(Path path) {
586

587
    Path binPath = path.resolve(IdeContext.FOLDER_BIN);
4✔
588
    if (Files.exists(binPath)) {
5✔
589
      return binPath;
2✔
590
    }
591
    return path;
2✔
592
  }
593

594
  /**
595
   * Reverse operation of {@link #getBinPath(Path)}.
596
   *
597
   * @param binPath the {@link Path} to a potential "bin" sub-folder of a tool {@link Path}.
598
   * @return the tool {@link Path} containing the "bin" sub-folder.
599
   */
600
  default Path getBinParentPath(Path binPath) {
601

602
    if (binPath.getFileName().toString().equals(IdeContext.FOLDER_BIN)) {
6✔
603
      return binPath.getParent();
3✔
604
    }
605
    return binPath;
2✔
606
  }
607
}
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