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