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  
23  import com.healthmarketscience.jackcess.Column;
24  import com.healthmarketscience.jackcess.Row;
25  import org.apache.commons.lang.ObjectUtils;
26  
27  
28  /**
29   * The RowFilter class encapsulates a filter test for a table row.  This can
30   * be used by the {@link #apply(Iterable)} method to create an Iterable over a
31   * table which returns only rows matching some criteria.
32   * 
33   * @author Patricia Donaldson, Xerox Corporation
34   * @usage _general_class_
35   */
36  public abstract class RowFilter
37  {
38  
39    /**
40     * Returns {@code true} if the given table row matches the Filter criteria,
41     * {@code false} otherwise.
42     * @param row current row to test for inclusion in the filter
43     */
44    public abstract boolean matches(Row row);
45  
46    /**
47     * Returns an iterable which filters the given iterable based on this
48     * filter.
49     *
50     * @param iterable row iterable to filter
51     *
52     * @return a filtering iterable
53     */
54    public Iterable<Row> apply(Iterable<? extends Row> iterable)
55    {
56      return new FilterIterable(iterable);
57    }
58  
59  
60    /**
61     * Creates a filter based on a row pattern.
62     * 
63     * @param rowPattern Map from column names to the values to be matched.
64     *                   A table row will match the target if
65     *                   {@code ObjectUtils.equals(rowPattern.get(s), row.get(s))}
66     *                   for all column names in the pattern map.
67     * @return a filter which matches table rows which match the values in the
68     *         row pattern
69     */
70    public static RowFilter matchPattern(final Map<String,?> rowPattern) 
71    {
72      return new RowFilter() {
73          @Override
74          public boolean matches(Row row) 
75          {
76            for(Map.Entry<String,?> e : rowPattern.entrySet()) {
77              if(!ObjectUtils.equals(e.getValue(), row.get(e.getKey()))) {
78                return false;
79              }
80            }
81            return true;
82          }
83        };
84    }
85  
86    /**
87     * Creates a filter based on a single value row pattern.
88     *
89     * @param columnPattern column to be matched
90     * @param valuePattern value to be matched.
91     *                     A table row will match the target if
92     *                     {@code ObjectUtils.equals(valuePattern, row.get(columnPattern.getName()))}.
93     * @return a filter which matches table rows which match the value in the
94     *         row pattern
95     */
96    public static RowFilter matchPattern(final Column columnPattern, 
97                                         final Object valuePattern) 
98    {
99      return new RowFilter() {
100         @Override
101         public boolean matches(Row row) 
102         {
103           return ObjectUtils.equals(valuePattern, columnPattern.getRowValue(row));
104         }
105       };
106   }
107 
108   /**
109    * Creates a filter which inverts the sense of the given filter (rows which
110    * are matched by the given filter will not be matched by the returned
111    * filter, and vice versa).
112    *
113    * @param filter filter which to invert
114    *
115    * @return a RowFilter which matches rows not matched by the given filter
116    */
117   public static RowFilter invert(final RowFilter filter)
118   {
119     return new RowFilter() {
120         @Override
121         public boolean matches(Row row) 
122         {
123           return !filter.matches(row);
124         }
125       };
126   }
127 
128 
129   /**
130    * Returns an iterable which filters the given iterable based on the given
131    * rowFilter.
132    *
133    * @param rowFilter the filter criteria, may be {@code null}
134    * @param iterable row iterable to filter
135    *
136    * @return a filtering iterable (or the given iterable if a {@code null}
137    *         filter was given)
138    */
139   @SuppressWarnings("unchecked")
140   public static Iterable<Row> apply(RowFilter rowFilter,
141                                     Iterable<? extends Row> iterable)
142   {
143     return((rowFilter != null) ? rowFilter.apply(iterable) : 
144            (Iterable<Row>)iterable);
145   }
146 
147 
148   /**
149    * Iterable which creates a filtered view of a another row iterable.
150    */
151   private class FilterIterable implements Iterable<Row>
152   {
153     private final Iterable<? extends Row> _iterable;
154 
155     private FilterIterable(Iterable<? extends Row> iterable) 
156     {
157       _iterable = iterable;
158     }
159 
160 
161     /**
162      * Returns an iterator which iterates through the rows of the underlying
163      * iterable, returning only rows for which the {@link RowFilter#matches}
164      * method returns {@code true}
165      */
166     public Iterator<Row> iterator() 
167     {
168       return new Iterator<Row>() {
169         private final Iterator<? extends Row> _iter = _iterable.iterator();
170         private Row _next;
171 
172         public boolean hasNext() {
173           while(_iter.hasNext()) {
174             _next = _iter.next();
175             if(RowFilter.this.matches(_next)) {
176               return true;
177             }
178           }
179           _next = null;
180           return false;
181         }
182 
183         public Row next() {
184           if(_next == null) {
185             throw new NoSuchElementException();
186           }
187           return _next;
188         }
189 
190         public void remove() {
191           throw new UnsupportedOperationException();
192         }
193 
194       };
195     }
196 
197   }
198 
199 }