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;
023
024import org.apache.hadoop.hbase.Cell;
025import org.apache.yetus.audience.InterfaceAudience;
026import org.apache.hadoop.hbase.exceptions.DeserializationException;
027import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
028import org.apache.hadoop.hbase.shaded.protobuf.generated.FilterProtos;
029import org.apache.hbase.thirdparty.com.google.protobuf.InvalidProtocolBufferException;
030
031/**
032 * A wrapper filter that filters an entire row if any of the Cell checks do
033 * not pass.
034 * <p>
035 * For example, if all columns in a row represent weights of different things,
036 * with the values being the actual weights, and we want to filter out the
037 * entire row if any of its weights are zero.  In this case, we want to prevent
038 * rows from being emitted if a single key is filtered.  Combine this filter
039 * with a {@link ValueFilter}:
040 * </p>
041 * <p>
042 * <code>
043 * scan.setFilter(new SkipFilter(new ValueFilter(CompareOp.NOT_EQUAL,
044 *     new BinaryComparator(Bytes.toBytes(0))));
045 * </code>
046 * Any row which contained a column whose value was 0 will be filtered out
047 * (since ValueFilter will not pass that Cell).
048 * Without this filter, the other non-zero valued columns in the row would still
049 * be emitted.
050 * </p>
051 */
052@InterfaceAudience.Public
053public class SkipFilter extends FilterBase {
054  private boolean filterRow = false;
055  private Filter filter;
056
057  public SkipFilter(Filter filter) {
058    this.filter = filter;
059  }
060
061  public Filter getFilter() {
062    return filter;
063  }
064
065  @Override
066  public void reset() throws IOException {
067    filter.reset();
068    filterRow = false;
069  }
070
071  private void changeFR(boolean value) {
072    filterRow = filterRow || value;
073  }
074
075  @Override
076  public boolean filterRowKey(Cell cell) throws IOException {
077    // Impl in FilterBase might do unnecessary copy for Off heap backed Cells.
078    return false;
079  }
080
081  @Deprecated
082  @Override
083  public ReturnCode filterKeyValue(final Cell c) throws IOException {
084    return filterCell(c);
085  }
086
087  @Override
088  public ReturnCode filterCell(final Cell c) throws IOException {
089    ReturnCode rc = filter.filterCell(c);
090    changeFR(rc != ReturnCode.INCLUDE);
091    return rc;
092  }
093
094  @Override
095  public Cell transformCell(Cell v) throws IOException {
096    return filter.transformCell(v);
097  }
098
099  @Override
100  public boolean filterRow() {
101    return filterRow;
102  }
103    
104  @Override
105  public boolean hasFilterRow() {
106    return true;
107  }
108
109  /**
110   * @return The filter serialized using pb
111   */
112  @Override
113  public byte[] toByteArray() throws IOException {
114    FilterProtos.SkipFilter.Builder builder =
115      FilterProtos.SkipFilter.newBuilder();
116    builder.setFilter(ProtobufUtil.toFilter(this.filter));
117    return builder.build().toByteArray();
118  }
119
120  /**
121   * @param pbBytes A pb serialized {@link SkipFilter} instance
122   * @return An instance of {@link SkipFilter} made from <code>bytes</code>
123   * @throws DeserializationException
124   * @see #toByteArray
125   */
126  public static SkipFilter parseFrom(final byte [] pbBytes)
127  throws DeserializationException {
128    FilterProtos.SkipFilter proto;
129    try {
130      proto = FilterProtos.SkipFilter.parseFrom(pbBytes);
131    } catch (InvalidProtocolBufferException e) {
132      throw new DeserializationException(e);
133    }
134    try {
135      return new SkipFilter(ProtobufUtil.toFilter(proto.getFilter()));
136    } catch (IOException ioe) {
137      throw new DeserializationException(ioe);
138    }
139  }
140
141  /**
142   * @param o the other filter to compare with
143   * @return true if and only if the fields of the filter that are serialized
144   * are equal to the corresponding fields in other.  Used for testing.
145   */
146  @Override
147  boolean areSerializedFieldsEqual(Filter o) {
148    if (o == this) return true;
149    if (!(o instanceof SkipFilter)) return false;
150
151    SkipFilter other = (SkipFilter)o;
152    return getFilter().areSerializedFieldsEqual(other.getFilter());
153  }
154
155  @Override
156  public boolean isFamilyEssential(byte[] name) throws IOException {
157    return filter.isFamilyEssential(name);
158  }
159
160  @Override
161  public String toString() {
162    return this.getClass().getSimpleName() + " " + this.filter.toString();
163  }
164}