package org.limewire.collection;
import org.limewire.collection.PatriciaTrie.KeyAnalyzer;
/**
* Analyzes <code>CharSequence</code> keys with case sensitivity. With
* <code>CharSequenceKeyAnalyzer</code> you can
* compare, check prefix, and determine the index of a bit.
* <p>
* A typical use case for a <code>CharSequenceKeyAnalyzer</code> is with a
* {@link PatriciaTrie}.
* <pre>
PatriciaTrie<String, String> trie = new PatriciaTrie<String, String>(new CharSequenceKeyAnalyzer());
trie.put("Lime", "Lime");
trie.put("LimeWire", "LimeWire");
trie.put("LimeRadio", "LimeRadio");
trie.put("Lax", "Lax");
trie.put("Lake", "Lake");
trie.put("Lovely", "Lovely");
System.out.println(trie.select("Lo"));
System.out.println(trie.select("Lime"));
System.out.println(trie.getPrefixedBy("La").toString());
Output:
Lovely
Lime
{Lake=Lake, Lax=Lax}
* </pre>
*/
public class CharSequenceKeyAnalyzer implements KeyAnalyzer<CharSequence> {
private static final long serialVersionUID = -7032449491269434877L;
private static final int[] BITS = createIntBitMask(16);
public static int[] createIntBitMask(int bitCount) {
int[] bits = new int[bitCount];
for(int i = 0; i < bitCount; i++) {
bits[i] = 1 << (bitCount - i - 1);
}
return bits;
}
public int length(CharSequence key) {
return (key != null ? key.length() * 16 : 0);
}
public int bitIndex(CharSequence key, int keyOff, int keyLength,
CharSequence found, int foundOff, int foundKeyLength) {
boolean allNull = true;
if(keyOff % 16 != 0 || foundOff % 16 != 0 ||
keyLength % 16 != 0 || foundKeyLength % 16 != 0)
throw new IllegalArgumentException("offsets & lengths must be at character boundaries");
int off1 = keyOff / 16;
int off2 = foundOff / 16;
int len1 = keyLength / 16 + off1;
int len2 = foundKeyLength / 16 + off2;
int length = Math.max(len1, len2);
// Look at each character, and if they're different
// then figure out which bit makes the difference
// and return it.
char k = 0, f = 0;
for(int i = 0; i < length; i++) {
int kOff = i + off1;
int fOff = i + off2;
if(kOff >= len1)
k = 0;
else
k = key.charAt(kOff);
if(found == null || fOff >= len2)
f = 0;
else
f = found.charAt(fOff);
if(k != f) {
int x = k ^ f;
return i * 16 + (Integer.numberOfLeadingZeros(x) - 16);
}
if(k != 0)
allNull = false;
}
if (allNull) {
return KeyAnalyzer.NULL_BIT_KEY;
}
return KeyAnalyzer.EQUAL_BIT_KEY;
}
public boolean isBitSet(CharSequence key, int keyLength, int bitIndex) {
if (key == null || bitIndex >= keyLength) {
return false;
}
int index = bitIndex / BITS.length;
int bit = bitIndex - index * BITS.length;
return (key.charAt(index) & BITS[bit]) != 0;
}
public int compare(CharSequence o1, CharSequence o2) {
return o1.toString().compareTo(o2.toString());
}
public int bitsPerElement() {
return 16;
}
public boolean isPrefix(CharSequence prefix, int offset, int length, CharSequence key) {
if(offset % 16 != 0 || length % 16 != 0)
throw new IllegalArgumentException("Cannot determine prefix outside of character boundaries");
String s1 = prefix.subSequence(offset / 16, length / 16).toString();
String s2 = key.toString();
return s2.startsWith(s1);
}
}