Skip to content

Commit 77a2e35

Browse files
committed
fix #1093 TreeGridPlugin: Uncaught TypeError: Cannot read properties of undefined
1 parent 1ccd10c commit 77a2e35

File tree

4 files changed

+142
-36
lines changed

4 files changed

+142
-36
lines changed

domino-ui/src/main/java/org/dominokit/domino/ui/data/FilterMode.java

Lines changed: 120 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,51 +19,158 @@
1919
import java.util.Collection;
2020
import java.util.List;
2121

22+
/**
23+
* Strategy interface for filtering collections using one or more {@link DataFilter} instances.
24+
*
25+
* <p>Implementations provided here are:
26+
*
27+
* <ul>
28+
* <li>{@link #denial()} — keep items that pass <em>all</em> filters (logical AND).
29+
* <li>{@link #acceptance()} — keep items that pass <em>any</em> filter (logical OR).
30+
* <li>{@link #acceptAll()} — ignore filters and keep all items.
31+
* <li>{@link #denyAll()} — ignore filters and keep no items.
32+
* </ul>
33+
*
34+
* <p><strong>Null-handling:</strong> For convenience and safety, all built-in modes treat {@code
35+
* data == null} as empty and {@code filters == null} or empty as "no filters", which accepts all
36+
* items (except for {@link #denyAll()}).
37+
*
38+
* <p><em>Note:</em> This interface uses private static helper methods (requires Java 9+).
39+
*
40+
* @param <T> the element type
41+
*/
42+
@FunctionalInterface
2243
public interface FilterMode<T> {
2344

45+
/**
46+
* Applies this filter mode to the provided data using the provided filters.
47+
*
48+
* @param data the input elements; may be {@code null} (treated as empty)
49+
* @param filters the filters to evaluate; may be {@code null} or empty
50+
* @return a new {@link List} containing the elements that satisfy this mode
51+
* @implNote Implementations should avoid mutating the input collections and should generally
52+
* preserve input iteration order.
53+
*/
2454
List<T> apply(Collection<T> data, Collection<? extends DataFilter<? super T>> filters);
2555

56+
/**
57+
* A mode that keeps only items which pass <em>all</em> filters (logical AND).
58+
*
59+
* <p>Behavior:
60+
*
61+
* <ul>
62+
* <li>If {@code data} is {@code null} or empty → returns an empty list.
63+
* <li>If {@code filters} is {@code null} or empty → returns a copy of {@code data}.
64+
* <li>Otherwise → includes an item only if every filter returns {@code true} for it.
65+
* </ul>
66+
*
67+
* @param <T> the element type
68+
* @return a {@link FilterMode} implementing AND semantics
69+
*/
2670
static <T> FilterMode<T> denial() {
2771
return (data, filters) -> {
2872
if (data == null || data.isEmpty()) return new ArrayList<>();
2973
if (filters == null || filters.isEmpty()) return new ArrayList<>(data);
74+
3075
List<T> out = new ArrayList<>(data.size());
31-
outer:
3276
for (T item : data) {
33-
for (DataFilter<? super T> f : filters) {
34-
if (!f.filter(item)) continue outer;
77+
if (passesAll(item, filters)) {
78+
out.add(item);
3579
}
36-
out.add(item);
3780
}
3881
return out;
3982
};
4083
}
4184

85+
/**
86+
* A mode that keeps items which pass <em>any</em> filter (logical OR).
87+
*
88+
* <p>Behavior:
89+
*
90+
* <ul>
91+
* <li>If {@code data} is {@code null} or empty → returns an empty list.
92+
* <li>If {@code filters} is {@code null} or empty → returns a copy of {@code data}.
93+
* <li>Otherwise → includes an item if at least one filter returns {@code true} for it.
94+
* </ul>
95+
*
96+
* @param <T> the element type
97+
* @return a {@link FilterMode} implementing OR semantics
98+
*/
4299
static <T> FilterMode<T> acceptance() {
43100
return (data, filters) -> {
44101
if (data == null || data.isEmpty()) return new ArrayList<>();
45102
if (filters == null || filters.isEmpty()) return new ArrayList<>(data);
103+
46104
List<T> out = new ArrayList<>();
47-
outer:
48105
for (T item : data) {
49-
for (DataFilter<? super T> f : filters) {
50-
if (f.filter(item)) {
51-
out.add(item);
52-
continue outer;
53-
}
106+
if (passesAny(item, filters)) {
107+
out.add(item);
54108
}
55109
}
56110
return out;
57111
};
58112
}
59113

60-
/** Ignore filters and accept all items */
114+
/**
115+
* A mode that ignores filters and accepts all items.
116+
*
117+
* <p>Behavior:
118+
*
119+
* <ul>
120+
* <li>If {@code data} is {@code null} → returns an empty list.
121+
* <li>Otherwise → returns a copy of {@code data}.
122+
* </ul>
123+
*
124+
* @param <T> the element type
125+
* @return a {@link FilterMode} that accepts everything
126+
*/
61127
static <T> FilterMode<T> acceptAll() {
62-
return (data, filters) -> new ArrayList<>(data);
128+
return (data, filters) -> data == null ? new ArrayList<>() : new ArrayList<>(data);
63129
}
64130

65-
/** Ignore filters and deny all items */
131+
/**
132+
* A mode that ignores filters and denies all items.
133+
*
134+
* @param <T> the element type
135+
* @return a {@link FilterMode} that accepts nothing
136+
*/
66137
static <T> FilterMode<T> denyAll() {
67138
return (data, filters) -> new ArrayList<>();
68139
}
140+
141+
/**
142+
* Returns {@code true} if the given {@code item} passes <em>all</em> {@code filters}.
143+
*
144+
* @param item the item to test
145+
* @param filters the filters to evaluate (assumed non-null/non-empty by callers)
146+
* @param <T> the element type
147+
* @return {@code true} if every filter returns {@code true} for {@code item}
148+
*/
149+
private static <T> boolean passesAll(
150+
T item, Collection<? extends DataFilter<? super T>> filters) {
151+
for (DataFilter<? super T> f : filters) {
152+
if (!f.filter(item)) {
153+
return false;
154+
}
155+
}
156+
return true;
157+
}
158+
159+
/**
160+
* Returns {@code true} if the given {@code item} passes <em>any</em> of the {@code filters}.
161+
*
162+
* @param item the item to test
163+
* @param filters the filters to evaluate (assumed non-null/non-empty by callers)
164+
* @param <T> the element type
165+
* @return {@code true} if at least one filter returns {@code true} for {@code item}
166+
*/
167+
private static <T> boolean passesAny(
168+
T item, Collection<? extends DataFilter<? super T>> filters) {
169+
for (DataFilter<? super T> f : filters) {
170+
if (f.filter(item)) {
171+
return true;
172+
}
173+
}
174+
return false;
175+
}
69176
}

domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/tree/store/LocalTreeDataStore.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@ public LocalTreeDataStore(
7272
* @param subItemsProvider the provider of sub-items
7373
*/
7474
public LocalTreeDataStore(List<T> records, SubItemsProvider<T> subItemsProvider) {
75-
super(records);
7675
this.subItemsProvider = subItemsProvider;
7776
this.treeNodeChildrenAware = record -> true;
77+
initData(records);
7878
}
7979

8080
/**
@@ -89,9 +89,9 @@ public LocalTreeDataStore(
8989
List<T> records,
9090
SubItemsProvider<T> subItemsProvider,
9191
TreeNodeChildrenAware<T> treeNodeChildrenAware) {
92-
super(records);
9392
this.subItemsProvider = subItemsProvider;
9493
this.treeNodeChildrenAware = treeNodeChildrenAware;
94+
initData(records);
9595
}
9696

9797
/**

domino-ui/src/main/java/org/dominokit/domino/ui/datatable/plugins/tree/store/SubItemsStore.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.dominokit.domino.ui.datatable.plugins.tree.store;
1818

19+
import java.util.ArrayList;
1920
import java.util.List;
2021
import org.dominokit.domino.ui.datatable.events.SearchEvent;
2122
import org.dominokit.domino.ui.datatable.events.SortEvent;
@@ -43,6 +44,7 @@ public class SubItemsStore<T> extends LocalListDataStore<T> {
4344
*/
4445
public SubItemsStore(LocalTreeDataStore<T> parent) {
4546
this.parent = parent;
47+
initData(new ArrayList<>());
4648
}
4749

4850
/**
@@ -53,8 +55,8 @@ public SubItemsStore(LocalTreeDataStore<T> parent) {
5355
* @param parent The parent LocalTreeDataStore to delegate data operations to.
5456
*/
5557
public SubItemsStore(List<T> data, LocalTreeDataStore<T> parent) {
56-
super(data);
5758
this.parent = parent;
59+
initData(data);
5860
}
5961

6062
/** {@inheritDoc} */

domino-ui/src/main/java/org/dominokit/domino/ui/datatable/store/LocalListDataStore.java

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public class LocalListDataStore<T>
5353

5454
private final List<StoreDataChangeListener<T>> listeners = new ArrayList<>();
5555

56-
protected final List<T> original;
56+
protected List<T> original;
5757
protected List<T> filtered;
5858
private HasPagination pagination;
5959
private SearchFilter<T> searchFilter;
@@ -95,15 +95,14 @@ public void onDraggedOut(T draggedOutRecord) {
9595
};
9696
private boolean filtersPaused = false;
9797
private Set<DataFilter<? super T>> dataFilters;
98-
private FilterMode<Object> filterMode;
98+
private FilterMode<T> filterMode;
9999

100100
/**
101101
* Constructs a new {@code LocalListDataStore} with an empty original data list and filtered data
102102
* list.
103103
*/
104104
public LocalListDataStore() {
105-
this.original = new ArrayList<>();
106-
this.filtered = new ArrayList<>();
105+
initData(new ArrayList<>());
107106
}
108107

109108
/**
@@ -112,6 +111,10 @@ public LocalListDataStore() {
112111
* @param data The list of data records.
113112
*/
114113
public LocalListDataStore(List<T> data) {
114+
initData(data);
115+
}
116+
117+
protected final void initData(List<T> data) {
115118
this.original = data;
116119
this.filtered = new ArrayList<>(filterData(data));
117120
}
@@ -352,9 +355,7 @@ public void handleEvent(DominoEvent event) {
352355
*/
353356
private void handleDraggedOutEvent(RecordDraggedOutEvent<T> event) {
354357
T rowToRemove = event.getDraggedOutRecord();
355-
356358
dragDropRecordActions.onDraggedOut(rowToRemove);
357-
358359
fireUpdate(true);
359360
}
360361

@@ -722,20 +723,6 @@ public void addRecords(Collection<T> records) {
722723
setData(new ArrayList<>(original));
723724
}
724725

725-
/**
726-
* Deprecated method to remove multiple records from the data store, updating both the original
727-
* and filtered lists.
728-
*
729-
* @param records A collection of records to be removed.
730-
* @deprecated Use {@link #removeRecords(Collection)} instead.
731-
*/
732-
@Deprecated
733-
public void removeRecord(Collection<T> records) {
734-
original.removeAll(records);
735-
filtered.removeAll(records);
736-
load();
737-
}
738-
739726
/**
740727
* Removes multiple records from the data store, updating both the original and filtered lists.
741728
*
@@ -806,6 +793,16 @@ public boolean isDataFiltersPaused() {
806793
return this.filtersPaused;
807794
}
808795

796+
public LocalListDataStore<T> setFilterMode(FilterMode<T> filterMode) {
797+
this.filterMode = filterMode;
798+
return this;
799+
}
800+
801+
@Override
802+
public FilterMode<T> getFilterMode() {
803+
return nonNull(filterMode) ? filterMode : FilterMode.denial();
804+
}
805+
809806
public LocalListDataStore<T> setDataFiltersEnabled(boolean state) {
810807
if (state) {
811808
this.filterMode = FilterMode.denial();

0 commit comments

Comments
 (0)