// Copyright 2017 JanusGraph Authors
//
// 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 org.janusgraph.diskstorage.cassandra.utils;
import java.nio.ByteBuffer;
import java.util.*;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterators;
import org.janusgraph.diskstorage.EntryList;
import org.janusgraph.diskstorage.StaticBuffer;
import org.janusgraph.diskstorage.Entry;
import org.janusgraph.diskstorage.keycolumnvalue.KeyRange;
import org.janusgraph.diskstorage.util.BufferUtil;
import org.janusgraph.diskstorage.util.StaticArrayBuffer;
import org.janusgraph.diskstorage.util.StaticArrayEntry;
import org.janusgraph.diskstorage.util.StaticArrayEntryList;
import org.apache.cassandra.dht.BytesToken;
import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;
import javax.annotation.Nullable;
public class CassandraHelper {
public static List<ByteBuffer> convert(List<StaticBuffer> keys) {
List<ByteBuffer> requestKeys = new ArrayList<ByteBuffer>(keys.size());
for (int i = 0; i < keys.size(); i++) {
requestKeys.add(keys.get(i).asByteBuffer());
}
return requestKeys;
}
/**
* Constructs an {@link EntryList} from the Iterable of entries while excluding the end slice
* (since the method contract states that the end slice is exclusive, yet Cassandra treats it as
* inclusive) and respecting the limit.
*
* @param entries
* @param getter
* @param lastColumn TODO: make this StaticBuffer so we can avoid the conversion and provide equals method
* @param limit
* @param <E>
* @return
*/
public static<E> EntryList makeEntryList(final Iterable<E> entries,
final StaticArrayEntry.GetColVal<E,ByteBuffer> getter,
final StaticBuffer lastColumn, final int limit) {
return StaticArrayEntryList.ofByteBuffer(new Iterable<E>() {
@Override
public Iterator<E> iterator() {
return Iterators.filter(entries.iterator(),new FilterResultColumns<E>(lastColumn,limit,getter));
}
},getter);
}
private static class FilterResultColumns<E> implements Predicate<E> {
private int count = 0;
private final int limit;
private final StaticBuffer lastColumn;
private final StaticArrayEntry.GetColVal<E,ByteBuffer> getter;
private FilterResultColumns(StaticBuffer lastColumn, int limit, StaticArrayEntry.GetColVal<E, ByteBuffer> getter) {
this.limit = limit;
this.lastColumn = lastColumn;
this.getter = getter;
}
@Override
public boolean apply(@Nullable E e) {
assert e!=null;
if (count>=limit || BufferUtil.equals(lastColumn, getter.getColumn(e))) return false;
count++;
return true;
}
}
public static<E> Iterator<Entry> makeEntryIterator(final Iterable<E> entries,
final StaticArrayEntry.GetColVal<E,ByteBuffer> getter,
final StaticBuffer lastColumn, final int limit) {
return Iterators.transform(Iterators.filter(entries.iterator(),
new FilterResultColumns<E>(lastColumn, limit, getter)), new Function<E, Entry>() {
@Nullable
@Override
public Entry apply(@Nullable E e) {
return StaticArrayEntry.ofByteBuffer(e,getter);
}
});
}
public static KeyRange transformRange(Range<Token> range) {
return transformRange(range.left, range.right);
}
public static KeyRange transformRange(Token leftKeyExclusive, Token rightKeyInclusive) {
if (!(leftKeyExclusive instanceof BytesToken))
throw new UnsupportedOperationException();
// if left part is BytesToken, right part should be too, otherwise there is no sense in the ring
assert rightKeyInclusive instanceof BytesToken;
// l is exclusive, r is inclusive
BytesToken l = (BytesToken) leftKeyExclusive;
BytesToken r = (BytesToken) rightKeyInclusive;
byte[] leftTokenValue = l.getTokenValue();
byte[] rightTokenValue = r.getTokenValue();
Preconditions.checkArgument(leftTokenValue.length == rightTokenValue.length, "Tokens have unequal length");
int tokenLength = leftTokenValue.length;
byte[][] tokens = new byte[][]{leftTokenValue, rightTokenValue};
byte[][] plusOne = new byte[2][tokenLength];
for (int j = 0; j < 2; j++) {
boolean carry = true;
for (int i = tokenLength - 1; i >= 0; i--) {
byte b = tokens[j][i];
if (carry) {
b++;
if (b == 0) carry = true;
else carry = false;
}
plusOne[j][i] = b;
}
}
StaticBuffer lb = StaticArrayBuffer.of(plusOne[0]);
StaticBuffer rb = StaticArrayBuffer.of(plusOne[1]);
Preconditions.checkArgument(lb.length() == tokenLength, lb.length());
Preconditions.checkArgument(rb.length() == tokenLength, rb.length());
return new KeyRange(lb, rb);
}
}