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