View Javadoc
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 }