/**
* Copyright © 2005-2012 Akiban Technologies, Inc. All rights reserved.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* This program may also be available under different license terms.
* For more information, see www.akiban.com or contact licensing@akiban.com.
*
* Contributors:
* Akiban Technologies, Inc.
*/
package com.persistit;
import com.persistit.encoding.CoderContext;
import com.persistit.exception.MissingKeySegmentException;
import com.persistit.util.Debug;
import com.persistit.util.Util;
/**
* <p>
* Specifies a subset of all possible keys values. A <code>KeyFilter</code> can
* be used with the {@link Exchange#traverse(Key.Direction, KeyFilter, int)}
* method to restrict the set of key values within a Persistit <code>Tree</code>
* that will actually be traversed.
* </p>
* <p>
* A <code>KeyFilter</code> provides two primary methods:
* <ul>
* <li>
* {@link #selected(Key)} indicates whether the value of the specified
* <code>Key</code> is a member of the subset specified by this filter, and</li>
* <li>
* {@link #next(Key, Key.Direction)} modifies the <code>Key</code> to the next
* larger or smaller key value that lies within the range specified by this
* filter.</li>
* </ul>
* These methods permit efficient traversal of a filtered subset of keys within
* a <code>Tree</code>.
* </p>
* <p>
* <h3>KeyFilter Terms</h3>
* A <code>KeyFilter</code> consists of an array of one or more <i>terms</i>.
* Each term corresponds to one segment in a key value that will be selected or
* excluded by this <code>KeyFilter</code>. The <i>K</i>th term in the list
* applies to the <i>K</i>th segment of a key.
* </p>
* <p>
* There are three kinds of term:
* <dl>
* <dt>SimpleTerm</dt>
* <dd>Represents a single segment value. To match a SimpleTerm, the value of
* the key segment in the corresponding position must exactly match the
* SimpleTerm's value.</dd>
* <dt>RangeTerm</dt>
* <dd>Represents a range of values. To match a RangeTerm, the value of the key
* segment in the corresponding position must fall between the end points of the
* range according to the <a href="Key.html#_keyOrdering">key ordering
* specification</a>. RangeTerms can be defined to include or exclude either end
* point.</dd>
* <dt>OrTerm</dt>
* <dd>Represents alternatives. Each alternative is itself a SimpleTerm or a
* RangeTerm. To match an OrTerm, the value of the key segment in the
* corresponding position must match one of the alternative terms associated
* with the OrTerm.</dd>
* </p>
* <p>
* The static methods {@link #simpleTerm(Object)},
* {@link #rangeTerm(Object, Object)},
* {@link #rangeTerm(Object, Object, CoderContext)},
* {@link #rangeTerm(Object, Object, boolean, boolean)},
* {@link #rangeTerm(Object, Object, boolean, boolean, CoderContext)}, and
* {@link #orTerm} produce these various kinds of <code>Term</code>s
* <p>
* For example, consider a key consisting of three segments: a last name, a
* first name and a person ID number for a person. Such a key value might be
* constructed with code such as this:
*
* <code><pre>
* key.clear().append("McDonald").append("Bob").append(12345);
* </pre></code>
*
* Suppose we now want to enumerate all members of a tree having keys of this
* form with last names falling between "M" and "Q", and first names equal to
* either "Alice" or "Bob". The following code constructs a
* <code>KeyFilter</code> for this purpose:
*
* <code><pre>
* KeyFilter keyFilter = new KeyFilter();
* keyFilter = keyFilter.append(KeyFilter.rangeTerm("M", "Q"));
* keyFilter = keyFilter.append(KeyFilter.orTerm(new KeyFilter.Term[]{
* KeyFilter.simpleTerm("Alice"), KeyFilter.simpleTerm("Bob"))});
* </pre></code>
*
* The first term specifies a range that includes any last name that sorts
* alphabetically between "M" and "Q" (inclusively). The second term is an
* OrTerm that selects the first names "Alice" or "Bob".
* </p>
* <p>
* A RangeTerm optionally specifies whether the end-points are inclusive. For
* example, the term
*
* <code><pre>
* KeyFilter.rangeTerm("Jones", "Smith", true, false)
* </pre></code>
*
* includes the name "Jones" and all names that follow up to, but not including,
* "Smith". If unspecified, the end-points of the range are included.
* </p>
* <p>
* <h3>Minimum and Maximum Depth</h3>
* A <code>KeyFilter</code> may also specify <i>minimum depth</i>, <i>maximum
* depth</i>, or both. These values control the number of segments that must be
* present in key value in order for it to be selected. A <code>KeyFilter</code>
* will select a key only if the number of segments in the key lies between the
* minimum depth and the maximum depth, inclusive.
* </p>
* <p>
* <a name="_stringRepresentation" />
* <h3>String Representation</h3>
* The {@link #toString()} method returns a canonical String representation of
* the current terms for this <code>KeyFilter</code>. For example, the string
* representation of the filter constructed in above is
*
* <code><pre>
* {"M":"Q",{"Alice","Bob"}}
* </pre></code>
*
* You can construct a <code>KeyFilter</code> from its string representation
* with the {@link KeyParser#parseKeyFilter} method. For example, the following
* code generates an equivalent <code>KeyFilter</code>:
*
* <code><pre>
* KeyParser parser = new KeyParser("{\"M\":\"Q\",{\"Alice\",\"Bob\"}};
* KeyFilter filter = parser.parseKeyFilter();
* </pre></code>
*
* As a convenience, the constructor {@link #KeyFilter(String)} automatically
* creates and invokes a <code>KeyParser</code> to create a
* <code>KeyFilter</code> from its string representation.
* </p>
* <p>
* Following is an informal grammar for the string representation of a key
* filter. See <a href="Key.html#_stringRepresentation">string
* representation</a> in {@link Key} for information on how to specify a key
* segment value.
*
* <code><pre>
* keyFilter ::= '{' termElement [',' termElement]... '}'
* termElement ::= [ '>' ] term [ '<' ]
* term ::= segment | range | qualifiedRange | orTerm
* segment ::= see <a href="Key.html#_stringRepresentation">segment</a>
* range ::= segment ':' segment | ':' segment | segment ':'
* qualifiedRange = ('(' | '[') range (')' | ']')
* orTerm ::= '{' term [',' term ]...'}'
* </pre></code>
*
* A <i>range</i> may omit either the starting segment value or the ending
* segment value. When the starting segment value is omitted, the range starts
* before the first possible key value, and when the ending segment value is
* omitted, the range ends after the last possible key value. Thus the range
* specification
*
* <code><pre>
* {"Smith":}
* </pre></code>
*
* include every key with a first segment value of "Smith" or above. Similarly,
*
* <code><pre>
* {:"Smith"}
* </pre></code>
*
* includes all keys up to and including "Smith".
* </p>
* <p>
* A <i>qualifiedRange</i> allows you to specify whether the end-points of a
* range are included or excluded from the selected subset. A square bracket
* indicates that the end-point is included, while a parenthesis indicates that
* it is excluded. For example
*
* <code><pre>
* {("Jones":"Smith"]}
* </pre></code>
*
* does not include "Jones" but does include "Smith". An unqualified
* <i>range</i> specification such as
*
* <code><pre>
* {"Jones":"Smith"}
* </pre></code>
*
* includes both end-points. It is equivelent to
*
* <code><pre>
* {["Jones":"Smith"]}
* </pre></code>
*
* </p>
* <p>
* Within the string representation of a <code>KeyFilter</code> at most one term
* element may specify the prefix ">" (greater-than sign), and at most one
* term element may specify the suffix "<" (less-than sign). These denote the
* minimum and maximum depths of the <code>KeyFilter</code>, respectively. The
* minimum depth is the count of term elements up to and including the term
* marked with a ">" and the maximum depth is the count of terms up to and
* including the term marked with a ">". For example, in the
* <code>KeyFilter</code> represented by the string
*
* <code><pre>
* {*,>100:200,*<}
* </pre></code>
*
* the minimum depth is 2 and the maximum depth is 3.
* </p>
* <h3>Building KeyFilters by Appending Terms</h3>
* <p>
* A <code>KeyFilter</code> is immutable. The methods {@link #append(KeyFilter)}, {@link #append(KeyFilter.Term)}, {@link #append(KeyFilter.Term[])}, create
* new <code>KeyFilter</code>s with additional terms supplied by the supplied
* <code>KeyFilter</code>, <code>Term</code> or array of <code>Term</code>s. The
* {@link #limit} method creates a new <code>KeyFilter</code> with modified
* minimum and maximum depth values. Each of these methods returns a new
* <code>KeyFilter</code> which results from combining the original
* <code>KeyFilter</code> with the supplied information.
* </p>
* <h3>
* Formal Specification of the {@link #next(Key, Key.Direction)} Method</h3>
* <p>
* A KeyFilter defines a subset of the the set of all Key values: the
* {@link Exchange#traverse(Key.Direction, KeyFilter, int)} method returns only
* values in this subset. The following definitions are used in describing the
* behavior of the {@link #next(Key, Key.Direction)} method.
* <dl>
* <dt>Range</dt>
* <dd>Let S be the ordered set of all possible key values. (Though large, this
* set is finite because a Keys have finite length.) The <i>range</i> R is the
* subset of keys in S selected by this KeyFilter.</dd>
* <dt>Adjacent</dt>
* <dd>Two keys K1 and K2 in this set are <i>adjacent</i> if K1 != K2 and there
* exists no other key value K such that K1 < K < K2. The terms
* <i>left-adjacent</i> and <i>right-adjacent</i> describe the precedence: K1 is
* left-adjacent to K2; K2 is right-adjacent to K1</dd>
* <dt>Contiguous</dt>
* <dd>Let C be a subset of R. Let Kmin and Kmax be the smallest and largest
* keys in C, respectively. Then C is <i>contiguous</i> if there is no key K not
* in R where Kmin < K < Kmax.</dd>
* <dt>Direction</dt>
* <dd>The {@link Key.Direction} is supplied to the
* {@link Exchange#traverse(Key.Direction, KeyFilter, int)} method to control
* navigation. There are five possible values:
* <ul>
* <li>LT: the result key must be strictly less than the supplied key.</li>
* <li>LTEQ: the result key must be less than or equal to the supplied key.</li>
* <li>EQ: the result key must be equal to the supplied key.</li>
* <li>GTEQ: the result key must be greater than or equal to the supplied key.</li>
* <li>GT: the result key must be strictly greater than the supplied key.</li>
* </ul>
* LT and GT are called <i>exclusive</i> because the result of
* <code>traverse</code> excludes the supplied key. LTEQ and GTEQ are
* <i>inclusive</i>.
* </dl>
* </p>
* <p>
* A KeyFilter defines the range R as zero or more contiguous subsets of S. The
* <code>next</code> method is used to assist the <code>traverse</code> method
* by skipping efficiently over keys that are not in contained in R. Given a
* {@link Key} K and a {@link Key.Direction} D, the method behaves as follows:
* </p>
* <p>
* <ol>
* <li>
* If K is in R (i.e., is <i>selected</i>) and either D is inclusive, or there
* exists a key value K' in R that is adjacent to K (where K' is larger than K
* if D is GT, or smaller than K if D is LT), then return <code>true</code>
* without modifying the K.</li>
* <li>
* Otherwise attempt to modify K to a new value K' which is either adjacent to
* or in the next contiguous subset of R, and return <code>true</code> to
* indicate that more keys exist in the range. Specifically, for each Direction
* D, K' is defined as follows:
* <ul>
* <li>LT: find the largest key J in R where J < K. Then K' is the key
* right-adjacent to J.</li>
* <li>LTEQ: find the largest key J in R where J < K. Then K' is J.</li>
* <li>EQ: there is no K'</li>
* <li>GTEQ: find the smallest key J in R where J > K. Then K' is J.</li>
* <li>GT: find the smallest key J in R where J < K. Then K' is the key
* left-adjacent to J.</li>
* </ul>
* </li>
* <li>
* Otherwise, if there is no key K' that satisfies the requirements of #2,
* return <code>false</code> to indicate that the range has been exhausted.</li>
* </ol>
* </p>
*
* @version 1.0
*/
public class KeyFilter {
/**
* A {@link KeyFilter.Term} that matches all values.
*/
public final static Term ALL = new SimpleTerm(null);
private Term[] _terms;
private int _minDepth = 0;
private int _maxDepth = Integer.MAX_VALUE;
/**
* Flag for filters that are guaranteed to be a subset of key range. For
* example, as created by {@link #KeyFilter(Key, int, int)}. Allows
* optimized traversal.
*/
private int _keyPrefixByteCount = 0;
private boolean _isKeyPrefixFilter = false;
/**
* Constructs an empty <code>KeyFilter</code>. This <code>KeyFilter</code>
* implicitly selects all key values.
*/
public KeyFilter() {
_terms = new Term[0];
}
/**
* Constructs a <code>KeyFilter</code> from its <a
* href="#_stringRepresentation"> string representation</a>.
*
* @param string
* The string representation
*
* @throws IllegalArgumentException
* if the string is not valid
*/
public KeyFilter(final String string) {
final KeyParser parser = new KeyParser(string);
final KeyFilter filter = parser.parseKeyFilter();
if (filter == null) {
throw new IllegalArgumentException("Invalid KeyFilter expression");
} else {
_terms = filter._terms;
_minDepth = filter._minDepth;
_maxDepth = filter._maxDepth;
}
}
/**
* Constructs a <code>KeyFilter</code> that selects the subset of all keys
* which are equal to, <a href="Key.html#_keyChildren">logical children</a>
* of, or logical ancestors of the supplied <code>Key</code>.
*
* @param key
* The <code>Key</code>
*/
public KeyFilter(final Key key) {
this(key, 0, Integer.MAX_VALUE);
}
/**
* Constructs a <code>KeyFilter</code> that selects the subset of all key
* values that are equal to, <a href="Key.html#_keyChildren">logical
* children</a> of, or logical ancestors of the supplied <code>Key</code>,
* and whose depth is greater than or equal to the supplied minimum depth
* and less than or equal to the supplied maximum depth. Suppose the
* supplied <code>key</code> value has <i>M</i> segments and some other
* <code>Key</code> value <i>K</i> has <i>N</i> segments. Then <i>K</i> is a
* member of the subset selected by this <code>KeyFilter</code> if and only
* if <i>N</i>>=<code>minDepth</code> and <i>N</i><=
* <code>maxDepth</code> and each of the first min(<i>M</i>, <i>N</i>)
* segments match.
*
* @param key
* The <code>Key</code>
*
* @param minDepth
* The minimum depth
*
* @param maxDepth
* The maximum depth
*
*/
public KeyFilter(final Key key, final int minDepth, final int maxDepth) {
checkLimits(minDepth, maxDepth);
int size = 0;
int index = 0;
if (key != null) {
_terms = new Term[key.getDepth()];
size = key.getEncodedSize();
}
if (key != null && size != 0) {
_isKeyPrefixFilter = true;
for (int level = 0;; level++) {
final int previous = index;
index = key.nextElementIndex(previous);
if (index < 0)
break;
if (level <= minDepth)
_keyPrefixByteCount += (index - previous);
final byte[] bytes = new byte[index - previous];
System.arraycopy(key.getEncodedBytes(), previous, bytes, 0, bytes.length);
_terms[level] = new SimpleTerm(bytes);
}
}
_minDepth = minDepth;
_maxDepth = maxDepth;
}
/**
* Constructs a <code>KeyFilter</code> that selects the subset of all keys
* whose segments are selected by the corresponding <code>Term</code>s of
* the supplied array. Suppose a Key <i>K</i> has <i>N</i> segments and the
* supplied array of <code>terms</code> has length <i>M</i>. Then <i>K</i>
* is a member of the key value subset selected by this
* <code>KeyFilter</code> if and only if each of the first min(<i>M</i>,
* <i>N</i>) segments of <i>K</i> is selected by the corresponding member of
* the <code>terms</code> array.
*
* @param terms
*/
public KeyFilter(final Term[] terms) {
this(terms, 0, Integer.MAX_VALUE);
}
/**
* Constructs a <code>KeyFilter</code> that selects the subset of all keys
* whose segments are selected by the corresponding <code>Term</code>s of
* the supplied array and whose depth is greater than or equal to the
* supplied minimum depth and less than or equal to the supplied maximum
* depth. Suppose some Key <i>K</i> has <i>N</i> segments and the supplied
* array of <code>terms</code> has length <i>M</i>. Then <i>K</i> is a
* member of the key value subset selected by this <code>KeyFilter</code> if
* and only if and only if <i>N</i>>=<code>minDepth</code> and
* <i>N</i><=<code>maxDepth</code> and each of the first min(<i>M</i>,
* <i>N</i>) segments of <i>K</i> is selected by the corresponding member of
* the <code>terms</code> array.
*
* @param terms
* The <code>Term</code> array
*
* @param minDepth
* The minimum depth
*
* @param maxDepth
* The maximum depth
*
*/
public KeyFilter(final Term[] terms, final int minDepth, final int maxDepth) {
checkLimits(minDepth, maxDepth);
_terms = terms;
_minDepth = minDepth;
_maxDepth = maxDepth;
}
/**
* Constructs and returns a new <code>KeyFilter</code> in which the terms of
* the supplied <code>filter</code> are appended to the array of terms
* already present in this <code>KeyFilter</code>. In addition, the minimum
* and maximum depths of the newly created <code>KeyFilter</code> are
* computed from the supplied <code>filter</code> value. Let M be the number
* of terms in this <code>KeyFilter</code>. The the minimum and maximum
* depth parameters for the newly created <code>KeyFilter</code> will be
* <code>filter.getMinimumDepth()+</code>M and
* <code>filter.getMaximumDepth()+</code>M, respectively.
*
* @param filter
* The <code>KeyFilter</code> to append
*
* @return The newly constructed <code>KeyFilter</code>.
*/
public KeyFilter append(final KeyFilter filter) {
final KeyFilter newFilter = new KeyFilter(merge(_terms, filter._terms));
final int size = _terms.length;
newFilter._minDepth = filter._minDepth + size;
if (Integer.MAX_VALUE - size > filter._maxDepth) {
newFilter._maxDepth = filter._maxDepth + size;
}
return newFilter;
}
/**
* Constructs and returns a new <code>KeyFilter</code> in which the supplied
* <code>term</code> is appended to the end of the array of terms in the
* current <code>KeyFilter</code>.
*
* @param term
* The <code>Term</code> to append
*
* @return The newly constructed <code>KeyFilter</code>
*/
public KeyFilter append(final Term term) {
final Term[] newTerms = new Term[_terms.length + 1];
System.arraycopy(_terms, 0, newTerms, 0, _terms.length);
newTerms[_terms.length] = term;
return new KeyFilter(newTerms);
}
/**
* Constructs and returns a new <code>KeyFilter</code> in which the supplied
* <code>terms</code> are appended to the array of terms in the current
* <code>KeyFilter</code>.
*
* @param terms
* The array of <code>Term</code> to append
*
* @return The newly constructed <code>KeyFilter</code>
*/
public KeyFilter append(final Term[] terms) {
final Term[] newTerms = merge(_terms, terms);
return new KeyFilter(newTerms);
}
/**
* Constructs and returns a new <code>KeyFilter</code> in which the minimum
* and maximum depth are set to the supplied values.
*
* @param minDepth
* The minimum depth
*
* @param maxDepth
* The maximum depth
*
* @return The newly constructed <code>KeyFilter</code>.
*/
public KeyFilter limit(final int minDepth, final int maxDepth) {
checkLimits(minDepth, maxDepth);
final KeyFilter newFilter = new KeyFilter(_terms);
newFilter._minDepth = minDepth;
newFilter._maxDepth = maxDepth;
return newFilter;
}
private void checkLimits(final int minDepth, final int maxDepth) {
if (minDepth < 0) {
throw new IllegalArgumentException("minDepth (" + minDepth + ") must be >= 0");
}
if (minDepth > maxDepth) {
throw new IllegalArgumentException("minDepth (" + minDepth + ") must be <= maxDepth (" + maxDepth + ")");
}
}
private Term[] merge(final Term[] a, final Term[] b) {
final int sizeA = a == null ? 0 : a.length;
final int sizeB = b == null ? 0 : b.length;
if (sizeA == 0 && b != null)
return b;
if (sizeB == 0 && a != null)
return a;
final Term[] terms = new Term[sizeA + sizeB];
if (sizeA > 0)
System.arraycopy(a, 0, terms, 0, sizeA);
if (sizeB > 0)
System.arraycopy(b, 0, terms, sizeA, sizeB);
return terms;
}
/**
* <p>
* Specifies criteria for selecting one segment of a <code>Key</code> value.
* This abstract class has three concrete subclasses,
* <code>SimpleTerm</code>, <code>RangeTerm</code> and <code>OrTerm</code>
* as described for {@link KeyFilter} .
* </p>
* <p>
* Use the static factory methods {@link KeyFilter#simpleTerm(Object)},
* {@link KeyFilter#rangeTerm(Object, Object)},
* {@link KeyFilter#rangeTerm(Object, Object, CoderContext)},
* {@link KeyFilter#rangeTerm(Object, Object, boolean, boolean)},
* {@link KeyFilter#rangeTerm(Object, Object, boolean, boolean, CoderContext)}
* , and {@link KeyFilter#orTerm} to create instances of <code>Term</code>.
*/
public static abstract class Term {
protected int _hashCode = -1;
/**
* Returns a <a href="KeyFilter.html#_stringRepresentation"> string
* representation</a> of this <code>Term</code>.
*
* @return A canonical String representation
*/
@Override
public String toString() {
return toString(null);
}
/**
* Returns a <a href="KeyFilter.html#_stringRepresentation"> string
* representation</a> of this <code>Term</code> using the supplied
* <code>context</code>. The <code>context</code> is used only if the
* segment value for a <code>SimpleTerm</code>, or values for a
* <code>RangeTerm</code> are members of a class with a registered
* {@link com.persistit.encoding.KeyCoder} the uses a
* {@link com.persistit.encoding.CoderContext}.
*
* @param context
* A <code>CoderContext</code> that will be passed to any
* registered {@link com.persistit.encoding.KeyCoder} used in
* decoding the value or values representing end-points in
* this <code>Term</code>. May be <code>null</code>.
*
* @return A canonical String representation
*/
public String toString(final CoderContext context) {
final StringBuilder sb = new StringBuilder();
toString(context, sb);
return sb.toString();
}
abstract void toString(CoderContext context, StringBuilder sb);
abstract boolean selected(byte[] keyBytes, int offset, int length);
abstract boolean atEdge(byte[] keyBytes, int offset, int length, boolean forward);
abstract boolean forward(Key key, int offset, int length);
abstract boolean backward(Key key, int offset, int length);
abstract byte[] leftBytes();
abstract byte[] rightBytes();
}
static class SimpleTerm extends Term {
private final byte[] _itemBytes;
SimpleTerm(final byte[] bytes) {
_itemBytes = bytes;
}
@Override
public int hashCode() {
if (_hashCode == -1) {
_hashCode = byteHash(_itemBytes) & 0x7FFFFFFF;
}
return _hashCode;
}
@Override
public boolean equals(final Object object) {
if (object instanceof SimpleTerm) {
final SimpleTerm t = (SimpleTerm) object;
return compare(_itemBytes, t._itemBytes) == 0;
}
return false;
}
/**
* Returns a <a href="KeyFilter.html#_stringRepresentation"> string
* representation</a> of this <code>Term</code>, using the supplied
* CoderContext when necessary.
*
* @param context
* A <code>CoderContext</code> that will be passed to any
* registered {@link com.persistit.encoding.KeyCoder} used in
* decoding the value or values representing end-points in
* this <code>Term</code>. May be <code>null</code>.
*
* @return A canonical String representation
*/
@Override
public void toString(final CoderContext context, final StringBuilder sb) {
if (this == ALL) {
sb.append("*");
} else {
final Key workKey = new Key((Persistit) null);
appendDisplayableKeySegment(workKey, sb, _itemBytes, context, false, false);
}
}
@Override
boolean selected(final byte[] keyBytes, final int offset, final int length) {
if (length == 0) {
return false;
}
if (this == ALL) {
return true;
}
if (length != _itemBytes.length) {
return false;
}
for (int index = 0; index < length; index++) {
if (_itemBytes[index] != keyBytes[offset + index])
return false;
}
return true;
}
@Override
boolean atEdge(final byte[] keyBytes, final int offset, final int length, final boolean forward) {
return this == ALL ? false : true;
}
@Override
boolean forward(final Key key, final int offset, final int length) {
if (this == ALL) {
if (length == 0) {
key.setEncodedSize(offset);
key.appendBefore();
} else if (!key.isSpecial()) {
key.nudgeRight();
}
return true;
}
final byte[] keyBytes = key.getEncodedBytes();
final int compare = compare(keyBytes, offset, length, _itemBytes, 0, _itemBytes.length);
if (compare < 0) {
System.arraycopy(_itemBytes, 0, keyBytes, offset, _itemBytes.length);
key.setEncodedSize(offset + _itemBytes.length);
return true;
} else {
return false;
}
}
@Override
boolean backward(final Key key, final int offset, final int length) {
if (this == ALL) {
if (length == 0) {
key.setEncodedSize(offset);
key.appendAfter();
} else if (!key.isSpecial()) {
key.nudgeLeft();
}
return true;
}
final byte[] keyBytes = key.getEncodedBytes();
final int compare = length == 0 ? 1 : compare(keyBytes, offset, length, _itemBytes, 0, _itemBytes.length);
if (compare > 0) {
System.arraycopy(_itemBytes, 0, keyBytes, offset, _itemBytes.length);
key.setEncodedSize(offset + _itemBytes.length);
return true;
} else {
return false;
}
}
@Override
byte[] leftBytes() {
return _itemBytes;
}
@Override
byte[] rightBytes() {
return _itemBytes;
}
}
/**
* Represents a restriction on an individual segment value within a key. A
* term may specify a single value, a range of values, or an array of terms.
* This class does not have a public constructor. Use the static factory
* methods {@link KeyFilter#simpleTerm(Object)},
* {@link KeyFilter#rangeTerm(Object, CoderContext)},
* {@link KeyFilter#rangeTerm(Object, Object)},
* {@link KeyFilter#rangeTerm(Object, Object, boolean, boolean, CoderContext)}
* , and {@link KeyFilter#rangeTerm(Object, Object, CoderContext)} to create
* appropriate <code>Term</code> instances.
*/
static class RangeTerm extends Term {
private final byte[] _itemFromBytes;
private final byte[] _itemToBytes;
private final boolean _leftInclusive;
private final boolean _rightInclusive;
RangeTerm(final byte[] leftBytes, final byte[] rightBytes, final boolean leftInclusive,
final boolean rightInclusive) {
_itemFromBytes = leftInclusive ? leftBytes : nudgeRight(leftBytes);
_itemToBytes = rightInclusive ? rightBytes : nudgeLeft(rightBytes);
_leftInclusive = leftInclusive;
_rightInclusive = rightInclusive;
}
/**
* Indicates whether two <code>Term</code> instances are equal. They are
* equal if the segment values delimiting their ranges and their start-
* and end-point inclusion settings are the same.
*
* @param object
* The Object to be compared
*
* @return <code>true</code> if the supplied object is equal to this
* <code>Term</code>; otherwise <code>false</code>.
*/
@Override
public boolean equals(final Object object) {
if (object instanceof RangeTerm) {
final RangeTerm t = (RangeTerm) object;
final boolean result = compare(_itemFromBytes, t._itemFromBytes) == 0
&& compare(_itemToBytes, t._itemToBytes) == 0;
return result;
}
return false;
}
/**
* Computes a hash code for this <code>Term</code>.
*
* @return The hash code
*/
@Override
public int hashCode() {
if (_hashCode == -1) {
_hashCode = (byteHash(_itemFromBytes) ^ byteHash(_itemToBytes)) & 0x7FFFFFFF;
}
return _hashCode;
}
/**
* Returns a <a href="KeyFilter.html#_stringRepresentation"> string
* representation</a> of this <code>Term</code>, using the supplied
* CoderContext when necessary.
*
* @param context
* A <code>CoderContext</code> that will be passed to any
* registered {@link com.persistit.encoding.KeyCoder} used in
* decoding the value or values representing end-points in
* this <code>Term</code>. May be <code>null</code>.
*
* @return A canonical String representation
*/
@Override
public void toString(final CoderContext context, final StringBuilder sb) {
final Key workKey = new Key((Persistit) null);
final boolean allInclusive = _leftInclusive && _rightInclusive;
if (!allInclusive) {
sb.append(_leftInclusive ? "[" : "(");
}
final byte[] from = _leftInclusive ? _itemFromBytes : unnudgeRight(_itemFromBytes);
appendDisplayableKeySegment(workKey, sb, from, context, true, false);
sb.append(":");
final byte[] to = _rightInclusive ? _itemToBytes : unnudgeLeft(_itemToBytes);
appendDisplayableKeySegment(workKey, sb, to, context, false, true);
if (!allInclusive) {
sb.append(_rightInclusive ? "]" : ")");
}
}
@Override
boolean selected(final byte[] keyBytes, final int offset, final int length) {
int compare = compare(keyBytes, offset, length, _itemFromBytes, 0, _itemFromBytes.length);
if (compare < 0) {
return false;
}
compare = compare(keyBytes, offset, length, _itemToBytes, 0, _itemToBytes.length);
if (compare > 0) {
return false;
}
return true;
}
@Override
boolean atEdge(final byte[] keyBytes, final int offset, final int length, final boolean forward) {
if (forward) {
return compare(keyBytes, offset, length, _itemToBytes, 0, _itemToBytes.length) == 0;
} else {
return compare(keyBytes, offset, length, _itemFromBytes, 0, _itemFromBytes.length) == 0;
}
}
@Override
boolean forward(final Key key, final int offset, final int length) {
final byte[] keyBytes = key.getEncodedBytes();
final int compare = compare(keyBytes, offset, length, _itemFromBytes, 0, _itemFromBytes.length);
if (compare < 0) {
System.arraycopy(_itemFromBytes, 0, keyBytes, offset, _itemFromBytes.length);
key.setEncodedSize(offset + _itemFromBytes.length);
keyBytes[offset + _itemFromBytes.length] = 0;
return true;
}
return false;
}
@Override
boolean backward(final Key key, final int offset, final int length) {
final byte[] keyBytes = key.getEncodedBytes();
final int compare = compare(keyBytes, offset, length, _itemToBytes, 0, _itemToBytes.length);
if (compare > 0) {
System.arraycopy(_itemToBytes, 0, keyBytes, offset, _itemToBytes.length);
key.setEncodedSize(offset + _itemToBytes.length);
keyBytes[offset + _itemFromBytes.length] = 0;
return true;
}
return false;
}
@Override
byte[] leftBytes() {
return _itemFromBytes;
}
@Override
byte[] rightBytes() {
return _itemToBytes;
}
private byte[] nudgeRight(final byte[] from) {
final int size = from.length;
final byte[] to = new byte[size];
System.arraycopy(from, 0, to, 0, size);
if (size > 1 && to[size - 1] == 0) {
to[size - 1] = 1;
}
return to;
}
private byte[] nudgeLeft(final byte[] from) {
final int size = from.length;
final byte[] to;
if (size > 1 && from[size - 1] == 0 && from[size - 2] != 0) {
to = new byte[size - 1];
System.arraycopy(from, 0, to, 0, size - 1);
} else {
to = new byte[size];
System.arraycopy(from, 0, to, 0, size);
}
return to;
}
private byte[] unnudgeRight(final byte[] from) {
final int size = from.length;
final byte[] to = new byte[size];
System.arraycopy(from, 0, to, 0, size);
if (size > 1 && to[size - 1] == 1) {
to[size - 1] = 0;
}
return to;
}
private byte[] unnudgeLeft(final byte[] from) {
final int size = from.length;
final byte[] to;
if (size > 1 && from[size - 1] != 0) {
to = new byte[size + 1];
System.arraycopy(from, 0, to, 0, size);
} else {
to = from;
}
return to;
}
}
static class OrTerm extends Term {
Term[] _terms;
OrTerm(final Term[] terms) {
_terms = new Term[terms.length];
byte[] previousBytes = null;
for (int index = 0; index < terms.length; index++) {
if (terms[index] instanceof OrTerm) {
throw new IllegalArgumentException("Nested OrTerm at index " + index);
}
if (index > 0) {
if (compare(previousBytes, terms[index].leftBytes()) > 0) {
throw new IllegalArgumentException("Overlapping Term at index " + index);
}
previousBytes = terms[index].rightBytes();
}
_terms[index] = terms[index];
}
}
/**
* Returns a <a href="KeyFilter.html#_stringRepresentation"> string
* representation</a> of this <code>Term</code>, using the supplied
* CoderContext when necessary.
*
* @param context
* A <code>CoderContext</code> that will be passed to any
* registered {@link com.persistit.encoding.KeyCoder} used in
* decoding the value or values representing end-points in
* this <code>Term</code>. May be <code>null</code>.
*
* @return A canonical String representation
*/
@Override
public void toString(final CoderContext context, final StringBuilder sb) {
sb.append("{");
for (int index = 0; index < _terms.length; index++) {
if (index > 0)
sb.append(',');
_terms[index].toString(context, sb);
}
sb.append("}");
}
/**
* Indicates whether two <code>Term</code> instances are equal. They are
* equal if the segment values delimiting their ranges and their start-
* and end-point inclusion settings are the same.
*
* @param object
* The Object to be compared
*
* @return <code>true</code> if the supplied object is equal to this
* <code>Term</code>; otherwise <code>false</code>.
*/
@Override
public boolean equals(final Object object) {
if (object instanceof OrTerm) {
final OrTerm t = (OrTerm) object;
if (t.hashCode() != hashCode() || t._terms.length != _terms.length)
return false;
for (int index = 0; index < _terms.length; index++) {
if (t._terms[index] != _terms[index])
return false;
}
}
return true;
}
/**
* Computes a hash code for this <code>Term</code>.
*
* @return The hash code
*/
@Override
public int hashCode() {
if (_hashCode == -1) {
for (int index = 0; index < _terms.length; index++) {
_hashCode ^= _terms[index].hashCode();
}
_hashCode &= 0x7FFFFFFF;
}
return _hashCode;
}
@Override
boolean selected(final byte[] keyBytes, final int offset, final int length) {
for (int index = 0; index < _terms.length; index++) {
if (_terms[index].selected(keyBytes, offset, length)) {
return true;
}
}
return false;
}
@Override
boolean atEdge(final byte[] keyBytes, final int offset, final int length, final boolean forward) {
for (int index = 0; index < _terms.length; index++) {
if (_terms[index].selected(keyBytes, offset, length)
&& _terms[index].atEdge(keyBytes, offset, length, forward)) {
return true;
}
}
return false;
}
@Override
boolean forward(final Key key, final int offset, final int length) {
for (int index = 0; index < _terms.length; index++) {
if (_terms[index].forward(key, offset, length))
return true;
}
return false;
}
@Override
boolean backward(final Key key, final int offset, final int length) {
for (int index = _terms.length; --index >= 0;) {
if (_terms[index].backward(key, offset, length))
return true;
}
return false;
}
@Override
byte[] leftBytes() {
return null;
}
@Override
byte[] rightBytes() {
return null;
}
}
/**
* Returns a <code>Term</code> that matches a single value. The value is
* interpreted in the same manner and has the same restrictions as described
* for the {@link Key} class.
*
* @param value
* The value
*
* @return The <code>Term</code>.
*/
public static Term simpleTerm(final Object value) {
return rangeTerm(value, null, true, true, null);
}
/**
* Returns a <code>Term</code> that matches a single value. The value is
* interpreted in the same manner and has the same restrictions as described
* for the {@link Key} class.
*
* @param value
* The value
*
* @param context
* A <code>CoderContext</code> supplied to any registered
* {@link com.persistit.encoding.KeyCoder} used in encoding the
* <code>fromValue</code> or <code>toValue</code>. May be
* <code>null</code>.
*
* @return The <code>Term</code>.
*/
public static Term simpleTerm(final Object value, final CoderContext context) {
return rangeTerm(value, null, true, true, context);
}
/**
* Returns a <code>Term</code> that accepts a range of values. The range
* includes these two values and all values that lie between them according
* to the <a href="Key.html#_keyOrdering">key ordering specification</a>.
*
* @param fromValue
* The first value that will be selected by this term
*
* @param toValue
* The last value that will be selected by this term
*
* @return The <code>term</code>
*
* @throws IllegalArgumentException
* if <code>fromValue</code> follows <code>toValue</code>.
*/
public static Term rangeTerm(final Object fromValue, final Object toValue) {
return rangeTerm(fromValue, toValue, true, true, null);
}
/**
* Returns a <code>Term</code> that accepts a range of values. The range
* includes these two values and all values that lie between them according
* to the <a href="Key.html#_keyOrdering">key ordering specification</a>.
*
* @param fromValue
* The first value that will be selected by this term
*
* @param toValue
* The last value that will be selected by this term
*
* @param context
* A <code>CoderContext</code> supplied to any registered
* {@link com.persistit.encoding.KeyCoder} used in encoding the
* <code>fromValue</code> or <code>toValue</code>. May be
* <code>null</code>.
*
* @return The <code>term</code>
*
* @throws IllegalArgumentException
* if <code>fromValue</code> follows <code>toValue</code>.
*/
public static Term rangeTerm(final Object fromValue, final Object toValue, final CoderContext context) {
return rangeTerm(fromValue, toValue, true, true, context);
}
/**
* Returns a <code>Term</code> that accepts a range of values. The range
* optionally includes these two values and all values that lie between them
* according to the <a href="Key.html#_keyOrdering">key ordering
* specification</a>.
*
* @param fromValue
* The first value that will be selected by this term
*
* @param toValue
* The last value that will be selected by this term
*
* @param leftInclusive
* Indicates whether a value exactly matching
* <code>fromValue</code> should be selected by this
* <code>Term</code>.
*
* @param rightInclusive
* Indicates whether a value exactly matching
* <code>toValue</code> should be selected by this
* <code>Term</code>.
*
* @return The <code>term</code>
*
* @throws IllegalArgumentException
* if <code>fromValue</code> follows <code>toValue</code>.
*/
public static Term rangeTerm(final Object fromValue, final Object toValue, final boolean leftInclusive,
final boolean rightInclusive) {
return rangeTerm(fromValue, toValue, leftInclusive, rightInclusive, null);
}
/**
* Returns a <code>Term</code> that accepts a range of values. The range
* optionally includes these two values and all values that lie between them
* according to the <a href="Key.html#_keyOrdering">key ordering
* specification</a>.
*
* @param fromValue
* The first value that will be selected by this term
*
* @param toValue
* The last value that will be selected by this term
*
* @param leftInclusive
* Indicates whether a value exactly matching
* <code>fromValue</code> should be selected by this
* <code>Term</code>.
*
* @param rightInclusive
* Indicates whether a value exactly matching
* <code>toValue</code> should be selected by this
* <code>Term</code>.
*
*
* @param context
* A <code>CoderContext</code> supplied to any registered
* {@link com.persistit.encoding.KeyCoder} used in encoding the
* <code>fromValue</code> or <code>toValue</code>. May be
* <code>null</code>.
*
* @return The <code>term</code>
*
* @throws IllegalArgumentException
* if <code>fromValue</code> follows <code>toValue</code>.
*/
public static Term rangeTerm(final Object fromValue, final Object toValue, final boolean leftInclusive,
final boolean rightInclusive, final CoderContext context) {
final Key key = new Key((Persistit) null);
if (fromValue == null)
key.appendBefore();
else
key.append(fromValue, context);
key.reset();
final byte[] leftBytes = segmentBytes(key);
key.clear();
if (toValue != fromValue && toValue != null) {
key.append(toValue, context);
key.reset();
final byte[] rightBytes = segmentBytes(key);
if (compare(leftBytes, rightBytes) > 0) {
throw new IllegalArgumentException("Start value \"" + fromValue + "\" is after end value \"" + toValue
+ "\".");
}
return new RangeTerm(leftBytes, rightBytes, leftInclusive, rightInclusive);
} else
return new SimpleTerm(leftBytes);
}
/**
* Returns a <code>Term</code> that accepts a range of values. The range is
* specified by values already encoded in two supplied {@link Key}s. The
* index of each Key object should be set on entry to the segment to be used
* in constructing the RangeTerm. As a side-effect, the index of each key is
* advanced to the next segment. If the two key segments are identical and
* if both leftInclusive and rightInclusive are true, this method returns a
* SimpleTerm containing the segment.
*
* @param fromKey
* A <code>Key</tt? from which the low value in the range is
* extracted
*
* @param toKey
* A <code>Key</tt? from which the high value in the range is
* extracted
*
* @param leftInclusive
* Indicates whether a value exactly matching <code>fromValue
* </code> should be selected by this <code>Term</code>.
*
* @param rightInclusive
* Indicates whether a value exactly matching
* <code>toValue</code> should be selected by this
* <code>Term</code>.
*
* @return The <code>term</code>
*/
public static Term termFromKeySegments(final Key fromKey, final Key toKey, final boolean leftInclusive,
final boolean rightInclusive) {
final byte[] leftBytes = segmentBytes(fromKey);
final byte[] rightBytes = segmentBytes(toKey);
toKey.nextElementIndex();
if (leftInclusive && rightInclusive && compare(leftBytes, rightBytes) == 0) {
return new SimpleTerm(leftBytes);
} else {
return new RangeTerm(leftBytes, rightBytes, leftInclusive, rightInclusive);
}
}
/**
* Returns a <code>Term</code> that selects a key segment value if and only
* if one of the members of the supplied <code>terms</code> array selects
* it. The <code>terms</code> array may not include a nested
* <code>OrTerm</code>.
*
* @param terms
* Array of <code>RangeTerm</code>s or <code>SimpleTerm</code>s.
*
* @return The <code>term</code>
*
* @throws IllegalArgumentException
* if any member of the <code>terms</code> array is itself an
* <code>OrTerm</code> or if the end points of the terms in that
* array are not strictly increasing in <a
* href="Key.html#_keyOrdering">key order</a>.
*/
public static Term orTerm(final Term[] terms) {
return new OrTerm(terms);
}
static byte[] segmentBytes(final Key key) {
final int from = key.getIndex();
int to = key.nextElementIndex();
if (to < 0)
to = key.getEncodedSize();
if (to <= 0)
throw new MissingKeySegmentException();
final byte[] bytes = new byte[to - from];
System.arraycopy(key.getEncodedBytes(), from, bytes, 0, bytes.length);
return bytes;
}
/**
* Returns the current size of this <code>KeyFilter</code>'s term array.
*
* @return The size.
*/
public int size() {
return _terms.length;
}
/**
* Returns the minimum {@link Key#getDepth() depth} of a key value that will
* be selected by this <code>KeyFilter</code>.
*
* @return The minimum depth
*/
public int getMinimumDepth() {
return _minDepth;
}
/**
* Returns the maximum {@link Key#getDepth() depth} of a key value that will
* be selected by this <code>KeyFilter</code>.
*
* @return The maximum depth
*/
public int getMaximumDepth() {
return _maxDepth;
}
/**
* Returns the term at the specified index.
*
* @param index
* The index of the term to be returned.
*
* @return The <code>Term</code>.
*
* @throws ArrayIndexOutOfBoundsException
* if <code>index</code> is less than zero or greater than or
* equal to the number of terms in the term array.
*/
public Term getTerm(final int index) {
return _terms[index];
}
/**
* Returns a <a href="#_stringRepresentation">string representation</a> of
* this <code>KeyFilter</code>
*
* @return The canonical string representation
*/
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("{");
int size = _terms.length;
if (_maxDepth > size && _maxDepth < 100)
size = _maxDepth;
for (int index = 0; index < size; index++) {
if (index > 0) {
sb.append(",");
if (index + 1 == _minDepth)
sb.append(">");
}
if (index >= _terms.length)
sb.append(ALL);
else
_terms[index].toString(null, sb);
if (index + 1 == _maxDepth)
sb.append("<");
}
sb.append("}");
return sb.toString();
}
/**
* Indicates whether the supplied key is selected by this filter.
*
* @param key
* The <code>Key</code> value to test.
*
* @return <code>true</code> if the supplied <code>Key</code> state
* satisfies the constraints of this filter, otherwise
* <code>false</code>.
*/
public boolean selected(final Key key) {
int index = 0;
final int size = key.getEncodedSize();
final byte[] keyBytes = key.getEncodedBytes();
for (int level = 0;; level++) {
if (index == size) {
return (level >= _minDepth);
} else if (level >= _maxDepth) {
return false;
} else {
int nextIndex = key.nextElementIndex(index);
if (nextIndex == -1) {
nextIndex = key.getEncodedSize();
}
final Term term = level < _terms.length ? _terms[level] : ALL;
if (term == null || !term.selected(keyBytes, index, nextIndex - index)) {
return false;
}
index = nextIndex;
}
}
}
/**
* <p>
* Determine the next key value from which B-Tree traversal should proceed.
* </p>
* <p>
* A KeyFilter defines a subset of the the set of all Key values: the
* {@link Exchange#traverse(Key.Direction, KeyFilter, int)} method returns
* only values in this subset. The following definitions are used in
* describing the behavior of this method.
* </p>
* <p>
* <dl>
* <dt>Range</dt>
* <dd>Let S be the ordered set of all possible key values. (Though large,
* this set is finite because of maximum length of a key.) T range R is the
* subset of S selected by this KeyFilter.</dd>
* <dt>Adjacent</dt>
* <dd>Two keys K1 and K2 in this set are <i>adjacent</i> if K1 != K2 and
* there exists no other key value K such that K1 < K < k2.</dd>
* <dt>Contiguous</dt>
* <dd>Let C be a subset of R. Let Kmin and Kmax be the smallest and largest
* keys in C, respectively. Then C is <i>contiguous</i> if there is no key K
* where K1 < K < K2 and K is not in R.</dd>
* </dl>
*
* A KeyFilter defines a subset of S
* </p>
* <p>
* This method modifies the supplied key as needed so that only key values
* in the range are traversed. For example suppose the KeyFilter admits key
* values between {5} and {10} (inclusive) and suppose the key currently
* contains {3}. Then if the traversal direction is GTEQ, this method
* modifies key to {5}, which is the next smallest memory of the range. If
* the direction is GT, this method modifies the value of key to {5}-, which
* is a pseudo-value immediately before {5}.
* </p>
* <p>
* In most cases, if the supplied key is <i>selected</i> (in the range) then
* this method returns <code>true</code> and does not modify the key. The
* exception is that if the current key is selected, the direction is LT or
* GT, and there is no adjacent key in the range, this method w
* <p>
* </p>
* Similarly, if key is {12} and then the directions LTEQ and LT result in
* key values {10} and {10}+, respectively. </p>
* <p>
* The return value indicates whether there exist any remaining values in
* the range. For example, if the value of key is {10} and the direction is
* GT, then this method returns <code>false</code>.
* </p>
*
* @param key
* The <code>Key</code>
*
* @param direction
* Direction specified in the <code>traverse</code> method using
* this KeyFilter
*
* @return <code>true</code> if a successor (or predecessor) key exists,
* otherwise <code>false</code>.
*/
public boolean next(final Key key, final Key.Direction direction) {
return next(key, 0, 0, direction == Key.GT || direction == Key.GTEQ, direction == Key.GTEQ
|| direction == Key.LTEQ);
}
/**
* Process the a Term in the KeyFilter. The first <code>level</code> terms
* of the KeyFilter have already been satisfied.
*
* @param key
* @param index
* @param level
* @param forward
* @param eq
* @return
*/
private boolean next(final Key key, final int index, final int level, final boolean forward, final boolean eq) {
int size = key.getEncodedSize();
final byte[] bytes = key.getEncodedBytes();
final Term term = level >= _terms.length ? ALL : _terms[level];
int nextIndex;
Debug.$assert0.t(level < _maxDepth);
Debug.$assert0.t(index <= size);
if (term == null) {
return false;
}
/*
* If at end of key and the key is deep enough to satisfy the KeyFilter,
* then
*/
if (forward && size == index && level >= _minDepth) {
return true;
}
if (size == index) {
nextIndex = size;
} else {
nextIndex = key.nextElementIndex(index);
if (nextIndex == -1) {
nextIndex = size;
}
}
boolean isLastKeySegment = nextIndex == size;
for (;;) {
if (term.selected(bytes, index, nextIndex - index)) {
if (level + 1 == _maxDepth) {
if (isLastKeySegment) {
if (eq || !term.atEdge(bytes, index, nextIndex - index, forward)) {
return true;
}
} else {
//
// The Key is deeper than this KeyFilter's max depth.
// Therefore truncate the key, which results in a
// smaller key value than the original. If the traversal
// direction is LT or LTEQ, then truncating the key is
// all that's needed nudge the key leftward to avoid
// traversing the same subtree. If the direction is
// GT or GTEQ, then the key needs to be nudged past
// any children.
//
key.setEncodedSize(nextIndex);
isLastKeySegment = true;
if (key.isSpecial()) {
return true;
}
if (forward) {
key.nudgeRight();
continue;
} else {
if (!eq) {
key.nudgeDeeper();
}
return true;
}
}
} else if (level + 1 < _minDepth) {
if (key.isSegmentSpecial(nextIndex) || (forward || !isLastKeySegment)
&& next(key, nextIndex, level + 1, forward, eq)) {
return true;
}
} else if (isLastKeySegment) {
if (eq || (forward && level + 1 < _maxDepth)
|| !term.atEdge(bytes, index, nextIndex - index, forward)) {
return true;
}
} else {
if (key.isSegmentSpecial(nextIndex) || next(key, nextIndex, level + 1, forward, eq)) {
return true;
}
}
}
//
// If the term was not selected, or if a deeper level of the
// KeyFilter was exhausted, then attempt to modify the current
// key segment to a value at the edge of a new contiguous
// area of the range.
//
key.setEncodedSize(nextIndex);
if (forward) {
if (term.selected(key.getEncodedBytes(), index, nextIndex - index)
&& !term.atEdge(key.getEncodedBytes(), index, nextIndex - index, forward)) {
key.nudgeRight();
return true;
} else {
if (!term.forward(key, index, nextIndex - index)) {
return false;
}
}
} else {
if (term.selected(key.getEncodedBytes(), index, nextIndex - index)
&& !term.atEdge(key.getEncodedBytes(), index, nextIndex - index, forward)) {
key.nudgeLeft();
return true;
} else {
if (!term.backward(key, index, nextIndex - index)) {
return false;
}
}
}
size = key.getEncodedSize();
nextIndex = size;
isLastKeySegment = true;
if (key.isSpecial()) {
return true;
}
if (forward) {
if (level + 1 >= _minDepth) {
if (!eq) {
// For the GT case, choose the left-adjacent key.
key.nudgeLeft();
}
return true;
}
} else {
if (level + 1 < _maxDepth) {
key.appendAfter();
return next(key, nextIndex, level + 1, forward, eq);
} else {
if (!eq) {
key.nudgeRight();
}
return true;
}
}
}
}
private static int compare(final byte[] a, final byte[] b) {
if ((a == b) || (a == null && b == null))
return 0;
if (a == null && b != null)
return Integer.MIN_VALUE;
if (a != null && b == null)
return Integer.MAX_VALUE;
return compare(a, 0, a.length, b, 0, b.length);
}
private static int compare(final byte[] a, final int offsetA, final int sizeA, final byte[] b, final int offsetB,
final int sizeB) {
final int size = Math.min(sizeA, sizeB);
for (int i = 0; i < size; i++) {
if (a[i + offsetA] != b[i + offsetB]) {
if ((a[i + offsetA] & 0xFF) > (b[i + offsetB] & 0xFF))
return 1;
else
return -1;
}
}
if (sizeA < sizeB)
return -1;
if (sizeA > sizeB)
return 1;
return 0;
}
private static int byteHash(final byte[] a) {
if (a == null)
return 0;
int h = 0;
for (int i = 0; i < a.length; i++) {
h = h * 17 + a[i];
}
return h;
}
private static void appendDisplayableKeySegment(final Key workKey, final StringBuilder sb, final byte[] bytes,
final CoderContext context, final boolean before, final boolean after) {
if (bytes == null) {
return;
}
System.arraycopy(bytes, 0, workKey.getEncodedBytes(), 0, bytes.length);
workKey.setEncodedSize(bytes.length);
if (before && workKey.isBefore() || after && workKey.isAfter()) {
return;
}
try {
workKey.decodeDisplayable(true, sb, context);
} catch (final Exception e) {
sb.append(e);
sb.append("(");
sb.append(Util.hexDump(bytes, 0, bytes.length));
sb.append(")");
}
}
boolean isKeyPrefixFilter() {
return _isKeyPrefixFilter;
}
int getKeyPrefixByteCount() {
return _keyPrefixByteCount;
}
}