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