001/**
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *     http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019package org.apache.hadoop.hbase.filter;
020
021import java.io.IOException;
022import java.util.ArrayList;
023import java.util.Arrays;
024import java.util.Collections;
025import java.util.List;
026import java.util.Objects;
027
028import org.apache.hadoop.hbase.Cell;
029import org.apache.yetus.audience.InterfaceAudience;
030import org.apache.hadoop.hbase.exceptions.DeserializationException;
031
032import org.apache.hbase.thirdparty.com.google.protobuf.InvalidProtocolBufferException;
033import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
034import org.apache.hadoop.hbase.shaded.protobuf.generated.FilterProtos;
035
036/**
037 * Implementation of {@link Filter} that represents an ordered List of Filters which will be
038 * evaluated with a specified boolean operator {@link Operator#MUST_PASS_ALL} (<code>AND</code>) or
039 * {@link Operator#MUST_PASS_ONE} (<code>OR</code>). Since you can use Filter Lists as children of
040 * Filter Lists, you can create a hierarchy of filters to be evaluated. <br>
041 * {@link Operator#MUST_PASS_ALL} evaluates lazily: evaluation stops as soon as one filter does not
042 * include the Cell. <br>
043 * {@link Operator#MUST_PASS_ONE} evaluates non-lazily: all filters are always evaluated. <br>
044 * Defaults to {@link Operator#MUST_PASS_ALL}.
045 */
046@InterfaceAudience.Public
047final public class FilterList extends FilterBase {
048
049  /** set operator */
050  @InterfaceAudience.Public
051  public enum Operator {
052    /** !AND */
053    MUST_PASS_ALL,
054    /** !OR */
055    MUST_PASS_ONE
056  }
057
058  private Operator operator;
059  private FilterListBase filterListBase;
060
061  /**
062   * Constructor that takes a set of {@link Filter}s and an operator.
063   * @param operator Operator to process filter set with.
064   * @param filters Set of row filters.
065   */
066  public FilterList(final Operator operator, final List<Filter> filters) {
067    if (operator == Operator.MUST_PASS_ALL) {
068      filterListBase = new FilterListWithAND(filters);
069    } else if (operator == Operator.MUST_PASS_ONE) {
070      filterListBase = new FilterListWithOR(filters);
071    } else {
072      throw new IllegalArgumentException("Invalid operator: " + operator);
073    }
074    this.operator = operator;
075  }
076
077  /**
078   * Constructor that takes a set of {@link Filter}s. The default operator MUST_PASS_ALL is assumed.
079   * All filters are cloned to internal list.
080   * @param filters list of filters
081   */
082  public FilterList(final List<Filter> filters) {
083    this(Operator.MUST_PASS_ALL, filters);
084  }
085
086  /**
087   * Constructor that takes a var arg number of {@link Filter}s. The default operator MUST_PASS_ALL
088   * is assumed.
089   * @param filters
090   */
091  public FilterList(final Filter... filters) {
092    this(Operator.MUST_PASS_ALL, Arrays.asList(filters));
093  }
094
095  /**
096   * Constructor that takes an operator.
097   * @param operator Operator to process filter set with.
098   */
099  public FilterList(final Operator operator) {
100    this(operator, new ArrayList<>());
101  }
102
103  /**
104   * Constructor that takes a var arg number of {@link Filter}s and an operator.
105   * @param operator Operator to process filter set with.
106   * @param filters Filters to use
107   */
108  public FilterList(final Operator operator, final Filter... filters) {
109    this(operator, Arrays.asList(filters));
110  }
111
112  /**
113   * Get the operator.
114   * @return operator
115   */
116  public Operator getOperator() {
117    return operator;
118  }
119
120  /**
121   * Get the filters.
122   * @return filters
123   */
124  public List<Filter> getFilters() {
125    return filterListBase.getFilters();
126  }
127
128  public int size() {
129    return filterListBase.size();
130  }
131
132  public void addFilter(List<Filter> filters) {
133    filterListBase.addFilterLists(filters);
134  }
135
136  /**
137   * Add a filter.
138   * @param filter another filter
139   */
140  public void addFilter(Filter filter) {
141    addFilter(Collections.singletonList(filter));
142  }
143
144  @Override
145  public void reset() throws IOException {
146    filterListBase.reset();
147  }
148
149  @Override
150  public boolean filterRowKey(Cell firstRowCell) throws IOException {
151    return filterListBase.filterRowKey(firstRowCell);
152  }
153
154  @Override
155  public boolean filterAllRemaining() throws IOException {
156    return filterListBase.filterAllRemaining();
157  }
158
159  @Override
160  public Cell transformCell(Cell c) throws IOException {
161    return filterListBase.transformCell(c);
162  }
163
164  @Override
165  public ReturnCode filterCell(final Cell c) throws IOException {
166    return filterListBase.filterCell(c);
167  }
168
169  /**
170   * Filters that never filter by modifying the returned List of Cells can inherit this
171   * implementation that does nothing. {@inheritDoc}
172   */
173  @Override
174  public void filterRowCells(List<Cell> cells) throws IOException {
175    filterListBase.filterRowCells(cells);
176  }
177
178  @Override
179  public boolean hasFilterRow() {
180    return filterListBase.hasFilterRow();
181  }
182
183  @Override
184  public boolean filterRow() throws IOException {
185    return filterListBase.filterRow();
186  }
187
188  /**
189   * @return The filter serialized using pb
190   */
191  @Override
192  public byte[] toByteArray() throws IOException {
193    FilterProtos.FilterList.Builder builder = FilterProtos.FilterList.newBuilder();
194    builder.setOperator(FilterProtos.FilterList.Operator.valueOf(operator.name()));
195    ArrayList<Filter> filters = filterListBase.getFilters();
196    for (int i = 0, n = filters.size(); i < n; i++) {
197      builder.addFilters(ProtobufUtil.toFilter(filters.get(i)));
198    }
199    return builder.build().toByteArray();
200  }
201
202  /**
203   * @param pbBytes A pb serialized {@link FilterList} instance
204   * @return An instance of {@link FilterList} made from <code>bytes</code>
205   * @throws DeserializationException
206   * @see #toByteArray
207   */
208  public static FilterList parseFrom(final byte[] pbBytes) throws DeserializationException {
209    FilterProtos.FilterList proto;
210    try {
211      proto = FilterProtos.FilterList.parseFrom(pbBytes);
212    } catch (InvalidProtocolBufferException e) {
213      throw new DeserializationException(e);
214    }
215
216    List<Filter> rowFilters = new ArrayList<>(proto.getFiltersCount());
217    try {
218      List<FilterProtos.Filter> filtersList = proto.getFiltersList();
219      for (int i = 0, n = filtersList.size(); i < n; i++) {
220        rowFilters.add(ProtobufUtil.toFilter(filtersList.get(i)));
221      }
222    } catch (IOException ioe) {
223      throw new DeserializationException(ioe);
224    }
225    return new FilterList(Operator.valueOf(proto.getOperator().name()), rowFilters);
226  }
227
228  /**
229   * @param other
230   * @return true if and only if the fields of the filter that are serialized are equal to the
231   *         corresponding fields in other. Used for testing.
232   */
233  @Override
234  boolean areSerializedFieldsEqual(Filter other) {
235    if (other == this) return true;
236    if (!(other instanceof FilterList)) return false;
237
238    FilterList o = (FilterList) other;
239    return this.getOperator().equals(o.getOperator())
240        && ((this.getFilters() == o.getFilters()) || this.getFilters().equals(o.getFilters()));
241  }
242
243  @Override
244  public Cell getNextCellHint(Cell currentCell) throws IOException {
245    return this.filterListBase.getNextCellHint(currentCell);
246  }
247
248  @Override
249  public boolean isFamilyEssential(byte[] name) throws IOException {
250    return this.filterListBase.isFamilyEssential(name);
251  }
252
253  @Override
254  public void setReversed(boolean reversed) {
255    this.reversed = reversed;
256    this.filterListBase.setReversed(reversed);
257  }
258
259  @Override
260  public boolean isReversed() {
261    assert this.reversed == this.filterListBase.isReversed();
262    return this.reversed;
263  }
264
265  @Override
266  public String toString() {
267    return this.filterListBase.toString();
268  }
269
270  @Override
271  public boolean equals(Object obj) {
272    return obj instanceof Filter && areSerializedFieldsEqual((Filter) obj);
273  }
274
275  @Override
276  public int hashCode() {
277    return Objects.hash(getOperator(), getFilters());
278  }
279}