/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.accumulo.core.data;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.apache.accumulo.core.data.thrift.TRange;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.WritableComparable;
/**
* This class is used to specify a range of Accumulo keys.
*
* @see Key
*/
public class Range implements WritableComparable<Range> {
private Key start;
private Key stop;
private boolean startKeyInclusive;
private boolean stopKeyInclusive;
private boolean infiniteStartKey;
private boolean infiniteStopKey;
/**
* Creates a range that goes from negative to positive infinity
*/
public Range() {
this((Key) null, true, (Key) null, true);
}
/**
* Creates a range from startKey inclusive to endKey inclusive.
*
* @param startKey
* starting key; set to null for negative infinity
* @param endKey
* ending key; set to null for positive infinity
* @throws IllegalArgumentException
* if end key is before start key
*/
public Range(Key startKey, Key endKey) {
this(startKey, true, endKey, true);
}
/**
* Creates a range that covers an entire row.
*
* @param row
* row to cover; set to null to cover all rows
*/
public Range(CharSequence row) {
this(row, true, row, true);
}
/**
* Creates a range that covers an entire row.
*
* @param row
* row to cover; set to null to cover all rows
*/
public Range(Text row) {
this(row, true, row, true);
}
/**
* Creates a range from startRow inclusive to endRow inclusive.
*
* @param startRow
* starting row; set to null for negative infinity
* @param endRow
* ending row; set to null for positive infinity
* @throws IllegalArgumentException
* if end row is before start row
*/
public Range(Text startRow, Text endRow) {
this(startRow, true, endRow, true);
}
/**
* Creates a range from startRow inclusive to endRow inclusive.
*
* @param startRow
* starting row; set to null for negative infinity
* @param endRow
* ending row; set to null for positive infinity
* @throws IllegalArgumentException
* if end row is before start row
*/
public Range(CharSequence startRow, CharSequence endRow) {
this(startRow, true, endRow, true);
}
/**
* Creates a range from startRow to endRow.
*
* @param startRow
* starting row; set to null for negative infinity
* @param startRowInclusive
* true to include start row, false to skip
* @param endRow
* ending row; set to null for positive infinity
* @param endRowInclusive
* true to include start row, false to skip
* @throws IllegalArgumentException
* if end row is before start row
*/
public Range(Text startRow, boolean startRowInclusive, Text endRow, boolean endRowInclusive) {
this((startRow == null ? null : (startRowInclusive ? new Key(startRow) : new Key(startRow).followingKey(PartialKey.ROW))), true, (endRow == null ? null
: (endRowInclusive ? new Key(endRow).followingKey(PartialKey.ROW) : new Key(endRow))), false);
}
/**
* Creates a range from startRow to endRow.
*
* @param startRow
* starting row; set to null for negative infinity
* @param startRowInclusive
* true to include start row, false to skip
* @param endRow
* ending row; set to null for positive infinity
* @param endRowInclusive
* true to include start row, false to skip
* @throws IllegalArgumentException
* if end row is before start row
*/
public Range(CharSequence startRow, boolean startRowInclusive, CharSequence endRow, boolean endRowInclusive) {
this(startRow == null ? null : new Text(startRow.toString()), startRowInclusive, endRow == null ? null : new Text(endRow.toString()), endRowInclusive);
}
/**
* Creates a range from startKey to endKey.
*
* @param startKey
* starting key; set to null for negative infinity
* @param startKeyInclusive
* true to include start key, false to skip
* @param endKey
* ending key; set to null for positive infinity
* @param endKeyInclusive
* true to include start key, false to skip
* @throws IllegalArgumentException
* if end key is before start key
*/
public Range(Key startKey, boolean startKeyInclusive, Key endKey, boolean endKeyInclusive) {
this.start = startKey;
this.startKeyInclusive = startKeyInclusive;
this.infiniteStartKey = startKey == null;
this.stop = endKey;
this.stopKeyInclusive = endKeyInclusive;
this.infiniteStopKey = stop == null;
if (!infiniteStartKey && !infiniteStopKey && beforeStartKey(endKey)) {
throw new IllegalArgumentException("Start key must be less than end key in range (" + startKey + ", " + endKey + ")");
}
}
/**
* Copies a range.
*
* @param range
* range to copy
*/
public Range(Range range) {
this(range.start, range.startKeyInclusive, range.infiniteStartKey, range.stop, range.stopKeyInclusive, range.infiniteStopKey);
}
/**
* Creates a range from start to stop.
*
* @param start
* set this to null when negative infinity is needed
* @param stop
* set this to null when infinity is needed
* @param startKeyInclusive
* determines if the ranges includes the start key
* @param stopKeyInclusive
* determines if the range includes the end key
* @param infiniteStartKey
* true if start key is negative infinity (null)
* @param infiniteStopKey
* true if stop key is positive infinity (null)
* @throws IllegalArgumentException
* if stop is before start, or infiniteStartKey is true but start is not null, or infiniteStopKey is true but stop is not null
*/
public Range(Key start, Key stop, boolean startKeyInclusive, boolean stopKeyInclusive, boolean infiniteStartKey, boolean infiniteStopKey) {
this(start, startKeyInclusive, infiniteStartKey, stop, stopKeyInclusive, infiniteStopKey);
if (!infiniteStartKey && !infiniteStopKey && beforeStartKey(stop)) {
throw new IllegalArgumentException("Start key must be less than end key in range (" + start + ", " + stop + ")");
}
}
/**
* Creates a range from start to stop. Unlike the public six-argument method, this one does not assure that stop is after start, which helps performance in
* cases where that assurance is already in place.
*
* @param start
* set this to null when negative infinity is needed
* @param startKeyInclusive
* determines if the ranges includes the start key
* @param infiniteStartKey
* true if start key is negative infinity (null)
* @param stop
* set this to null when infinity is needed
* @param stopKeyInclusive
* determines if the range includes the end key
* @param infiniteStopKey
* true if stop key is positive infinity (null)
* @throws IllegalArgumentException
* if infiniteStartKey is true but start is not null, or infiniteStopKey is true but stop is not null
*/
protected Range(Key start, boolean startKeyInclusive, boolean infiniteStartKey, Key stop, boolean stopKeyInclusive, boolean infiniteStopKey) {
if (infiniteStartKey && start != null)
throw new IllegalArgumentException();
if (infiniteStopKey && stop != null)
throw new IllegalArgumentException();
this.start = start;
this.stop = stop;
this.startKeyInclusive = startKeyInclusive;
this.stopKeyInclusive = stopKeyInclusive;
this.infiniteStartKey = infiniteStartKey;
this.infiniteStopKey = infiniteStopKey;
}
/**
* Creates a range from a Thrift range.
*
* @param trange
* Thrift range
*/
public Range(TRange trange) {
this(trange.start == null ? null : new Key(trange.start), trange.startKeyInclusive, trange.infiniteStartKey, trange.stop == null ? null : new Key(
trange.stop), trange.stopKeyInclusive, trange.infiniteStopKey);
if (!infiniteStartKey && !infiniteStopKey && beforeStartKey(stop)) {
throw new IllegalArgumentException("Start key must be less than end key in range (" + start + ", " + stop + ")");
}
}
/**
* Gets the start key, or null if the start is negative infinity.
*
* @return start key
*/
public Key getStartKey() {
if (infiniteStartKey) {
return null;
}
return start;
}
/**
* Determines if the given key is before the start key of this range.
*
* @param key
* key to check
* @return true if the given key is before the range, otherwise false
*/
public boolean beforeStartKey(Key key) {
if (infiniteStartKey) {
return false;
}
if (startKeyInclusive)
return key.compareTo(start) < 0;
return key.compareTo(start) <= 0;
}
/**
* Gets the ending key, or null if the end is positive infinity.
*
* @return ending key
*/
public Key getEndKey() {
if (infiniteStopKey) {
return null;
}
return stop;
}
/**
* Determines if the given key is after the ending key of this range.
*
* @param key
* key to check
* @return true if the given key is after the range, otherwise false
*/
public boolean afterEndKey(Key key) {
if (infiniteStopKey)
return false;
if (stopKeyInclusive)
return stop.compareTo(key) < 0;
return stop.compareTo(key) <= 0;
}
@Override
public int hashCode() {
int startHash = infiniteStartKey ? 0 : start.hashCode() + (startKeyInclusive ? 1 : 0);
int stopHash = infiniteStopKey ? 0 : stop.hashCode() + (stopKeyInclusive ? 1 : 0);
return startHash + stopHash;
}
@Override
public boolean equals(Object o) {
if (o instanceof Range)
return equals((Range) o);
return false;
}
/**
* Determines if this range equals another.
*
* @param otherRange
* range to compare
* @return true if ranges are equals, false otherwise
* @see #compareTo(Range)
*/
public boolean equals(Range otherRange) {
return compareTo(otherRange) == 0;
}
/**
* Compares this range to another range. Compares in order: start key, inclusiveness of start key, end key, inclusiveness of end key. Infinite keys sort
* first, and non-infinite keys are compared with {@link Key#compareTo(Key)}. Inclusive sorts before non-inclusive.
*
* @param o
* range to compare
* @return comparison result
*/
@Override
public int compareTo(Range o) {
int comp;
if (infiniteStartKey)
if (o.infiniteStartKey)
comp = 0;
else
comp = -1;
else if (o.infiniteStartKey)
comp = 1;
else {
comp = start.compareTo(o.start);
if (comp == 0)
if (startKeyInclusive && !o.startKeyInclusive)
comp = -1;
else if (!startKeyInclusive && o.startKeyInclusive)
comp = 1;
}
if (comp == 0)
if (infiniteStopKey)
if (o.infiniteStopKey)
comp = 0;
else
comp = 1;
else if (o.infiniteStopKey)
comp = -1;
else {
comp = stop.compareTo(o.stop);
if (comp == 0)
if (stopKeyInclusive && !o.stopKeyInclusive)
comp = 1;
else if (!stopKeyInclusive && o.stopKeyInclusive)
comp = -1;
}
return comp;
}
/**
* Determines if the given key falls within this range.
*
* @param key
* key to consider
* @return true if the given key falls within the range, false otherwise
*/
public boolean contains(Key key) {
return !beforeStartKey(key) && !afterEndKey(key);
}
/**
* Merges overlapping and adjacent ranges. For example given the following input:
*
* <pre>
* [a,c], (c, d], (g,m), (j,t]
* </pre>
*
* the following ranges would be returned:
*
* <pre>
* [a,d], (g,t]
* </pre>
*
* @param ranges
* to merge
* @return list of merged ranges
*/
public static List<Range> mergeOverlapping(Collection<Range> ranges) {
if (ranges.size() == 0)
return Collections.emptyList();
if (ranges.size() == 1)
return Collections.singletonList(ranges.iterator().next());
List<Range> ral = new ArrayList<>(ranges);
Collections.sort(ral);
ArrayList<Range> ret = new ArrayList<>(ranges.size());
Range currentRange = ral.get(0);
boolean currentStartKeyInclusive = ral.get(0).startKeyInclusive;
for (int i = 1; i < ral.size(); i++) {
// because of inclusive switch, equal keys may not be seen
if (currentRange.infiniteStopKey) {
// this range has the minimal start key and
// an infinite end key so it will contain all
// other ranges
break;
}
Range range = ral.get(i);
boolean startKeysEqual;
if (range.infiniteStartKey) {
// previous start key must be infinite because it is sorted
assert currentRange.infiniteStartKey;
startKeysEqual = true;
} else if (currentRange.infiniteStartKey) {
startKeysEqual = false;
} else if (currentRange.start.equals(range.start)) {
startKeysEqual = true;
} else {
startKeysEqual = false;
}
if (startKeysEqual || currentRange.contains(range.start)
|| (!currentRange.stopKeyInclusive && range.startKeyInclusive && range.start.equals(currentRange.stop))) {
int cmp;
if (range.infiniteStopKey || (cmp = range.stop.compareTo(currentRange.stop)) > 0 || (cmp == 0 && range.stopKeyInclusive)) {
currentRange = new Range(currentRange.getStartKey(), currentStartKeyInclusive, range.getEndKey(), range.stopKeyInclusive);
} /* else currentRange contains ral.get(i) */
} else {
ret.add(currentRange);
currentRange = range;
currentStartKeyInclusive = range.startKeyInclusive;
}
}
ret.add(currentRange);
return ret;
}
/**
* Creates a range which represents the intersection of this range and the passed in range. The following example will print true.
*
* <pre>
* Range range1 = new Range("a", "f");
* Range range2 = new Range("c", "n");
* Range range3 = range1.clip(range2);
* System.out.println(range3.equals(new Range("c", "f")));
* </pre>
*
* @param range
* range to clip to
* @return the intersection of this range and the given range
* @throws IllegalArgumentException
* if ranges does not overlap
*/
public Range clip(Range range) {
return clip(range, false);
}
/**
* Creates a range which represents the intersection of this range and the passed in range. Unlike {@link #clip(Range)}, this method can optionally return
* null if the ranges do not overlap, instead of throwing an exception. The returnNullIfDisjoint parameter controls this behavior.
*
* @param range
* range to clip to
* @param returnNullIfDisjoint
* true to return null if ranges are disjoint, false to throw an exception
* @return the intersection of this range and the given range, or null if ranges do not overlap and returnNullIfDisjoint is true
* @throws IllegalArgumentException
* if ranges does not overlap and returnNullIfDisjoint is false
* @see Range#clip(Range)
*/
public Range clip(Range range, boolean returnNullIfDisjoint) {
Key sk = range.getStartKey();
boolean ski = range.isStartKeyInclusive();
Key ek = range.getEndKey();
boolean eki = range.isEndKeyInclusive();
if (range.getStartKey() == null) {
if (getStartKey() != null) {
sk = getStartKey();
ski = isStartKeyInclusive();
}
} else if (afterEndKey(range.getStartKey())
|| (getEndKey() != null && range.getStartKey().equals(getEndKey()) && !(range.isStartKeyInclusive() && isEndKeyInclusive()))) {
if (returnNullIfDisjoint)
return null;
throw new IllegalArgumentException("Range " + range + " does not overlap " + this);
} else if (beforeStartKey(range.getStartKey())) {
sk = getStartKey();
ski = isStartKeyInclusive();
}
if (range.getEndKey() == null) {
if (getEndKey() != null) {
ek = getEndKey();
eki = isEndKeyInclusive();
}
} else if (beforeStartKey(range.getEndKey())
|| (getStartKey() != null && range.getEndKey().equals(getStartKey()) && !(range.isEndKeyInclusive() && isStartKeyInclusive()))) {
if (returnNullIfDisjoint)
return null;
throw new IllegalArgumentException("Range " + range + " does not overlap " + this);
} else if (afterEndKey(range.getEndKey())) {
ek = getEndKey();
eki = isEndKeyInclusive();
}
return new Range(sk, ski, ek, eki);
}
/**
* Creates a new range that is bounded by the columns passed in. The start key in the returned range will have a column >= to the minimum column. The end
* key in the returned range will have a column <= the max column.
*
* @param min
* minimum column
* @param max
* maximum column
* @return a column bounded range
* @throws IllegalArgumentException
* if the minimum column compares greater than the maximum column
*/
public Range bound(Column min, Column max) {
if (min.compareTo(max) > 0) {
throw new IllegalArgumentException("min column > max column " + min + " " + max);
}
Key sk = getStartKey();
boolean ski = isStartKeyInclusive();
if (sk != null) {
ByteSequence cf = sk.getColumnFamilyData();
ByteSequence cq = sk.getColumnQualifierData();
ByteSequence mincf = new ArrayByteSequence(min.columnFamily);
ByteSequence mincq;
if (min.columnQualifier != null)
mincq = new ArrayByteSequence(min.columnQualifier);
else
mincq = new ArrayByteSequence(new byte[0]);
int cmp = cf.compareTo(mincf);
if (cmp < 0 || (cmp == 0 && cq.compareTo(mincq) < 0)) {
ski = true;
sk = new Key(sk.getRowData().toArray(), mincf.toArray(), mincq.toArray(), new byte[0], Long.MAX_VALUE, true);
}
}
Key ek = getEndKey();
boolean eki = isEndKeyInclusive();
if (ek != null) {
ByteSequence row = ek.getRowData();
ByteSequence cf = ek.getColumnFamilyData();
ByteSequence cq = ek.getColumnQualifierData();
ByteSequence cv = ek.getColumnVisibilityData();
ByteSequence maxcf = new ArrayByteSequence(max.columnFamily);
ByteSequence maxcq = null;
if (max.columnQualifier != null)
maxcq = new ArrayByteSequence(max.columnQualifier);
boolean set = false;
int comp = cf.compareTo(maxcf);
if (comp > 0) {
set = true;
} else if (comp == 0 && maxcq != null && cq.compareTo(maxcq) > 0) {
set = true;
} else if (!eki && row.length() > 0 && row.byteAt(row.length() - 1) == 0 && cf.length() == 0 && cq.length() == 0 && cv.length() == 0
&& ek.getTimestamp() == Long.MAX_VALUE) {
row = row.subSequence(0, row.length() - 1);
set = true;
}
if (set) {
eki = false;
if (maxcq == null)
ek = new Key(row.toArray(), maxcf.toArray(), new byte[0], new byte[0], 0, false).followingKey(PartialKey.ROW_COLFAM);
else
ek = new Key(row.toArray(), maxcf.toArray(), maxcq.toArray(), new byte[0], 0, false).followingKey(PartialKey.ROW_COLFAM_COLQUAL);
}
}
return new Range(sk, ski, ek, eki);
}
@Override
public String toString() {
return ((startKeyInclusive && start != null) ? "[" : "(") + (start == null ? "-inf" : start) + "," + (stop == null ? "+inf" : stop)
+ ((stopKeyInclusive && stop != null) ? "]" : ")");
}
@Override
public void readFields(DataInput in) throws IOException {
infiniteStartKey = in.readBoolean();
infiniteStopKey = in.readBoolean();
if (!infiniteStartKey) {
start = new Key();
start.readFields(in);
} else {
start = null;
}
if (!infiniteStopKey) {
stop = new Key();
stop.readFields(in);
} else {
stop = null;
}
startKeyInclusive = in.readBoolean();
stopKeyInclusive = in.readBoolean();
if (!infiniteStartKey && !infiniteStopKey && beforeStartKey(stop)) {
throw new InvalidObjectException("Start key must be less than end key in range (" + start + ", " + stop + ")");
}
}
@Override
public void write(DataOutput out) throws IOException {
out.writeBoolean(infiniteStartKey);
out.writeBoolean(infiniteStopKey);
if (!infiniteStartKey)
start.write(out);
if (!infiniteStopKey)
stop.write(out);
out.writeBoolean(startKeyInclusive);
out.writeBoolean(stopKeyInclusive);
}
/**
* Gets whether the start key of this range is inclusive.
*
* @return true if start key is inclusive
*/
public boolean isStartKeyInclusive() {
return startKeyInclusive;
}
/**
* Gets whether the end key of this range is inclusive.
*
* @return true if end key is inclusive
*/
public boolean isEndKeyInclusive() {
return stopKeyInclusive;
}
/**
* Converts this range to Thrift.
*
* @return Thrift range
*/
public TRange toThrift() {
return new TRange(start == null ? null : start.toThrift(), stop == null ? null : stop.toThrift(), startKeyInclusive, stopKeyInclusive, infiniteStartKey,
infiniteStopKey);
}
/**
* Gets whether the start key is negative infinity.
*
* @return true if start key is negative infinity
*/
public boolean isInfiniteStartKey() {
return infiniteStartKey;
}
/**
* Gets whether the end key is positive infinity.
*
* @return true if end key is positive infinity
*/
public boolean isInfiniteStopKey() {
return infiniteStopKey;
}
/**
* Creates a range that covers an exact row. Returns the same Range as {@link #Range(Text)}.
*
* @param row
* row to cover; set to null to cover all rows
*/
public static Range exact(Text row) {
return new Range(row);
}
/**
* Creates a range that covers an exact row and column family.
*
* @param row
* row row to cover
* @param cf
* column family to cover
*/
public static Range exact(Text row, Text cf) {
Key startKey = new Key(row, cf);
return new Range(startKey, true, startKey.followingKey(PartialKey.ROW_COLFAM), false);
}
/**
* Creates a range that covers an exact row, column family, and column qualifier.
*
* @param row
* row row to cover
* @param cf
* column family to cover
* @param cq
* column qualifier to cover
*/
public static Range exact(Text row, Text cf, Text cq) {
Key startKey = new Key(row, cf, cq);
return new Range(startKey, true, startKey.followingKey(PartialKey.ROW_COLFAM_COLQUAL), false);
}
/**
* Creates a range that covers an exact row, column family, column qualifier, and column visibility.
*
* @param row
* row row to cover
* @param cf
* column family to cover
* @param cq
* column qualifier to cover
* @param cv
* column visibility to cover
*/
public static Range exact(Text row, Text cf, Text cq, Text cv) {
Key startKey = new Key(row, cf, cq, cv);
return new Range(startKey, true, startKey.followingKey(PartialKey.ROW_COLFAM_COLQUAL_COLVIS), false);
}
/**
* Creates a range that covers an exact row, column family, column qualifier, column visibility, and timestamp.
*
* @param row
* row row to cover
* @param cf
* column family to cover
* @param cq
* column qualifier to cover
* @param cv
* column visibility to cover
* @param ts
* timestamp to cover
*/
public static Range exact(Text row, Text cf, Text cq, Text cv, long ts) {
Key startKey = new Key(row, cf, cq, cv, ts);
return new Range(startKey, true, startKey.followingKey(PartialKey.ROW_COLFAM_COLQUAL_COLVIS_TIME), false);
}
/**
* Returns a Text that sorts just after all Texts beginning with a prefix.
*
* @param prefix
* to follow
* @return prefix that immediately follows the given prefix when sorted, or null if no prefix can follow (i.e., the string is all 0xff bytes)
*/
public static Text followingPrefix(Text prefix) {
byte[] prefixBytes = prefix.getBytes();
// find the last byte in the array that is not 0xff
int changeIndex = prefix.getLength() - 1;
while (changeIndex >= 0 && prefixBytes[changeIndex] == (byte) 0xff)
changeIndex--;
if (changeIndex < 0)
return null;
// copy prefix bytes into new array
byte[] newBytes = new byte[changeIndex + 1];
System.arraycopy(prefixBytes, 0, newBytes, 0, changeIndex + 1);
// increment the selected byte
newBytes[changeIndex]++;
return new Text(newBytes);
}
/**
* Returns a Range that covers all rows beginning with a prefix.
*
* @param rowPrefix
* prefix of rows to cover
*/
public static Range prefix(Text rowPrefix) {
Text fp = followingPrefix(rowPrefix);
return new Range(new Key(rowPrefix), true, fp == null ? null : new Key(fp), false);
}
/**
* Returns a Range that covers all column families beginning with a prefix within a given row.
*
* @param row
* row to cover
* @param cfPrefix
* prefix of column families to cover
*/
public static Range prefix(Text row, Text cfPrefix) {
Text fp = followingPrefix(cfPrefix);
return new Range(new Key(row, cfPrefix), true, fp == null ? new Key(row).followingKey(PartialKey.ROW) : new Key(row, fp), false);
}
/**
* Returns a Range that covers all column qualifiers beginning with a prefix within a given row and column family.
*
* @param row
* row to cover
* @param cf
* column family to cover
* @param cqPrefix
* prefix of column qualifiers to cover
*/
public static Range prefix(Text row, Text cf, Text cqPrefix) {
Text fp = followingPrefix(cqPrefix);
return new Range(new Key(row, cf, cqPrefix), true, fp == null ? new Key(row, cf).followingKey(PartialKey.ROW_COLFAM) : new Key(row, cf, fp), false);
}
/**
* Returns a Range that covers all column visibilities beginning with a prefix within a given row, column family, and column qualifier.
*
* @param row
* row to cover
* @param cf
* column family to cover
* @param cq
* column qualifier to cover
* @param cvPrefix
* prefix of column visibilities to cover
*/
public static Range prefix(Text row, Text cf, Text cq, Text cvPrefix) {
Text fp = followingPrefix(cvPrefix);
return new Range(new Key(row, cf, cq, cvPrefix), true, fp == null ? new Key(row, cf, cq).followingKey(PartialKey.ROW_COLFAM_COLQUAL) : new Key(row, cf, cq,
fp), false);
}
/**
* Creates a range that covers an exact row.
*
* @param row
* row to cover; set to null to cover all rows
* @see #exact(Text)
*/
public static Range exact(CharSequence row) {
return Range.exact(new Text(row.toString()));
}
/**
* Creates a range that covers an exact row and column family.
*
* @param row
* row row to cover
* @param cf
* column family to cover
* @see #exact(Text, Text)
*/
public static Range exact(CharSequence row, CharSequence cf) {
return Range.exact(new Text(row.toString()), new Text(cf.toString()));
}
/**
* Creates a range that covers an exact row, column family, and column qualifier.
*
* @param row
* row row to cover
* @param cf
* column family to cover
* @param cq
* column qualifier to cover
* @see #exact(Text, Text, Text)
*/
public static Range exact(CharSequence row, CharSequence cf, CharSequence cq) {
return Range.exact(new Text(row.toString()), new Text(cf.toString()), new Text(cq.toString()));
}
/**
* Creates a range that covers an exact row, column family, column qualifier, and column visibility.
*
* @param row
* row row to cover
* @param cf
* column family to cover
* @param cq
* column qualifier to cover
* @param cv
* column visibility to cover
* @see #exact(Text, Text, Text, Text)
*/
public static Range exact(CharSequence row, CharSequence cf, CharSequence cq, CharSequence cv) {
return Range.exact(new Text(row.toString()), new Text(cf.toString()), new Text(cq.toString()), new Text(cv.toString()));
}
/**
* Creates a range that covers an exact row, column family, column qualifier, column visibility, and timestamp.
*
* @param row
* row row to cover
* @param cf
* column family to cover
* @param cq
* column qualifier to cover
* @param cv
* column visibility to cover
* @param ts
* timestamp to cover
* @see #exact(Text, Text, Text, Text, long)
*/
public static Range exact(CharSequence row, CharSequence cf, CharSequence cq, CharSequence cv, long ts) {
return Range.exact(new Text(row.toString()), new Text(cf.toString()), new Text(cq.toString()), new Text(cv.toString()), ts);
}
/**
* Returns a Range that covers all rows beginning with a prefix.
*
* @param rowPrefix
* prefix of rows to cover
* @see #prefix(Text)
*/
public static Range prefix(CharSequence rowPrefix) {
return Range.prefix(new Text(rowPrefix.toString()));
}
/**
* Returns a Range that covers all column families beginning with a prefix within a given row.
*
* @param row
* row to cover
* @param cfPrefix
* prefix of column families to cover
* @see #prefix(Text, Text)
*/
public static Range prefix(CharSequence row, CharSequence cfPrefix) {
return Range.prefix(new Text(row.toString()), new Text(cfPrefix.toString()));
}
/**
* Returns a Range that covers all column qualifiers beginning with a prefix within a given row and column family.
*
* @param row
* row to cover
* @param cf
* column family to cover
* @param cqPrefix
* prefix of column qualifiers to cover
* @see #prefix(Text, Text, Text)
*/
public static Range prefix(CharSequence row, CharSequence cf, CharSequence cqPrefix) {
return Range.prefix(new Text(row.toString()), new Text(cf.toString()), new Text(cqPrefix.toString()));
}
/**
* Returns a Range that covers all column visibilities beginning with a prefix within a given row, column family, and column qualifier.
*
* @param row
* row to cover
* @param cf
* column family to cover
* @param cq
* column qualifier to cover
* @param cvPrefix
* prefix of column visibilities to cover
* @see #prefix(Text, Text, Text, Text)
*/
public static Range prefix(CharSequence row, CharSequence cf, CharSequence cq, CharSequence cvPrefix) {
return Range.prefix(new Text(row.toString()), new Text(cf.toString()), new Text(cq.toString()), new Text(cvPrefix.toString()));
}
}