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 */
019
020package org.apache.hadoop.hbase.filter;
021
022import java.io.IOException;
023import java.util.ArrayList;
024import java.util.Arrays;
025import java.util.List;
026
027import org.apache.hadoop.hbase.Cell;
028import org.apache.hadoop.hbase.CellComparator;
029import org.apache.yetus.audience.InterfaceAudience;
030
031/**
032 * Base class for FilterList. Currently, we have two sub-classes to extend this class:
033 * {@link FilterListWithOR}, {@link FilterListWithAND}.
034 */
035@InterfaceAudience.Private
036public abstract class FilterListBase extends FilterBase {
037  private static final int MAX_LOG_FILTERS = 5;
038  protected final ArrayList<Filter> filters;
039  /**
040   * For each sub-filter in filter list, we save a boolean flag to indicate that whether the return
041   * code of filterCell(c) for sub-filter is INCLUDE* (INCLUDE, INCLUDE_AND_NEXT_COL,
042   * INCLUDE_AND_SEEK_NEXT_ROW) case. if true, we need to transform cell for the sub-filter.
043   */
044  protected ArrayList<Boolean> subFiltersIncludedCell;
045
046  public FilterListBase(List<Filter> filters) {
047    reversed = checkAndGetReversed(filters, reversed);
048    this.filters = new ArrayList<>(filters);
049  }
050
051  protected static boolean isInReturnCodes(ReturnCode testRC, ReturnCode... returnCodes) {
052    return Arrays.stream(returnCodes).anyMatch(testRC::equals);
053  }
054
055  protected static boolean checkAndGetReversed(List<Filter> rowFilters, boolean defaultValue) {
056    if (rowFilters.isEmpty()) {
057      return defaultValue;
058    }
059    boolean retValue = rowFilters.get(0).isReversed();
060    boolean allEqual = rowFilters.stream().allMatch(f -> f.isReversed() == retValue);
061    if (!allEqual) {
062      throw new IllegalArgumentException("Filters in the list must have the same reversed flag");
063    }
064    return retValue;
065  }
066
067  public abstract void addFilterLists(List<Filter> filters);
068
069  public int size() {
070    return this.filters.size();
071  }
072
073  public boolean isEmpty() {
074    return this.filters.isEmpty();
075  }
076
077  public ArrayList<Filter> getFilters() {
078    return this.filters;
079  }
080
081  protected int compareCell(Cell a, Cell b) {
082    int cmp = CellComparator.getInstance().compare(a, b);
083    return reversed ? -1 * cmp : cmp;
084  }
085
086  /**
087   * For FilterList, we can consider a filter list as a node in a tree. sub-filters of the filter
088   * list are children of the relative node. The logic of transforming cell of a filter list, well,
089   * we can consider it as the process of post-order tree traverse. For a node , before we traverse
090   * the current child, we should set the traverse result (transformed cell) of previous node(s) as
091   * the initial value. (HBASE-18879).
092   * @param c The cell in question.
093   * @return the transformed cell.
094   * @throws IOException
095   */
096  @Override
097  public Cell transformCell(Cell c) throws IOException {
098    if (isEmpty()) {
099      return super.transformCell(c);
100    }
101    Cell transformed = c;
102    for (int i = 0, n = filters.size(); i < n; i++) {
103      if (subFiltersIncludedCell.get(i)) {
104        transformed = filters.get(i).transformCell(transformed);
105      }
106    }
107    return transformed;
108  }
109
110  @Override
111  public ReturnCode filterKeyValue(final Cell c) throws IOException {
112    return filterCell(c);
113  }
114
115  /**
116   * Filters that never filter by modifying the returned List of Cells can inherit this
117   * implementation that does nothing. {@inheritDoc}
118   */
119  @Override
120  public void filterRowCells(List<Cell> cells) throws IOException {
121    for (int i = 0, n = filters.size(); i < n; i++) {
122      filters.get(i).filterRowCells(cells);
123    }
124  }
125
126  @Override
127  public boolean hasFilterRow() {
128    for (int i = 0, n = filters.size(); i < n; i++) {
129      if (filters.get(i).hasFilterRow()) {
130        return true;
131      }
132    }
133    return false;
134  }
135
136  @Override
137  public boolean isFamilyEssential(byte[] name) throws IOException {
138    if (this.filters.isEmpty()) {
139      return super.isFamilyEssential(name);
140    }
141    for (int i = 0, n = filters.size(); i < n; i++) {
142      if (filters.get(i).isFamilyEssential(name)) {
143        return true;
144      }
145    }
146    return false;
147  }
148
149  @Override
150  public void setReversed(boolean reversed) {
151    for (int i = 0, n = filters.size(); i < n; i++) {
152      filters.get(i).setReversed(reversed);
153    }
154    this.reversed = reversed;
155  }
156
157  @Override
158  public String toString() {
159    int endIndex = this.size() < MAX_LOG_FILTERS ? this.size() : MAX_LOG_FILTERS;
160    return formatLogFilters(filters.subList(0, endIndex));
161  }
162
163  protected abstract String formatLogFilters(List<Filter> logFilters);
164}