Skip to content

Commit 23b4ca2

Browse files
implement case-insensitive StringMultiTrie querying
1 parent ca8a434 commit 23b4ca2

File tree

4 files changed

+144
-49
lines changed

4 files changed

+144
-49
lines changed

enigma-swing/src/main/java/org/quiltmc/enigma/gui/element/menu_bar/SearchMenusMenu.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
import org.quiltmc.enigma.gui.element.SearchableElement;
77
import org.quiltmc.enigma.util.I18n;
88
import org.quiltmc.enigma.util.multi_trie.CompositeStringMultiTrie;
9-
import org.quiltmc.enigma.util.multi_trie.MultiTrie;
109
import org.quiltmc.enigma.util.multi_trie.StringMultiTrie;
10+
import org.quiltmc.enigma.util.multi_trie.StringMultiTrie.CharacterNode;
1111

1212
import javax.annotation.Nullable;
1313
import javax.swing.JMenuItem;
@@ -168,7 +168,7 @@ JMenuItem getItem() {
168168

169169
private class ResultManager {
170170
@Nullable
171-
StringMultiTrie.View<Result> resultTrie;
171+
StringMultiTrie.View<Result, ?, ?> resultTrie;
172172
@Nullable
173173
CurrentResults currentResults;
174174

@@ -182,9 +182,9 @@ UpdateOutcome updateResultItems(String searchTerm) {
182182
if (this.currentResults.searchTerm.length() == searchTerm.length()) {
183183
return UpdateOutcome.SAME_RESULTS;
184184
} else {
185-
MultiTrie.Node<Character, Result> resultNode = this.currentResults.results;
185+
CharacterNode<Result> resultNode = this.currentResults.results;
186186
for (int i = this.currentResults.searchTerm.length(); i < searchTerm.length(); i++) {
187-
resultNode = resultNode.next(searchTerm.charAt(i));
187+
resultNode = resultNode.nextIgnoreCase(searchTerm.charAt(i));
188188
}
189189

190190
if (resultNode.isEmpty()) {
@@ -215,7 +215,7 @@ UpdateOutcome updateResultItems(String searchTerm) {
215215
}
216216

217217
UpdateOutcome initializeCurrentResults(String searchTerm) {
218-
final MultiTrie.Node<Character, Result> results = this.getResultTrie().get(searchTerm);
218+
final CharacterNode<Result> results = this.getResultTrie().getIgnoreCase(searchTerm);
219219
if (results.isEmpty()) {
220220
this.clearCurrent();
221221

@@ -228,7 +228,7 @@ UpdateOutcome initializeCurrentResults(String searchTerm) {
228228
}
229229
}
230230

231-
StringMultiTrie.View<Result> getResultTrie() {
231+
StringMultiTrie.View<Result, ?, ?> getResultTrie() {
232232
if (this.resultTrie == null) {
233233
this.resultTrie = this.buildResultTrie();
234234
}
@@ -251,7 +251,7 @@ void clearCurrent() {
251251
}
252252
}
253253

254-
StringMultiTrie.View<Result> buildResultTrie() {
254+
StringMultiTrie.View<Result, ?, ?> buildResultTrie() {
255255
final CompositeStringMultiTrie<Result> elementsBuilder = CompositeStringMultiTrie.createHashed();
256256
SearchMenusMenu.this.gui.getMenuBar()
257257
.streamMenus()
@@ -269,7 +269,7 @@ StringMultiTrie.View<Result> buildResultTrie() {
269269
return elementsBuilder.getView();
270270
}
271271

272-
record CurrentResults(MultiTrie.Node<Character, Result> results, String searchTerm) { }
272+
record CurrentResults(CharacterNode<Result> results, String searchTerm) { }
273273

274274
enum UpdateOutcome {
275275
NO_RESULTS, SAME_RESULTS, DIFFERENT_RESULTS

enigma/src/main/java/org/quiltmc/enigma/util/multi_trie/CompositeStringMultiTrie.java

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package org.quiltmc.enigma.util.multi_trie;
22

3+
import org.quiltmc.enigma.util.multi_trie.CompositeStringMultiTrie.Branch;
4+
import org.quiltmc.enigma.util.multi_trie.CompositeStringMultiTrie.Root;
5+
6+
import javax.annotation.Nonnull;
37
import java.util.Collection;
48
import java.util.HashMap;
59
import java.util.HashSet;
@@ -14,7 +18,7 @@
1418
* @see #of(Supplier, Supplier)
1519
* @see #createHashed()
1620
*/
17-
public final class CompositeStringMultiTrie<V> extends StringMultiTrie<V, CompositeStringMultiTrie.Branch<V>> {
21+
public final class CompositeStringMultiTrie<V> extends StringMultiTrie<V, Branch<V>, Root<V>> {
1822
private final Root<V> root;
1923
private final View view = new View();
2024

@@ -59,22 +63,24 @@ private CompositeStringMultiTrie(
5963
}
6064

6165
@Override
62-
public MutableMapNode<Character, V, Branch<V>> getRoot() {
66+
public Root<V> getRoot() {
6367
return this.root;
6468
}
6569

6670
@Override
67-
public StringMultiTrie.View<V> getView() {
71+
public StringMultiTrie.View<V, Branch<V>, Root<V>> getView() {
6872
return this.view;
6973
}
7074

71-
private static final class Root<V> extends MutableMapNode<Character, V, Branch<V>> {
75+
public static final class Root<V>
76+
extends MutableMapNode<Character, V, Branch<V>>
77+
implements MutableCharacterNode<V, Branch<V>> {
7278
private final Collection<V> leaves;
7379
private final Map<Character, CompositeStringMultiTrie.Branch<V>> branches;
7480

7581
private final CompositeStringMultiTrie.Branch.Factory<V> branchFactory;
7682

77-
private final View<Character, V> view = new View<>(this);
83+
private final NodeView<V> view = new NodeView<>(this);
7884

7985
private Root(
8086
Map<Character, CompositeStringMultiTrie.Branch<V>> branches, Collection<V> leaves,
@@ -101,12 +107,12 @@ protected Map<Character, CompositeStringMultiTrie.Branch<V>> getBranches() {
101107
}
102108

103109
@Override
104-
public MultiTrie.Node<Character, V> getView() {
110+
public CharacterNode<V> getView() {
105111
return this.view;
106112
}
107113
}
108114

109-
public static final class Branch<V> extends MutableMapNode.Branch<Character, V, Branch<V>> {
115+
public static final class Branch<V> extends StringMultiTrie.BranchNode<V, Branch<V>> {
110116
private final MutableMapNode<Character, V, CompositeStringMultiTrie.Branch<V>> parent;
111117
private final Character key;
112118

@@ -115,7 +121,7 @@ public static final class Branch<V> extends MutableMapNode.Branch<Character, V,
115121

116122
private final CompositeStringMultiTrie.Branch.Factory<V> branchFactory;
117123

118-
private final MultiTrie.Node<Character, V> view = new View<>(this);
124+
private final NodeView<V> view = new NodeView<>(this);
119125

120126
private Branch(
121127
MutableMapNode<Character, V, CompositeStringMultiTrie.Branch<V>> parent, char key,
@@ -161,7 +167,7 @@ protected Map<Character, CompositeStringMultiTrie.Branch<V>> getBranches() {
161167
}
162168

163169
@Override
164-
public MultiTrie.Node<Character, V> getView() {
170+
public CharacterNode<V> getView() {
165171
return this.view;
166172
}
167173

@@ -180,10 +186,29 @@ CompositeStringMultiTrie.Branch<V> create(
180186
}
181187
}
182188

183-
private class View extends StringMultiTrie.View<V> {
189+
private class View extends StringMultiTrie.View<V, Branch<V>, Root<V>> {
184190
@Override
185-
protected StringMultiTrie<V, ?> getViewed() {
191+
protected CompositeStringMultiTrie<V> getViewed() {
186192
return CompositeStringMultiTrie.this;
187193
}
188194
}
195+
196+
private static final class NodeView<V> extends Node.View<Character, V> implements CharacterNode<V> {
197+
private final MutableCharacterNode<V, Branch<V>> viewed;
198+
199+
private NodeView(MutableCharacterNode<V, Branch<V>> viewed) {
200+
this.viewed = viewed;
201+
}
202+
203+
@Nonnull
204+
@Override
205+
public CharacterNode<V> next(Character key) {
206+
return this.viewed.next(key).getView();
207+
}
208+
209+
@Override
210+
protected Node<Character, V> getViewed() {
211+
return this.viewed;
212+
}
213+
}
189214
}

enigma/src/main/java/org/quiltmc/enigma/util/multi_trie/MutableMultiTrie.java

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.quiltmc.enigma.util.multi_trie;
22

3-
import javax.annotation.Nonnull;
43
import java.util.stream.Stream;
54

65
/**
@@ -52,33 +51,23 @@ interface Node<K, V> extends MultiTrie.Node<K, V> {
5251
*/
5352
MultiTrie.Node<K, V> getView();
5453

55-
class View<K, V> implements MultiTrie.Node<K, V> {
56-
protected final Node<K, V> viewed;
57-
58-
protected View(Node<K, V> viewed) {
59-
this.viewed = viewed;
60-
}
61-
54+
abstract class View<K, V> implements MultiTrie.Node<K, V> {
6255
@Override
6356
public Stream<V> streamLeaves() {
64-
return this.viewed.streamLeaves();
57+
return this.getViewed().streamLeaves();
6558
}
6659

6760
@Override
6861
public Stream<V> streamStems() {
69-
return this.viewed.streamStems();
62+
return this.getViewed().streamStems();
7063
}
7164

7265
@Override
7366
public Stream<V> streamValues() {
74-
return this.viewed.streamValues();
67+
return this.getViewed().streamValues();
7568
}
7669

77-
@Nonnull
78-
@Override
79-
public MultiTrie.Node<K, V> next(K key) {
80-
return this.viewed.next(key).getView();
81-
}
70+
protected abstract Node<K, V> getViewed();
8271
}
8372
}
8473
}

enigma/src/main/java/org/quiltmc/enigma/util/multi_trie/StringMultiTrie.java

Lines changed: 95 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
package org.quiltmc.enigma.util.multi_trie;
22

33
import org.quiltmc.enigma.util.Utils;
4+
import org.quiltmc.enigma.util.multi_trie.StringMultiTrie.BranchNode;
5+
import org.quiltmc.enigma.util.multi_trie.StringMultiTrie.MutableCharacterNode;
6+
7+
import java.util.Optional;
8+
import java.util.function.BiFunction;
49

510
/**
611
* A {@link MutableMultiTrie} that associates sequences of characters with values of type {@code V}.
@@ -19,36 +24,69 @@
1924
* @param <V> the type of values
2025
* @param <B> the type of branch nodes
2126
*/
22-
public abstract class StringMultiTrie<V, B extends MutableMapNode.Branch<Character, V, B>>
27+
public abstract class StringMultiTrie
28+
<
29+
V, B extends BranchNode<V, B> & MutableCharacterNode<V, B>,
30+
R extends MutableMapNode<Character, V, B> & MutableCharacterNode<V, B>
31+
>
2332
implements MutableMultiTrie<Character, V> {
33+
private static Optional<Character> tryToggleCase(char c) {
34+
if (Character.isUpperCase(c)) {
35+
return Optional.of(Character.toLowerCase(c));
36+
} else if (Character.isLowerCase(c)) {
37+
return Optional.of(Character.toUpperCase(c));
38+
} else {
39+
return Optional.empty();
40+
}
41+
}
42+
2443
private static final String PREFIX = "prefix";
2544
private static final String STRING = "string";
2645
private static final String VALUE = "value";
2746

2847
@Override
29-
public abstract MutableMapNode<Character, V, B> getRoot();
48+
public abstract R getRoot();
3049

3150
@Override
32-
public abstract View<V> getView();
51+
public abstract View<V, B, R> getView();
52+
53+
public MutableCharacterNode<V, B> get(String prefix) {
54+
return this.getImpl(prefix, MutableCharacterNode::next);
55+
}
56+
57+
public MutableCharacterNode<V, B> getIgnoreCase(String prefix) {
58+
return this.getImpl(prefix, MutableCharacterNode::nextIgnoreCase);
59+
}
3360

34-
public MutableMapNode<Character, V, ?> get(String prefix) {
61+
private MutableCharacterNode<V, B> getImpl(
62+
String prefix, BiFunction<MutableCharacterNode<V, B>, Character, MutableCharacterNode<V, B>> next
63+
) {
3564
Utils.requireNonNull(prefix, PREFIX);
3665

37-
MutableMapNode<Character, V, ?> node = this.getRoot();
66+
MutableCharacterNode<V, B> node = this.getRoot();
67+
if (prefix.isEmpty()) {
68+
return node;
69+
}
70+
3871
for (int i = 0; i < prefix.length(); i++) {
39-
node = node.next(prefix.charAt(i));
72+
node = next.apply(node, prefix.charAt(i));
4073
}
4174

4275
return node;
4376
}
4477

45-
public MutableMapNode<Character, V, ?> put(String string, V value) {
78+
public MutableCharacterNode<V, B> put(String string, V value) {
4679
Utils.requireNonNull(string, STRING);
4780
Utils.requireNonNull(value, VALUE);
4881

49-
MutableMapNode<Character, V, B> node = this.getRoot();
50-
for (int i = 0; i < string.length(); i++) {
51-
final MutableMapNode<Character, V, B> parent = node;
82+
final R root = this.getRoot();
83+
if (string.isEmpty()) {
84+
return root;
85+
}
86+
87+
B node = root.next(string.charAt(0));
88+
for (int i = 1; i < string.length(); i++) {
89+
final B parent = node;
5290
final char key = string.charAt(i);
5391
node = node.getBranches().computeIfAbsent(key, ignored -> parent.createBranch(key));
5492
}
@@ -89,16 +127,59 @@ public boolean removeAll(String string) {
89127
return node.clearLeaves();
90128
}
91129

92-
public abstract static class View<V> implements MultiTrie<Character, V> {
130+
public interface CharacterNode<V> extends MultiTrie.Node<Character, V> {
131+
@Override
132+
CharacterNode<V> next(Character key);
133+
134+
default CharacterNode<V> nextIgnoreCase(Character key) {
135+
final CharacterNode<V> next = this.next(key);
136+
return next.isEmpty()
137+
? tryToggleCase(key).map(this::next).orElse(next)
138+
: next;
139+
}
140+
}
141+
142+
public interface MutableCharacterNode
143+
<V, B extends MutableMapNode.Branch<Character, V, B> & MutableCharacterNode<V, B>>
144+
extends CharacterNode<V>, MutableMultiTrie.Node<Character, V> {
145+
@Override
146+
B next(Character key);
147+
148+
@Override
149+
CharacterNode<V> getView();
150+
93151
@Override
94-
public Node<Character, V> getRoot() {
152+
default MutableCharacterNode<V, B> nextIgnoreCase(Character key) {
153+
final MutableCharacterNode<V, B> next = this.next(key);
154+
return next.isEmpty()
155+
? tryToggleCase(key).<MutableCharacterNode<V, B>>map(this::next).orElse(next)
156+
: next;
157+
}
158+
}
159+
160+
public abstract static class BranchNode<V, B extends BranchNode<V, B>>
161+
extends MutableMapNode.Branch<Character, V, B>
162+
implements MutableCharacterNode<V, B> { }
163+
164+
public abstract static class View
165+
<
166+
V, B extends BranchNode<V, B> & MutableCharacterNode<V, B>,
167+
R extends MutableMapNode<Character, V, B> & MutableCharacterNode<V, B>
168+
>
169+
implements MultiTrie<Character, V> {
170+
@Override
171+
public CharacterNode<V> getRoot() {
95172
return this.getViewed().getRoot().getView();
96173
}
97174

98-
public MultiTrie.Node<Character, V> get(String prefix) {
175+
public CharacterNode<V> get(String prefix) {
99176
return this.getViewed().get(prefix).getView();
100177
}
101178

102-
protected abstract StringMultiTrie<V, ?> getViewed();
179+
public CharacterNode<V> getIgnoreCase(String prefix) {
180+
return this.getViewed().getIgnoreCase(prefix).getView();
181+
}
182+
183+
protected abstract StringMultiTrie<V, B, R> getViewed();
103184
}
104185
}

0 commit comments

Comments
 (0)