Skip to content

Commit 279bdb6

Browse files
Reworked ChildLimitBuilder
1 parent ddc26bb commit 279bdb6

File tree

6 files changed

+88
-62
lines changed

6 files changed

+88
-62
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1717
- Filtering: split into distinct directories and files filters for better control
1818
- `PathUtils` and `PathPredicates` removed, use `PathMatchers` instead
1919
- Line extension: empty string is now permitted
20+
- Renamed some `ChildLimitBuilder` methods
2021

2122
### Fixed
2223
- The folder name is properly displayed at root when calling `prettyPrint(".")` (instead of "./")

README.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -219,14 +219,13 @@ Use the `ChildLimitBuilder` and `PathMatchers` classes to help you build the lim
219219

220220
```java
221221
// Example: ChildLimitDynamic.java
222-
var isNodeModuleMatcher = PathMatchers.hasName("node_modules");
223-
var childLimit = ChildLimitBuilder.builder()
224-
.defaultLimit(ChildLimitBuilder.UNLIMITED)
225-
.limit(isNodeModuleMatcher, 0)
222+
var childLimit = ChildLimitBuilder.newInstance()
223+
.setDefault(ChildLimitBuilder.UNLIMITED) // Unlimited children by default
224+
.add(PathMatchers.hasName("node_modules"), 0) // Do NOT print any children in "node_modules" folder
226225
.build();
227226
var prettyPrinter = FileTreePrettyPrinter.builder()
228-
.customizeOptions(options -> options.withChildLimit(childLimit))
229-
.build();
227+
.customizeOptions(options -> options.withChildLimit(childLimit))
228+
.build();
230229
```
231230
```
232231
child_limit_dynamic/

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77
public class ChildLimitDynamic {
88

99
public static void main(String[] args) {
10-
var isNodeModuleMatcher = PathMatchers.hasName("node_modules");
11-
var childLimit = ChildLimitBuilder.builder()
12-
.defaultLimit(ChildLimitBuilder.UNLIMITED)
13-
.limit(isNodeModuleMatcher, 0)
10+
var childLimit = ChildLimitBuilder.newInstance()
11+
.setDefault(ChildLimitBuilder.UNLIMITED) // Unlimited children by default
12+
.add(PathMatchers.hasName("node_modules"), 0) // Do NOT print any children in "node_modules" folder
1413
.build();
1514
var prettyPrinter = FileTreePrettyPrinter.builder()
1615
.customizeOptions(options -> options.withChildLimit(childLimit))

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

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

33
import io.github.computerdaddyguy.jfiletreeprettyprinter.ChildLimitBuilder;
44
import io.github.computerdaddyguy.jfiletreeprettyprinter.FileTreePrettyPrinter;
5+
import io.github.computerdaddyguy.jfiletreeprettyprinter.LineExtensionBuilder;
56
import io.github.computerdaddyguy.jfiletreeprettyprinter.PathMatchers;
67
import io.github.computerdaddyguy.jfiletreeprettyprinter.PrettyPrintOptions.Sorts;
78
import java.nio.file.Path;
@@ -61,10 +62,10 @@ public static void main(String[] args) {
6162
/*
6263
* Limit the number of displayed children by directory: some content is not relevant and clutters the final result!
6364
*/
64-
var childLimitFunction = ChildLimitBuilder.builder()
65+
var childLimitFunction = ChildLimitBuilder.newInstance()
6566
// Hide all files under renderer and scanner packages
66-
.limit(PathMatchers.hasAbsolutePathMatchingGlob("**/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer"), 0)
67-
.limit(PathMatchers.hasAbsolutePathMatchingGlob("**/io/github/computerdaddyguy/jfiletreeprettyprinter/scanner"), 0)
67+
.add(PathMatchers.hasAbsolutePathMatchingGlob("**/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer"), 0)
68+
.add(PathMatchers.hasAbsolutePathMatchingGlob("**/io/github/computerdaddyguy/jfiletreeprettyprinter/scanner"), 0)
6869
.build();
6970

7071
/*

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

Lines changed: 71 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -12,103 +12,129 @@
1212
* A builder for creating a {@code ToIntFunction<Path>} that defines
1313
* how many child entries (files or directories) are allowed under a given path.
1414
* <p>
15-
* The resulting function evaluates added rules in the order they were defined.
16-
* The first matching rule determines the child limit for a given path.
15+
* Rules are evaluated in the order they are added (first-match-wins).
16+
* The first rule that matches a path determines the limit.
1717
* If no rules match, the default limit is applied.
1818
* </p>
1919
*
2020
* <p>
21-
* A limit of {@link #UNLIMITED} ({@code -1}) means no restriction.
22-
* A limit of {@code 0} means the directory cannot have any children.
21+
* Special values:
22+
* <ul>
23+
* <li>{@link #UNLIMITED} ({@code -1}) — no restriction on children.</li>
24+
* <li>{@code 0} — directory cannot contain any children.</li>
25+
* <li>Any positive number — maximum number of children allowed.</li>
26+
* </ul>
2327
* </p>
2428
*
2529
* <h2>Example usage:</h2>
2630
* <pre>{@code
27-
* var childLimit = ChildLimitBuilder.builder()
28-
* .defaultLimit(ChildLimit.UNLIMITED) // unlimited unless specified
29-
* .limit(PathMatchers.hasName("bigDir"), 10) // max 10 children in "bigDir"
30-
* .limit(PathMatchers.hasName("emptyDir"), 0) // disallow children in "emptyDir"
31+
* var childLimit = ChildLimitBuilder.newInstance()
32+
* .setDefault(ChildLimitBuilder.UNLIMITED) // unlimited unless specified
33+
* .add(PathMatchers.hasName("bigDir"), 10) // max 10 children in "bigDir"
34+
* .add(PathMatchers.hasName("emptyDir"), 0) // disallow children in "emptyDir"
3135
* .build();
32-
*
3336
* }</pre>
3437
*/
3538
@NullMarked
3639
public class ChildLimitBuilder {
3740

38-
/**
39-
* Unlimited children.
41+
/**
42+
* Special value indicating unlimited children ({@code -1}).
4043
*/
4144
public static final int UNLIMITED = -1;
4245

43-
private static final ChildControl UNLIMITED_CONTROL = new ChildControl(p -> true, UNLIMITED);
46+
private List<ToIntFunction<Path>> limits;
47+
private int defaultLimit;
4448

45-
private List<ChildControl> controls;
46-
private ChildControl defaultControl;
49+
private ChildLimitBuilder(int defaultLimit) {
50+
this.limits = new ArrayList<>();
51+
this.defaultLimit = defaultLimit;
52+
}
4753

4854
/**
49-
* Creates a new builder.
55+
* Returns a new {@link ChildLimitBuilder}.
56+
*
57+
* @return a fresh builder instance
5058
*/
51-
private ChildLimitBuilder() {
52-
this.controls = new ArrayList<>();
53-
this.defaultControl = UNLIMITED_CONTROL;
59+
public static ChildLimitBuilder newInstance() {
60+
return newInstance(UNLIMITED);
5461
}
5562

56-
private record ChildControl(PathMatcher pathMatcher, int limit) {
57-
58-
}
59-
60-
public static ChildLimitBuilder builder() {
61-
return new ChildLimitBuilder();
63+
public static ChildLimitBuilder newInstance(int defaultLimit) {
64+
return new ChildLimitBuilder(defaultLimit);
6265
}
6366

6467
/**
65-
* Builds the child limit function based on the configured rules.
68+
* Builds the final child limit function based on the configured rules.
6669
* <p>
67-
* Rules are tested in the order they were added. The first matching rule
68-
* provides the limit. If no rule matches, the default limit is used.
70+
* Rules are evaluated in insertion order. The first rule that matches a path
71+
* determines its child limit. If no rules match, the default limit is returned.
6972
* </p>
7073
*
7174
* @return a function mapping a {@link Path} to its maximum number of children
7275
*/
7376
public ToIntFunction<Path> build() {
74-
var immutControls = List.copyOf(controls);
75-
var immutDefaultControl = this.defaultControl;
76-
return p -> immutControls.stream()
77-
.filter(control -> control.pathMatcher().matches(p))
78-
.findFirst()
79-
.orElse(immutDefaultControl)
80-
.limit();
77+
var immutLimits = List.copyOf(limits);
78+
return path -> {
79+
int result = defaultLimit;
80+
for (var rule : immutLimits) {
81+
result = rule.applyAsInt(path);
82+
if (result >= 0) {
83+
break;
84+
}
85+
}
86+
return result;
87+
};
8188
}
8289

8390
/**
8491
* Sets the default child limit to apply when no specific rule matches.
8592
*
8693
* @param limit the default limit (use {@link #UNLIMITED} for no restriction)
87-
*
8894
* @return this builder for chaining
8995
*/
90-
public ChildLimitBuilder defaultLimit(int limit) {
91-
this.defaultControl = new ChildControl(p -> true, limit);
96+
public ChildLimitBuilder setDefault(int limit) {
97+
this.defaultLimit = limit;
98+
return this;
99+
}
100+
101+
/**
102+
* Adds a custom rule expressed as a {@link ToIntFunction}.
103+
* <p>
104+
* This function should return:
105+
* <ul>
106+
* <li>{@link #UNLIMITED} ({@code -1}) if it does not apply</li>
107+
* <li>Any non-negative integer as the effective child limit if it applies</li>
108+
* </ul>
109+
* </p>
110+
*
111+
* @param childLimit the function mapping a path to a child limit
112+
* @return this builder for chaining
113+
* @throws NullPointerException if {@code childLimit} is null
114+
*/
115+
public ChildLimitBuilder add(ToIntFunction<Path> childLimit) {
116+
Objects.requireNonNull(childLimit, "childLimit is null");
117+
this.limits.add(childLimit);
92118
return this;
93119
}
94120

95121
/**
96122
* Adds a child limit rule for paths matching the given matcher.
97123
* <p>
98-
* Rules are evaluated in the order they are added. The first matching rule wins.
124+
* If the path matches, this rule applies the given limit.
125+
* Otherwise, it yields {@link #UNLIMITED} so that subsequent rules may apply.
126+
* Because rules are evaluated in insertion order, the first matching rule wins.
99127
* </p>
100128
*
101-
* @param pathMatcher the condition for paths
102-
* @param limit the maximum number of children (use {@link #UNLIMITED} for no restriction)
103-
*
129+
* @param pathMatcher the matcher used to test paths (non-null)
130+
* @param limit the maximum number of children for matching paths
131+
* (use {@link #UNLIMITED} for no restriction)
104132
* @return this builder for chaining
105-
*
106133
* @throws NullPointerException if {@code pathMatcher} is null
107134
*/
108-
public ChildLimitBuilder limit(PathMatcher pathMatcher, int limit) {
135+
public ChildLimitBuilder add(PathMatcher pathMatcher, int limit) {
109136
Objects.requireNonNull(pathMatcher, "pathMatcher is null");
110-
this.controls.add(new ChildControl(pathMatcher, limit));
111-
return this;
137+
return add(path -> pathMatcher.matches(path) ? limit : UNLIMITED);
112138
}
113139

114140
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ class ChildLimitDynamicTest {
1818

1919
// @formatter:off
2020
options -> options.withChildLimit(
21-
ChildLimitBuilder.builder()
22-
.limit(PathMatchers.hasName("limit_1"), 1)
23-
.limit(PathMatchers.hasName("limit_3"), 3)
24-
.defaultLimit(ChildLimitBuilder.UNLIMITED)
21+
ChildLimitBuilder.newInstance()
22+
.add(PathMatchers.hasName("limit_1"), 1)
23+
.add(PathMatchers.hasName("limit_3"), 3)
24+
.setDefault(ChildLimitBuilder.UNLIMITED)
2525
.build()
2626
)
2727
// @formatter:on

0 commit comments

Comments
 (0)