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