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.client;
019
020import org.apache.hadoop.hbase.CompareOperator;
021import org.apache.hadoop.hbase.filter.Filter;
022import org.apache.hadoop.hbase.io.TimeRange;
023import org.apache.hadoop.hbase.util.Bytes;
024import org.apache.yetus.audience.InterfaceAudience;
025import org.apache.yetus.audience.InterfaceStability;
026
027import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
028
029/**
030 * Used to perform CheckAndMutate operations.
031 * <p>
032 * Use the builder class to instantiate a CheckAndMutate object. This builder class is fluent style
033 * APIs, the code are like:
034 *
035 * <pre>
036 * <code>
037 * // A CheckAndMutate operation where do the specified action if the column (specified by the
038 * // family and the qualifier) of the row equals to the specified value
039 * CheckAndMutate checkAndMutate = CheckAndMutate.newBuilder(row)
040 *   .ifEquals(family, qualifier, value)
041 *   .build(put);
042 *
043 * // A CheckAndMutate operation where do the specified action if the column (specified by the
044 * // family and the qualifier) of the row doesn't exist
045 * CheckAndMutate checkAndMutate = CheckAndMutate.newBuilder(row)
046 *   .ifNotExists(family, qualifier)
047 *   .build(put);
048 *
049 * // A CheckAndMutate operation where do the specified action if the row matches the filter
050 * CheckAndMutate checkAndMutate = CheckAndMutate.newBuilder(row)
051 *   .ifMatches(filter)
052 *   .build(delete);
053 * </code>
054 * </pre>
055 */
056@InterfaceAudience.Public
057@InterfaceStability.Evolving
058public final class CheckAndMutate implements Row {
059
060  /**
061   * A builder class for building a CheckAndMutate object.
062   */
063  @InterfaceAudience.Public
064  @InterfaceStability.Evolving
065  public static final class Builder {
066    private final byte[] row;
067    private byte[] family;
068    private byte[] qualifier;
069    private CompareOperator op;
070    private byte[] value;
071    private Filter filter;
072    private TimeRange timeRange;
073
074    private Builder(byte[] row) {
075      this.row = Preconditions.checkNotNull(row, "row is null");
076    }
077
078    /**
079     * Check for lack of column
080     * @param family    family to check
081     * @param qualifier qualifier to check
082     * @return the CheckAndMutate object
083     */
084    public Builder ifNotExists(byte[] family, byte[] qualifier) {
085      return ifEquals(family, qualifier, null);
086    }
087
088    /**
089     * Check for equality
090     * @param family    family to check
091     * @param qualifier qualifier to check
092     * @param value     the expected value
093     * @return the CheckAndMutate object
094     */
095    public Builder ifEquals(byte[] family, byte[] qualifier, byte[] value) {
096      return ifMatches(family, qualifier, CompareOperator.EQUAL, value);
097    }
098
099    /**
100     * @param family    family to check
101     * @param qualifier qualifier to check
102     * @param compareOp comparison operator to use
103     * @param value     the expected value
104     * @return the CheckAndMutate object
105     */
106    public Builder ifMatches(byte[] family, byte[] qualifier, CompareOperator compareOp,
107      byte[] value) {
108      this.family = Preconditions.checkNotNull(family, "family is null");
109      this.qualifier = qualifier;
110      this.op = Preconditions.checkNotNull(compareOp, "compareOp is null");
111      this.value = value;
112      return this;
113    }
114
115    /**
116     * @param filter filter to check
117     * @return the CheckAndMutate object
118     */
119    public Builder ifMatches(Filter filter) {
120      this.filter = Preconditions.checkNotNull(filter, "filter is null");
121      return this;
122    }
123
124    /**
125     * @param timeRange time range to check
126     * @return the CheckAndMutate object
127     */
128    public Builder timeRange(TimeRange timeRange) {
129      this.timeRange = timeRange;
130      return this;
131    }
132
133    private void preCheck(Row action) {
134      Preconditions.checkNotNull(action, "action is null");
135      if (!Bytes.equals(row, action.getRow())) {
136        throw new IllegalArgumentException(
137          "The row of the action <" + Bytes.toStringBinary(action.getRow())
138            + "> doesn't match the original one <" + Bytes.toStringBinary(this.row) + ">");
139      }
140      Preconditions.checkState(op != null || filter != null,
141        "condition is null. You need to"
142          + " specify the condition by calling ifNotExists/ifEquals/ifMatches before building a"
143          + " CheckAndMutate object");
144    }
145
146    /**
147     * @param put data to put if check succeeds
148     * @return a CheckAndMutate object
149     */
150    public CheckAndMutate build(Put put) {
151      preCheck(put);
152      if (filter != null) {
153        return new CheckAndMutate(row, filter, timeRange, put);
154      } else {
155        return new CheckAndMutate(row, family, qualifier, op, value, timeRange, put);
156      }
157    }
158
159    /**
160     * @param delete data to delete if check succeeds
161     * @return a CheckAndMutate object
162     */
163    public CheckAndMutate build(Delete delete) {
164      preCheck(delete);
165      if (filter != null) {
166        return new CheckAndMutate(row, filter, timeRange, delete);
167      } else {
168        return new CheckAndMutate(row, family, qualifier, op, value, timeRange, delete);
169      }
170    }
171
172    /**
173     * @param increment data to increment if check succeeds
174     * @return a CheckAndMutate object
175     */
176    public CheckAndMutate build(Increment increment) {
177      preCheck(increment);
178      if (filter != null) {
179        return new CheckAndMutate(row, filter, timeRange, increment);
180      } else {
181        return new CheckAndMutate(row, family, qualifier, op, value, timeRange, increment);
182      }
183    }
184
185    /**
186     * @param append data to append if check succeeds
187     * @return a CheckAndMutate object
188     */
189    public CheckAndMutate build(Append append) {
190      preCheck(append);
191      if (filter != null) {
192        return new CheckAndMutate(row, filter, timeRange, append);
193      } else {
194        return new CheckAndMutate(row, family, qualifier, op, value, timeRange, append);
195      }
196    }
197
198    /**
199     * @param mutations mutations to perform if check succeeds
200     * @return a CheckAndMutate object
201     */
202    public CheckAndMutate build(RowMutations mutations) {
203      preCheck(mutations);
204      if (filter != null) {
205        return new CheckAndMutate(row, filter, timeRange, mutations);
206      } else {
207        return new CheckAndMutate(row, family, qualifier, op, value, timeRange, mutations);
208      }
209    }
210  }
211
212  /**
213   * returns a builder object to build a CheckAndMutate object
214   * @param row row
215   * @return a builder object
216   */
217  public static Builder newBuilder(byte[] row) {
218    return new Builder(row);
219  }
220
221  private final byte[] row;
222  private final byte[] family;
223  private final byte[] qualifier;
224  private final CompareOperator op;
225  private final byte[] value;
226  private final Filter filter;
227  private final TimeRange timeRange;
228  private final Row action;
229
230  private CheckAndMutate(byte[] row, byte[] family, byte[] qualifier, final CompareOperator op,
231    byte[] value, TimeRange timeRange, Row action) {
232    this.row = row;
233    this.family = family;
234    this.qualifier = qualifier;
235    this.op = op;
236    this.value = value;
237    this.filter = null;
238    this.timeRange = timeRange != null ? timeRange : TimeRange.allTime();
239    this.action = action;
240  }
241
242  private CheckAndMutate(byte[] row, Filter filter, TimeRange timeRange, Row action) {
243    this.row = row;
244    this.family = null;
245    this.qualifier = null;
246    this.op = null;
247    this.value = null;
248    this.filter = filter;
249    this.timeRange = timeRange != null ? timeRange : TimeRange.allTime();
250    this.action = action;
251  }
252
253  /** Returns the row */
254  @Override
255  public byte[] getRow() {
256    return row;
257  }
258
259  @Override
260  public int compareTo(Row row) {
261    return Bytes.compareTo(this.getRow(), row.getRow());
262  }
263
264  // Added to get rid of the stopbugs error
265  @Override
266  public boolean equals(Object obj) {
267    if (this == obj) {
268      return true;
269    }
270    if (obj == null || getClass() != obj.getClass()) {
271      return false;
272    }
273    Row other = (Row) obj;
274    return compareTo(other) == 0;
275  }
276
277  @Override
278  public int hashCode() {
279    return Bytes.hashCode(this.getRow());
280  }
281
282  /** Returns the family to check */
283  public byte[] getFamily() {
284    return family;
285  }
286
287  /** Returns the qualifier to check */
288  public byte[] getQualifier() {
289    return qualifier;
290  }
291
292  /** Returns the comparison operator */
293  public CompareOperator getCompareOp() {
294    return op;
295  }
296
297  /** Returns the expected value */
298  public byte[] getValue() {
299    return value;
300  }
301
302  /** Returns the filter to check */
303  public Filter getFilter() {
304    return filter;
305  }
306
307  /** Returns whether this has a filter or not */
308  public boolean hasFilter() {
309    return filter != null;
310  }
311
312  /** Returns the time range to check */
313  public TimeRange getTimeRange() {
314    return timeRange;
315  }
316
317  /** Returns the action done if check succeeds */
318  public Row getAction() {
319    return action;
320  }
321}