/*
* Copyright © 2014-2015 Cask Data, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package co.cask.cdap.data2.dataset2.lib.table;
import co.cask.cdap.api.common.Bytes;
import co.cask.cdap.data2.dataset2.lib.table.inmemory.InMemoryTableService;
import com.google.common.base.Function;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
/**
* Utility class for working with {@link Update} instances.
*/
public final class Updates {
public static final Function<Long, Update> LONG_TO_PUTS = new Function<Long, Update>() {
@Override
public Update apply(Long input) {
return new PutValue(Bytes.toBytes(input));
}
};
/**
* Returns a new {@code NavigableMap} with the underlying updates represented as {@code byte[]}.
*
* <p><emphasis>Note:</emphasis> the map returned is <strong>not thread safe</strong>.</p>
*/
public static NavigableMap<byte[], NavigableMap<Long, byte[]>> rowToBytes(
NavigableMap<byte[], NavigableMap<Long, Update>> row) {
if (row == null) {
return null;
}
NavigableMap<byte[], NavigableMap<Long, byte[]>> returnMap =
new TreeMap<>(Bytes.BYTES_COMPARATOR);
// TODO: make this use a wrapper to represent the existing map as <Long, byte[]> instead of copying
for (Map.Entry<byte[], NavigableMap<Long, Update>> entry : row.entrySet()) {
for (Map.Entry<Long, Update> cellEntry : entry.getValue().entrySet()) {
NavigableMap<Long, byte[]> currentCell = returnMap.get(entry.getKey());
if (currentCell == null) {
currentCell = new TreeMap<>(InMemoryTableService.VERSIONED_VALUE_MAP_COMPARATOR);
returnMap.put(entry.getKey(), currentCell);
}
byte[] bytes = null;
if (cellEntry.getValue() != null) {
bytes = cellEntry.getValue().getBytes();
}
currentCell.put(cellEntry.getKey(), bytes);
}
}
return returnMap;
}
/**
* Merges together two Update instances:
* <ul>
* <li>Put a + Put b = Put b</li>
* <li>Put a + Increment b = new Put(a + b)</li>
* <li>Increment a + Put b = Put b</li>
* <li>Increment a + Increment b = new Increment(a + b)</li>
* </ul>
* @param base The currently stored or buffered update
* @param modifier The new update to combine
* @return A new update combining the base update and modifier, according to the rules above
*/
public static Update mergeUpdates(Update base, Update modifier) {
if (base == null || modifier instanceof PutValue) {
return modifier;
}
if (modifier instanceof IncrementValue) {
IncrementValue increment = (IncrementValue) modifier;
if (base instanceof PutValue) {
PutValue put = (PutValue) base;
byte[] putBytes = put.getBytes();
if (putBytes != null && putBytes.length != Bytes.SIZEOF_LONG) {
throw new NumberFormatException("Attempted to increment a value that is not convertible to long");
}
long newValue = (putBytes == null ? 0L : Bytes.toLong(putBytes)) + increment.getValue();
return new PutValue(Bytes.toBytes(newValue));
} else if (base instanceof IncrementValue) {
IncrementValue baseIncrement = (IncrementValue) base;
return new IncrementValue(baseIncrement.getValue() + increment.getValue());
}
}
// should not happen: modifier is neither Put nor Increment!
return base;
}
}