Reference for org.tomitribe.util.dir Dir proxy-based filesystem library. TRIGGER when: code imports from org.tomitribe.util.dir or org.tomitribe.util.paths, uses Dir.of(), or user needs strongly-typed filesystem path references via dynamic proxy interfaces. DO NOT TRIGGER when: working with standard java.nio.file.Path or java.io.File directly.
Java utility for compile-time checked, code-completable file path references using dynamic proxies.
Eliminates string-based Path references by modeling directory structures as interfaces.
Package: org.tomitribe.util.paths
Location: ~/work/tomitribe/tomitribe-util
<groupId>org.tomitribe</groupId>
<artifactId>tomitribe-util</artifactId>
Define an interface whose methods mirror a directory structure. Dir.of() returns a dynamic proxy
that resolves method calls to java.nio.file.Path references.
// Instead of error-prone strings:
Path srcMainResources = moduleDir.resolve("src/main/resources");
Path srcTestResources = moduleDir.resolve("src/test/resource"); // typo!
// Use compile-time checked interfaces:
Module module = Dir.of(Module.class, moduleDir);
Path srcMainResources = module.src().main().resources();
Path srcTestResources = module.src().test().resources();
Methods return Path for leaf entries, or another interface for subdirectories:
public interface Project {
Src src(); // returns another interface (subdirectory)
Path target(); // returns Path
@Name("pom.xml")
Path pomXml(); // @Name for non-Java-identifier filenames
@Name(".gitignore")
Path gitignore(); // works for hidden files too
}
public interface Src {
Section main();
Section test();
Section section(String name); // dynamic subdirectory name
}
public interface Section {
Path java();
Path resources();
}
import org.tomitribe.util.paths.Dir;
Path mydir = java.nio.file.Paths.get("/some/path/to/project");
Project project = Dir.of(Project.class, mydir);
| Annotation | Target | Purpose |
|---|---|---|
@Name("filename") | Method | Override the filename (default: method name) |
@Filter(Predicate.class) | Method | Filter listings by Predicate<Path> (repeatable) |
@Walk | Method | Recursive traversal instead of direct children only |
@Walk(maxDepth=N) | Method | Limit recursion depth |
@Walk(minDepth=N) | Method | Skip results shallower than N levels |
@Walk(minDepth=M, maxDepth=N) | Method | Window into a specific depth range |
@Mkdir | Method | Create directory on access (parent must exist) |
@Mkdirs | Method | Create directory and all parents on access |
@Parent | Method | Navigate to parent directory (1 level up) |
@Parent(N) | Method | Navigate N levels up |
Methods can return:
Path -- resolves to a file or directory pathString arg -- creates a proxy for a dynamically-named subdirectoryPath[] -- lists directory contents as an arrayStream<Path> -- lists directory contents as a streamModule[]) -- lists contents, each wrapped in a Dir proxyStream<Module>) -- streams contents, each wrapped in a Dir proxypublic interface Project extends Dir {
Path[] modules(); // array of all direct children
Stream<Path> modules(); // stream of all direct children
}
@FilterImplement Predicate<Path> and reference it with @Filter:
public static class HasPomXml implements Predicate<Path> {
@Override
public boolean test(final Path path) {
return java.nio.file.Files.exists(path.resolve("pom.xml"));
}
}
public interface Project extends Dir {
@Filter(HasPomXml.class)
Module[] modules();
@Filter(HasPomXml.class)
Stream<Module> modules();
}
@FilterStack multiple @Filter annotations; all must pass:
public interface Work extends Dir {
@Walk
@Filter(IsJunit.class)
@Filter(IsTxt.class)
Stream<Path> junitTxtFiles();
}
@Walkpublic interface Project extends Dir {
@Walk // unlimited depth
@Filter(HasPomXml.class)
Stream<Module> allModules();
@Walk(maxDepth = 3) // limit depth
@Filter(HasPomXml.class)
Stream<Module> nearModules();
@Walk(minDepth = 2, maxDepth = 2) // exact depth
Stream<Path> exactlyTwoDeep();
}
@Walk works with both Stream and array return types.
@Mkdir and @Mkdirspublic interface Section {
@Mkdirs // creates directory AND all missing parents
Path java();
@Mkdir // creates directory only (parent must exist)
Path resources();
}
Directories are created lazily when the method is invoked:
// src/main/java is created (along with src/ and src/main/) at this point:
Path srcMainJava = project.src().main().java();
@Parent for Navigating Uppublic interface Module extends Dir {
@Name("pom.xml") Path pomXml();
Src src();
}
public interface Src {
@Parent Module module(); // 1 level up: src/ -> project/
Section main();
Section test();
}
public interface Section {
@Parent(2) Module module(); // 2 levels up: src/main/ -> project/
Path java();
Path resources();
}
public interface Java extends Dir {
@Parent(3) Module module(); // 3 levels up: src/main/java/ -> project/
}
Dir InterfaceInterfaces can extend Dir to gain built-in methods:
public interface Module extends Dir {
@Name("pom.xml") Path pomXml();
Src src();
}
Built-in Dir methods:
| Method | Returns | Description |
|---|---|---|
dir() | Path | The path for this directory |
get() | Path | Synonym for dir() |
dir(String name) | Dir | A Dir wrapping a named subdirectory |
file(String name) | Path | Path for a named file or subdirectory |
parent() | Path | The parent path |
mkdir() | Path | Create this directory (parent must exist) |
mkdirs() | Path | Create this directory and all parents |
delete() | void | Recursively delete this directory |
walk() | Stream<Path> | All files and directories recursively |
walk(int depth) | Stream<Path> | Walk limited to given depth |
files() | Stream<Path> | Only files (no directories) recursively |
files(int depth) | Stream<Path> | Files limited to given depth |
Methods can return custom classes that wrap a Path. The class needs either:
Path argument, ORPath and returning the class type// Constructor-based
public class Pom {
private final Path file;
public Pom(final Path file) { this.file = file; }
public Path getFile() { return file; }
}
// Factory method-based
public class Archivo {
private final Path path;
private Archivo(final Path path) { this.path = path; }
public static Archivo from(final Path path) { return new Archivo(path); }
}
public interface Module extends Dir {
@Name("pom.xml") Pom pomXml(); // single wrapper
@Walk @Filter(IsJava.class)
Stream<Archivo> javaFiles(); // stream of wrappers
@Walk @Filter(IsJava.class)
Archivo[] javaFileArray(); // array of wrappers
}
Interfaces support default methods for custom logic:
public interface Repository extends Dir {
default Group group(final String name) {
final String path = name.replace(".", "/");
final Group group = Dir.of(Group.class, dir().resolve(path));
group.mkdirs();
return group;
}
}
Repository repo = Dir.of(Repository.class, someDir);
Group group = repo.group("org.color"); // creates org/color/ directory
equals, hashCode, toStringDir proxies implement equals/hashCode based on the underlying Path, and toString returns the absolute path string.
Dir a = Dir.of(Dir.class, Paths.get("/foo/bar"));
Dir b = Dir.of(Dir.class, Paths.get("/foo/bar"));
a.equals(b); // true
a.toString(); // "/foo/bar"