001/**
002 * Copyright 2010 The Apache Software Foundation
003 *
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *     http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020
021package org.apache.hadoop.hbase.filter;
022
023import org.apache.hadoop.hbase.io.HbaseObjectWritable;
024import org.apache.hadoop.hbase.util.Bytes;
025
026import java.io.DataInput;
027import java.io.DataOutput;
028import java.io.IOException;
029import java.util.ArrayList;
030
031import com.google.common.base.Preconditions;
032/**
033 * This is a generic filter to be used to filter by comparison.  It takes an
034 * operator (equal, greater, not equal, etc) and a byte [] comparator.
035 * <p>
036 * To filter by row key, use {@link RowFilter}.
037 * <p>
038 * To filter by column qualifier, use {@link QualifierFilter}.
039 * <p>
040 * To filter by value, use {@link SingleColumnValueFilter}.
041 * <p>
042 * These filters can be wrapped with {@link SkipFilter} and {@link WhileMatchFilter}
043 * to add more control.
044 * <p>
045 * Multiple filters can be combined using {@link FilterList}.
046 */
047public abstract class CompareFilter extends FilterBase {
048
049  /** Comparison operators. */
050  public enum CompareOp {
051    /** less than */
052    LESS,
053    /** less than or equal to */
054    LESS_OR_EQUAL,
055    /** equals */
056    EQUAL,
057    /** not equal */
058    NOT_EQUAL,
059    /** greater than or equal to */
060    GREATER_OR_EQUAL,
061    /** greater than */
062    GREATER,
063    /** no operation */
064    NO_OP,
065  }
066
067  protected CompareOp compareOp;
068  protected WritableByteArrayComparable comparator;
069
070  /**
071   * Writable constructor, do not use.
072   */
073  public CompareFilter() {
074  }
075
076  /**
077   * Constructor.
078   * @param compareOp the compare op for row matching
079   * @param comparator the comparator for row matching
080   */
081  public CompareFilter(final CompareOp compareOp,
082      final WritableByteArrayComparable comparator) {
083    this.compareOp = compareOp;
084    this.comparator = comparator;
085  }
086
087  /**
088   * @return operator
089   */
090  public CompareOp getOperator() {
091    return compareOp;
092  }
093
094  /**
095   * @return the comparator
096   */
097  public WritableByteArrayComparable getComparator() {
098    return comparator;
099  }
100
101  protected boolean doCompare(final CompareOp compareOp,
102      final WritableByteArrayComparable comparator, final byte [] data,
103      final int offset, final int length) {
104    if (compareOp == CompareOp.NO_OP) {
105      return true;
106    }
107    int compareResult = comparator.compareTo(data, offset, length);
108    switch (compareOp) {
109      case LESS:
110        return compareResult <= 0;
111      case LESS_OR_EQUAL:
112        return compareResult < 0;
113      case EQUAL:
114        return compareResult != 0;
115      case NOT_EQUAL:
116        return compareResult == 0;
117      case GREATER_OR_EQUAL:
118        return compareResult > 0;
119      case GREATER:
120        return compareResult >= 0;
121      default:
122        throw new RuntimeException("Unknown Compare op " +
123          compareOp.name());
124    }
125  }
126
127  public static ArrayList extractArguments(ArrayList<byte []> filterArguments) {
128    Preconditions.checkArgument(filterArguments.size() == 2,
129                                "Expected 2 but got: %s", filterArguments.size());
130    CompareOp compareOp = ParseFilter.createCompareOp(filterArguments.get(0));
131    WritableByteArrayComparable comparator = ParseFilter.createComparator(
132      ParseFilter.removeQuotesFromByteArray(filterArguments.get(1)));
133
134    if (comparator instanceof RegexStringComparator ||
135        comparator instanceof SubstringComparator) {
136      if (compareOp != CompareOp.EQUAL &&
137          compareOp != CompareOp.NOT_EQUAL) {
138        throw new IllegalArgumentException ("A regexstring comparator and substring comparator" +
139                                            " can only be used with EQUAL and NOT_EQUAL");
140      }
141    }
142    ArrayList arguments = new ArrayList();
143    arguments.add(compareOp);
144    arguments.add(comparator);
145    return arguments;
146  }
147
148  public void readFields(DataInput in) throws IOException {
149    compareOp = CompareOp.valueOf(in.readUTF());
150    comparator = (WritableByteArrayComparable)
151      HbaseObjectWritable.readObject(in, null);
152  }
153
154  public void write(DataOutput out) throws IOException {
155    out.writeUTF(compareOp.name());
156    HbaseObjectWritable.writeObject(out, comparator,
157      WritableByteArrayComparable.class, null);
158  }
159
160  @Override
161  public String toString() {
162    return String.format("%s (%s, %s)",
163        this.getClass().getSimpleName(),
164        this.compareOp.name(),
165        Bytes.toStringBinary(this.comparator.getValue()));
166  }
167}