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 */
018
019package org.apache.hadoop.hbase.client;
020
021import java.io.IOException;
022import java.nio.ByteBuffer;
023import java.util.ArrayList;
024import java.util.Arrays;
025import java.util.HashMap;
026import java.util.Iterator;
027import java.util.List;
028import java.util.Map;
029import java.util.NavigableMap;
030import java.util.Optional;
031import java.util.TreeMap;
032import java.util.UUID;
033import java.util.stream.Collectors;
034import org.apache.hadoop.hbase.Cell;
035import org.apache.hadoop.hbase.CellScannable;
036import org.apache.hadoop.hbase.CellScanner;
037import org.apache.hadoop.hbase.CellUtil;
038import org.apache.hadoop.hbase.ExtendedCell;
039import org.apache.hadoop.hbase.HConstants;
040import org.apache.hadoop.hbase.IndividualBytesFieldCell;
041import org.apache.hadoop.hbase.KeyValue;
042import org.apache.hadoop.hbase.PrivateCellUtil;
043import org.apache.hadoop.hbase.RawCell;
044import org.apache.hadoop.hbase.Tag;
045import org.apache.hadoop.hbase.exceptions.DeserializationException;
046import org.apache.hadoop.hbase.io.HeapSize;
047import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
048import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
049import org.apache.hadoop.hbase.security.access.AccessControlConstants;
050import org.apache.hadoop.hbase.security.access.AccessControlUtil;
051import org.apache.hadoop.hbase.security.access.Permission;
052import org.apache.hadoop.hbase.security.visibility.CellVisibility;
053import org.apache.hadoop.hbase.security.visibility.VisibilityConstants;
054import org.apache.hadoop.hbase.util.Bytes;
055import org.apache.hadoop.hbase.util.ClassSize;
056import org.apache.yetus.audience.InterfaceAudience;
057
058import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
059import org.apache.hbase.thirdparty.com.google.common.collect.ArrayListMultimap;
060import org.apache.hbase.thirdparty.com.google.common.collect.ListMultimap;
061import org.apache.hbase.thirdparty.com.google.common.io.ByteArrayDataInput;
062import org.apache.hbase.thirdparty.com.google.common.io.ByteArrayDataOutput;
063import org.apache.hbase.thirdparty.com.google.common.io.ByteStreams;
064
065@InterfaceAudience.Public
066public abstract class Mutation extends OperationWithAttributes implements Row, CellScannable,
067    HeapSize {
068  public static final long MUTATION_OVERHEAD = ClassSize.align(
069      // This
070      ClassSize.OBJECT +
071      // row + OperationWithAttributes.attributes
072      2 * ClassSize.REFERENCE +
073      // Timestamp
074      1 * Bytes.SIZEOF_LONG +
075      // durability
076      ClassSize.REFERENCE +
077      // familyMap
078      ClassSize.REFERENCE +
079      // familyMap
080      ClassSize.TREEMAP +
081      // priority
082      ClassSize.INTEGER
083  );
084
085  /**
086   * The attribute for storing the list of clusters that have consumed the change.
087   */
088  private static final String CONSUMED_CLUSTER_IDS = "_cs.id";
089
090  /**
091   * The attribute for storing TTL for the result of the mutation.
092   */
093  private static final String OP_ATTRIBUTE_TTL = "_ttl";
094
095  private static final String RETURN_RESULTS = "_rr_";
096
097  // TODO: row should be final
098  protected byte [] row = null;
099  protected long ts = HConstants.LATEST_TIMESTAMP;
100  protected Durability durability = Durability.USE_DEFAULT;
101
102  // TODO: familyMap should be final
103  // A Map sorted by column family.
104  protected NavigableMap<byte [], List<Cell>> familyMap;
105
106  /**
107   * empty construction.
108   * We need this empty construction to keep binary compatibility.
109   */
110  protected Mutation() {
111    this.familyMap = new TreeMap<>(Bytes.BYTES_COMPARATOR);
112  }
113
114  protected Mutation(Mutation clone) {
115    super(clone);
116    this.row = clone.getRow();
117    this.ts = clone.getTimestamp();
118    this.familyMap = clone.getFamilyCellMap().entrySet().stream().
119      collect(Collectors.toMap(e -> e.getKey(), e -> new ArrayList<>(e.getValue()), (k, v) -> {
120        throw new RuntimeException("collisions!!!");
121      }, () -> new TreeMap<>(Bytes.BYTES_COMPARATOR)));
122  }
123
124  /**
125   * Construct the mutation with user defined data.
126   * @param row row. CAN'T be null
127   * @param ts timestamp
128   * @param familyMap the map to collect all cells internally. CAN'T be null
129   */
130  protected Mutation(byte[] row, long ts, NavigableMap<byte [], List<Cell>> familyMap) {
131    this.row = Preconditions.checkNotNull(row);
132    if (row.length == 0) {
133      throw new IllegalArgumentException("Row can't be empty");
134    }
135    this.ts = ts;
136    this.familyMap = Preconditions.checkNotNull(familyMap);
137  }
138
139  @Override
140  public CellScanner cellScanner() {
141    return CellUtil.createCellScanner(getFamilyCellMap());
142  }
143
144  /**
145   * Creates an empty list if one doesn't exist for the given column family
146   * or else it returns the associated list of Cell objects.
147   *
148   * @param family column family
149   * @return a list of Cell objects, returns an empty list if one doesn't exist.
150   */
151  List<Cell> getCellList(byte[] family) {
152    List<Cell> list = this.familyMap.get(family);
153    if (list == null) {
154      list = new ArrayList<>();
155      this.familyMap.put(family, list);
156    }
157    return list;
158  }
159
160  /*
161   * Create a KeyValue with this objects row key and the Put identifier.
162   *
163   * @return a KeyValue with this objects row key and the Put identifier.
164   */
165  KeyValue createPutKeyValue(byte[] family, byte[] qualifier, long ts, byte[] value) {
166    return new KeyValue(this.row, family, qualifier, ts, KeyValue.Type.Put, value);
167  }
168
169  /**
170   * Create a KeyValue with this objects row key and the Put identifier.
171   * @param family
172   * @param qualifier
173   * @param ts
174   * @param value
175   * @param tags - Specify the Tags as an Array
176   * @return a KeyValue with this objects row key and the Put identifier.
177   */
178  KeyValue createPutKeyValue(byte[] family, byte[] qualifier, long ts, byte[] value, Tag[] tags) {
179    KeyValue kvWithTag = new KeyValue(this.row, family, qualifier, ts, value, tags);
180    return kvWithTag;
181  }
182
183  /*
184   * Create a KeyValue with this objects row key and the Put identifier.
185   *
186   * @return a KeyValue with this objects row key and the Put identifier.
187   */
188  KeyValue createPutKeyValue(byte[] family, ByteBuffer qualifier, long ts, ByteBuffer value,
189      Tag[] tags) {
190    return new KeyValue(this.row, 0, this.row == null ? 0 : this.row.length,
191        family, 0, family == null ? 0 : family.length,
192        qualifier, ts, KeyValue.Type.Put, value, tags != null ? Arrays.asList(tags) : null);
193  }
194
195  /**
196   * Compile the column family (i.e. schema) information
197   * into a Map. Useful for parsing and aggregation by debugging,
198   * logging, and administration tools.
199   * @return Map
200   */
201  @Override
202  public Map<String, Object> getFingerprint() {
203    Map<String, Object> map = new HashMap<>();
204    List<String> families = new ArrayList<>(this.familyMap.entrySet().size());
205    // ideally, we would also include table information, but that information
206    // is not stored in each Operation instance.
207    map.put("families", families);
208    for (Map.Entry<byte [], List<Cell>> entry : this.familyMap.entrySet()) {
209      families.add(Bytes.toStringBinary(entry.getKey()));
210    }
211    return map;
212  }
213
214  /**
215   * Compile the details beyond the scope of getFingerprint (row, columns,
216   * timestamps, etc.) into a Map along with the fingerprinted information.
217   * Useful for debugging, logging, and administration tools.
218   * @param maxCols a limit on the number of columns output prior to truncation
219   * @return Map
220   */
221  @Override
222  public Map<String, Object> toMap(int maxCols) {
223    // we start with the fingerprint map and build on top of it.
224    Map<String, Object> map = getFingerprint();
225    // replace the fingerprint's simple list of families with a
226    // map from column families to lists of qualifiers and kv details
227    Map<String, List<Map<String, Object>>> columns = new HashMap<>();
228    map.put("families", columns);
229    map.put("row", Bytes.toStringBinary(this.row));
230    int colCount = 0;
231    // iterate through all column families affected
232    for (Map.Entry<byte [], List<Cell>> entry : this.familyMap.entrySet()) {
233      // map from this family to details for each cell affected within the family
234      List<Map<String, Object>> qualifierDetails = new ArrayList<>();
235      columns.put(Bytes.toStringBinary(entry.getKey()), qualifierDetails);
236      colCount += entry.getValue().size();
237      if (maxCols <= 0) {
238        continue;
239      }
240      // add details for each cell
241      for (Cell cell: entry.getValue()) {
242        if (--maxCols <= 0) {
243          continue;
244        }
245        Map<String, Object> cellMap = cellToStringMap(cell);
246        qualifierDetails.add(cellMap);
247      }
248    }
249    map.put("totalColumns", colCount);
250    // add the id if set
251    if (getId() != null) {
252      map.put("id", getId());
253    }
254    // Add the TTL if set
255    // Long.MAX_VALUE is the default, and is interpreted to mean this attribute
256    // has not been set.
257    if (getTTL() != Long.MAX_VALUE) {
258      map.put("ttl", getTTL());
259    }
260    map.put("ts", this.ts);
261    return map;
262  }
263
264  private static Map<String, Object> cellToStringMap(Cell c) {
265    Map<String, Object> stringMap = new HashMap<>();
266    stringMap.put("qualifier", Bytes.toStringBinary(c.getQualifierArray(), c.getQualifierOffset(),
267                c.getQualifierLength()));
268    stringMap.put("timestamp", c.getTimestamp());
269    stringMap.put("vlen", c.getValueLength());
270    List<Tag> tags = PrivateCellUtil.getTags(c);
271    if (tags != null) {
272      List<String> tagsString = new ArrayList<>(tags.size());
273      for (Tag t : tags) {
274        tagsString
275            .add((t.getType()) + ":" + Bytes.toStringBinary(Tag.cloneValue(t)));
276      }
277      stringMap.put("tag", tagsString);
278    }
279    return stringMap;
280  }
281
282  /**
283   * Set the durability for this mutation
284   * @param d
285   */
286  public Mutation setDurability(Durability d) {
287    this.durability = d;
288    return this;
289  }
290
291  /** Get the current durability */
292  public Durability getDurability() {
293    return this.durability;
294  }
295
296  /**
297   * Method for retrieving the put's familyMap
298   * @return familyMap
299   */
300  public NavigableMap<byte [], List<Cell>> getFamilyCellMap() {
301    return this.familyMap;
302  }
303
304  /**
305   * Method for setting the mutation's familyMap
306   * @deprecated As of release 2.0.0, this will be removed in HBase 3.0.0.
307   *             Use {@link Mutation#Mutation(byte[], long, NavigableMap)} instead
308   */
309  @Deprecated
310  public Mutation setFamilyCellMap(NavigableMap<byte [], List<Cell>> map) {
311    // TODO: Shut this down or move it up to be a Constructor.  Get new object rather than change
312    // this internal data member.
313    this.familyMap = map;
314    return this;
315  }
316
317  /**
318   * Method to check if the familyMap is empty
319   * @return true if empty, false otherwise
320   */
321  public boolean isEmpty() {
322    return familyMap.isEmpty();
323  }
324
325  /**
326   * Method for retrieving the delete's row
327   * @return row
328   */
329  @Override
330  public byte [] getRow() {
331    return this.row;
332  }
333
334  /**
335   * @deprecated As of release 2.0.0, this will be removed in HBase 3.0.0.
336   *             Use {@link Row#COMPARATOR} instead
337   */
338  @Deprecated
339  @Override
340  public int compareTo(final Row d) {
341    return Bytes.compareTo(this.getRow(), d.getRow());
342  }
343
344  /**
345   * Method for retrieving the timestamp
346   * @return timestamp
347   * @deprecated As of release 2.0.0, this will be removed in HBase 3.0.0.
348   *             Use {@link #getTimestamp()} instead
349   */
350  @Deprecated
351  public long getTimeStamp() {
352    return this.getTimestamp();
353  }
354
355  /**
356   * Method for retrieving the timestamp.
357   *
358   * @return timestamp
359   */
360  public long getTimestamp() {
361    return this.ts;
362  }
363
364  /**
365   * Marks that the clusters with the given clusterIds have consumed the mutation
366   * @param clusterIds of the clusters that have consumed the mutation
367   */
368  public Mutation setClusterIds(List<UUID> clusterIds) {
369    ByteArrayDataOutput out = ByteStreams.newDataOutput();
370    out.writeInt(clusterIds.size());
371    for (UUID clusterId : clusterIds) {
372      out.writeLong(clusterId.getMostSignificantBits());
373      out.writeLong(clusterId.getLeastSignificantBits());
374    }
375    setAttribute(CONSUMED_CLUSTER_IDS, out.toByteArray());
376    return this;
377  }
378
379  /**
380   * @return the set of clusterIds that have consumed the mutation
381   */
382  public List<UUID> getClusterIds() {
383    List<UUID> clusterIds = new ArrayList<>();
384    byte[] bytes = getAttribute(CONSUMED_CLUSTER_IDS);
385    if(bytes != null) {
386      ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
387      int numClusters = in.readInt();
388      for(int i=0; i<numClusters; i++){
389        clusterIds.add(new UUID(in.readLong(), in.readLong()));
390      }
391    }
392    return clusterIds;
393  }
394
395  /**
396   * Sets the visibility expression associated with cells in this Mutation.
397   * @param expression
398   */
399  public Mutation setCellVisibility(CellVisibility expression) {
400    this.setAttribute(VisibilityConstants.VISIBILITY_LABELS_ATTR_KEY,
401        toCellVisibility(expression).toByteArray());
402    return this;
403  }
404
405  /**
406   * @return CellVisibility associated with cells in this Mutation.
407   * @throws DeserializationException
408   */
409  public CellVisibility getCellVisibility() throws DeserializationException {
410    byte[] cellVisibilityBytes = this.getAttribute(VisibilityConstants.VISIBILITY_LABELS_ATTR_KEY);
411    if (cellVisibilityBytes == null) return null;
412    return toCellVisibility(cellVisibilityBytes);
413  }
414
415  /**
416   * Create a protocol buffer CellVisibility based on a client CellVisibility.
417   *
418   * @param cellVisibility
419   * @return a protocol buffer CellVisibility
420   */
421  static ClientProtos.CellVisibility toCellVisibility(CellVisibility cellVisibility) {
422    ClientProtos.CellVisibility.Builder builder = ClientProtos.CellVisibility.newBuilder();
423    builder.setExpression(cellVisibility.getExpression());
424    return builder.build();
425  }
426
427  /**
428   * Convert a protocol buffer CellVisibility to a client CellVisibility
429   *
430   * @param proto
431   * @return the converted client CellVisibility
432   */
433  private static CellVisibility toCellVisibility(ClientProtos.CellVisibility proto) {
434    if (proto == null) return null;
435    return new CellVisibility(proto.getExpression());
436  }
437
438  /**
439   * Convert a protocol buffer CellVisibility bytes to a client CellVisibility
440   *
441   * @param protoBytes
442   * @return the converted client CellVisibility
443   * @throws DeserializationException
444   */
445  private static CellVisibility toCellVisibility(byte[] protoBytes) throws DeserializationException {
446    if (protoBytes == null) return null;
447    ClientProtos.CellVisibility.Builder builder = ClientProtos.CellVisibility.newBuilder();
448    ClientProtos.CellVisibility proto = null;
449    try {
450      ProtobufUtil.mergeFrom(builder, protoBytes);
451      proto = builder.build();
452    } catch (IOException e) {
453      throw new DeserializationException(e);
454    }
455    return toCellVisibility(proto);
456  }
457
458  /**
459   * Number of KeyValues carried by this Mutation.
460   * @return the total number of KeyValues
461   */
462  public int size() {
463    int size = 0;
464    for (List<Cell> cells : this.familyMap.values()) {
465      size += cells.size();
466    }
467    return size;
468  }
469
470  /**
471   * @return the number of different families
472   */
473  public int numFamilies() {
474    return familyMap.size();
475  }
476
477  /**
478   * @return Calculate what Mutation adds to class heap size.
479   */
480  @Override
481  public long heapSize() {
482    long heapsize = MUTATION_OVERHEAD;
483    // Adding row
484    heapsize += ClassSize.align(ClassSize.ARRAY + this.row.length);
485
486    // Adding map overhead
487    heapsize +=
488      ClassSize.align(this.familyMap.size() * ClassSize.MAP_ENTRY);
489    for(Map.Entry<byte [], List<Cell>> entry : this.familyMap.entrySet()) {
490      //Adding key overhead
491      heapsize +=
492        ClassSize.align(ClassSize.ARRAY + entry.getKey().length);
493
494      //This part is kinds tricky since the JVM can reuse references if you
495      //store the same value, but have a good match with SizeOf at the moment
496      //Adding value overhead
497      heapsize += ClassSize.align(ClassSize.ARRAYLIST);
498      int size = entry.getValue().size();
499      heapsize += ClassSize.align(ClassSize.ARRAY +
500          size * ClassSize.REFERENCE);
501
502      for (Cell cell : entry.getValue()) {
503        heapsize += cell.heapSize();
504      }
505    }
506    heapsize += getAttributeSize();
507    heapsize += extraHeapSize();
508    return ClassSize.align(heapsize);
509  }
510
511  /**
512   * @return The serialized ACL for this operation, or null if none
513   */
514  public byte[] getACL() {
515    return getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL);
516  }
517
518  /**
519   * @param user User short name
520   * @param perms Permissions for the user
521   */
522  public Mutation setACL(String user, Permission perms) {
523    setAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL,
524      AccessControlUtil.toUsersAndPermissions(user, perms).toByteArray());
525    return this;
526  }
527
528  /**
529   * @param perms A map of permissions for a user or users
530   */
531  public Mutation setACL(Map<String, Permission> perms) {
532    ListMultimap<String, Permission> permMap = ArrayListMultimap.create();
533    for (Map.Entry<String, Permission> entry : perms.entrySet()) {
534      permMap.put(entry.getKey(), entry.getValue());
535    }
536    setAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL,
537      AccessControlUtil.toUsersAndPermissions(permMap).toByteArray());
538    return this;
539  }
540
541  /**
542   * Return the TTL requested for the result of the mutation, in milliseconds.
543   * @return the TTL requested for the result of the mutation, in milliseconds,
544   * or Long.MAX_VALUE if unset
545   */
546  public long getTTL() {
547    byte[] ttlBytes = getAttribute(OP_ATTRIBUTE_TTL);
548    if (ttlBytes != null) {
549      return Bytes.toLong(ttlBytes);
550    }
551    return Long.MAX_VALUE;
552  }
553
554  /**
555   * Set the TTL desired for the result of the mutation, in milliseconds.
556   * @param ttl the TTL desired for the result of the mutation, in milliseconds
557   * @return this
558   */
559  public Mutation setTTL(long ttl) {
560    setAttribute(OP_ATTRIBUTE_TTL, Bytes.toBytes(ttl));
561    return this;
562  }
563
564  /**
565   * @return current value for returnResults
566   */
567  // Used by Increment and Append only.
568  @InterfaceAudience.Private
569  protected boolean isReturnResults() {
570    byte[] v = getAttribute(RETURN_RESULTS);
571    return v == null ? true : Bytes.toBoolean(v);
572  }
573
574  @InterfaceAudience.Private
575  // Used by Increment and Append only.
576  protected Mutation setReturnResults(boolean returnResults) {
577    setAttribute(RETURN_RESULTS, Bytes.toBytes(returnResults));
578    return this;
579  }
580
581  /**
582   * Subclasses should override this method to add the heap size of their own fields.
583   * @return the heap size to add (will be aligned).
584   */
585  protected long extraHeapSize(){
586    return 0L;
587  }
588
589  /**
590   * Set the timestamp of the delete.
591   */
592  public Mutation setTimestamp(long timestamp) {
593    if (timestamp < 0) {
594      throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + timestamp);
595    }
596    this.ts = timestamp;
597    return this;
598  }
599
600  /**
601   * A convenience method to determine if this object's familyMap contains
602   * a value assigned to the given family &amp; qualifier.
603   * Both given arguments must match the KeyValue object to return true.
604   *
605   * @param family column family
606   * @param qualifier column qualifier
607   * @return returns true if the given family and qualifier already has an
608   * existing KeyValue object in the family map.
609   */
610  public boolean has(byte [] family, byte [] qualifier) {
611    return has(family, qualifier, this.ts, HConstants.EMPTY_BYTE_ARRAY, true, true);
612  }
613
614  /**
615   * A convenience method to determine if this object's familyMap contains
616   * a value assigned to the given family, qualifier and timestamp.
617   * All 3 given arguments must match the KeyValue object to return true.
618   *
619   * @param family column family
620   * @param qualifier column qualifier
621   * @param ts timestamp
622   * @return returns true if the given family, qualifier and timestamp already has an
623   * existing KeyValue object in the family map.
624   */
625  public boolean has(byte [] family, byte [] qualifier, long ts) {
626    return has(family, qualifier, ts, HConstants.EMPTY_BYTE_ARRAY, false, true);
627  }
628
629  /**
630   * A convenience method to determine if this object's familyMap contains
631   * a value assigned to the given family, qualifier and timestamp.
632   * All 3 given arguments must match the KeyValue object to return true.
633   *
634   * @param family column family
635   * @param qualifier column qualifier
636   * @param value value to check
637   * @return returns true if the given family, qualifier and value already has an
638   * existing KeyValue object in the family map.
639   */
640  public boolean has(byte [] family, byte [] qualifier, byte [] value) {
641    return has(family, qualifier, this.ts, value, true, false);
642  }
643
644  /**
645   * A convenience method to determine if this object's familyMap contains
646   * the given value assigned to the given family, qualifier and timestamp.
647   * All 4 given arguments must match the KeyValue object to return true.
648   *
649   * @param family column family
650   * @param qualifier column qualifier
651   * @param ts timestamp
652   * @param value value to check
653   * @return returns true if the given family, qualifier timestamp and value
654   *   already has an existing KeyValue object in the family map.
655   */
656  public boolean has(byte [] family, byte [] qualifier, long ts, byte [] value) {
657    return has(family, qualifier, ts, value, false, false);
658  }
659
660  /**
661   * Returns a list of all KeyValue objects with matching column family and qualifier.
662   *
663   * @param family column family
664   * @param qualifier column qualifier
665   * @return a list of KeyValue objects with the matching family and qualifier,
666   *   returns an empty list if one doesn't exist for the given family.
667   */
668  public List<Cell> get(byte[] family, byte[] qualifier) {
669    List<Cell> filteredList = new ArrayList<>();
670    for (Cell cell: getCellList(family)) {
671      if (CellUtil.matchingQualifier(cell, qualifier)) {
672        filteredList.add(cell);
673      }
674    }
675    return filteredList;
676  }
677
678  /*
679   * Private method to determine if this object's familyMap contains
680   * the given value assigned to the given family, qualifier and timestamp
681   * respecting the 2 boolean arguments
682   *
683   * @param family
684   * @param qualifier
685   * @param ts
686   * @param value
687   * @param ignoreTS
688   * @param ignoreValue
689   * @return returns true if the given family, qualifier timestamp and value
690   * already has an existing KeyValue object in the family map.
691   */
692  protected boolean has(byte[] family, byte[] qualifier, long ts, byte[] value,
693      boolean ignoreTS, boolean ignoreValue) {
694    List<Cell> list = getCellList(family);
695    if (list.isEmpty()) {
696      return false;
697    }
698    // Boolean analysis of ignoreTS/ignoreValue.
699    // T T => 2
700    // T F => 3 (first is always true)
701    // F T => 2
702    // F F => 1
703    if (!ignoreTS && !ignoreValue) {
704      for (Cell cell : list) {
705        if (CellUtil.matchingFamily(cell, family) &&
706            CellUtil.matchingQualifier(cell, qualifier)  &&
707            CellUtil.matchingValue(cell, value) &&
708            cell.getTimestamp() == ts) {
709          return true;
710        }
711      }
712    } else if (ignoreValue && !ignoreTS) {
713      for (Cell cell : list) {
714        if (CellUtil.matchingFamily(cell, family) && CellUtil.matchingQualifier(cell, qualifier)
715            && cell.getTimestamp() == ts) {
716          return true;
717        }
718      }
719    } else if (!ignoreValue && ignoreTS) {
720      for (Cell cell : list) {
721        if (CellUtil.matchingFamily(cell, family) && CellUtil.matchingQualifier(cell, qualifier)
722            && CellUtil.matchingValue(cell, value)) {
723          return true;
724        }
725      }
726    } else {
727      for (Cell cell : list) {
728        if (CellUtil.matchingFamily(cell, family) &&
729            CellUtil.matchingQualifier(cell, qualifier)) {
730          return true;
731        }
732      }
733    }
734    return false;
735  }
736
737  /**
738   * @param row Row to check
739   * @throws IllegalArgumentException Thrown if <code>row</code> is empty or null or
740   * &gt; {@link HConstants#MAX_ROW_LENGTH}
741   * @return <code>row</code>
742   */
743  static byte [] checkRow(final byte [] row) {
744    return checkRow(row, 0, row == null? 0: row.length);
745  }
746
747  /**
748   * @param row Row to check
749   * @param offset
750   * @param length
751   * @throws IllegalArgumentException Thrown if <code>row</code> is empty or null or
752   * &gt; {@link HConstants#MAX_ROW_LENGTH}
753   * @return <code>row</code>
754   */
755  static byte [] checkRow(final byte [] row, final int offset, final int length) {
756    if (row == null) {
757      throw new IllegalArgumentException("Row buffer is null");
758    }
759    if (length == 0) {
760      throw new IllegalArgumentException("Row length is 0");
761    }
762    if (length > HConstants.MAX_ROW_LENGTH) {
763      throw new IllegalArgumentException("Row length " + length + " is > " +
764        HConstants.MAX_ROW_LENGTH);
765    }
766    return row;
767  }
768
769  static void checkRow(ByteBuffer row) {
770    if (row == null) {
771      throw new IllegalArgumentException("Row buffer is null");
772    }
773    if (row.remaining() == 0) {
774      throw new IllegalArgumentException("Row length is 0");
775    }
776    if (row.remaining() > HConstants.MAX_ROW_LENGTH) {
777      throw new IllegalArgumentException("Row length " + row.remaining() + " is > " +
778          HConstants.MAX_ROW_LENGTH);
779    }
780  }
781
782  Mutation add(Cell cell) throws IOException {
783    //Checking that the row of the kv is the same as the mutation
784    // TODO: It is fraught with risk if user pass the wrong row.
785    // Throwing the IllegalArgumentException is more suitable I'd say.
786    if (!CellUtil.matchingRows(cell, this.row)) {
787      throw new WrongRowIOException("The row in " + cell.toString() +
788        " doesn't match the original one " +  Bytes.toStringBinary(this.row));
789    }
790
791    byte[] family;
792
793    if (cell instanceof IndividualBytesFieldCell) {
794      family = cell.getFamilyArray();
795    } else {
796      family = CellUtil.cloneFamily(cell);
797    }
798
799    if (family == null || family.length == 0) {
800      throw new IllegalArgumentException("Family cannot be null");
801    }
802
803    if (cell instanceof ExtendedCell) {
804      getCellList(family).add(cell);
805    } else {
806      getCellList(family).add(new CellWrapper(cell));
807    }
808    return this;
809  }
810
811  private static final class CellWrapper implements ExtendedCell {
812    private static final long FIXED_OVERHEAD = ClassSize.align(
813      ClassSize.OBJECT              // object header
814        + KeyValue.TIMESTAMP_SIZE       // timestamp
815        + Bytes.SIZEOF_LONG             // sequence id
816        + 1 * ClassSize.REFERENCE);     // references to cell
817    private final Cell cell;
818    private long sequenceId;
819    private long timestamp;
820
821    CellWrapper(Cell cell) {
822      assert !(cell instanceof ExtendedCell);
823      this.cell = cell;
824      this.sequenceId = cell.getSequenceId();
825      this.timestamp = cell.getTimestamp();
826    }
827
828    @Override
829    public void setSequenceId(long seqId) {
830      sequenceId = seqId;
831    }
832
833    @Override
834    public void setTimestamp(long ts) {
835      timestamp = ts;
836    }
837
838    @Override
839    public void setTimestamp(byte[] ts) {
840      timestamp = Bytes.toLong(ts);
841    }
842
843    @Override
844    public long getSequenceId() {
845      return sequenceId;
846    }
847
848    @Override
849    public byte[] getValueArray() {
850      return cell.getValueArray();
851    }
852
853    @Override
854    public int getValueOffset() {
855      return cell.getValueOffset();
856    }
857
858    @Override
859    public int getValueLength() {
860      return cell.getValueLength();
861    }
862
863    @Override
864    public byte[] getTagsArray() {
865      return cell.getTagsArray();
866    }
867
868    @Override
869    public int getTagsOffset() {
870      return cell.getTagsOffset();
871    }
872
873    @Override
874    public int getTagsLength() {
875      return cell.getTagsLength();
876    }
877
878    @Override
879    public byte[] getRowArray() {
880      return cell.getRowArray();
881    }
882
883    @Override
884    public int getRowOffset() {
885      return cell.getRowOffset();
886    }
887
888    @Override
889    public short getRowLength() {
890      return cell.getRowLength();
891    }
892
893    @Override
894    public byte[] getFamilyArray() {
895      return cell.getFamilyArray();
896    }
897
898    @Override
899    public int getFamilyOffset() {
900      return cell.getFamilyOffset();
901    }
902
903    @Override
904    public byte getFamilyLength() {
905      return cell.getFamilyLength();
906    }
907
908    @Override
909    public byte[] getQualifierArray() {
910      return cell.getQualifierArray();
911    }
912
913    @Override
914    public int getQualifierOffset() {
915      return cell.getQualifierOffset();
916    }
917
918    @Override
919    public int getQualifierLength() {
920      return cell.getQualifierLength();
921    }
922
923    @Override
924    public long getTimestamp() {
925      return timestamp;
926    }
927
928    @Override
929    public byte getTypeByte() {
930      return cell.getTypeByte();
931    }
932
933    @Override
934    public Optional<Tag> getTag(byte type) {
935      if (cell instanceof RawCell) {
936        return ((RawCell) cell).getTag(type);
937      }
938      return PrivateCellUtil.getTag(cell, type);
939    }
940
941    @Override
942    public Iterator<Tag> getTags() {
943      if (cell instanceof RawCell) {
944        return ((RawCell) cell).getTags();
945      }
946      return PrivateCellUtil.tagsIterator(cell);
947    }
948
949    @Override
950    public byte[] cloneTags() {
951      if (cell instanceof RawCell) {
952        return ((RawCell) cell).cloneTags();
953      }
954      return PrivateCellUtil.cloneTags(cell);
955    }
956
957    private long heapOverhead() {
958      return FIXED_OVERHEAD
959        + ClassSize.ARRAY // row
960        + getFamilyLength() == 0 ? 0 : ClassSize.ARRAY
961        + getQualifierLength() == 0 ? 0 : ClassSize.ARRAY
962        + getValueLength() == 0 ? 0 : ClassSize.ARRAY
963        + getTagsLength() == 0 ? 0 : ClassSize.ARRAY;
964    }
965
966    @Override
967    public long heapSize() {
968      return heapOverhead()
969        + ClassSize.align(getRowLength())
970        + ClassSize.align(getFamilyLength())
971        + ClassSize.align(getQualifierLength())
972        + ClassSize.align(getValueLength())
973        + ClassSize.align(getTagsLength());
974    }
975  }
976}