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