Skip to content

Commit 49c07dc

Browse files
Hierarchy path predicates
1 parent 361d393 commit 49c07dc

File tree

6 files changed

+825
-74
lines changed

6 files changed

+825
-74
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
---
9+
## [0.0.5] - Unreleased
10+
11+
### Added
12+
- New path predicates: `hasParentMatching`, `hasAncestorMatching`, `hasDirectChildMatching`, `hasDescendantMatching`, `hasSiblingMatching`
13+
814
---
915
## [0.0.4] - 2025-09-27
1016

ROADMAP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
- [x] Option: Line extension (=additional text after the file name)
2222

2323
## To do
24-
- [ ] More `PathPredicates` functions!
24+
- [x] More `PathPredicates` functions!
2525
- [ ] Option: custom emojis
2626

2727
## Backlog / To analyze / To implement if requested

src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/PathPredicateBuilder.java

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,4 +158,83 @@ public PathPredicateBuilder isFile() {
158158
return pathTest(PathPredicates.isFile());
159159
}
160160

161+
// ---------- Hierarchy ----------
162+
163+
/**
164+
* Adds a condition that tests the direct parent of the path.
165+
*
166+
* @param parentPredicate the predicate to apply on the direct parent
167+
*
168+
* @return this builder for chaining
169+
*
170+
* @see PathPredicates#hasParentMatching(Predicate)
171+
*/
172+
public PathPredicateBuilder hasParentMatching(Predicate<Path> parentPredicate) {
173+
return pathTest(PathPredicates.hasParentMatching(parentPredicate));
174+
}
175+
176+
/**
177+
* Adds a condition that tests all ancestors of the path (stopping at the first match).
178+
*
179+
* The condition is satisfied if the given {@code ancestorPredicate} evaluates
180+
* to {@code true} for any ancestor in the {@link Path#getParent()} chain.
181+
*
182+
* @param ancestorPredicate the predicate to apply on each ancestor
183+
*
184+
* @return this builder for chaining
185+
*
186+
* @see PathPredicates#hasAncestorMatching(Predicate)
187+
*/
188+
public PathPredicateBuilder hasAncestorMatching(Predicate<Path> ancestorPredicate) {
189+
return pathTest(PathPredicates.hasAncestorMatching(ancestorPredicate));
190+
}
191+
192+
/**
193+
* Adds a condition that tests the direct children of the path.
194+
*
195+
* The condition is satisfied if the given {@code childPredicate} evaluates
196+
* to {@code true} for at least one direct child of the tested path.
197+
*
198+
* @param childPredicate the predicate to apply on each direct child
199+
*
200+
* @return this builder for chaining
201+
*
202+
* @see PathPredicates#hasDirectChildMatching(Predicate)
203+
*/
204+
public PathPredicateBuilder hasDirectChildMatching(Predicate<Path> childPredicate) {
205+
return pathTest(PathPredicates.hasDirectChildMatching(childPredicate));
206+
}
207+
208+
/**
209+
* Adds a condition that tests all descendants of the path (children at any depth).
210+
*
211+
* The condition is satisfied if the given {@code descendantPredicate} evaluates
212+
* to {@code true} for at least one descendant in the directory tree.
213+
*
214+
* @param descendantPredicate the predicate to apply on each descendant
215+
*
216+
* @return this builder for chaining
217+
*
218+
* @see PathPredicates#hasDescendantMatching(Predicate)
219+
*/
220+
public PathPredicateBuilder hasDescendantMatching(Predicate<Path> descendantPredicate) {
221+
return pathTest(PathPredicates.hasDescendantMatching(descendantPredicate));
222+
}
223+
224+
/**
225+
* Adds a condition that tests the siblings of the path.
226+
*
227+
* The condition is satisfied if the given {@code siblingPredicate} evaluates
228+
* to {@code true} for at least one sibling of the tested path.
229+
*
230+
* @param siblingPredicate the predicate to apply on each sibling
231+
*
232+
* @return this builder for chaining
233+
*
234+
* @see PathPredicates#hasSiblingMatching(Predicate)
235+
*/
236+
public PathPredicateBuilder hasSiblingMatching(Predicate<Path> siblingPredicate) {
237+
return pathTest(PathPredicates.hasSiblingMatching(siblingPredicate));
238+
}
239+
161240
}

src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/PathPredicates.java

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.github.computerdaddyguy.jfiletreeprettyprinter;
22

33
import java.nio.file.Path;
4+
import java.util.Objects;
45
import java.util.function.Predicate;
56
import java.util.regex.Pattern;
67
import org.jspecify.annotations.NullMarked;
@@ -118,4 +119,96 @@ public static Predicate<Path> isFile() {
118119
return PathUtils::isFile;
119120
}
120121

122+
// ---------- Hierarchy ----------
123+
124+
/**
125+
* Creates a predicate that evaluates the direct parent of a given path.
126+
* The returned predicate applies the provided {@code parentPredicate} to {@link Path#getParent()} of the tested path.
127+
* If tested path has no parent, the returned predicate will always return {@code false}.
128+
*
129+
* @param parentPredicate the predicate to apply on the parent path (must not be {@code null})
130+
*
131+
* @return a predicate that returns {@code true} if the parent of the tested path matches the given predicate
132+
*
133+
* @throws NullPointerException if {@code parentPredicate} is {@code null}
134+
*/
135+
public static Predicate<Path> hasParentMatching(Predicate<Path> parentPredicate) {
136+
Objects.requireNonNull(parentPredicate, "parentPredicate is null");
137+
return path -> PathUtils.hasParentMatching(path, parentPredicate);
138+
}
139+
140+
/**
141+
* Creates a predicate that evaluates all ancestors of a given path.
142+
* <p>
143+
* The returned predicate applies the provided {@code ancestorPredicate}
144+
* to each parent in the chain obtained via successive calls to {@link Path#getParent()}.
145+
* If any ancestor matches, the predicate returns {@code true}.
146+
* <p>
147+
* If the tested path has no parent, the returned predicate always returns {@code false}.
148+
*
149+
* @param ancestorPredicate the predicate to apply on each ancestor path (must not be {@code null})
150+
*
151+
* @return a predicate that returns {@code true} if any ancestor of the tested path matches the given predicate
152+
*
153+
* @throws NullPointerException if {@code ancestorPredicate} is {@code null}
154+
*/
155+
public static Predicate<Path> hasAncestorMatching(Predicate<Path> ancestorPredicate) {
156+
Objects.requireNonNull(ancestorPredicate, "ancestorPredicate is null");
157+
return path -> PathUtils.hasAncestorMatching(path, ancestorPredicate);
158+
}
159+
160+
/**
161+
* Creates a predicate that evaluates the direct children of a given path.
162+
* <p>
163+
* The returned predicate applies the provided {@code childPredicate} to each
164+
* direct child of the tested path. The predicate returns {@code true} if at least
165+
* one child matches. If the path is not a directory, the predicate always returns {@code false}.
166+
*
167+
* @param childPredicate the predicate to apply on each direct child
168+
*
169+
* @return a predicate that returns {@code true} if at least one direct child matches
170+
*
171+
* @throws NullPointerException if {@code childPredicate} is {@code null}
172+
*/
173+
public static Predicate<Path> hasDirectChildMatching(Predicate<Path> childPredicate) {
174+
Objects.requireNonNull(childPredicate, "childPredicate is null");
175+
return path -> PathUtils.hasDirectChildMatching(path, childPredicate);
176+
}
177+
178+
/**
179+
* Creates a predicate that evaluates all descendants (children at any depth) of a given path.
180+
* <p>
181+
* The returned predicate applies the provided {@code descendantPredicate} recursively to each
182+
* child and sub-child. It returns {@code true} if at least one descendant matches.
183+
*
184+
* @param descendantPredicate the predicate to apply on each descendant
185+
*
186+
* @return a predicate that returns {@code true} if at least one descendant matches
187+
*
188+
* @throws NullPointerException if {@code descendantPredicate} is {@code null}
189+
*/
190+
public static Predicate<Path> hasDescendantMatching(Predicate<Path> descendantPredicate) {
191+
Objects.requireNonNull(descendantPredicate, "descendantPredicate is null");
192+
return path -> PathUtils.hasDescendantMatching(path, descendantPredicate);
193+
}
194+
195+
/**
196+
* Creates a predicate that evaluates the siblings of a given path.
197+
* <p>
198+
* The returned predicate applies the provided {@code siblingPredicate} to each
199+
* sibling of the tested path (other files/directories in the same parent).
200+
* The predicate returns {@code true} if at least one sibling matches.
201+
* If the tested path has no parent, the predicate always returns {@code false}.
202+
*
203+
* @param siblingPredicate the predicate to apply on each sibling
204+
*
205+
* @return a predicate that returns {@code true} if at least one sibling matches
206+
*
207+
* @throws NullPointerException if {@code siblingPredicate} is {@code null}
208+
*/
209+
public static Predicate<Path> hasSiblingMatching(Predicate<Path> siblingPredicate) {
210+
Objects.requireNonNull(siblingPredicate, "siblingPredicate is null");
211+
return path -> PathUtils.hasSiblingMatching(path, siblingPredicate);
212+
}
213+
121214
}

0 commit comments

Comments
 (0)