Skip to content

Commit 1f5d3b1

Browse files
New "glob" path matching methods
1 parent 351b2d0 commit 1f5d3b1

File tree

7 files changed

+224
-33
lines changed

7 files changed

+224
-33
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
## [0.0.5] - Unreleased
1010

1111
### Added
12-
- New path predicates: `hasParentMatching`, `hasAncestorMatching`, `hasDirectChildMatching`, `hasDescendantMatching`, `hasSiblingMatching`
12+
- New path predicates: `hasParentMatching`, `hasAncestorMatching`, `hasDirectChildMatching`, `hasDescendantMatching`, `hasSiblingMatching`, `hasFullPathMatchingGlob`, `hasFullPathMatching`, `hasNameMatchingGlob`
1313

1414
### Changed
1515
- `PathUtils` removed, `PathPredicates`rework

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -310,16 +310,16 @@ If the function returns `null`, nothing is added.
310310
```java
311311
// Example: LineExtension.java
312312
Function<Path, String> lineExtension = path -> {
313-
if (PathPredicates.isDirectory(path) && PathPredicates.hasName(path, "api")) {
313+
if (PathPredicates.hasFullPathMatchingGlob(path, "**/src/main/java/api")) {
314314
return "\t\t\t// All API code: controllers, etc.";
315315
}
316-
if (PathPredicates.isDirectory(path) && PathPredicates.hasName(path, "domain")) {
316+
if (PathPredicates.hasFullPathMatchingGlob(path, "**/src/main/java/domain")) {
317317
return "\t\t\t// All domain code: value objects, etc.";
318318
}
319-
if (PathPredicates.isDirectory(path) && PathPredicates.hasName(path, "infra")) {
319+
if (PathPredicates.hasFullPathMatchingGlob(path, "**/src/main/java/infra")) {
320320
return "\t\t\t// All infra code: database, email service, etc.";
321321
}
322-
if (PathPredicates.isFile(path) && PathPredicates.hasName(path, "application.properties")) {
322+
if (PathPredicates.hasNameMatchingGlob(path, "*.properties")) {
323323
return "\t// Config file";
324324
}
325325
return null;

src/example/java/io/github/computerdaddyguy/jfiletreeprettyprinter/example/LineExtension.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ public class LineExtension {
99

1010
public static void main(String[] args) {
1111
Function<Path, String> lineExtension = path -> {
12-
if (PathPredicates.isDirectory(path) && PathPredicates.hasName(path, "api")) {
12+
if (PathPredicates.hasFullPathMatchingGlob(path, "**/src/main/java/api")) {
1313
return "\t\t\t// All API code: controllers, etc.";
1414
}
15-
if (PathPredicates.isDirectory(path) && PathPredicates.hasName(path, "domain")) {
15+
if (PathPredicates.hasFullPathMatchingGlob(path, "**/src/main/java/domain")) {
1616
return "\t\t\t// All domain code: value objects, etc.";
1717
}
18-
if (PathPredicates.isDirectory(path) && PathPredicates.hasName(path, "infra")) {
18+
if (PathPredicates.hasFullPathMatchingGlob(path, "**/src/main/java/infra")) {
1919
return "\t\t\t// All infra code: database, email service, etc.";
2020
}
21-
if (PathPredicates.isFile(path) && PathPredicates.hasName(path, "application.properties")) {
21+
if (PathPredicates.hasNameMatchingGlob(path, "*.properties")) {
2222
return "\t// Config file";
2323
}
2424
return null;

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.io.File;
44
import java.nio.file.Path;
5+
import java.nio.file.PathMatcher;
56
import java.util.Objects;
67
import java.util.function.Predicate;
78
import java.util.regex.Pattern;
@@ -75,6 +76,32 @@ public PathPredicateBuilder fileTest(Predicate<File> predicate) {
7576
return pathTest(path -> predicate.test(path.toFile()));
7677
}
7778

79+
// ---------- PathMatcher ----------
80+
81+
/**
82+
* Adds a condition that tests whether the path matches the specified glob pattern.
83+
*
84+
* @param glob the glob pattern to match; must not be {@code null}
85+
*
86+
* @return this builder for chaining
87+
*/
88+
public PathPredicateBuilder hasFullPathMatchingGlob(String glob) {
89+
Objects.requireNonNull(glob, "glob is null");
90+
return pathTest(path -> PathPredicates.hasFullPathMatchingGlob(path, glob));
91+
}
92+
93+
/**
94+
* Adds a condition that tests whether the path matches the provided {@link PathMatcher}.
95+
*
96+
* @param matcher the {@code PathMatcher} to use; must not be {@code null}
97+
*
98+
* @return this builder for chaining
99+
*/
100+
public PathPredicateBuilder hasFullPathMatching(PathMatcher matcher) {
101+
Objects.requireNonNull(matcher, "matcher is null");
102+
return pathTest(path -> PathPredicates.hasFullPathMatching(path, matcher));
103+
}
104+
78105
// ---------- Name ----------
79106

80107
/**
@@ -115,6 +142,26 @@ public PathPredicateBuilder hasNameMatching(Pattern pattern) {
115142
return pathTest(path -> PathPredicates.hasNameMatching(path, pattern));
116143
}
117144

145+
/**
146+
* Adds a condition that tests whether the file name of the given path
147+
* matches the specified glob pattern.
148+
*
149+
* <p><b>Note:</b> Only the file name (the last element of the path) is tested,
150+
* not the entire path. For example, {@code "*.txt"} will match {@code "file.txt"}.
151+
*
152+
* <p>The glob syntax follows {@link java.nio.file.FileSystem#getPathMatcher(String)} conventions.
153+
*
154+
* @param glob the glob pattern to match against the file name; must not be {@code null}
155+
*
156+
* @return this builder for chaining
157+
*
158+
* @see #hasFullPathMatchingGlob(String)
159+
*/
160+
public PathPredicateBuilder hasNameMatchingGlob(String glob) {
161+
Objects.requireNonNull(glob, "glob is null");
162+
return pathTest(path -> PathPredicates.hasNameMatchingGlob(path, glob));
163+
}
164+
118165
/**
119166
* Adds a condition that tests whether the given path's file name ends with the specified suffix.
120167
*

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

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.io.UncheckedIOException;
66
import java.nio.file.Files;
77
import java.nio.file.Path;
8+
import java.nio.file.PathMatcher;
89
import java.util.Objects;
910
import java.util.function.Predicate;
1011
import java.util.regex.Pattern;
@@ -39,6 +40,53 @@ public static PathPredicateBuilder builder() {
3940
return new PathPredicateBuilder();
4041
}
4142

43+
// ---------- PathMatcher ----------
44+
45+
/**
46+
* Tests whether the given {@link Path} matches the specified glob pattern.
47+
*
48+
* <p>The glob syntax follows {@link java.nio.file.FileSystem#getPathMatcher(String)} conventions.
49+
*
50+
* @param path the path to test; must not be {@code null}
51+
* @param glob the glob pattern; must not be {@code null}
52+
*
53+
* @return {@code true} if the path matches the glob pattern, {@code false} otherwise
54+
*
55+
* @throws NullPointerException if {@code path} or {@code glob} is {@code null}
56+
*
57+
* @see #hasNameMatchingGlob(Path, String)
58+
* @see #hasFullPathMatching(Path, PathMatcher)
59+
*/
60+
public static boolean hasFullPathMatchingGlob(Path path, String glob) {
61+
Objects.requireNonNull(path, "path is null");
62+
Objects.requireNonNull(glob, "glob is null");
63+
64+
// From Files.newDirectoryStream(Path dir, String glob)
65+
if (glob.equals("*")) {
66+
return true;
67+
}
68+
var matcher = path.getFileSystem().getPathMatcher("glob:" + glob);
69+
return matcher.matches(path);
70+
}
71+
72+
/**
73+
* Checks if the given {@link Path} matches the provided {@link PathMatcher}.
74+
*
75+
* @param path the path to test; must not be {@code null}
76+
* @param matcher the {@code PathMatcher} to use; must not be {@code null}
77+
*
78+
* @return {@code true} if the path matches the matcher, {@code false} otherwise
79+
*
80+
* @throws NullPointerException if {@code path} or {@code matcher} is {@code null}
81+
*
82+
* @see #hasFullPathMatchingGlob(Path, String)
83+
*/
84+
public static boolean hasFullPathMatching(Path path, PathMatcher matcher) {
85+
Objects.requireNonNull(path, "path is null");
86+
Objects.requireNonNull(matcher, "matcher is null");
87+
return matcher.matches(path);
88+
}
89+
4290
// ---------- Name ----------
4391

4492
/**
@@ -90,6 +138,26 @@ public static boolean hasNameMatching(Path path, Pattern pattern) {
90138
return pattern.matcher(path.getFileName().toString()).matches();
91139
}
92140

141+
/**
142+
* Tests whether the given path's file name matches the provided glob.
143+
*
144+
* <p>The glob syntax follows {@link java.nio.file.FileSystem#getPathMatcher(String)} conventions.
145+
*
146+
* @param path the path to test
147+
* @param glob the glob pattern to match against the file name; must not be {@code null}
148+
*
149+
* @return {@code true} if the file name matches the glob
150+
*
151+
* @throws NullPointerException if {@code path} or {@code glob} is {@code null}
152+
*
153+
* @see #hasFullPathMatchingGlob(Path, String)
154+
*/
155+
public static boolean hasNameMatchingGlob(Path path, String glob) {
156+
Objects.requireNonNull(path, "path is null");
157+
Objects.requireNonNull(glob, "glob is null");
158+
return hasFullPathMatchingGlob(path.getFileName(), glob);
159+
}
160+
93161
/**
94162
* Tests whether the given path's file name ends with the specified suffix.
95163
*

src/test/java/io/github/computerdaddyguy/jfiletreeprettyprinter/LineExtensionTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,16 @@ void emptyDir() {
3030
void example_dir_match() {
3131

3232
Function<Path, String> lineExtension = path -> {
33-
if (PathPredicates.isDirectory(path) && PathPredicates.hasName(path, "api")) {
33+
if (PathPredicates.hasFullPathMatchingGlob(path, "**/src/main/java/api")) {
3434
return "\t\t\t// All API code: controllers, etc.";
3535
}
36-
if (PathPredicates.isDirectory(path) && PathPredicates.hasName(path, "domain")) {
36+
if (PathPredicates.hasFullPathMatchingGlob(path, "**/src/main/java/domain")) {
3737
return "\t\t\t// All domain code: value objects, etc.";
3838
}
39-
if (PathPredicates.isDirectory(path) && PathPredicates.hasName(path, "infra")) {
39+
if (PathPredicates.hasFullPathMatchingGlob(path, "**/src/main/java/infra")) {
4040
return "\t\t\t// All infra code: database, email service, etc.";
4141
}
42-
if (PathPredicates.isFile(path) && PathPredicates.hasName(path, "application.properties")) {
42+
if (PathPredicates.hasNameMatchingGlob(path, "*.properties")) {
4343
return "\t// Config file";
4444
}
4545
return null;

src/test/java/io/github/computerdaddyguy/jfiletreeprettyprinter/PathPredicateBuilderTest.java

Lines changed: 96 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -46,32 +46,82 @@ void noPredicate_then_nulll() {
4646
assertThat(filter).isNull();
4747
}
4848

49-
@Test
50-
void pathTest_match() {
51-
var path = createTempFile("myFile.java");
52-
var filter = PathPredicates.builder().pathTest(p -> p.equals(path)).build();
53-
assertThat(filter.test(path)).isTrue();
49+
@Nested
50+
class PathTest {
51+
52+
@Test
53+
void match() {
54+
var path = createTempFile("myFile.java");
55+
var filter = PathPredicates.builder().pathTest(p -> p.equals(path)).build();
56+
assertThat(filter.test(path)).isTrue();
57+
}
58+
59+
@Test
60+
void no_match() {
61+
var path = createTempFile("myFile.java");
62+
var filter = PathPredicates.builder().pathTest(p -> p.equals(new Object())).build();
63+
assertThat(filter.test(path)).isFalse();
64+
}
65+
5466
}
5567

56-
@Test
57-
void pathTest_noMatch() {
58-
var path = createTempFile("myFile.java");
59-
var filter = PathPredicates.builder().pathTest(p -> p.equals(new Object())).build();
60-
assertThat(filter.test(path)).isFalse();
68+
@Nested
69+
class FileTest {
70+
71+
@Test
72+
void match() {
73+
var path = createTempFile("myFile.java");
74+
var filter = PathPredicates.builder().fileTest(p -> p.equals(path.toFile())).build();
75+
assertThat(filter.test(path)).isTrue();
76+
}
77+
78+
@Test
79+
void no_match() {
80+
var path = createTempFile("myFile.java");
81+
var filter = PathPredicates.builder().fileTest(p -> p.equals(new Object())).build();
82+
assertThat(filter.test(path)).isFalse();
83+
}
84+
6185
}
6286

63-
@Test
64-
void fileTest_match() {
65-
var path = createTempFile("myFile.java");
66-
var filter = PathPredicates.builder().fileTest(p -> p.equals(path.toFile())).build();
67-
assertThat(filter.test(path)).isTrue();
87+
// ---------- PathMatcher ----------
88+
89+
@Nested
90+
class HasFullPathMatchingGlob {
91+
92+
@Test
93+
void match_wildcard() {
94+
var path = createTempFile("myFile.java");
95+
var filter = PathPredicates.builder().hasFullPathMatchingGlob("*").build();
96+
assertThat(filter.test(path)).isTrue();
97+
}
98+
99+
@Test
100+
void match() {
101+
var path = createTempFile("myFile.java");
102+
var filter = PathPredicates.builder().hasFullPathMatchingGlob("**/*.java").build();
103+
assertThat(filter.test(path)).isTrue();
104+
}
105+
106+
@Test
107+
void no_match_because_full_path() {
108+
var path = createTempFile("myFile.java");
109+
var filter = PathPredicates.builder().hasFullPathMatchingGlob("*.java").build();
110+
assertThat(filter.test(path)).isFalse();
111+
}
112+
113+
@Test
114+
void no_match() {
115+
var path = createTempFile("myFile.java");
116+
var filter = PathPredicates.builder().hasFullPathMatchingGlob("**/*.php").build();
117+
assertThat(filter.test(path)).isFalse();
118+
}
119+
68120
}
69121

70-
@Test
71-
void fileTest_noMatch() {
72-
var path = createTempFile("myFile.java");
73-
var filter = PathPredicates.builder().fileTest(p -> p.equals(new Object())).build();
74-
assertThat(filter.test(path)).isFalse();
122+
@Nested
123+
class MatchesPathMatcher {
124+
75125
}
76126

77127
// ---------- Name ----------
@@ -147,6 +197,32 @@ void no_match() {
147197

148198
}
149199

200+
@Nested
201+
class HasNameMatchingGlob {
202+
203+
@Test
204+
void match() {
205+
var path = createTempFile("myFile.java");
206+
var filter = PathPredicates.builder().hasNameMatchingGlob("my*").build();
207+
assertThat(filter.test(path)).isTrue();
208+
}
209+
210+
@Test
211+
void no_match() {
212+
var path = createTempFile("myFile.java");
213+
var filter = PathPredicates.builder().hasNameMatchingGlob("ma*").build();
214+
assertThat(filter.test(path)).isFalse();
215+
}
216+
217+
@Test
218+
void no_match_dir() {
219+
var path = createTempFile("myFile.java");
220+
var filter = PathPredicates.builder().hasNameMatchingGlob("*/my*").build();
221+
assertThat(filter.test(path)).isFalse();
222+
}
223+
224+
}
225+
150226
@Nested
151227
class HasNameEndingWith {
152228

0 commit comments

Comments
 (0)