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 to check if the familyMap is empty
306   * @return true if empty, false otherwise
307   */
308  public boolean isEmpty() {
309    return familyMap.isEmpty();
310  }
311
312  /**
313   * Method for retrieving the delete's row
314   * @return row
315   */
316  @Override
317  public byte [] getRow() {
318    return this.row;
319  }
320
321  /**
322   * @deprecated As of release 2.0.0, this will be removed in HBase 3.0.0.
323   *             Use {@link Row#COMPARATOR} instead
324   */
325  @Deprecated
326  @Override
327  public int compareTo(final Row d) {
328    return Bytes.compareTo(this.getRow(), d.getRow());
329  }
330
331  /**
332   * Method for retrieving the timestamp.
333   *
334   * @return timestamp
335   */
336  public long getTimestamp() {
337    return this.ts;
338  }
339
340  /**
341   * Marks that the clusters with the given clusterIds have consumed the mutation
342   * @param clusterIds of the clusters that have consumed the mutation
343   */
344  public Mutation setClusterIds(List<UUID> clusterIds) {
345    ByteArrayDataOutput out = ByteStreams.newDataOutput();
346    out.writeInt(clusterIds.size());
347    for (UUID clusterId : clusterIds) {
348      out.writeLong(clusterId.getMostSignificantBits());
349      out.writeLong(clusterId.getLeastSignificantBits());
350    }
351    setAttribute(CONSUMED_CLUSTER_IDS, out.toByteArray());
352    return this;
353  }
354
355  /**
356   * @return the set of clusterIds that have consumed the mutation
357   */
358  public List<UUID> getClusterIds() {
359    List<UUID> clusterIds = new ArrayList<>();
360    byte[] bytes = getAttribute(CONSUMED_CLUSTER_IDS);
361    if(bytes != null) {
362      ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
363      int numClusters = in.readInt();
364      for(int i=0; i<numClusters; i++){
365        clusterIds.add(new UUID(in.readLong(), in.readLong()));
366      }
367    }
368    return clusterIds;
369  }
370
371  /**
372   * Sets the visibility expression associated with cells in this Mutation.
373   * @param expression
374   */
375  public Mutation setCellVisibility(CellVisibility expression) {
376    this.setAttribute(VisibilityConstants.VISIBILITY_LABELS_ATTR_KEY,
377        toCellVisibility(expression).toByteArray());
378    return this;
379  }
380
381  /**
382   * @return CellVisibility associated with cells in this Mutation.
383   * @throws DeserializationException
384   */
385  public CellVisibility getCellVisibility() throws DeserializationException {
386    byte[] cellVisibilityBytes = this.getAttribute(VisibilityConstants.VISIBILITY_LABELS_ATTR_KEY);
387    if (cellVisibilityBytes == null) return null;
388    return toCellVisibility(cellVisibilityBytes);
389  }
390
391  /**
392   * Create a protocol buffer CellVisibility based on a client CellVisibility.
393   *
394   * @param cellVisibility
395   * @return a protocol buffer CellVisibility
396   */
397  static ClientProtos.CellVisibility toCellVisibility(CellVisibility cellVisibility) {
398    ClientProtos.CellVisibility.Builder builder = ClientProtos.CellVisibility.newBuilder();
399    builder.setExpression(cellVisibility.getExpression());
400    return builder.build();
401  }
402
403  /**
404   * Convert a protocol buffer CellVisibility to a client CellVisibility
405   *
406   * @param proto
407   * @return the converted client CellVisibility
408   */
409  private static CellVisibility toCellVisibility(ClientProtos.CellVisibility proto) {
410    if (proto == null) return null;
411    return new CellVisibility(proto.getExpression());
412  }
413
414  /**
415   * Convert a protocol buffer CellVisibility bytes to a client CellVisibility
416   *
417   * @param protoBytes
418   * @return the converted client CellVisibility
419   * @throws DeserializationException
420   */
421  private static CellVisibility toCellVisibility(byte[] protoBytes) throws DeserializationException {
422    if (protoBytes == null) return null;
423    ClientProtos.CellVisibility.Builder builder = ClientProtos.CellVisibility.newBuilder();
424    ClientProtos.CellVisibility proto = null;
425    try {
426      ProtobufUtil.mergeFrom(builder, protoBytes);
427      proto = builder.build();
428    } catch (IOException e) {
429      throw new DeserializationException(e);
430    }
431    return toCellVisibility(proto);
432  }
433
434  /**
435   * Number of KeyValues carried by this Mutation.
436   * @return the total number of KeyValues
437   */
438  public int size() {
439    int size = 0;
440    for (List<Cell> cells : this.familyMap.values()) {
441      size += cells.size();
442    }
443    return size;
444  }
445
446  /**
447   * @return the number of different families
448   */
449  public int numFamilies() {
450    return familyMap.size();
451  }
452
453  /**
454   * @return Calculate what Mutation adds to class heap size.
455   */
456  @Override
457  public long heapSize() {
458    long heapsize = MUTATION_OVERHEAD;
459    // Adding row
460    heapsize += ClassSize.align(ClassSize.ARRAY + this.row.length);
461
462    // Adding map overhead
463    heapsize +=
464      ClassSize.align(this.familyMap.size() * ClassSize.MAP_ENTRY);
465    for(Map.Entry<byte [], List<Cell>> entry : this.familyMap.entrySet()) {
466      //Adding key overhead
467      heapsize +=
468        ClassSize.align(ClassSize.ARRAY + entry.getKey().length);
469
470      //This part is kinds tricky since the JVM can reuse references if you
471      //store the same value, but have a good match with SizeOf at the moment
472      //Adding value overhead
473      heapsize += ClassSize.align(ClassSize.ARRAYLIST);
474      int size = entry.getValue().size();
475      heapsize += ClassSize.align(ClassSize.ARRAY +
476          size * ClassSize.REFERENCE);
477
478      for (Cell cell : entry.getValue()) {
479        heapsize += cell.heapSize();
480      }
481    }
482    heapsize += getAttributeSize();
483    heapsize += extraHeapSize();
484    return ClassSize.align(heapsize);
485  }
486
487  /**
488   * @return The serialized ACL for this operation, or null if none
489   */
490  public byte[] getACL() {
491    return getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL);
492  }
493
494  /**
495   * @param user User short name
496   * @param perms Permissions for the user
497   */
498  public Mutation setACL(String user, Permission perms) {
499    setAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL,
500      AccessControlUtil.toUsersAndPermissions(user, perms).toByteArray());
501    return this;
502  }
503
504  /**
505   * @param perms A map of permissions for a user or users
506   */
507  public Mutation setACL(Map<String, Permission> perms) {
508    ListMultimap<String, Permission> permMap = ArrayListMultimap.create();
509    for (Map.Entry<String, Permission> entry : perms.entrySet()) {
510      permMap.put(entry.getKey(), entry.getValue());
511    }
512    setAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL,
513      AccessControlUtil.toUsersAndPermissions(permMap).toByteArray());
514    return this;
515  }
516
517  /**
518   * Return the TTL requested for the result of the mutation, in milliseconds.
519   * @return the TTL requested for the result of the mutation, in milliseconds,
520   * or Long.MAX_VALUE if unset
521   */
522  public long getTTL() {
523    byte[] ttlBytes = getAttribute(OP_ATTRIBUTE_TTL);
524    if (ttlBytes != null) {
525      return Bytes.toLong(ttlBytes);
526    }
527    return Long.MAX_VALUE;
528  }
529
530  /**
531   * Set the TTL desired for the result of the mutation, in milliseconds.
532   * @param ttl the TTL desired for the result of the mutation, in milliseconds
533   * @return this
534   */
535  public Mutation setTTL(long ttl) {
536    setAttribute(OP_ATTRIBUTE_TTL, Bytes.toBytes(ttl));
537    return this;
538  }
539
540  /**
541   * @return current value for returnResults
542   */
543  // Used by Increment and Append only.
544  @InterfaceAudience.Private
545  protected boolean isReturnResults() {
546    byte[] v = getAttribute(RETURN_RESULTS);
547    return v == null ? true : Bytes.toBoolean(v);
548  }
549
550  @InterfaceAudience.Private
551  // Used by Increment and Append only.
552  protected Mutation setReturnResults(boolean returnResults) {
553    setAttribute(RETURN_RESULTS, Bytes.toBytes(returnResults));
554    return this;
555  }
556
557  /**
558   * Subclasses should override this method to add the heap size of their own fields.
559   * @return the heap size to add (will be aligned).
560   */
561  protected long extraHeapSize(){
562    return 0L;
563  }
564
565  /**
566   * Set the timestamp of the delete.
567   */
568  public Mutation setTimestamp(long timestamp) {
569    if (timestamp < 0) {
570      throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + timestamp);
571    }
572    this.ts = timestamp;
573    return this;
574  }
575
576  /**
577   * A convenience method to determine if this object's familyMap contains
578   * a value assigned to the given family &amp; qualifier.
579   * Both given arguments must match the KeyValue object to return true.
580   *
581   * @param family column family
582   * @param qualifier column qualifier
583   * @return returns true if the given family and qualifier already has an
584   * existing KeyValue object in the family map.
585   */
586  public boolean has(byte [] family, byte [] qualifier) {
587    return has(family, qualifier, this.ts, HConstants.EMPTY_BYTE_ARRAY, true, true);
588  }
589
590  /**
591   * A convenience method to determine if this object's familyMap contains
592   * a value assigned to the given family, qualifier and timestamp.
593   * All 3 given arguments must match the KeyValue object to return true.
594   *
595   * @param family column family
596   * @param qualifier column qualifier
597   * @param ts timestamp
598   * @return returns true if the given family, qualifier and timestamp already has an
599   * existing KeyValue object in the family map.
600   */
601  public boolean has(byte [] family, byte [] qualifier, long ts) {
602    return has(family, qualifier, ts, HConstants.EMPTY_BYTE_ARRAY, false, true);
603  }
604
605  /**
606   * A convenience method to determine if this object's familyMap contains
607   * a value assigned to the given family, qualifier and timestamp.
608   * All 3 given arguments must match the KeyValue object to return true.
609   *
610   * @param family column family
611   * @param qualifier column qualifier
612   * @param value value to check
613   * @return returns true if the given family, qualifier and value already has an
614   * existing KeyValue object in the family map.
615   */
616  public boolean has(byte [] family, byte [] qualifier, byte [] value) {
617    return has(family, qualifier, this.ts, value, true, false);
618  }
619
620  /**
621   * A convenience method to determine if this object's familyMap contains
622   * the given value assigned to the given family, qualifier and timestamp.
623   * All 4 given arguments must match the KeyValue object to return true.
624   *
625   * @param family column family
626   * @param qualifier column qualifier
627   * @param ts timestamp
628   * @param value value to check
629   * @return returns true if the given family, qualifier timestamp and value
630   *   already has an existing KeyValue object in the family map.
631   */
632  public boolean has(byte [] family, byte [] qualifier, long ts, byte [] value) {
633    return has(family, qualifier, ts, value, false, false);
634  }
635
636  /**
637   * Returns a list of all KeyValue objects with matching column family and qualifier.
638   *
639   * @param family column family
640   * @param qualifier column qualifier
641   * @return a list of KeyValue objects with the matching family and qualifier,
642   *   returns an empty list if one doesn't exist for the given family.
643   */
644  public List<Cell> get(byte[] family, byte[] qualifier) {
645    List<Cell> filteredList = new ArrayList<>();
646    for (Cell cell: getCellList(family)) {
647      if (CellUtil.matchingQualifier(cell, qualifier)) {
648        filteredList.add(cell);
649      }
650    }
651    return filteredList;
652  }
653
654  /*
655   * Private method to determine if this object's familyMap contains
656   * the given value assigned to the given family, qualifier and timestamp
657   * respecting the 2 boolean arguments
658   *
659   * @param family
660   * @param qualifier
661   * @param ts
662   * @param value
663   * @param ignoreTS
664   * @param ignoreValue
665   * @return returns true if the given family, qualifier timestamp and value
666   * already has an existing KeyValue object in the family map.
667   */
668  protected boolean has(byte[] family, byte[] qualifier, long ts, byte[] value,
669      boolean ignoreTS, boolean ignoreValue) {
670    List<Cell> list = getCellList(family);
671    if (list.isEmpty()) {
672      return false;
673    }
674    // Boolean analysis of ignoreTS/ignoreValue.
675    // T T => 2
676    // T F => 3 (first is always true)
677    // F T => 2
678    // F F => 1
679    if (!ignoreTS && !ignoreValue) {
680      for (Cell cell : list) {
681        if (CellUtil.matchingFamily(cell, family) &&
682            CellUtil.matchingQualifier(cell, qualifier)  &&
683            CellUtil.matchingValue(cell, value) &&
684            cell.getTimestamp() == ts) {
685          return true;
686        }
687      }
688    } else if (ignoreValue && !ignoreTS) {
689      for (Cell cell : list) {
690        if (CellUtil.matchingFamily(cell, family) && CellUtil.matchingQualifier(cell, qualifier)
691            && cell.getTimestamp() == ts) {
692          return true;
693        }
694      }
695    } else if (!ignoreValue && ignoreTS) {
696      for (Cell cell : list) {
697        if (CellUtil.matchingFamily(cell, family) && CellUtil.matchingQualifier(cell, qualifier)
698            && CellUtil.matchingValue(cell, value)) {
699          return true;
700        }
701      }
702    } else {
703      for (Cell cell : list) {
704        if (CellUtil.matchingFamily(cell, family) &&
705            CellUtil.matchingQualifier(cell, qualifier)) {
706          return true;
707        }
708      }
709    }
710    return false;
711  }
712
713  /**
714   * @param row Row to check
715   * @throws IllegalArgumentException Thrown if <code>row</code> is empty or null or
716   * &gt; {@link HConstants#MAX_ROW_LENGTH}
717   * @return <code>row</code>
718   */
719  static byte [] checkRow(final byte [] row) {
720    return checkRow(row, 0, row == null? 0: row.length);
721  }
722
723  /**
724   * @param row Row to check
725   * @param offset
726   * @param length
727   * @throws IllegalArgumentException Thrown if <code>row</code> is empty or null or
728   * &gt; {@link HConstants#MAX_ROW_LENGTH}
729   * @return <code>row</code>
730   */
731  static byte [] checkRow(final byte [] row, final int offset, final int length) {
732    if (row == null) {
733      throw new IllegalArgumentException("Row buffer is null");
734    }
735    if (length == 0) {
736      throw new IllegalArgumentException("Row length is 0");
737    }
738    if (length > HConstants.MAX_ROW_LENGTH) {
739      throw new IllegalArgumentException("Row length " + length + " is > " +
740        HConstants.MAX_ROW_LENGTH);
741    }
742    return row;
743  }
744
745  static void checkRow(ByteBuffer row) {
746    if (row == null) {
747      throw new IllegalArgumentException("Row buffer is null");
748    }
749    if (row.remaining() == 0) {
750      throw new IllegalArgumentException("Row length is 0");
751    }
752    if (row.remaining() > HConstants.MAX_ROW_LENGTH) {
753      throw new IllegalArgumentException("Row length " + row.remaining() + " is > " +
754          HConstants.MAX_ROW_LENGTH);
755    }
756  }
757
758  Mutation add(Cell cell) throws IOException {
759    //Checking that the row of the kv is the same as the mutation
760    // TODO: It is fraught with risk if user pass the wrong row.
761    // Throwing the IllegalArgumentException is more suitable I'd say.
762    if (!CellUtil.matchingRows(cell, this.row)) {
763      throw new WrongRowIOException("The row in " + cell.toString() +
764        " doesn't match the original one " +  Bytes.toStringBinary(this.row));
765    }
766
767    byte[] family;
768
769    if (cell instanceof IndividualBytesFieldCell) {
770      family = cell.getFamilyArray();
771    } else {
772      family = CellUtil.cloneFamily(cell);
773    }
774
775    if (family == null || family.length == 0) {
776      throw new IllegalArgumentException("Family cannot be null");
777    }
778
779    if (cell instanceof ExtendedCell) {
780      getCellList(family).add(cell);
781    } else {
782      getCellList(family).add(new CellWrapper(cell));
783    }
784    return this;
785  }
786
787  private static final class CellWrapper implements ExtendedCell {
788    private static final long FIXED_OVERHEAD = ClassSize.align(
789      ClassSize.OBJECT              // object header
790        + KeyValue.TIMESTAMP_SIZE       // timestamp
791        + Bytes.SIZEOF_LONG             // sequence id
792        + 1 * ClassSize.REFERENCE);     // references to cell
793    private final Cell cell;
794    private long sequenceId;
795    private long timestamp;
796
797    CellWrapper(Cell cell) {
798      assert !(cell instanceof ExtendedCell);
799      this.cell = cell;
800      this.sequenceId = cell.getSequenceId();
801      this.timestamp = cell.getTimestamp();
802    }
803
804    @Override
805    public void setSequenceId(long seqId) {
806      sequenceId = seqId;
807    }
808
809    @Override
810    public void setTimestamp(long ts) {
811      timestamp = ts;
812    }
813
814    @Override
815    public void setTimestamp(byte[] ts) {
816      timestamp = Bytes.toLong(ts);
817    }
818
819    @Override
820    public long getSequenceId() {
821      return sequenceId;
822    }
823
824    @Override
825    public byte[] getValueArray() {
826      return cell.getValueArray();
827    }
828
829    @Override
830    public int getValueOffset() {
831      return cell.getValueOffset();
832    }
833
834    @Override
835    public int getValueLength() {
836      return cell.getValueLength();
837    }
838
839    @Override
840    public byte[] getTagsArray() {
841      return cell.getTagsArray();
842    }
843
844    @Override
845    public int getTagsOffset() {
846      return cell.getTagsOffset();
847    }
848
849    @Override
850    public int getTagsLength() {
851      return cell.getTagsLength();
852    }
853
854    @Override
855    public byte[] getRowArray() {
856      return cell.getRowArray();
857    }
858
859    @Override
860    public int getRowOffset() {
861      return cell.getRowOffset();
862    }
863
864    @Override
865    public short getRowLength() {
866      return cell.getRowLength();
867    }
868
869    @Override
870    public byte[] getFamilyArray() {
871      return cell.getFamilyArray();
872    }
873
874    @Override
875    public int getFamilyOffset() {
876      return cell.getFamilyOffset();
877    }
878
879    @Override
880    public byte getFamilyLength() {
881      return cell.getFamilyLength();
882    }
883
884    @Override
885    public byte[] getQualifierArray() {
886      return cell.getQualifierArray();
887    }
888
889    @Override
890    public int getQualifierOffset() {
891      return cell.getQualifierOffset();
892    }
893
894    @Override
895    public int getQualifierLength() {
896      return cell.getQualifierLength();
897    }
898
899    @Override
900    public long getTimestamp() {
901      return timestamp;
902    }
903
904    @Override
905    public byte getTypeByte() {
906      return cell.getTypeByte();
907    }
908
909    @Override
910    public Optional<Tag> getTag(byte type) {
911      if (cell instanceof RawCell) {
912        return ((RawCell) cell).getTag(type);
913      }
914      return PrivateCellUtil.getTag(cell, type);
915    }
916
917    @Override
918    public Iterator<Tag> getTags() {
919      if (cell instanceof RawCell) {
920        return ((RawCell) cell).getTags();
921      }
922      return PrivateCellUtil.tagsIterator(cell);
923    }
924
925    @Override
926    public byte[] cloneTags() {
927      if (cell instanceof RawCell) {
928        return ((RawCell) cell).cloneTags();
929      }
930      return PrivateCellUtil.cloneTags(cell);
931    }
932
933    private long heapOverhead() {
934      return FIXED_OVERHEAD
935        + ClassSize.ARRAY // row
936        + getFamilyLength() == 0 ? 0 : ClassSize.ARRAY
937        + getQualifierLength() == 0 ? 0 : ClassSize.ARRAY
938        + getValueLength() == 0 ? 0 : ClassSize.ARRAY
939        + getTagsLength() == 0 ? 0 : ClassSize.ARRAY;
940    }
941
942    @Override
943    public long heapSize() {
944      return heapOverhead()
945        + ClassSize.align(getRowLength())
946        + ClassSize.align(getFamilyLength())
947        + ClassSize.align(getQualifierLength())
948        + ClassSize.align(getValueLength())
949        + ClassSize.align(getTagsLength());
950    }
951  }
952}