[COLLECTION-225] Added first version patricia trie contribution.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@1365732 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Thomas Neidhart 2012-07-25 20:42:48 +00:00
parent 15a747624f
commit c57a9af3e7
22 changed files with 12399 additions and 0 deletions

View File

@ -130,6 +130,9 @@
<contributor>
<name>Ola Berg</name>
</contributor>
<contributor>
<name>Sam Berlin</name>
</contributor>
<contributor>
<name>Christopher Berry</name>
</contributor>
@ -205,6 +208,9 @@
<contributor>
<name>Marc Johnson</name>
</contributor>
<contributor>
<name>Roger Kapsi</name>
</contributor>
<contributor>
<name>Nissim Karpenstein</name>
</contributor>

View File

@ -0,0 +1,238 @@
/*
* 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.commons.collections;
import java.util.Map;
import java.util.SortedMap;
import java.util.Map.Entry;
/**
* Defines the interface for a prefix tree, an ordered tree data structure. For
* more information, see <a href="http://en.wikipedia.org/wiki/Trie">Tries</a>.
*
* @since 4.0
* @version $Id$
*/
public interface Trie<K, V> extends SortedMap<K, V> {
/**
* Returns the {@link Entry} whose key is closest in a bitwise XOR
* metric to the given key. This is NOT lexicographic closeness.
* For example, given the keys:
*
* <ol>
* <li>D = 1000100
* <li>H = 1001000
* <li>L = 1001100
* </ol>
*
* If the {@link Trie} contained 'H' and 'L', a lookup of 'D' would
* return 'L', because the XOR distance between D &amp; L is smaller
* than the XOR distance between D &amp; H.
*
* @return The {@link Entry} whose key is closest in a bitwise XOR metric
* to the provided key.
*/
public Map.Entry<K, V> select(K key);
/**
* Returns the key that is closest in a bitwise XOR metric to the
* provided key. This is NOT lexicographic closeness!
*
* For example, given the keys:
*
* <ol>
* <li>D = 1000100
* <li>H = 1001000
* <li>L = 1001100
* </ol>
*
* If the {@link Trie} contained 'H' and 'L', a lookup of 'D' would
* return 'L', because the XOR distance between D &amp; L is smaller
* than the XOR distance between D &amp; H.
*
* @return The key that is closest in a bitwise XOR metric to the provided key.
*/
public K selectKey(K key);
/**
* Returns the value whose key is closest in a bitwise XOR metric to
* the provided key. This is NOT lexicographic closeness!
*
* For example, given the keys:
*
* <ol>
* <li>D = 1000100
* <li>H = 1001000
* <li>L = 1001100
* </ol>
*
* If the {@link Trie} contained 'H' and 'L', a lookup of 'D' would
* return 'L', because the XOR distance between D &amp; L is smaller
* than the XOR distance between D &amp; H.
*
* @return The value whose key is closest in a bitwise XOR metric
* to the provided key.
*/
public V selectValue(K key);
/**
* Iterates through the {@link Trie}, starting with the entry whose bitwise
* value is closest in an XOR metric to the given key. After the closest
* entry is found, the {@link Trie} will call select on that entry and continue
* calling select for each entry (traversing in order of XOR closeness,
* NOT lexicographically) until the cursor returns {@link Decision#EXIT}.
*
* <p>The cursor can return {@link Decision#CONTINUE} to continue traversing.
*
* <p>{@link Decision#REMOVE_AND_EXIT} is used to remove the current element
* and stop traversing.
*
* <p>Note: The {@link Decision#REMOVE} operation is not supported.
*
* @return The entry the cursor returned {@link Decision#EXIT} on, or null
* if it continued till the end.
*/
public Map.Entry<K,V> select(K key, Cursor<? super K, ? super V> cursor);
/**
* Traverses the {@link Trie} in lexicographical order.
* {@link Cursor#select(java.util.Map.Entry)} will be called on each entry.
*
* <p>The traversal will stop when the cursor returns {@link Decision#EXIT},
* {@link Decision#CONTINUE} is used to continue traversing and
* {@link Decision#REMOVE} is used to remove the element that was selected
* and continue traversing.
*
* <p>{@link Decision#REMOVE_AND_EXIT} is used to remove the current element
* and stop traversing.
*
* @return The entry the cursor returned {@link Decision#EXIT} on, or null
* if it continued till the end.
*/
public Map.Entry<K,V> traverse(Cursor<? super K, ? super V> cursor);
/**
* Returns a view of this {@link SortedTrie} of all elements that are prefixed
* by the given key.
*
* <p>In a {@link SortedTrie} with fixed size keys, this is essentially a
* {@link #get(Object)} operation.
*
* <p>For example, if the {@link SortedTrie} contains 'Anna', 'Anael',
* 'Analu', 'Andreas', 'Andrea', 'Andres', and 'Anatole', then
* a lookup of 'And' would return 'Andreas', 'Andrea', and 'Andres'.
*/
public SortedMap<K, V> getPrefixedBy(K key);
/**
* Returns a view of this {@link SortedTrie} of all elements that are prefixed
* by the length of the key.
*
* <p>{@link SortedTrie}s with fixed size keys will not support this operation
* (because all keys are the same length).
*
* <p>For example, if the {@link SortedTrie} contains 'Anna', 'Anael', 'Analu',
* 'Andreas', 'Andrea', 'Andres', and 'Anatole', then a lookup for 'Andrey'
* and a length of 4 would return 'Andreas', 'Andrea', and 'Andres'.
*/
public SortedMap<K, V> getPrefixedBy(K key, int length);
/**
* Returns a view of this {@link SortedTrie} of all elements that are prefixed
* by the key, starting at the given offset and for the given length.
*
* <p>{@link SortedTrie}s with fixed size keys will not support this operation
* (because all keys are the same length).
*
* <p>For example, if the {@link SortedTrie} contains 'Anna', 'Anael', 'Analu',
* 'Andreas', 'Andrea', 'Andres', and 'Anatole', then a lookup for
* 'Hello Andrey Smith', an offset of 6 and a length of 4 would return
* 'Andreas', 'Andrea', and 'Andres'.
*/
public SortedMap<K, V> getPrefixedBy(K key, int offset, int length);
/**
* Returns a view of this {@link SortedTrie} of all elements that are prefixed
* by the number of bits in the given Key.
*
* <p>In {@link SortedTrie}s with fixed size keys like IP addresses this method
* can be used to lookup partial keys. That is you can lookup all addresses
* that begin with '192.168' by providing the key '192.168.X.X' and a
* length of 16.
*/
public SortedMap<K, V> getPrefixedByBits(K key, int lengthInBits);
/**
* Returns a view of this {@link SortedTrie} of all elements that are prefixed
* by the number of bits in the given Key.
*/
public SortedMap<K, V> getPrefixedByBits(K key, int offsetInBits, int lengthInBits);
/**
* A {@link Cursor} can be used to traverse a {@link Trie}, visit each node
* step by step and make {@link Decision}s on each step how to continue with
* traversing the {@link Trie}.
*/
public interface Cursor<K, V> {
/**
* The {@link Decision} tells the {@link Cursor} what to do on each step
* while traversing the {@link Trie}.
*
* NOTE: Not all operations that work with a {@link Cursor} support all
* {@link Decision} types
*/
public static enum Decision {
/**
* Exit the traverse operation
*/
EXIT,
/**
* Continue with the traverse operation
*/
CONTINUE,
/**
* Remove the previously returned element
* from the {@link Trie} and continue
*/
REMOVE,
/**
* Remove the previously returned element
* from the {@link Trie} and exit from the
* traverse operation
*/
REMOVE_AND_EXIT;
}
/**
* Called for each {@link Entry} in the {@link Trie}. Return
* {@link Decision#EXIT} to finish the {@link Trie} operation,
* {@link Decision#CONTINUE} to go to the next {@link Entry},
* {@link Decision#REMOVE} to remove the {@link Entry} and
* continue iterating or {@link Decision#REMOVE_AND_EXIT} to
* remove the {@link Entry} and stop iterating.
*
* Note: Not all operations support {@link Decision#REMOVE}.
*/
public Decision select(Map.Entry<? extends K, ? extends V> entry);
}
}

View File

@ -0,0 +1,53 @@
/*
* 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.commons.collections;
import org.apache.commons.collections.trie.SynchronizedTrie;
import org.apache.commons.collections.trie.UnmodifiableTrie;
/**
* A collection of {@link Trie} utilities.
*
* @since 4.0
* @version $Id$
*/
public class TrieUtils {
/**
* {@link TrieUtils} should not normally be instantiated.
*/
private TrieUtils() {}
/**
* Returns a synchronized instance of a {@link Trie}
*
* @see Collections#synchronizedMap(Map)
*/
public static <K, V> Trie<K, V> synchronizedTrie(Trie<K, V> trie) {
return SynchronizedTrie.synchronizedTrie(trie);
}
/**
* Returns an unmodifiable instance of a {@link Trie}
*
* @see Collections#unmodifiableMap(Map)
*/
public static <K, V> Trie<K, V> unmodifiableTrie(Trie<K, V> trie) {
return UnmodifiableTrie.unmodifiableTrie(trie);
}
}

View File

@ -0,0 +1,72 @@
/*
* 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.commons.collections.trie;
/**
* TODO: add javadoc
*
* @since 4.0
* @version $Id$
*/
public abstract class AbstractKeyAnalyzer<K> implements KeyAnalyzer<K> {
private static final long serialVersionUID = -20497563720380683L;
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public int compare(K o1, K o2) {
if (o1 == null) {
return (o2 == null) ? 0 : -1;
} else if (o2 == null) {
return (o1 == null) ? 0 : 1;
}
return ((Comparable<K>)o1).compareTo(o2);
}
/**
* Returns true if bitIndex is a {@link KeyAnalyzer#OUT_OF_BOUNDS_BIT_KEY}
*/
static boolean isOutOfBoundsIndex(int bitIndex) {
return bitIndex == OUT_OF_BOUNDS_BIT_KEY;
}
/**
* Returns true if bitIndex is a {@link KeyAnalyzer#EQUAL_BIT_KEY}
*/
static boolean isEqualBitKey(int bitIndex) {
return bitIndex == EQUAL_BIT_KEY;
}
/**
* Returns true if bitIndex is a {@link KeyAnalyzer#NULL_BIT_KEY}
*/
static boolean isNullBitKey(int bitIndex) {
return bitIndex == NULL_BIT_KEY;
}
/**
* Returns true if the given bitIndex is valid. Indices
* are considered valid if they're between 0 and
* {@link Integer#MAX_VALUE}
*/
static boolean isValidBitIndex(int bitIndex) {
return 0 <= bitIndex && bitIndex <= Integer.MAX_VALUE;
}
}

View File

@ -0,0 +1,250 @@
/*
* 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.commons.collections.trie;
import java.io.Serializable;
import java.util.AbstractMap;
import java.util.Map;
import org.apache.commons.collections.Trie;
/**
* This class provides some basic {@link Trie} functionality and
* utility methods for actual {@link Trie} implementations.
*
* @since 4.0
* @version $Id$
*/
abstract class AbstractTrie<K, V> extends AbstractMap<K, V>
implements Trie<K, V>, Serializable {
private static final long serialVersionUID = 5826987063535505652L;
/**
* The {@link KeyAnalyzer} that's being used to build the
* PATRICIA {@link Trie}.
*/
protected final KeyAnalyzer<? super K> keyAnalyzer;
/**
* Constructs a new {@link Trie} using the given {@link KeyAnalyzer}.
*/
public AbstractTrie(KeyAnalyzer<? super K> keyAnalyzer) {
if (keyAnalyzer == null) {
throw new NullPointerException("keyAnalyzer");
}
this.keyAnalyzer = keyAnalyzer;
}
/**
* Returns the {@link KeyAnalyzer} that constructed the {@link Trie}.
*/
public KeyAnalyzer<? super K> getKeyAnalyzer() {
return keyAnalyzer;
}
/**
* {@inheritDoc}
*/
public K selectKey(K key) {
Map.Entry<K, V> entry = select(key);
if (entry == null) {
return null;
}
return entry.getKey();
}
/**
* {@inheritDoc}
*/
public V selectValue(K key) {
Map.Entry<K, V> entry = select(key);
if (entry == null) {
return null;
}
return entry.getValue();
}
@Override
public String toString() {
StringBuilder buffer = new StringBuilder();
buffer.append("Trie[").append(size()).append("]={\n");
for (Map.Entry<K, V> entry : entrySet()) {
buffer.append(" ").append(entry).append("\n");
}
buffer.append("}\n");
return buffer.toString();
}
/**
* A utility method to cast keys. It actually doesn't
* cast anything. It's just fooling the compiler!
*/
@SuppressWarnings("unchecked")
final K castKey(Object key) {
return (K)key;
}
/**
* Returns the length of the given key in bits
*
* @see KeyAnalyzer#lengthInBits(Object)
*/
final int lengthInBits(K key) {
if (key == null) {
return 0;
}
return keyAnalyzer.lengthInBits(key);
}
/**
* Returns the number of bits per element in the key
*
* @see KeyAnalyzer#bitsPerElement()
*/
final int bitsPerElement() {
return keyAnalyzer.bitsPerElement();
}
/**
* Returns whether or not the given bit on the
* key is set or false if the key is null.
*
* @see KeyAnalyzer#isBitSet(Object, int, int)
*/
final boolean isBitSet(K key, int bitIndex, int lengthInBits) {
if (key == null) { // root's might be null!
return false;
}
return keyAnalyzer.isBitSet(key, bitIndex, lengthInBits);
}
/**
* Utility method for calling {@link KeyAnalyzer#bitIndex(Object, int, int, Object, int, int)}
*/
final int bitIndex(K key, K foundKey) {
return keyAnalyzer.bitIndex(key, 0, lengthInBits(key),
foundKey, 0, lengthInBits(foundKey));
}
/**
* An utility method for calling {@link KeyAnalyzer#compare(Object, Object)}
*/
final boolean compareKeys(K key, K other) {
if (key == null) {
return (other == null);
} else if (other == null) {
return (key == null);
}
return keyAnalyzer.compare(key, other) == 0;
}
/**
* Returns true if both values are either null or equal
*/
static boolean compare(Object a, Object b) {
return (a == null ? b == null : a.equals(b));
}
/**
* A basic implementation of {@link Entry}
*/
abstract static class BasicEntry<K, V> implements Map.Entry<K, V>, Serializable {
private static final long serialVersionUID = -944364551314110330L;
protected K key;
protected V value;
private final int hashCode;
public BasicEntry(K key) {
this.key = key;
this.hashCode = (key != null ? key.hashCode() : 0);
}
public BasicEntry(K key, V value) {
this.key = key;
this.value = value;
this.hashCode = (key != null ? key.hashCode() : 0)
^ (value != null ? value.hashCode() : 0);
}
/**
* Replaces the current key and value with the provided
* key &amp; value
*/
public V setKeyValue(K key, V value) {
this.key = key;
return setValue(value);
}
/**
* {@inheritDoc}
*/
public K getKey() {
return key;
}
/**
* {@inheritDoc}
*/
public V getValue() {
return value;
}
/**
* {@inheritDoc}
*/
public V setValue(V value) {
V previous = this.value;
this.value = value;
return previous;
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof Map.Entry)) {
return false;
}
Map.Entry<?, ?> other = (Map.Entry<?, ?>)o;
if (compare(key, other.getKey())
&& compare(value, other.getValue())) {
return true;
}
return false;
}
@Override
public String toString() {
return key + "=" + value;
}
}
}

View File

@ -0,0 +1,200 @@
/*
* 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.commons.collections.trie;
/**
* A {@link KeyAnalyzer} for byte[]s.
*
* @since 4.0
* @version $Id$
*/
public class ByteArrayKeyAnalyzer extends AbstractKeyAnalyzer<byte[]> {
private static final long serialVersionUID = 7382825097492285877L;
/**
* A singleton instance of {@link ByteArrayKeyAnalyzer}
*/
public static final ByteArrayKeyAnalyzer INSTANCE
= new ByteArrayKeyAnalyzer(Integer.MAX_VALUE);
/**
* The length of an {@link Byte} in bits
*/
public static final int LENGTH = Byte.SIZE;
/**
* A bit mask where the first bit is 1 and the others are zero
*/
private static final int MSB = 0x80;
/**
* A place holder for null
*/
private static final byte[] NULL = new byte[0];
/**
* The maximum length of a key in bits
*/
private final int maxLengthInBits;
public ByteArrayKeyAnalyzer(int maxLengthInBits) {
if (maxLengthInBits < 0) {
throw new IllegalArgumentException(
"maxLengthInBits=" + maxLengthInBits);
}
this.maxLengthInBits = maxLengthInBits;
}
/**
* Returns a bit mask where the given bit is set
*/
private static int mask(int bit) {
return MSB >>> bit;
}
/**
* Returns the maximum length of a key in bits
* @return the maximum key length in bits
*/
public int getMaxLengthInBits() {
return maxLengthInBits;
}
/**
* {@inheritDoc}
*/
public int bitsPerElement() {
return LENGTH;
}
/**
* {@inheritDoc}
*/
public int lengthInBits(byte[] key) {
return (key != null ? key.length * bitsPerElement() : 0);
}
/**
* {@inheritDoc}
*/
public boolean isBitSet(byte[] key, int bitIndex, int lengthInBits) {
if (key == null) {
return false;
}
int prefix = maxLengthInBits - lengthInBits;
int keyBitIndex = bitIndex - prefix;
if (keyBitIndex >= lengthInBits || keyBitIndex < 0) {
return false;
}
int index = (int)(keyBitIndex / LENGTH);
int bit = (int)(keyBitIndex % LENGTH);
return (key[index] & mask(bit)) != 0;
}
/**
* {@inheritDoc}
*/
public int bitIndex(byte[] key, int offsetInBits, int lengthInBits,
byte[] other, int otherOffsetInBits, int otherLengthInBits) {
if (other == null) {
other = NULL;
}
boolean allNull = true;
int length = Math.max(lengthInBits, otherLengthInBits);
int prefix = maxLengthInBits - length;
if (prefix < 0) {
return KeyAnalyzer.OUT_OF_BOUNDS_BIT_KEY;
}
for (int i = 0; i < length; i++) {
int index = prefix + (offsetInBits + i);
boolean value = isBitSet(key, index, lengthInBits);
if (value) {
allNull = false;
}
int otherIndex = prefix + (otherOffsetInBits + i);
boolean otherValue = isBitSet(other, otherIndex, otherLengthInBits);
if (value != otherValue) {
return index;
}
}
if (allNull) {
return KeyAnalyzer.NULL_BIT_KEY;
}
return KeyAnalyzer.EQUAL_BIT_KEY;
}
/**
* {@inheritDoc}
*/
public boolean isPrefix(byte[] prefix, int offsetInBits,
int lengthInBits, byte[] key) {
int keyLength = lengthInBits(key);
if (lengthInBits > keyLength) {
return false;
}
int elements = lengthInBits - offsetInBits;
for (int i = 0; i < elements; i++) {
if (isBitSet(prefix, i+offsetInBits, lengthInBits)
!= isBitSet(key, i, keyLength)) {
return false;
}
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public int compare(byte[] o1, byte[] o2) {
if (o1 == null) {
return (o2 == null) ? 0 : -1;
} else if (o2 == null) {
return (o1 == null) ? 0 : 1;
}
if (o1.length != o2.length) {
return o1.length - o2.length;
}
for (int i = 0; i < o1.length; i++) {
int diff = (o1[i] & 0xFF) - (o2[i] & 0xFF);
if (diff != 0) {
return diff;
}
}
return 0;
}
}

View File

@ -0,0 +1,118 @@
/*
* 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.commons.collections.trie;
/**
* A {@link KeyAnalyzer} for {@link Byte}s.
*
* @since 4.0
* @version $Id$
*/
public class ByteKeyAnalyzer extends AbstractKeyAnalyzer<Byte> {
private static final long serialVersionUID = 3395803342983289829L;
/**
* A singleton instance of {@link ByteKeyAnalyzer}
*/
public static final ByteKeyAnalyzer INSTANCE = new ByteKeyAnalyzer();
/**
* The length of an {@link Byte} in bits
*/
public static final int LENGTH = Byte.SIZE;
/**
* A bit mask where the first bit is 1 and the others are zero
*/
private static final int MSB = 0x80;
/**
* Returns a bit mask where the given bit is set
*/
private static int mask(int bit) {
return MSB >>> bit;
}
/**
* {@inheritDoc}
*/
public int bitsPerElement() {
return 1;
}
/**
* {@inheritDoc}
*/
public int lengthInBits(Byte key) {
return LENGTH;
}
/**
* {@inheritDoc}
*/
public boolean isBitSet(Byte key, int bitIndex, int lengthInBits) {
return (key & mask(bitIndex)) != 0;
}
/**
* {@inheritDoc}
*/
public int bitIndex(Byte key, int offsetInBits, int lengthInBits,
Byte other, int otherOffsetInBits, int otherLengthInBits) {
if (offsetInBits != 0 || otherOffsetInBits != 0) {
throw new IllegalArgumentException("offsetInBits=" + offsetInBits
+ ", otherOffsetInBits=" + otherOffsetInBits);
}
byte keyValue = key.byteValue();
if (keyValue == 0) {
return NULL_BIT_KEY;
}
byte otherValue = (other != null ? other.byteValue() : 0);
if (keyValue != otherValue) {
int xorValue = keyValue ^ otherValue;
for (int i = 0; i < LENGTH; i++) {
if ((xorValue & mask(i)) != 0) {
return i;
}
}
}
return KeyAnalyzer.EQUAL_BIT_KEY;
}
/**
* {@inheritDoc}
*/
public boolean isPrefix(Byte prefix, int offsetInBits,
int lengthInBits, Byte key) {
int value1 = (prefix.byteValue() << offsetInBits);
int value2 = key.byteValue();
int mask = 0;
for (int i = 0; i < lengthInBits; i++) {
mask |= (0x1 << i);
}
return (value1 & mask) == (value2 & mask);
}
}

View File

@ -0,0 +1,159 @@
/*
* 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.commons.collections.trie;
/**
* An {@link KeyAnalyzer} for {@code char[]}s.
*
* @since 4.0
* @version $Id$
*/
public class CharArrayKeyAnalyzer extends AbstractKeyAnalyzer<char[]> {
private static final long serialVersionUID = -8167897361549463457L;
/**
* A singleton instance of {@link CharArrayKeyAnalyzer}
*/
public static final CharArrayKeyAnalyzer INSTANCE = new CharArrayKeyAnalyzer();
/**
* The number of bits per {@link Character}
*/
public static final int LENGTH = Character.SIZE;
/**
* A bit mask where the first bit is 1 and the others are zero
*/
private static final int MSB = 0x8000;
/**
* Returns a bit mask where the given bit is set
*/
private static int mask(int bit) {
return MSB >>> bit;
}
/**
* {@inheritDoc}
*/
public int bitsPerElement() {
return LENGTH;
}
/**
* {@inheritDoc}
*/
public int lengthInBits(char[] key) {
return (key != null ? key.length * LENGTH : 0);
}
/**
* {@inheritDoc}
*/
public int bitIndex(char[] key, int offsetInBits, int lengthInBits,
char[] other, int otherOffsetInBits, int otherLengthInBits) {
boolean allNull = true;
if (offsetInBits % LENGTH != 0 || otherOffsetInBits % LENGTH != 0
|| lengthInBits % LENGTH != 0 || otherLengthInBits % LENGTH != 0) {
throw new IllegalArgumentException(
"The offsets and lengths must be at Character boundaries");
}
int beginIndex1 = offsetInBits / LENGTH;
int beginIndex2 = otherOffsetInBits / LENGTH;
int endIndex1 = beginIndex1 + lengthInBits / LENGTH;
int endIndex2 = beginIndex2 + otherLengthInBits / LENGTH;
int length = Math.max(endIndex1, endIndex2);
// 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 index1 = beginIndex1 + i;
int index2 = beginIndex2 + i;
if (index1 >= endIndex1) {
k = 0;
} else {
k = key[index1];
}
if (other == null || index2 >= endIndex2) {
f = 0;
} else {
f = other[index2];
}
if (k != f) {
int x = k ^ f;
return i * LENGTH + (Integer.numberOfLeadingZeros(x) - LENGTH);
}
if (k != 0) {
allNull = false;
}
}
// All bits are 0
if (allNull) {
return KeyAnalyzer.NULL_BIT_KEY;
}
// Both keys are equal
return KeyAnalyzer.EQUAL_BIT_KEY;
}
/**
* {@inheritDoc}
*/
public boolean isBitSet(char[] key, int bitIndex, int lengthInBits) {
if (key == null || bitIndex >= lengthInBits) {
return false;
}
int index = (int)(bitIndex / LENGTH);
int bit = (int)(bitIndex % LENGTH);
return (key[index] & mask(bit)) != 0;
}
/**
* {@inheritDoc}
*/
public boolean isPrefix(char[] prefix, int offsetInBits,
int lengthInBits, char[] key) {
if (offsetInBits % LENGTH != 0 || lengthInBits % LENGTH != 0) {
throw new IllegalArgumentException(
"Cannot determine prefix outside of Character boundaries");
}
int off = offsetInBits / LENGTH;
int len = lengthInBits / LENGTH;
for (int i = 0; i < len; i ++) {
if (prefix[i + off] != key[i]) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,123 @@
/*
* 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.commons.collections.trie;
/**
* A {@link KeyAnalyzer} for {@link Character}s.
*
* @since 4.0
* @version $Id$
*/
public class CharacterKeyAnalyzer extends AbstractKeyAnalyzer<Character> {
private static final long serialVersionUID = 3928565962744720753L;
/**
* A singleton instance of the {@link CharacterKeyAnalyzer}.
*/
public static final CharacterKeyAnalyzer INSTANCE
= new CharacterKeyAnalyzer();
/**
* The length of a {@link Character} in bits
*/
public static final int LENGTH = Character.SIZE;
/**
* A bit mask where the first bit is 1 and the others are zero
*/
private static final int MSB = 0x8000;
/**
* Returns a bit mask where the given bit is set
*/
private static int mask(int bit) {
return MSB >>> bit;
}
/**
* {@inheritDoc}
*/
public int bitsPerElement() {
return 1;
}
/**
* {@inheritDoc}
*/
public int lengthInBits(Character key) {
return LENGTH;
}
/**
* {@inheritDoc}
*/
public boolean isBitSet(Character key, int bitIndex, int lengthInBits) {
return (key & mask(bitIndex)) != 0;
}
/**
* {@inheritDoc}
*/
public int bitIndex(Character key, int offsetInBits, int lengthInBits,
Character other, int otherOffsetInBits, int otherLengthInBits) {
if (offsetInBits != 0 || otherOffsetInBits != 0) {
throw new IllegalArgumentException("offsetInBits=" + offsetInBits
+ ", otherOffsetInBits=" + otherOffsetInBits);
}
char keyValue = key.charValue();
if (keyValue == Character.MIN_VALUE) {
return NULL_BIT_KEY;
}
if (other == null) {
other = Character.MIN_VALUE;
}
char otherValue = (other != null ? other.charValue() : Character.MIN_VALUE);
if (keyValue != otherValue) {
int xorValue = keyValue ^ otherValue;
for (int i = 0; i < LENGTH; i++) {
if ((xorValue & mask(i)) != 0) {
return i;
}
}
}
return KeyAnalyzer.EQUAL_BIT_KEY;
}
/**
* {@inheritDoc}
*/
public boolean isPrefix(Character prefix, int offsetInBits,
int lengthInBits, Character key) {
int value1 = (prefix.charValue() << offsetInBits);
int value2 = key.charValue();
int mask = 0;
for(int i = 0; i < lengthInBits; i++) {
mask |= (0x1 << i);
}
return (value1 & mask) == (value2 & mask);
}
}

View File

@ -0,0 +1,118 @@
/*
* 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.commons.collections.trie;
/**
* A {@link KeyAnalyzer} for {@link Integer}s.
*
* @since 4.0
* @version $Id$
*/
public class IntegerKeyAnalyzer extends AbstractKeyAnalyzer<Integer> {
private static final long serialVersionUID = 4928508653722068982L;
/**
* A singleton instance of {@link IntegerKeyAnalyzer}
*/
public static final IntegerKeyAnalyzer INSTANCE = new IntegerKeyAnalyzer();
/**
* The length of an {@link Integer} in bits
*/
public static final int LENGTH = Integer.SIZE;
/**
* A bit mask where the first bit is 1 and the others are zero
*/
private static final int MSB = 0x80000000;
/**
* Returns a bit mask where the given bit is set
*/
private static int mask(int bit) {
return MSB >>> bit;
}
/**
* {@inheritDoc}
*/
public int bitsPerElement() {
return 1;
}
/**
* {@inheritDoc}
*/
public int lengthInBits(Integer key) {
return LENGTH;
}
/**
* {@inheritDoc}
*/
public boolean isBitSet(Integer key, int bitIndex, int lengthInBits) {
return (key & mask(bitIndex)) != 0;
}
/**
* {@inheritDoc}
*/
public int bitIndex(Integer key, int offsetInBits, int lengthInBits,
Integer other, int otherOffsetInBits, int otherLengthInBits) {
if (offsetInBits != 0 || otherOffsetInBits != 0) {
throw new IllegalArgumentException("offsetInBits=" + offsetInBits
+ ", otherOffsetInBits=" + otherOffsetInBits);
}
int keyValue = key.intValue();
if (keyValue == 0) {
return NULL_BIT_KEY;
}
int otherValue = (other != null ? other.intValue() : 0);
if (keyValue != otherValue) {
int xorValue = keyValue ^ otherValue;
for (int i = 0; i < LENGTH; i++) {
if ((xorValue & mask(i)) != 0) {
return i;
}
}
}
return KeyAnalyzer.EQUAL_BIT_KEY;
}
/**
* {@inheritDoc}
*/
public boolean isPrefix(Integer prefix, int offsetInBits,
int lengthInBits, Integer key) {
int value1 = (prefix.intValue() << offsetInBits);
int value2 = key.intValue();
int mask = 0;
for (int i = 0; i < lengthInBits; i++) {
mask |= (0x1 << i);
}
return (value1 & mask) == (value2 & mask);
}
}

View File

@ -0,0 +1,82 @@
/*
* 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.commons.collections.trie;
import java.io.Serializable;
import java.util.Comparator;
/**
* Defines the interface to analyze {@link Trie} keys on a bit level.
* {@link KeyAnalyzer}'s methods return the length of the key in bits,
* whether or not a bit is set, and bits per element in the key.
*
* <p>Additionally, a method determines if a key is a prefix of another
* key and returns the bit index where one key is different from another
* key (if the key and found key are equal than the return value is
* {@link #EQUAL_BIT_KEY}).
*
* @since 4.0
* @version $Id$
*/
public interface KeyAnalyzer<K> extends Comparator<K>, Serializable {
/**
* Returned by {@link #bitIndex(Object, int, int, Object, int, int)}
* if key's bits are all 0
*/
public static final int NULL_BIT_KEY = -1;
/**
* Returned by {@link #bitIndex(Object, int, int, Object, int, int)}
* if key and found key are equal. This is a very very specific case
* and shouldn't happen on a regular basis
*/
public static final int EQUAL_BIT_KEY = -2;
public static final int OUT_OF_BOUNDS_BIT_KEY = -3;
/**
* Returns the number of bits per element in the key.
* This is only useful for variable-length keys, such as Strings.
*/
public int bitsPerElement();
/**
* Returns the length of the Key in bits.
*/
public int lengthInBits(K key);
/**
* Returns whether or not a bit is set
*/
public boolean isBitSet(K key, int bitIndex, int lengthInBits);
/**
* Returns the n-th different bit between key and found.
* This starts the comparison in key at 'keyStart' and goes
* for 'keyLength' bits, and compares to the found key
* starting at 'foundStart' and going for 'foundLength' bits.
*/
public int bitIndex(K key, int offsetInBits, int lengthInBits,
K other, int otherOffsetInBits, int otherLengthInBits);
/**
* Determines whether or not the given prefix (from offset to length)
* is a prefix of the given key.
*/
public boolean isPrefix(K prefix, int offsetInBits, int lengthInBits, K key);
}

View File

@ -0,0 +1,118 @@
/*
* 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.commons.collections.trie;
/**
* A {@link KeyAnalyzer} for {@link Long}s.
*
* @since 4.0
* @version $Id$
*/
public class LongKeyAnalyzer extends AbstractKeyAnalyzer<Long> {
private static final long serialVersionUID = -4119639247588227409L;
/**
* A singleton instance of {@link LongKeyAnalyzer}
*/
public static final LongKeyAnalyzer INSTANCE = new LongKeyAnalyzer();
/**
* The length of an {@link Long} in bits
*/
public static final int LENGTH = Long.SIZE;
/**
* A bit mask where the first bit is 1 and the others are zero
*/
private static final long MSB = 0x8000000000000000L;
/**
* Returns a bit mask where the given bit is set
*/
private static long mask(int bit) {
return MSB >>> bit;
}
/**
* {@inheritDoc}
*/
public int bitsPerElement() {
return 1;
}
/**
* {@inheritDoc}
*/
public int lengthInBits(Long key) {
return LENGTH;
}
/**
* {@inheritDoc}
*/
public boolean isBitSet(Long key, int bitIndex, int lengthInBits) {
return (key & mask(bitIndex)) != 0;
}
/**
* {@inheritDoc}
*/
public int bitIndex(Long key, int offsetInBits, int lengthInBits,
Long other, int otherOffsetInBits, int otherLengthInBits) {
if (offsetInBits != 0 || otherOffsetInBits != 0) {
throw new IllegalArgumentException("offsetInBits=" + offsetInBits
+ ", otherOffsetInBits=" + otherOffsetInBits);
}
long keyValue = key.longValue();
if (keyValue == 0L) {
return NULL_BIT_KEY;
}
long otherValue = (other != null ? other.longValue() : 0L);
if (keyValue != otherValue) {
long xorValue = keyValue ^ otherValue;
for (int i = 0; i < LENGTH; i++) {
if ((xorValue & mask(i)) != 0L) {
return i;
}
}
}
return KeyAnalyzer.EQUAL_BIT_KEY;
}
/**
* {@inheritDoc}
*/
public boolean isPrefix(Long prefix, int offsetInBits,
int lengthInBits, Long key) {
long value1 = (prefix.longValue() << offsetInBits);
long value2 = key.longValue();
long mask = 0L;
for (int i = 0; i < lengthInBits; i++) {
mask |= (0x1L << i);
}
return (value1 & mask) == (value2 & mask);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,125 @@
/*
* 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.commons.collections.trie;
/**
* A {@link KeyAnalyzer} for {@link Short}s.
*
* @since 4.0
* @version $Id$
*/
public class ShortKeyAnalyzer implements KeyAnalyzer<Short> {
private static final long serialVersionUID = -8631376733513512017L;
/**
* A singleton instance of {@link ShortKeyAnalyzer}
*/
public static final ShortKeyAnalyzer INSTANCE = new ShortKeyAnalyzer();
/**
* The length of an {@link Short} in bits
*/
public static final int LENGTH = Short.SIZE;
/**
* A bit mask where the first bit is 1 and the others are zero
*/
private static final int MSB = 0x8000;
/**
* Returns a bit mask where the given bit is set
*/
private static int mask(int bit) {
return MSB >>> bit;
}
/**
* {@inheritDoc}
*/
public int bitsPerElement() {
return 1;
}
/**
* {@inheritDoc}
*/
public int lengthInBits(Short key) {
return LENGTH;
}
/**
* {@inheritDoc}
*/
public boolean isBitSet(Short key, int bitIndex, int lengthInBits) {
return (key & mask(bitIndex)) != 0;
}
/**
* {@inheritDoc}
*/
public int bitIndex(Short key, int offsetInBits, int lengthInBits,
Short other, int otherOffsetInBits, int otherLengthInBits) {
if (offsetInBits != 0 || otherOffsetInBits != 0) {
throw new IllegalArgumentException("offsetInBits=" + offsetInBits
+ ", otherOffsetInBits=" + otherOffsetInBits);
}
int keyValue = key.shortValue();
if (keyValue == 0) {
return NULL_BIT_KEY;
}
int otherValue = (other != null ? other.shortValue() : 0);
if (keyValue != otherValue) {
int xorValue = keyValue ^ otherValue;
for (int i = 0; i < LENGTH; i++) {
if ((xorValue & mask(i)) != 0) {
return i;
}
}
}
return KeyAnalyzer.EQUAL_BIT_KEY;
}
/**
* {@inheritDoc}
*/
public int compare(Short o1, Short o2) {
return o1.compareTo(o2);
}
/**
* {@inheritDoc}
*/
public boolean isPrefix(Short prefix, int offsetInBits,
int lengthInBits, Short key) {
int value1 = (prefix.shortValue() << offsetInBits);
int value2 = key.shortValue();
int mask = 0;
for (int i = 0; i < lengthInBits; i++) {
mask |= (0x1 << i);
}
return (value1 & mask) == (value2 & mask);
}
}

View File

@ -0,0 +1,153 @@
/*
* 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.commons.collections.trie;
/**
* An {@link KeyAnalyzer} for {@link String}s.
*
* @since 4.0
* @version $Id$
*/
public class StringKeyAnalyzer extends AbstractKeyAnalyzer<String> {
private static final long serialVersionUID = -7032449491269434877L;
/**
* A singleton instance of {@link StringKeyAnalyzer}
*/
public static final StringKeyAnalyzer INSTANCE = new StringKeyAnalyzer();
/**
* The number of bits per {@link Character}
*/
public static final int LENGTH = Character.SIZE;
/**
* A bit mask where the first bit is 1 and the others are zero
*/
private static final int MSB = 0x8000;
/**
* Returns a bit mask where the given bit is set
*/
private static int mask(int bit) {
return MSB >>> bit;
}
/**
* {@inheritDoc}
*/
public int bitsPerElement() {
return LENGTH;
}
/**
* {@inheritDoc}
*/
public int lengthInBits(String key) {
return (key != null ? key.length() * LENGTH : 0);
}
/**
* {@inheritDoc}
*/
public int bitIndex(String key, int offsetInBits, int lengthInBits,
String other, int otherOffsetInBits, int otherLengthInBits) {
boolean allNull = true;
if (offsetInBits % LENGTH != 0 || otherOffsetInBits % LENGTH != 0
|| lengthInBits % LENGTH != 0 || otherLengthInBits % LENGTH != 0) {
throw new IllegalArgumentException(
"The offsets and lengths must be at Character boundaries");
}
int beginIndex1 = offsetInBits / LENGTH;
int beginIndex2 = otherOffsetInBits / LENGTH;
int endIndex1 = beginIndex1 + lengthInBits / LENGTH;
int endIndex2 = beginIndex2 + otherLengthInBits / LENGTH;
int length = Math.max(endIndex1, endIndex2);
// 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 index1 = beginIndex1 + i;
int index2 = beginIndex2 + i;
if (index1 >= endIndex1) {
k = 0;
} else {
k = key.charAt(index1);
}
if (other == null || index2 >= endIndex2) {
f = 0;
} else {
f = other.charAt(index2);
}
if (k != f) {
int x = k ^ f;
return i * LENGTH + (Integer.numberOfLeadingZeros(x) - LENGTH);
}
if (k != 0) {
allNull = false;
}
}
// All bits are 0
if (allNull) {
return KeyAnalyzer.NULL_BIT_KEY;
}
// Both keys are equal
return KeyAnalyzer.EQUAL_BIT_KEY;
}
/**
* {@inheritDoc}
*/
public boolean isBitSet(String key, int bitIndex, int lengthInBits) {
if (key == null || bitIndex >= lengthInBits) {
return false;
}
int index = (int)(bitIndex / LENGTH);
int bit = (int)(bitIndex % LENGTH);
return (key.charAt(index) & mask(bit)) != 0;
}
/**
* {@inheritDoc}
*/
public boolean isPrefix(String prefix, int offsetInBits,
int lengthInBits, String key) {
if (offsetInBits % LENGTH != 0 || lengthInBits % LENGTH != 0) {
throw new IllegalArgumentException(
"Cannot determine prefix outside of Character boundaries");
}
String s1 = prefix.substring(offsetInBits / LENGTH, lengthInBits / LENGTH);
return key.startsWith(s1);
}
}

View File

@ -0,0 +1,280 @@
/*
* 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.commons.collections.trie;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import org.apache.commons.collections.Trie;
import org.apache.commons.collections.collection.SynchronizedCollection;
import org.apache.commons.collections.set.SynchronizedSet;
/**
* A synchronized {@link Trie}.
*
* @since 4.0
* @version $Id$
*/
public class SynchronizedTrie<K, V> implements Trie<K, V>, Serializable {
private static final long serialVersionUID = 3121878833178676939L;
private final Trie<K, V> delegate;
/**
* Factory method to create a synchronized trie.
*
* @param trie the trie to decorate, must not be null
* @return a new synchronized trie
* @throws IllegalArgumentException if trie is null
*/
public static <K, V> SynchronizedTrie<K, V> synchronizedTrie(Trie<K, V> trie) {
return new SynchronizedTrie<K, V>(trie);
}
//-----------------------------------------------------------------------
/**
* Constructor that wraps (not copies).
*
* @param trie the trie to decorate, must not be null
* @throws IllegalArgumentException if set is null
*/
public SynchronizedTrie(Trie<K, V> trie) {
if (trie == null) {
throw new IllegalArgumentException("Collection must not be null");
}
this.delegate = trie;
}
/**
* {@inheritDoc}
*/
public synchronized Entry<K, V> select(K key,
Cursor<? super K, ? super V> cursor) {
return delegate.select(key, cursor);
}
/**
* {@inheritDoc}
*/
public synchronized Entry<K, V> select(K key) {
return delegate.select(key);
}
/**
* {@inheritDoc}
*/
public synchronized K selectKey(K key) {
return delegate.selectKey(key);
}
/**
* {@inheritDoc}
*/
public synchronized V selectValue(K key) {
return delegate.selectValue(key);
}
/**
* {@inheritDoc}
*/
public synchronized Entry<K, V> traverse(Cursor<? super K, ? super V> cursor) {
return delegate.traverse(cursor);
}
/**
* {@inheritDoc}
*/
public synchronized Set<Entry<K, V>> entrySet() {
return SynchronizedSet.synchronizedSet(delegate.entrySet());
}
/**
* {@inheritDoc}
*/
public synchronized Set<K> keySet() {
return SynchronizedSet.synchronizedSet(delegate.keySet());
}
/**
* {@inheritDoc}
*/
public synchronized Collection<V> values() {
return SynchronizedCollection.synchronizedCollection(delegate.values());
}
/**
* {@inheritDoc}
*/
public synchronized void clear() {
delegate.clear();
}
/**
* {@inheritDoc}
*/
public synchronized boolean containsKey(Object key) {
return delegate.containsKey(key);
}
/**
* {@inheritDoc}
*/
public synchronized boolean containsValue(Object value) {
return delegate.containsValue(value);
}
/**
* {@inheritDoc}
*/
public synchronized V get(Object key) {
return delegate.get(key);
}
/**
* {@inheritDoc}
*/
public synchronized boolean isEmpty() {
return delegate.isEmpty();
}
/**
* {@inheritDoc}
*/
public synchronized V put(K key, V value) {
return delegate.put(key, value);
}
/**
* {@inheritDoc}
*/
public synchronized void putAll(Map<? extends K, ? extends V> m) {
delegate.putAll(m);
}
/**
* {@inheritDoc}
*/
public synchronized V remove(Object key) {
return delegate.remove(key);
}
/**
* {@inheritDoc}
*/
public synchronized K lastKey() {
return delegate.lastKey();
}
/**
* {@inheritDoc}
*/
public synchronized SortedMap<K, V> subMap(K fromKey, K toKey) {
return Collections.synchronizedSortedMap(delegate.subMap(fromKey, toKey));
}
/**
* {@inheritDoc}
*/
public synchronized SortedMap<K, V> tailMap(K fromKey) {
return Collections.synchronizedSortedMap(delegate.tailMap(fromKey));
}
/**
* {@inheritDoc}
*/
public synchronized Comparator<? super K> comparator() {
return delegate.comparator();
}
/**
* {@inheritDoc}
*/
public synchronized K firstKey() {
return delegate.firstKey();
}
/**
* {@inheritDoc}
*/
public synchronized SortedMap<K, V> headMap(K toKey) {
return Collections.synchronizedSortedMap(delegate.headMap(toKey));
}
/**
* {@inheritDoc}
*/
public synchronized SortedMap<K, V> getPrefixedBy(K key, int offset, int length) {
return Collections.synchronizedSortedMap(delegate.getPrefixedBy(key, offset, length));
}
/**
* {@inheritDoc}
*/
public synchronized SortedMap<K, V> getPrefixedBy(K key, int length) {
return Collections.synchronizedSortedMap(delegate.getPrefixedBy(key, length));
}
/**
* {@inheritDoc}
*/
public synchronized SortedMap<K, V> getPrefixedBy(K key) {
return Collections.synchronizedSortedMap(delegate.getPrefixedBy(key));
}
/**
* {@inheritDoc}
*/
public synchronized SortedMap<K, V> getPrefixedByBits(K key, int lengthInBits) {
return Collections.synchronizedSortedMap(delegate.getPrefixedByBits(key, lengthInBits));
}
/**
* {@inheritDoc}
*/
public synchronized SortedMap<K, V> getPrefixedByBits(K key,
int offsetInBits, int lengthInBits) {
return Collections.synchronizedSortedMap(delegate.getPrefixedByBits(key, offsetInBits, lengthInBits));
}
/**
* {@inheritDoc}
*/
public synchronized int size() {
return delegate.size();
}
@Override
public synchronized int hashCode() {
return delegate.hashCode();
}
@Override
public synchronized boolean equals(Object obj) {
return delegate.equals(obj);
}
@Override
public synchronized String toString() {
return delegate.toString();
}
}

View File

@ -0,0 +1,301 @@
package org.apache.commons.collections.trie;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import org.apache.commons.collections.Trie;
/**
* An unmodifiable {@link Trie}.
*
* @since 4.0
* @version $Id$
*/
public class UnmodifiableTrie<K, V> implements Trie<K, V>, Serializable {
private static final long serialVersionUID = -7156426030315945159L;
private final Trie<K, V> delegate;
/**
* Factory method to create a unmodifiable trie.
*
* @param trie the trie to decorate, must not be null
* @return a new unmodifiable trie
* @throws IllegalArgumentException if trie is null
*/
public static <K, V> UnmodifiableTrie<K, V> unmodifiableTrie(Trie<K, V> trie) {
return new UnmodifiableTrie<K, V>(trie);
}
//-----------------------------------------------------------------------
/**
* Constructor that wraps (not copies).
*
* @param trie the trie to decorate, must not be null
* @throws IllegalArgumentException if set is null
*/
public UnmodifiableTrie(Trie<K, V> trie) {
if (trie == null) {
throw new IllegalArgumentException("Collection must not be null");
}
this.delegate = trie;
}
/**
* {@inheritDoc}
*/
public Entry<K, V> select(K key, final Cursor<? super K, ? super V> cursor) {
Cursor<K, V> c = new Cursor<K, V>() {
public Decision select(Map.Entry<? extends K, ? extends V> entry) {
Decision decision = cursor.select(entry);
switch (decision) {
case REMOVE:
case REMOVE_AND_EXIT:
throw new UnsupportedOperationException();
default:
// other decisions are fine
break;
}
return decision;
}
};
return delegate.select(key, c);
}
/**
* {@inheritDoc}
*/
public Entry<K, V> select(K key) {
return delegate.select(key);
}
/**
* {@inheritDoc}
*/
public K selectKey(K key) {
return delegate.selectKey(key);
}
/**
* {@inheritDoc}
*/
public V selectValue(K key) {
return delegate.selectValue(key);
}
/**
* {@inheritDoc}
*/
public Entry<K, V> traverse(final Cursor<? super K, ? super V> cursor) {
Cursor<K, V> c = new Cursor<K, V>() {
public Decision select(Map.Entry<? extends K, ? extends V> entry) {
Decision decision = cursor.select(entry);
switch (decision) {
case REMOVE:
case REMOVE_AND_EXIT:
throw new UnsupportedOperationException();
default:
// other decisions are fine
break;
}
return decision;
}
};
return delegate.traverse(c);
}
/**
* {@inheritDoc}
*/
public Set<Entry<K, V>> entrySet() {
return Collections.unmodifiableSet(delegate.entrySet());
}
/**
* {@inheritDoc}
*/
public Set<K> keySet() {
return Collections.unmodifiableSet(delegate.keySet());
}
/**
* {@inheritDoc}
*/
public Collection<V> values() {
return Collections.unmodifiableCollection(delegate.values());
}
/**
* {@inheritDoc}
*/
public void clear() {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
public boolean containsKey(Object key) {
return delegate.containsKey(key);
}
/**
* {@inheritDoc}
*/
public boolean containsValue(Object value) {
return delegate.containsValue(value);
}
/**
* {@inheritDoc}
*/
public V get(Object key) {
return delegate.get(key);
}
/**
* {@inheritDoc}
*/
public boolean isEmpty() {
return delegate.isEmpty();
}
/**
* {@inheritDoc}
*/
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
public void putAll(Map<? extends K, ? extends V> m) {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
public V remove(Object key) {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
public K firstKey() {
return delegate.firstKey();
}
/**
* {@inheritDoc}
*/
public SortedMap<K, V> headMap(K toKey) {
return Collections.unmodifiableSortedMap(delegate.headMap(toKey));
}
/**
* {@inheritDoc}
*/
public K lastKey() {
return delegate.lastKey();
}
/**
* {@inheritDoc}
*/
public SortedMap<K, V> subMap(K fromKey, K toKey) {
return Collections.unmodifiableSortedMap(
delegate.subMap(fromKey, toKey));
}
/**
* {@inheritDoc}
*/
public SortedMap<K, V> tailMap(K fromKey) {
return Collections.unmodifiableSortedMap(delegate.tailMap(fromKey));
}
/**
* {@inheritDoc}
*/
public SortedMap<K, V> getPrefixedBy(K key, int offset, int length) {
return Collections.unmodifiableSortedMap(
delegate.getPrefixedBy(key, offset, length));
}
/**
* {@inheritDoc}
*/
public SortedMap<K, V> getPrefixedBy(K key, int length) {
return Collections.unmodifiableSortedMap(
delegate.getPrefixedBy(key, length));
}
/**
* {@inheritDoc}
*/
public SortedMap<K, V> getPrefixedBy(K key) {
return Collections.unmodifiableSortedMap(
delegate.getPrefixedBy(key));
}
/**
* {@inheritDoc}
*/
public SortedMap<K, V> getPrefixedByBits(K key, int lengthInBits) {
return Collections.unmodifiableSortedMap(
delegate.getPrefixedByBits(key, lengthInBits));
}
/**
* {@inheritDoc}
*/
public SortedMap<K, V> getPrefixedByBits(K key, int offsetInBits,
int lengthInBits) {
return Collections.unmodifiableSortedMap(
delegate.getPrefixedByBits(key, offsetInBits, lengthInBits));
}
/**
* {@inheritDoc}
*/
public Comparator<? super K> comparator() {
return delegate.comparator();
}
/**
* {@inheritDoc}
*/
public int size() {
return delegate.size();
}
/**
* {@inheritDoc}
*/
public int hashCode() {
return delegate.hashCode();
}
@Override
public boolean equals(Object obj) {
return delegate.equals(obj);
}
@Override
public String toString() {
return delegate.toString();
}
}

View File

@ -0,0 +1,38 @@
/*
* 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.
*/
/**
* This package contains implementations of the
* {@link org.apache.commons.collections.Trie Trie} interface.
* <p>
* The implementations are in the form of direct implementations and decorators.
* A decorator wraps another implementation of the interface to add some
* specific additional functionality.
* <p>
* The following implementations are provided in the package:
* <ul>
* <li>PatriciaTrie - an implementation of a PATRICIA trie
* </ul>
* <p>
* The following decorators are provided:
* <ul>
* <li>Synchronized - synchronizes method access for multi-threaded environments
* <li>Unmodifiable - ensures the collection cannot be altered
* </ul>
*
* @version $Id$
*/
package org.apache.commons.collections.trie;

View File

@ -0,0 +1,110 @@
/*
* 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.commons.collections.trie;
import java.math.BigInteger;
import java.util.Map;
import java.util.TreeMap;
import org.junit.Assert;
import org.junit.Test;
public class ByteArrayKeyAnalyzerTest {
private static final int SIZE = 20000;
@Test
public void bitSet() {
byte[] key = toByteArray("10100110", 2);
ByteArrayKeyAnalyzer ka = new ByteArrayKeyAnalyzer(key.length * 8);
int length = ka.lengthInBits(key);
Assert.assertTrue(ka.isBitSet(key, 0, length));
Assert.assertFalse(ka.isBitSet(key, 1, length));
Assert.assertTrue(ka.isBitSet(key, 2, length));
Assert.assertFalse(ka.isBitSet(key, 3, length));
Assert.assertFalse(ka.isBitSet(key, 4, length));
Assert.assertTrue(ka.isBitSet(key, 5, length));
Assert.assertTrue(ka.isBitSet(key, 6, length));
Assert.assertFalse(ka.isBitSet(key, 7, length));
}
@Test
public void keys() {
PatriciaTrie<byte[], BigInteger> trie
= new PatriciaTrie<byte[], BigInteger>(ByteArrayKeyAnalyzer.INSTANCE);
Map<byte[], BigInteger> map
= new TreeMap<byte[], BigInteger>(ByteArrayKeyAnalyzer.INSTANCE);
for (int i = 0; i < SIZE; i++) {
BigInteger value = BigInteger.valueOf(i);
byte[] key = toByteArray(value);
BigInteger existing = trie.put(key, value);
Assert.assertNull(existing);
map.put(key, value);
}
Assert.assertEquals(map.size(), trie.size());
for (byte[] key : map.keySet()) {
BigInteger expected = new BigInteger(1, key);
BigInteger value = trie.get(key);
Assert.assertEquals(expected, value);
}
}
@Test
public void prefix() {
byte[] prefix = toByteArray("00001010", 2);
byte[] key1 = toByteArray("11001010", 2);
byte[] key2 = toByteArray("10101100", 2);
ByteArrayKeyAnalyzer keyAnalyzer = new ByteArrayKeyAnalyzer(key1.length * 8);
int prefixLength = keyAnalyzer.lengthInBits(prefix);
Assert.assertFalse(keyAnalyzer.isPrefix(prefix, 4, prefixLength, key1));
Assert.assertTrue(keyAnalyzer.isPrefix(prefix, 4, prefixLength, key2));
}
private static byte[] toByteArray(String value, int radix) {
return toByteArray(Long.parseLong(value, radix));
}
private static byte[] toByteArray(long value) {
return toByteArray(BigInteger.valueOf(value));
}
private static byte[] toByteArray(BigInteger value) {
byte[] src = value.toByteArray();
if (src.length <= 1) {
return src;
}
if (src[0] != 0) {
return src;
}
byte[] dst = new byte[src.length-1];
System.arraycopy(src, 1, dst, 0, dst.length);
return dst;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff