|
19 | 19 | package com.here.naksha.lib.handlers.util; |
20 | 20 |
|
21 | 21 | import com.here.naksha.lib.core.lambdas.F1; |
| 22 | +import java.util.Collections; |
| 23 | +import java.util.HashSet; |
| 24 | +import java.util.List; |
| 25 | +import java.util.Optional; |
| 26 | +import java.util.Set; |
| 27 | +import java.util.function.Supplier; |
22 | 28 | import naksha.model.request.RequestQuery; |
23 | | -import naksha.model.request.query.*; |
| 29 | +import naksha.model.request.query.IPropertyQuery; |
| 30 | +import naksha.model.request.query.PAnd; |
| 31 | +import naksha.model.request.query.PNot; |
| 32 | +import naksha.model.request.query.POr; |
| 33 | +import naksha.model.request.query.PQuery; |
24 | 34 | import org.jetbrains.annotations.NotNull; |
25 | 35 |
|
26 | | -import java.util.*; |
27 | | - |
28 | 36 | public final class PropertyOperationUtil { |
29 | | - private static final IPropertyQuery AND_NEUTRAL = PTrue.INSTANCE; |
30 | | - private static final IPropertyQuery OR_NEUTRAL = PFalse.INSTANCE; |
31 | 37 |
|
32 | | - private PropertyOperationUtil() { |
33 | | - } |
| 38 | + private PropertyOperationUtil() { |
| 39 | + } |
34 | 40 |
|
35 | | - public static Set<PQuery> disablePQueriesInRequest(@NotNull RequestQuery requestQuery, @NotNull F1<Boolean, PQuery> shouldDisable) { |
36 | | - IPropertyQuery rootPropertyQuery = requestQuery.getProperties(); |
37 | | - if (rootPropertyQuery != null) { |
38 | | - HashSet<PQuery> disabledProperties = new HashSet<>(); |
39 | | - IPropertyQuery newRootPropertyQuery = disablePropertyInPropertyQueryTree( |
40 | | - rootPropertyQuery, shouldDisable, disabledProperties, PTrue.INSTANCE |
41 | | - ) |
42 | | - .flatMap(newRoot -> { |
43 | | - if (newRoot == PTrue.INSTANCE) { |
44 | | - return Optional.empty(); |
45 | | - } |
46 | | - return Optional.of(newRoot); |
47 | | - }).orElse(null); |
48 | | - requestQuery.setProperties(newRootPropertyQuery); |
49 | | - return disabledProperties; |
50 | | - } |
51 | | - // root property query is null -> no disabled property queries -> empty set |
52 | | - return Collections.emptySet(); |
| 41 | + /** |
| 42 | + * Traverses the property query tree of the given {@link RequestQuery} and disables any {@link PQuery} nodes that match the provided predicate. |
| 43 | + * <p> |
| 44 | + * The removed queries are collected and returned as a set. |
| 45 | + * <b>Important:</b> this method <b>mutates</b> the given {@code requestQuery}. |
| 46 | + * After execution, {@code requestQuery.getProperties()} may reference a different {@link IPropertyQuery} object than before, reflecting the removal of matching queries. |
| 47 | + * <p> |
| 48 | + * If the request has no property query, the returned set will be empty and the request is left unchanged. |
| 49 | + * |
| 50 | + * @param requestQuery the request whose property query tree is to be traversed and modified |
| 51 | + * @param shouldDisable a predicate that determines whether a {@link PQuery} should be removed |
| 52 | + * @return a set containing all {@link PQuery} instances that were removed from the property query tree; returns an empty set if no queries were removed |
| 53 | + */ |
| 54 | + public static Set<PQuery> disablePQueriesInRequest(@NotNull RequestQuery requestQuery, @NotNull F1<Boolean, PQuery> shouldDisable) { |
| 55 | + IPropertyQuery rootPropertyQuery = requestQuery.getProperties(); |
| 56 | + if (rootPropertyQuery != null) { |
| 57 | + HashSet<PQuery> disabledProperties = new HashSet<>(); |
| 58 | + IPropertyQuery newRootPropertyQuery = disablePropertyInPropertyQueryTree( |
| 59 | + rootPropertyQuery, shouldDisable, disabledProperties |
| 60 | + ).orElse(null); |
| 61 | + requestQuery.setProperties(newRootPropertyQuery); |
| 62 | + return disabledProperties; |
53 | 63 | } |
| 64 | + // root property query is null -> no disabled property queries -> empty set |
| 65 | + return Collections.emptySet(); |
| 66 | + } |
54 | 67 |
|
55 | | - /** |
56 | | - * @param current Currently traversed node |
57 | | - * @param removalCondition If evaluates to true, it effectively disables the check by replacing it with `true-ish` query |
58 | | - * @param disabledProperties Set of so-far disabled property queries |
59 | | - * @param parentsNeutral The parent's neutral value |
60 | | - */ |
61 | | - private static Optional<IPropertyQuery> disablePropertyInPropertyQueryTree( |
62 | | - @NotNull IPropertyQuery current, |
63 | | - @NotNull F1<Boolean, PQuery> removalCondition, |
64 | | - @NotNull Set<PQuery> disabledProperties, |
65 | | - @NotNull IPropertyQuery parentsNeutral |
66 | | - ) { |
67 | | - switch (current) { |
68 | | - case PAnd pAnd -> { |
69 | | - if (pAnd.stream().allMatch(pq -> pq == AND_NEUTRAL)) { |
70 | | - return Optional.of(AND_NEUTRAL); |
71 | | - } |
72 | | - List<IPropertyQuery> newChildren = disablePropertyInChildrenQueryTree( |
73 | | - pAnd, |
74 | | - removalCondition, |
75 | | - disabledProperties, |
76 | | - AND_NEUTRAL |
77 | | - ).stream() |
78 | | - .filter(pq -> pq != AND_NEUTRAL) |
79 | | - .toList(); |
80 | | - pAnd = new PAnd(); |
81 | | - pAnd.addAll(newChildren); |
82 | | - boolean isAlwaysFalse = pAnd.stream().anyMatch(q -> q == PFalse.INSTANCE); |
83 | | - if (isAlwaysFalse) { |
84 | | - return Optional.of(PFalse.INSTANCE); |
85 | | - } |
86 | | - return handleCompoundQuery(pAnd, parentsNeutral); |
87 | | - } |
88 | | - case POr pOr -> { |
89 | | - if (pOr.stream().allMatch(pq -> pq == OR_NEUTRAL)) { |
90 | | - return Optional.of(OR_NEUTRAL); |
91 | | - } |
92 | | - List<IPropertyQuery> newChildren = disablePropertyInChildrenQueryTree( |
93 | | - pOr, |
94 | | - removalCondition, |
95 | | - disabledProperties, |
96 | | - OR_NEUTRAL |
97 | | - ).stream() |
98 | | - .filter(pq -> pq != OR_NEUTRAL) |
99 | | - .toList(); |
100 | | - pOr = new POr(); |
101 | | - pOr.addAll(newChildren); |
102 | | - boolean isAlwaysTrue = pOr.stream().anyMatch(q -> q == PTrue.INSTANCE); |
103 | | - if (isAlwaysTrue) { |
104 | | - return Optional.of(PTrue.INSTANCE); |
105 | | - } |
106 | | - return handleCompoundQuery(pOr, parentsNeutral); |
107 | | - } |
108 | | - case PNot pNot -> { |
109 | | - // The pNot neutral is the negated parent's neutral |
110 | | - IPropertyQuery notNeutral = parentsNeutral == PFalse.INSTANCE ? PTrue.INSTANCE : PFalse.INSTANCE; |
111 | | - return disablePropertyInPropertyQueryTree( |
112 | | - pNot.getQuery(), removalCondition, disabledProperties, notNeutral |
113 | | - ).flatMap(pq -> { |
114 | | - if (pq == PFalse.INSTANCE) { |
115 | | - return Optional.of(PTrue.INSTANCE); |
116 | | - } |
117 | | - if (pq == PTrue.INSTANCE) { |
118 | | - return Optional.of(PFalse.INSTANCE); |
119 | | - } |
120 | | - return Optional.of(new PNot(pq)); |
121 | | - }); |
122 | | - } |
123 | | - case PQuery currentPQuery when removalCondition.call(currentPQuery) -> { |
124 | | - disabledProperties.add(currentPQuery); |
125 | | - return Optional.empty(); |
126 | | - } |
127 | | - default -> { |
128 | | - // unhandled type / not matching pQuery => stop traversal without failing |
129 | | - return Optional.of(current); |
130 | | - } |
131 | | - } |
132 | | - } |
| 68 | + /** |
| 69 | + * @param current Currently traversed node |
| 70 | + * @param removalCondition Predicate that determines whether a {@link PQuery} should be disabled |
| 71 | + * @param disabledProperties Set of so-far disabled property queries |
| 72 | + * @return an {@link Optional} containing the updated query node, or an empty optional if the node is removed as a result of disabling |
| 73 | + */ |
| 74 | + private static Optional<IPropertyQuery> disablePropertyInPropertyQueryTree( |
| 75 | + @NotNull IPropertyQuery current, |
| 76 | + @NotNull F1<Boolean, PQuery> removalCondition, |
| 77 | + @NotNull Set<PQuery> disabledProperties |
| 78 | + ) { |
| 79 | + return switch (current) { |
| 80 | + case PAnd pAnd -> handleCompoundQuery( |
| 81 | + pAnd, |
| 82 | + removalCondition, |
| 83 | + disabledProperties, |
| 84 | + PAnd::new |
| 85 | + ); |
| 86 | + case POr pOr -> handleCompoundQuery( |
| 87 | + pOr, |
| 88 | + removalCondition, |
| 89 | + disabledProperties, |
| 90 | + POr::new |
| 91 | + ); |
| 92 | + case PNot pNot -> disablePropertyInPropertyQueryTree( |
| 93 | + pNot.getQuery(), removalCondition, disabledProperties |
| 94 | + ).flatMap(pq -> Optional.of(new PNot(pq))); |
| 95 | + case PQuery currentPQuery when removalCondition.call(currentPQuery) -> { |
| 96 | + disabledProperties.add(currentPQuery); |
| 97 | + yield disabledPropertyQuery(); |
| 98 | + } |
| 99 | + default -> Optional.of(current); |
| 100 | + }; |
| 101 | + } |
133 | 102 |
|
134 | | - private static <T extends List<IPropertyQuery> & IPropertyQuery> Optional<IPropertyQuery> handleCompoundQuery( |
135 | | - T compoundQuery, |
136 | | - IPropertyQuery parentsNeutral |
137 | | - ) { |
138 | | - // all the children were removed so should be neutral to parent |
139 | | - if (compoundQuery.isEmpty()) { |
140 | | - return Optional.of(parentsNeutral); |
141 | | - } |
142 | | - if (compoundQuery.size() == 1) { |
143 | | - return Optional.of(compoundQuery.getFirst()); |
144 | | - } |
145 | | - return Optional.of(compoundQuery); |
146 | | - } |
| 103 | + private static Optional<IPropertyQuery> disabledPropertyQuery() { |
| 104 | + return Optional.empty(); |
| 105 | + } |
147 | 106 |
|
148 | | - private static <T extends List<IPropertyQuery> & IPropertyQuery> List<IPropertyQuery> disablePropertyInChildrenQueryTree( |
149 | | - T compoundQuery, |
150 | | - F1<Boolean, PQuery> removalCondition, |
151 | | - Set<PQuery> disabledProperties, |
152 | | - IPropertyQuery parentsNeutral |
153 | | - ) { |
154 | | - return compoundQuery.stream() |
155 | | - .map(child -> disablePropertyInPropertyQueryTree( |
156 | | - child, removalCondition, disabledProperties, parentsNeutral |
157 | | - )) |
158 | | - .filter(Optional::isPresent) |
159 | | - .map(Optional::get) |
160 | | - .toList(); |
| 107 | + private static boolean allChildrenDisabled(List<Optional<IPropertyQuery>> children) { |
| 108 | + return children.stream().allMatch(Optional::isEmpty); |
| 109 | + } |
| 110 | + |
| 111 | + private static List<IPropertyQuery> removeDisabledChildren(List<Optional<IPropertyQuery>> children) { |
| 112 | + return children.stream() |
| 113 | + .filter(Optional::isPresent) |
| 114 | + .map(Optional::get) |
| 115 | + .toList(); |
| 116 | + } |
| 117 | + |
| 118 | + private static <T extends List<IPropertyQuery> & IPropertyQuery> Optional<IPropertyQuery> handleCompoundQuery( |
| 119 | + T compoundQuery, |
| 120 | + F1<Boolean, PQuery> removalCondition, |
| 121 | + Set<PQuery> disabledProperties, |
| 122 | + Supplier<T> constructor |
| 123 | + ) { |
| 124 | + List<Optional<IPropertyQuery>> newChildren = disablePropertyInChildrenQueryTree( |
| 125 | + compoundQuery, |
| 126 | + removalCondition, |
| 127 | + disabledProperties |
| 128 | + ); |
| 129 | + if (!compoundQuery.isEmpty() && allChildrenDisabled(newChildren)) { |
| 130 | + return disabledPropertyQuery(); |
161 | 131 | } |
| 132 | + compoundQuery = constructor.get(); |
| 133 | + compoundQuery.addAll(removeDisabledChildren(newChildren)); |
| 134 | + return Optional.of(compoundQuery); |
| 135 | + } |
| 136 | + |
| 137 | + private static <T extends List<IPropertyQuery> & IPropertyQuery> List<Optional<IPropertyQuery>> disablePropertyInChildrenQueryTree( |
| 138 | + T compoundQuery, |
| 139 | + F1<Boolean, PQuery> removalCondition, |
| 140 | + Set<PQuery> disabledProperties |
| 141 | + ) { |
| 142 | + return compoundQuery.stream() |
| 143 | + .map(child -> disablePropertyInPropertyQueryTree( |
| 144 | + child, removalCondition, disabledProperties |
| 145 | + )) |
| 146 | + .toList(); |
| 147 | + } |
162 | 148 | } |
0 commit comments