1 /*
2 Copyright (c) 2008 Health Market Science, Inc.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 package com.healthmarketscience.jackcess.util;
18
19 import java.util.Iterator;
20 import java.util.Map;
21 import java.util.NoSuchElementException;
22 import java.util.Objects;
23 import java.util.function.Predicate;
24 import java.util.stream.Stream;
25 import java.util.stream.StreamSupport;
26
27 import com.healthmarketscience.jackcess.Column;
28 import com.healthmarketscience.jackcess.Row;
29
30
31 /**
32 * The RowFilter class encapsulates a filter test for a table row. This can
33 * be used by the {@link #apply(Iterable)} method to create an Iterable over a
34 * table which returns only rows matching some criteria.
35 *
36 * @author Patricia Donaldson, Xerox Corporation
37 * @usage _general_class_
38 */
39 public abstract class RowFilter implements Predicate<Row>
40 {
41
42 /**
43 * Returns {@code true} if the given table row matches the Filter criteria,
44 * {@code false} otherwise.
45 * @param row current row to test for inclusion in the filter
46 */
47 public abstract boolean matches(Row row);
48
49 /**
50 * Adaptation of this class for {@link Predicate} support. Uses the
51 * {@link #matches} method.
52 */
53 @Override
54 public boolean test(Row row) {
55 return matches(row);
56 }
57
58 /**
59 * Returns an iterable which filters the given iterable based on this
60 * filter.
61 *
62 * @param iterable row iterable to filter
63 *
64 * @return a filtering iterable
65 */
66 public Iterable<Row> apply(Iterable<? extends Row> iterable)
67 {
68 return new FilterIterable(iterable);
69 }
70
71 /**
72 * Convenience method to apply this filter to the given iterable and return
73 * it as a Stream.
74 */
75 public Stream<Row> filter(Iterable<? extends Row> iterable) {
76 return StreamSupport.stream(
77 new FilterIterable(iterable).spliterator(), false)
78 .filter(this);
79 }
80
81 /**
82 * Creates a filter based on a row pattern.
83 *
84 * @param rowPattern Map from column names to the values to be matched.
85 * A table row will match the target if
86 * {@code Objects.equals(rowPattern.get(s), row.get(s))}
87 * for all column names in the pattern map.
88 * @return a filter which matches table rows which match the values in the
89 * row pattern
90 */
91 public static RowFilter matchPattern(final Map<String,?> rowPattern)
92 {
93 return new RowFilter() {
94 @Override
95 public boolean matches(Row row)
96 {
97 for(Map.Entry<String,?> e : rowPattern.entrySet()) {
98 if(!Objects.equals(e.getValue(), row.get(e.getKey()))) {
99 return false;
100 }
101 }
102 return true;
103 }
104 };
105 }
106
107 /**
108 * Creates a filter based on a single value row pattern.
109 *
110 * @param columnPattern column to be matched
111 * @param valuePattern value to be matched.
112 * A table row will match the target if
113 * {@code Objects.equals(valuePattern, row.get(columnPattern.getName()))}.
114 * @return a filter which matches table rows which match the value in the
115 * row pattern
116 */
117 public static RowFilter matchPattern(final Column columnPattern,
118 final Object valuePattern)
119 {
120 return new RowFilter() {
121 @Override
122 public boolean matches(Row row)
123 {
124 return Objects.equals(valuePattern, columnPattern.getRowValue(row));
125 }
126 };
127 }
128
129 /**
130 * Creates a filter which inverts the sense of the given filter (rows which
131 * are matched by the given filter will not be matched by the returned
132 * filter, and vice versa).
133 *
134 * @param filter filter which to invert
135 *
136 * @return a RowFilter which matches rows not matched by the given filter
137 */
138 public static RowFilters/util/RowFilter.html#RowFilter">RowFilter invert(final RowFilter filter)
139 {
140 return new RowFilter() {
141 @Override
142 public boolean matches(Row row)
143 {
144 return !filter.matches(row);
145 }
146 };
147 }
148
149
150 /**
151 * Returns an iterable which filters the given iterable based on the given
152 * rowFilter.
153 *
154 * @param rowFilter the filter criteria, may be {@code null}
155 * @param iterable row iterable to filter
156 *
157 * @return a filtering iterable (or the given iterable if a {@code null}
158 * filter was given)
159 */
160 @SuppressWarnings("unchecked")
161 public static Iterable<Row> apply(RowFilter rowFilter,
162 Iterable<? extends Row> iterable)
163 {
164 return((rowFilter != null) ? rowFilter.apply(iterable) :
165 (Iterable<Row>)iterable);
166 }
167
168
169 /**
170 * Iterable which creates a filtered view of a another row iterable.
171 */
172 private class FilterIterable implements Iterable<Row>
173 {
174 private final Iterable<? extends Row> _iterable;
175
176 private FilterIterable(Iterable<? extends Row> iterable)
177 {
178 _iterable = iterable;
179 }
180
181
182 /**
183 * Returns an iterator which iterates through the rows of the underlying
184 * iterable, returning only rows for which the {@link RowFilter#matches}
185 * method returns {@code true}
186 */
187 @Override
188 public Iterator<Row> iterator()
189 {
190 return new Iterator<Row>() {
191 private final Iterator<? extends Row> _iter = _iterable.iterator();
192 private Row _next;
193
194 @Override
195 public boolean hasNext() {
196 while(_iter.hasNext()) {
197 _next = _iter.next();
198 if(RowFilter.this.matches(_next)) {
199 return true;
200 }
201 }
202 _next = null;
203 return false;
204 }
205
206 @Override
207 public Row next() {
208 if(_next == null) {
209 throw new NoSuchElementException();
210 }
211 return _next;
212 }
213
214 };
215 }
216
217 }
218
219 }