Skip to content

Commit 5053309

Browse files
New PathSortBuilder
1 parent b8e6c00 commit 5053309

File tree

11 files changed

+441
-169
lines changed

11 files changed

+441
-169
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,12 @@ filtering/
132132
Files and directories can be sorted using a custom comparator (default is alphabetical order).
133133
If the provided comparator considers two paths equal (i.e., returns `0`), an alphabetical comparator is applied as a tie-breaker to ensure consistent results across all systems.
134134

135-
The `PrettyPrintOptions.Sorts` class provides a set of basic, ready-to-use comparators.
135+
The `PathSorts` class provides a set of basic, ready-to-use comparators, as well as a builder for creating your own tailor-made sorts.
136136

137137
```java
138138
// Example: Sorting.java
139139
var prettyPrinter = FileTreePrettyPrinter.builder()
140-
.customizeOptions(options -> options.sort(PrettyPrintOptions.Sorts.DIRECTORY_FIRST))
140+
.customizeOptions(options -> options.sort(PathSorts.DIRECTORY_FIRST))
141141
.build();
142142
```
143143
```

ROADMAP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
## To do
2929
- [x] More `PathMatchers` functions!
3030
- [x] Helper class for line extension
31-
- [ ] Helper class for sorting
31+
- [x] Helper class for sorting
3232
- [ ] Option: custom emojis
3333
- [ ] Option: hide number of skipped files and folders for child limit
3434
- [ ] Rework/fix Github wiki to be up to date

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

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import io.github.computerdaddyguy.jfiletreeprettyprinter.FileTreePrettyPrinter;
55
import io.github.computerdaddyguy.jfiletreeprettyprinter.LineExtensionBuilder;
66
import io.github.computerdaddyguy.jfiletreeprettyprinter.PathMatchers;
7-
import io.github.computerdaddyguy.jfiletreeprettyprinter.PrettyPrintOptions.Sorts;
7+
import io.github.computerdaddyguy.jfiletreeprettyprinter.PathSorts;
88
import java.nio.file.Path;
99
import java.util.Comparator;
1010
import java.util.function.Function;
@@ -48,15 +48,7 @@ public static void main(String[] args) {
4848
var fileFilter = PathMatchers.allOf(
4949

5050
// Hide files with names starting with "."
51-
PathMatchers.not(PathMatchers.hasNameStartingWith(".")),
52-
53-
// Inside "jfiletreeprettyprinter" folder, keep only "FileTreePrettyPrinter.java"
54-
// Files in other folders are not restricted by this rule.
55-
PathMatchers.ifMatchesThenElse(
56-
/* if */ PathMatchers.hasDirectParentMatching(PathMatchers.hasName("jfiletreeprettyprinter")),
57-
/* then */ PathMatchers.hasName("FileTreePrettyPrinter.java"),
58-
/* else */ path -> true
59-
)
51+
PathMatchers.not(PathMatchers.hasNameStartingWith("."))
6052
);
6153

6254
/*
@@ -66,6 +58,7 @@ public static void main(String[] args) {
6658
// Hide all files under renderer and scanner packages
6759
.add(PathMatchers.hasAbsolutePathMatchingGlob("**/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer"), 0)
6860
.add(PathMatchers.hasAbsolutePathMatchingGlob("**/io/github/computerdaddyguy/jfiletreeprettyprinter/scanner"), 0)
61+
.add(PathMatchers.hasAbsolutePathMatchingGlob("**/io/github/computerdaddyguy/jfiletreeprettyprinter"), 3)
6962
.build();
7063

7164
/*
@@ -79,9 +72,14 @@ public static void main(String[] args) {
7972
.build();
8073

8174
/*
82-
* Sort all paths by directory first (then alphabetically by default)
75+
* Sort all paths by directory first (with highest precedence),
76+
* then "FileTreePrettyPrinter.java" has precedence "-100".
77+
* All other files have default precedence "0", and are then sorted alphabetically by default.
8378
*/
84-
Comparator<Path> pathComparator = Sorts.DIRECTORY_FIRST;
79+
Comparator<Path> pathComparator = PathSorts.builder()
80+
.addFirst(PathMatchers.isDirectory())
81+
.add(PathMatchers.hasName("FileTreePrettyPrinter.java"), -100) // Default precedence is "0"
82+
.build();
8583

8684
/*
8785
* Build the final FileTreePrettyPrinter
@@ -110,25 +108,27 @@ public static void main(String[] args) {
110108
Expected result
111109
================================
112110
113-
📂 JFileTreePrettyPrinter/
114-
├─ 📂 assets/
115-
│ └─ 🖼️ project-structure.png // This image
116-
├─ 📂 src/main/java/
117-
│ └─ 📂 io/github/computerdaddyguy/jfiletreeprettyprinter/
118-
│ ├─ 📂 renderer/
119-
│ │ └─ ... (5 files and 2 directories skipped)
120-
│ ├─ 📂 scanner/
121-
│ │ └─ ... (4 files skipped)
122-
│ └─ ☕ FileTreePrettyPrinter.java // Main entry point
123-
├─ 🗺️ CHANGELOG.md
124-
├─ 📖 CONTRIBUTING.md
125-
├─ 📄 LICENSE
126-
├─ 📖 README.md // You're reading at this!
127-
├─ 🗺️ ROADMAP.md
128-
├─ 🛡️ SECURITY.md
129-
├─ 🏗️ pom.xml
130-
├─ 📖 release_process.md
131-
└─ 📜 runMutationTests.sh
111+
📂 JFileTreePrettyPrinter/
112+
├─ 📂 assets/
113+
│ └─ 🖼️ project-structure.png // This image
114+
├─ 📂 src/main/java/
115+
│ └─ 📂 io/github/computerdaddyguy/jfiletreeprettyprinter/
116+
│ ├─ 📂 renderer/
117+
│ │ └─ ... (5 files and 2 directories skipped)
118+
│ ├─ 📂 scanner/
119+
│ │ └─ ... (4 files skipped)
120+
│ ├─ ☕ FileTreePrettyPrinter.java // Main entry point
121+
│ └─ ... (8 files skipped)
122+
├─ 🗺️ CHANGELOG.md
123+
├─ 📖 CONTRIBUTING.md
124+
├─ 📄 LICENSE
125+
├─ 📖 README.md // You're reading at this!
126+
├─ 🗺️ ROADMAP.md
127+
├─ 🛡️ SECURITY.md
128+
├─ 🏗️ pom.xml
129+
├─ 📖 release_process.md
130+
└─ 📜 runMutationTests.sh
131+
132132
*/
133133
}
134134

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
package io.github.computerdaddyguy.jfiletreeprettyprinter.example;
22

33
import io.github.computerdaddyguy.jfiletreeprettyprinter.FileTreePrettyPrinter;
4-
import io.github.computerdaddyguy.jfiletreeprettyprinter.PrettyPrintOptions;
4+
import io.github.computerdaddyguy.jfiletreeprettyprinter.PathSorts;
55

66
public class Sorting {
77

88
public static void main(String[] args) {
99
var prettyPrinter = FileTreePrettyPrinter.builder()
10-
.customizeOptions(options -> options.sort(PrettyPrintOptions.Sorts.DIRECTORY_FIRST))
10+
.customizeOptions(options -> options.sort(PathSorts.DIRECTORY_FIRST))
1111
.build();
1212
var tree = prettyPrinter.prettyPrint("src/example/resources/sorting");
1313
System.out.println(tree);

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@
3131
@NullMarked
3232
public class FileTreePrettyPrinterBuilder {
3333

34-
private PrettyPrintOptions options = PrettyPrintOptions.createDefault();
34+
private PrettyPrintOptions options;
35+
36+
/* package */ FileTreePrettyPrinterBuilder() {
37+
options = PrettyPrintOptions.createDefault();
38+
}
3539

3640
/**
3741
* Builds the pretty printer using the configured options.
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package io.github.computerdaddyguy.jfiletreeprettyprinter;
2+
3+
import java.nio.file.Path;
4+
import java.nio.file.PathMatcher;
5+
import java.util.ArrayList;
6+
import java.util.Comparator;
7+
import java.util.List;
8+
import java.util.Objects;
9+
import java.util.function.Function;
10+
import java.util.function.ToIntFunction;
11+
import org.jspecify.annotations.NullMarked;
12+
13+
/**
14+
* A builder for creating a {@link Comparator Comparator&lt;Path&gt;} that defines
15+
* a custom sorting order for file system paths based on rule precedence.
16+
* <p>
17+
* Each rule assigns an integer "precedence" value to paths. The first rule
18+
* that matches a path determines its precedence. Paths are then sorted by
19+
* ascending precedence value (lower values come first), followed by an
20+
* alphabetical fallback comparison.
21+
* </p>
22+
*
23+
* <p>
24+
* Predefined precedence constants:
25+
* </p>
26+
* <ul>
27+
* <li>{@link #HIGHEST_PRECEDENCE} ({@code Integer.MIN_VALUE}) — top priority</li>
28+
* <li>{@link #DEFAULT_PRECEDENCE} ({@code 0}) — default order</li>
29+
* <li>{@link #LOWEST_PRECEDENCE} ({@code Integer.MAX_VALUE}) — last priority</li>
30+
* </ul>
31+
*
32+
* <h2>Example usage:</h2>
33+
* <pre>{@code
34+
* var customSort = PathSortBuilder.newInstance()
35+
* .addFirst(PathMatchers.hasName("README.md")) // always first
36+
* .addLast(PathMatchers.hasName("target")) // always last
37+
* .add(path -> path.toString().contains("core") ? -10 : 0) // custom priority rule
38+
* .build();
39+
*
40+
* var printer = FileTreePrettyPrinter.builder()
41+
* .customizeOptions(options -> options.sort(customSort))
42+
* .build();
43+
* }</pre>
44+
*/
45+
@NullMarked
46+
public class PathSortBuilder {
47+
48+
/** Highest possible precedence — items appear first. */
49+
public static final int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
50+
51+
/** Default precedence (neutral value). */
52+
public static final int DEFAULT_PRECEDENCE = 0;
53+
54+
/** Lowest possible precedence — items appear last. */
55+
public static final int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
56+
57+
private List<ToIntFunction<Path>> orders;
58+
59+
/* package */ PathSortBuilder() {
60+
this.orders = new ArrayList<>();
61+
}
62+
63+
/**
64+
* Builds the final {@link Comparator Comparator&lt;Path&gt;} based on the configured rules.
65+
* <p>
66+
* Rules are tested in the order they were added. The first matching rule
67+
* having a result different than {@link DEFAULT_PRECEDENCE} (meaning, {@code 0})
68+
* determines the precedence value for a given path. Paths are sorted by
69+
* this precedence, and then alphabetically as a tiebreaker.
70+
* </p>
71+
*
72+
* @return a comparator defining the final path order
73+
*/
74+
public Comparator<Path> build() {
75+
var immutOrders = List.copyOf(orders);
76+
Function<Path, Integer> finalFunction = path -> {
77+
int result = DEFAULT_PRECEDENCE;
78+
for (var rule : immutOrders) {
79+
result = rule.applyAsInt(path);
80+
if (result != DEFAULT_PRECEDENCE) {
81+
break;
82+
}
83+
}
84+
return result;
85+
};
86+
return Comparator.comparing(finalFunction).thenComparing(PathSorts.ALPHABETICAL);
87+
}
88+
89+
/**
90+
* Adds a custom rule function defining a precedence for a path.
91+
*
92+
* @param order a function returning a precedence value
93+
* @return this builder for chaining
94+
*
95+
* @throws NullPointerException if {@code order} is null
96+
*/
97+
public PathSortBuilder add(ToIntFunction<Path> order) {
98+
Objects.requireNonNull(order, "order is null");
99+
this.orders.add(order);
100+
return this;
101+
}
102+
103+
/**
104+
* Adds a rule that assigns a precedence value to all paths matching
105+
* the specified {@link PathMatcher}.
106+
*
107+
* @param pathMatcher the matcher to test paths
108+
* @param order the precedence value to assign
109+
* @return this builder for chaining
110+
*
111+
* @throws NullPointerException if {@code pathMatcher} is null
112+
*/
113+
public PathSortBuilder add(PathMatcher pathMatcher, int order) {
114+
Objects.requireNonNull(pathMatcher, "pathMatcher is null");
115+
return add(path -> pathMatcher.matches(path) ? order : DEFAULT_PRECEDENCE);
116+
}
117+
118+
/**
119+
* Adds a rule that forces matching paths to appear first in the sort order.
120+
*
121+
* @param pathMatcher the matcher identifying high-priority paths
122+
* @return this builder for chaining
123+
*/
124+
public PathSortBuilder addFirst(PathMatcher pathMatcher) {
125+
return add(pathMatcher, HIGHEST_PRECEDENCE);
126+
}
127+
128+
/**
129+
* Adds a rule that forces matching paths to appear last in the sort order.
130+
*
131+
* @param pathMatcher the matcher identifying low-priority paths
132+
* @return this builder for chaining
133+
*/
134+
public PathSortBuilder addLast(PathMatcher pathMatcher) {
135+
return add(pathMatcher, LOWEST_PRECEDENCE);
136+
}
137+
138+
}

0 commit comments

Comments
 (0)