Refactor trie package: reduce interface by extending IterableSortedMap and only adding prefixMap method, remove all key analyzers but the StringKeyAnalyzer, refactor PatriciaTrie class by moving all remaining methods to AbstractPatriciaTrie and fixing the key type to String, integrating the test classes into the framework.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@1492866 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Thomas Neidhart 2013-06-13 21:01:00 +00:00
parent ed085d064c
commit 83226e162d
21 changed files with 2727 additions and 4248 deletions

View File

@ -30,22 +30,22 @@ import org.apache.commons.collections4.Trie;
* @since 4.0
* @version $Id$
*/
abstract class AbstractBitwiseTrie<K, V> extends AbstractMap<K, V>
public abstract class AbstractBitwiseTrie<K, V> extends AbstractMap<K, V>
implements Trie<K, V>, Serializable {
private static final long serialVersionUID = 5826987063535505652L;
// TODO Privatise fields?
/**
* The {@link KeyAnalyzer} that's being used to build the PATRICIA {@link Trie}.
*/
protected final KeyAnalyzer<? super K> keyAnalyzer;
private final KeyAnalyzer<? super K> keyAnalyzer;
/**
* Constructs a new {@link Trie} using the given {@link KeyAnalyzer}.
*
* @param keyAnalyzer the {@link KeyAnalyzer} to use
*/
public AbstractBitwiseTrie(final KeyAnalyzer<? super K> keyAnalyzer) {
protected AbstractBitwiseTrie(final KeyAnalyzer<? super K> keyAnalyzer) {
if (keyAnalyzer == null) {
throw new NullPointerException("keyAnalyzer");
}
@ -55,108 +55,12 @@ abstract class AbstractBitwiseTrie<K, V> extends AbstractMap<K, V>
/**
* Returns the {@link KeyAnalyzer} that constructed the {@link Trie}.
* @return the {@link KeyAnalyzer} used by this {@link Trie}
*/
public KeyAnalyzer<? super K> getKeyAnalyzer() {
protected KeyAnalyzer<? super K> getKeyAnalyzer() {
return keyAnalyzer;
}
/**
* 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.
*
* @param key the key to use in the search
* @return the {@link Entry} whose key is closest in a bitwise XOR metric
* to the provided key
*/
public abstract 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.
*
* @param key the key to use in the search
* @return the key that is closest in a bitwise XOR metric to the provided key
*/
public K selectKey(final K key) {
final Map.Entry<K, V> entry = select(key);
if (entry == null) {
return null;
}
return entry.getKey();
}
/**
* 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.
*
* @param key the key to use in the search
* @return the value whose key is closest in a bitwise XOR metric
* to the provided key
*/
public V selectValue(final K key) {
final Map.Entry<K, V> entry = select(key);
if (entry == null) {
return null;
}
return entry.getValue();
}
/**
* 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 Cursor.Decision#EXIT}.
* <p>
* The cursor can return {@link Cursor.Decision#CONTINUE} to continue traversing.
* <p>
* {@link Cursor.Decision#REMOVE_AND_EXIT} is used to remove the current element
* and stop traversing.
* <p>
* Note: The {@link Cursor.Decision#REMOVE} operation is not supported.
*
* @param key the key to use in the search
* @param cursor the cursor used throughout the search
* @return the entry the cursor returned {@link Cursor.Decision#EXIT} on, or null
* if it continued till the end
*/
public abstract Map.Entry<K,V> select(K key, Cursor<? super K, ? super V> cursor);
@Override
public String toString() {
final StringBuilder buffer = new StringBuilder();
@ -248,17 +152,13 @@ abstract class AbstractBitwiseTrie<K, V> extends AbstractMap<K, V>
protected V value;
private final int hashCode;
public BasicEntry(final K key) {
this.key = key;
this.hashCode = key != null ? key.hashCode() : 0;
}
public BasicEntry(final K key, final V value) {
this.key = key;
this.value = value;
this.hashCode = (key != null ? key.hashCode() : 0) ^ (value != null ? value.hashCode() : 0);
}
/**
@ -285,7 +185,8 @@ abstract class AbstractBitwiseTrie<K, V> extends AbstractMap<K, V>
@Override
public int hashCode() {
return hashCode;
return (getKey() == null ? 0 : getKey().hashCode()) ^
(getValue() == null ? 0 : getValue().hashCode());
}
@Override

File diff suppressed because it is too large Load Diff

View File

@ -24,6 +24,7 @@ import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import org.apache.commons.collections4.OrderedMapIterator;
import org.apache.commons.collections4.Trie;
import org.apache.commons.collections4.collection.SynchronizedCollection;
@ -66,10 +67,6 @@ public class SynchronizedTrie<K, V> implements Trie<K, V>, Serializable {
this.delegate = trie;
}
public synchronized Entry<K, V> traverse(final Cursor<? super K, ? super V> cursor) {
return delegate.traverse(cursor);
}
public synchronized Set<Entry<K, V>> entrySet() {
return Collections.synchronizedSet(delegate.entrySet());
}
@ -114,6 +111,10 @@ public class SynchronizedTrie<K, V> implements Trie<K, V>, Serializable {
return delegate.remove(key);
}
public synchronized int size() {
return delegate.size();
}
public synchronized K lastKey() {
return delegate.lastKey();
}
@ -138,31 +139,26 @@ public class SynchronizedTrie<K, V> implements Trie<K, V>, Serializable {
return Collections.synchronizedSortedMap(delegate.headMap(toKey));
}
public synchronized SortedMap<K, V> getPrefixedBy(final K key, final int offset, final int length) {
return Collections.synchronizedSortedMap(delegate.getPrefixedBy(key, offset, length));
public synchronized SortedMap<K, V> prefixMap(final K key) {
return Collections.synchronizedSortedMap(delegate.prefixMap(key));
}
public synchronized SortedMap<K, V> getPrefixedBy(final K key, final int length) {
return Collections.synchronizedSortedMap(delegate.getPrefixedBy(key, length));
//-----------------------------------------------------------------------
public synchronized OrderedMapIterator<K, V> mapIterator() {
// TODO: make ordered map iterator synchronized too
final OrderedMapIterator<K, V> it = delegate.mapIterator();
return it;
}
public synchronized SortedMap<K, V> getPrefixedBy(final K key) {
return Collections.synchronizedSortedMap(delegate.getPrefixedBy(key));
public synchronized K nextKey(K key) {
return delegate.nextKey(key);
}
public synchronized SortedMap<K, V> getPrefixedByBits(final K key, final int lengthInBits) {
return Collections.synchronizedSortedMap(delegate.getPrefixedByBits(key, lengthInBits));
}
public synchronized SortedMap<K, V> getPrefixedByBits(final K key,
final int offsetInBits, final int lengthInBits) {
return Collections.synchronizedSortedMap(delegate.getPrefixedByBits(key, offsetInBits, lengthInBits));
}
public synchronized int size() {
return delegate.size();
public synchronized K previousKey(K key) {
return delegate.previousKey(key);
}
//-----------------------------------------------------------------------
@Override
public synchronized int hashCode() {
return delegate.hashCode();
@ -177,4 +173,5 @@ public class SynchronizedTrie<K, V> implements Trie<K, V>, Serializable {
public synchronized String toString() {
return delegate.toString();
}
}

View File

@ -24,8 +24,10 @@ import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import org.apache.commons.collections4.OrderedMapIterator;
import org.apache.commons.collections4.Trie;
import org.apache.commons.collections4.Unmodifiable;
import org.apache.commons.collections4.iterators.UnmodifiableOrderedMapIterator;
/**
* An unmodifiable {@link Trie}.
@ -35,6 +37,7 @@ import org.apache.commons.collections4.Unmodifiable;
*/
public class UnmodifiableTrie<K, V> implements Trie<K, V>, Serializable, Unmodifiable {
/** Serialization version */
private static final long serialVersionUID = -7156426030315945159L;
private final Trie<K, V> delegate;
@ -66,25 +69,7 @@ public class UnmodifiableTrie<K, V> implements Trie<K, V>, Serializable, Unmodif
this.delegate = trie;
}
public Entry<K, V> traverse(final Cursor<? super K, ? super V> cursor) {
final Cursor<K, V> c = new Cursor<K, V>() {
public Decision select(final Map.Entry<? extends K, ? extends V> entry) {
final 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);
}
//-----------------------------------------------------------------------
public Set<Entry<K, V>> entrySet() {
return Collections.unmodifiableSet(delegate.entrySet());
@ -130,6 +115,10 @@ public class UnmodifiableTrie<K, V> implements Trie<K, V>, Serializable, Unmodif
throw new UnsupportedOperationException();
}
public int size() {
return delegate.size();
}
public K firstKey() {
return delegate.firstKey();
}
@ -150,34 +139,29 @@ public class UnmodifiableTrie<K, V> implements Trie<K, V>, Serializable, Unmodif
return Collections.unmodifiableSortedMap(delegate.tailMap(fromKey));
}
public SortedMap<K, V> getPrefixedBy(final K key, final int offset, final int length) {
return Collections.unmodifiableSortedMap(delegate.getPrefixedBy(key, offset, length));
}
public SortedMap<K, V> getPrefixedBy(final K key, final int length) {
return Collections.unmodifiableSortedMap(delegate.getPrefixedBy(key, length));
}
public SortedMap<K, V> getPrefixedBy(final K key) {
return Collections.unmodifiableSortedMap(delegate.getPrefixedBy(key));
}
public SortedMap<K, V> getPrefixedByBits(final K key, final int lengthInBits) {
return Collections.unmodifiableSortedMap(delegate.getPrefixedByBits(key, lengthInBits));
}
public SortedMap<K, V> getPrefixedByBits(final K key, final int offsetInBits, final int lengthInBits) {
return Collections.unmodifiableSortedMap(delegate.getPrefixedByBits(key, offsetInBits, lengthInBits));
public SortedMap<K, V> prefixMap(final K key) {
return Collections.unmodifiableSortedMap(delegate.prefixMap(key));
}
public Comparator<? super K> comparator() {
return delegate.comparator();
}
public int size() {
return delegate.size();
//-----------------------------------------------------------------------
public OrderedMapIterator<K, V> mapIterator() {
final OrderedMapIterator<K, V> it = delegate.mapIterator();
return UnmodifiableOrderedMapIterator.unmodifiableOrderedMapIterator(it);
}
public K nextKey(K key) {
return delegate.nextKey(key);
}
public K previousKey(K key) {
return delegate.previousKey(key);
}
//-----------------------------------------------------------------------
@Override
public int hashCode() {
return delegate.hashCode();
@ -192,4 +176,5 @@ public class UnmodifiableTrie<K, V> implements Trie<K, V>, Serializable, Unmodif
public String toString() {
return delegate.toString();
}
}

View File

@ -1,172 +0,0 @@
/*
* 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.collections4.trie.analyzer;
import org.apache.commons.collections4.trie.KeyAnalyzer;
/**
* A {@link KeyAnalyzer} for byte[]s.
*
* @since 4.0
* @version $Id$
*/
public class ByteArrayKeyAnalyzer extends KeyAnalyzer<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(final 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(final 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;
}
public int bitsPerElement() {
return LENGTH;
}
public int lengthInBits(final byte[] key) {
return key != null ? key.length * bitsPerElement() : 0;
}
public boolean isBitSet(final byte[] key, final int bitIndex, final int lengthInBits) {
if (key == null) {
return false;
}
final int prefix = maxLengthInBits - lengthInBits;
final int keyBitIndex = bitIndex - prefix;
if (keyBitIndex >= lengthInBits || keyBitIndex < 0) {
return false;
}
final int index = keyBitIndex / LENGTH;
final int bit = keyBitIndex % LENGTH;
return (key[index] & mask(bit)) != 0;
}
public int bitIndex(final byte[] key, final int offsetInBits, final int lengthInBits,
byte[] other, final int otherOffsetInBits, final int otherLengthInBits) {
if (other == null) {
other = NULL;
}
boolean allNull = true;
final int length = Math.max(lengthInBits, otherLengthInBits);
final int prefix = maxLengthInBits - length;
if (prefix < 0) {
return KeyAnalyzer.OUT_OF_BOUNDS_BIT_KEY;
}
for (int i = 0; i < length; i++) {
final int index = prefix + offsetInBits + i;
final boolean value = isBitSet(key, index, lengthInBits);
if (value) {
allNull = false;
}
final int otherIndex = prefix + otherOffsetInBits + i;
final boolean otherValue = isBitSet(other, otherIndex, otherLengthInBits);
if (value != otherValue) {
return index;
}
}
if (allNull) {
return KeyAnalyzer.NULL_BIT_KEY;
}
return KeyAnalyzer.EQUAL_BIT_KEY;
}
public boolean isPrefix(final byte[] prefix, final int offsetInBits, final int lengthInBits, final byte[] key) {
final int keyLength = lengthInBits(key);
if (lengthInBits > keyLength) {
return false;
}
final 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;
}
@Override
public int compare(final byte[] o1, final byte[] o2) {
if (o1 == null) {
return o2 == null ? 0 : -1;
} else if (o2 == null) {
return 1;
}
if (o1.length != o2.length) {
return o1.length - o2.length;
}
for (int i = 0; i < o1.length; i++) {
final int diff = (o1[i] & 0xFF) - (o2[i] & 0xFF);
if (diff != 0) {
return diff;
}
}
return 0;
}
}

View File

@ -1,96 +0,0 @@
/*
* 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.collections4.trie.analyzer;
import org.apache.commons.collections4.trie.KeyAnalyzer;
/**
* A {@link KeyAnalyzer} for {@link Byte}s.
*
* @since 4.0
* @version $Id$
*/
public class ByteKeyAnalyzer extends KeyAnalyzer<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(final int bit) {
return MSB >>> bit;
}
public int bitsPerElement() {
return 1;
}
public int lengthInBits(final Byte key) {
return LENGTH;
}
public boolean isBitSet(final Byte key, final int bitIndex, final int lengthInBits) {
return (key.intValue() & mask(bitIndex)) != 0;
}
public int bitIndex(final Byte key, final int offsetInBits, final int lengthInBits,
final Byte other, final int otherOffsetInBits, final int otherLengthInBits) {
if (offsetInBits != 0 || otherOffsetInBits != 0) {
throw new IllegalArgumentException("offsetInBits=" + offsetInBits
+ ", otherOffsetInBits=" + otherOffsetInBits);
}
final byte keyValue = key.byteValue();
if (keyValue == 0) {
return NULL_BIT_KEY;
}
final byte otherValue = other != null ? other.byteValue() : 0;
if (keyValue != otherValue) {
final int xorValue = keyValue ^ otherValue;
for (int i = 0; i < LENGTH; i++) {
if ((xorValue & mask(i)) != 0) {
return i;
}
}
}
return KeyAnalyzer.EQUAL_BIT_KEY;
}
public boolean isPrefix(final Byte prefix, final int offsetInBits, final int lengthInBits, final Byte key) {
final int value1 = prefix.byteValue() << offsetInBits;
final int value2 = key.byteValue();
int mask = 0;
for (int i = 0; i < lengthInBits; i++) {
mask |= 0x1 << i;
}
return (value1 & mask) == (value2 & mask);
}
}

View File

@ -1,137 +0,0 @@
/*
* 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.collections4.trie.analyzer;
import org.apache.commons.collections4.trie.KeyAnalyzer;
/**
* An {@link KeyAnalyzer} for {@code char[]}s.
*
* @since 4.0
* @version $Id$
*/
public class CharArrayKeyAnalyzer extends KeyAnalyzer<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(final int bit) {
return MSB >>> bit;
}
public int bitsPerElement() {
return LENGTH;
}
public int lengthInBits(final char[] key) {
return key != null ? key.length * LENGTH : 0;
}
public int bitIndex(final char[] key, final int offsetInBits, final int lengthInBits,
final char[] other, final int otherOffsetInBits, final 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");
}
final int beginIndex1 = offsetInBits / LENGTH;
final int beginIndex2 = otherOffsetInBits / LENGTH;
final int endIndex1 = beginIndex1 + lengthInBits / LENGTH;
final int endIndex2 = beginIndex2 + otherLengthInBits / LENGTH;
final 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++) {
final int index1 = beginIndex1 + i;
final 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) {
final 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;
}
public boolean isBitSet(final char[] key, final int bitIndex, final int lengthInBits) {
if (key == null || bitIndex >= lengthInBits) {
return false;
}
final int index = bitIndex / LENGTH;
final int bit = bitIndex % LENGTH;
return (key[index] & mask(bit)) != 0;
}
public boolean isPrefix(final char[] prefix, final int offsetInBits, final int lengthInBits, final char[] key) {
if (offsetInBits % LENGTH != 0 || lengthInBits % LENGTH != 0) {
throw new IllegalArgumentException(
"Cannot determine prefix outside of Character boundaries");
}
final int off = offsetInBits / LENGTH;
final int len = lengthInBits / LENGTH;
for (int i = 0; i < len; i ++) {
if (prefix[i + off] != key[i]) {
return false;
}
}
return true;
}
}

View File

@ -1,98 +0,0 @@
/*
* 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.collections4.trie.analyzer;
import org.apache.commons.collections4.trie.KeyAnalyzer;
/**
* A {@link KeyAnalyzer} for {@link Character}s.
*
* @since 4.0
* @version $Id$
*/
public class CharacterKeyAnalyzer extends KeyAnalyzer<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(final int bit) {
return MSB >>> bit;
}
public int bitsPerElement() {
return 1;
}
public int lengthInBits(final Character key) {
return LENGTH;
}
public boolean isBitSet(final Character key, final int bitIndex, final int lengthInBits) {
return (key.charValue() & mask(bitIndex)) != 0;
}
public int bitIndex(final Character key, final int offsetInBits, final int lengthInBits,
final Character other, final int otherOffsetInBits, final int otherLengthInBits) {
if (offsetInBits != 0 || otherOffsetInBits != 0) {
throw new IllegalArgumentException("offsetInBits=" + offsetInBits
+ ", otherOffsetInBits=" + otherOffsetInBits);
}
final char keyValue = key.charValue();
if (keyValue == Character.MIN_VALUE) {
return NULL_BIT_KEY;
}
final char otherValue = other != null ? other.charValue() : Character.MIN_VALUE;
if (keyValue != otherValue) {
final int xorValue = keyValue ^ otherValue;
for (int i = 0; i < LENGTH; i++) {
if ((xorValue & mask(i)) != 0) {
return i;
}
}
}
return KeyAnalyzer.EQUAL_BIT_KEY;
}
public boolean isPrefix(final Character prefix, final int offsetInBits,
final int lengthInBits, final Character key) {
final int value1 = prefix.charValue() << offsetInBits;
final int value2 = key.charValue();
int mask = 0;
for(int i = 0; i < lengthInBits; i++) {
mask |= 0x1 << i;
}
return (value1 & mask) == (value2 & mask);
}
}

View File

@ -1,97 +0,0 @@
/*
* 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.collections4.trie.analyzer;
import org.apache.commons.collections4.trie.KeyAnalyzer;
/**
* A {@link KeyAnalyzer} for {@link Integer}s.
*
* @since 4.0
* @version $Id$
*/
public class IntegerKeyAnalyzer extends KeyAnalyzer<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(final int bit) {
return MSB >>> bit;
}
public int bitsPerElement() {
return 1;
}
public int lengthInBits(final Integer key) {
return LENGTH;
}
public boolean isBitSet(final Integer key, final int bitIndex, final int lengthInBits) {
return (key.intValue() & mask(bitIndex)) != 0;
}
public int bitIndex(final Integer key, final int offsetInBits, final int lengthInBits,
final Integer other, final int otherOffsetInBits, final int otherLengthInBits) {
if (offsetInBits != 0 || otherOffsetInBits != 0) {
throw new IllegalArgumentException("offsetInBits=" + offsetInBits
+ ", otherOffsetInBits=" + otherOffsetInBits);
}
final int keyValue = key.intValue();
if (keyValue == 0) {
return NULL_BIT_KEY;
}
final int otherValue = other != null ? other.intValue() : 0;
if (keyValue != otherValue) {
final int xorValue = keyValue ^ otherValue;
for (int i = 0; i < LENGTH; i++) {
if ((xorValue & mask(i)) != 0) {
return i;
}
}
}
return KeyAnalyzer.EQUAL_BIT_KEY;
}
public boolean isPrefix(final Integer prefix, final int offsetInBits,
final int lengthInBits, final Integer key) {
final int value1 = prefix.intValue() << offsetInBits;
final int value2 = key.intValue();
int mask = 0;
for (int i = 0; i < lengthInBits; i++) {
mask |= 0x1 << i;
}
return (value1 & mask) == (value2 & mask);
}
}

View File

@ -1,97 +0,0 @@
/*
* 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.collections4.trie.analyzer;
import org.apache.commons.collections4.trie.KeyAnalyzer;
/**
* A {@link KeyAnalyzer} for {@link Long}s.
*
* @since 4.0
* @version $Id$
*/
public class LongKeyAnalyzer extends KeyAnalyzer<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(final int bit) {
return MSB >>> bit;
}
public int bitsPerElement() {
return 1;
}
public int lengthInBits(final Long key) {
return LENGTH;
}
public boolean isBitSet(final Long key, final int bitIndex, final int lengthInBits) {
return (key.longValue() & mask(bitIndex)) != 0;
}
public int bitIndex(final Long key, final int offsetInBits, final int lengthInBits,
final Long other, final int otherOffsetInBits, final int otherLengthInBits) {
if (offsetInBits != 0 || otherOffsetInBits != 0) {
throw new IllegalArgumentException("offsetInBits=" + offsetInBits
+ ", otherOffsetInBits=" + otherOffsetInBits);
}
final long keyValue = key.longValue();
if (keyValue == 0L) {
return NULL_BIT_KEY;
}
final long otherValue = other != null ? other.longValue() : 0L;
if (keyValue != otherValue) {
final long xorValue = keyValue ^ otherValue;
for (int i = 0; i < LENGTH; i++) {
if ((xorValue & mask(i)) != 0L) {
return i;
}
}
}
return KeyAnalyzer.EQUAL_BIT_KEY;
}
public boolean isPrefix(final Long prefix, final int offsetInBits,
final int lengthInBits, final Long key) {
final long value1 = prefix.longValue() << offsetInBits;
final long value2 = key.longValue();
long mask = 0L;
for (int i = 0; i < lengthInBits; i++) {
mask |= 0x1L << i;
}
return (value1 & mask) == (value2 & mask);
}
}

View File

@ -1,103 +0,0 @@
/*
* 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.collections4.trie.analyzer;
import org.apache.commons.collections4.trie.KeyAnalyzer;
/**
* A {@link KeyAnalyzer} for {@link Short}s.
*
* @since 4.0
* @version $Id$
*/
public class ShortKeyAnalyzer extends 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(final int bit) {
return MSB >>> bit;
}
public int bitsPerElement() {
return 1;
}
public int lengthInBits(final Short key) {
return LENGTH;
}
public boolean isBitSet(final Short key, final int bitIndex, final int lengthInBits) {
return (key.intValue() & mask(bitIndex)) != 0;
}
public int bitIndex(final Short key, final int offsetInBits, final int lengthInBits,
final Short other, final int otherOffsetInBits, final int otherLengthInBits) {
if (offsetInBits != 0 || otherOffsetInBits != 0) {
throw new IllegalArgumentException("offsetInBits=" + offsetInBits
+ ", otherOffsetInBits=" + otherOffsetInBits);
}
final int keyValue = key.shortValue();
if (keyValue == 0) {
return NULL_BIT_KEY;
}
final int otherValue = other != null ? other.shortValue() : 0;
if (keyValue != otherValue) {
final int xorValue = keyValue ^ otherValue;
for (int i = 0; i < LENGTH; i++) {
if ((xorValue & mask(i)) != 0) {
return i;
}
}
}
return KeyAnalyzer.EQUAL_BIT_KEY;
}
public boolean isPrefix(final Short prefix, final int offsetInBits,
final int lengthInBits, final Short key) {
final int value1 = prefix.shortValue() << offsetInBits;
final int value2 = key.shortValue();
int mask = 0;
for (int i = 0; i < lengthInBits; i++) {
mask |= 0x1 << i;
}
return (value1 & mask) == (value2 & mask);
}
@Override
public int compare(final Short o1, final Short o2) {
return o1.compareTo(o2);
}
}

View File

@ -0,0 +1,58 @@
/*
* 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.collections4.trie;
import junit.framework.Test;
import org.apache.commons.collections4.BulkTest;
import org.apache.commons.collections4.OrderedMap;
import org.apache.commons.collections4.map.AbstractOrderedMapTest;
/**
* JUnit test of the OrderedMap interface of a PatriciaTrie.
*
* @since 4.0
* @version $Id$
*/
public class PatriciaTrie2Test<V> extends AbstractOrderedMapTest<String, V> {
public PatriciaTrie2Test(final String testName) {
super(testName);
}
public static Test suite() {
return BulkTest.makeSuite(PatriciaTrie2Test.class);
}
@Override
public OrderedMap<String, V> makeObject() {
return new PatriciaTrie<V>();
}
@Override
public boolean isAllowNullKey() {
return false;
}
//-----------------------------------------------------------------------
@Override
public String getCompatibilityVersion() {
return "4";
}
}

View File

@ -0,0 +1,106 @@
/*
* 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.collections4.trie;
import java.util.SortedMap;
import junit.framework.Test;
import org.apache.commons.collections4.BulkTest;
import org.apache.commons.collections4.Unmodifiable;
import org.apache.commons.collections4.map.AbstractSortedMapTest;
import org.apache.commons.collections4.map.UnmodifiableSortedMap;
/**
* Extension of {@link AbstractSortedMapTest} for exercising the
* {@link UnmodifiableTrie} implementation.
*
* @since 4.0
* @version $Id$
*/
public class UnmodifiableTrieTest<V> extends AbstractSortedMapTest<String, V> {
public UnmodifiableTrieTest(final String testName) {
super(testName);
}
public static Test suite() {
return BulkTest.makeSuite(UnmodifiableTrieTest.class);
}
//-------------------------------------------------------------------
@Override
public SortedMap<String, V> makeObject() {
return UnmodifiableSortedMap.unmodifiableSortedMap(new PatriciaTrie<V>());
}
@Override
public boolean isPutChangeSupported() {
return false;
}
@Override
public boolean isPutAddSupported() {
return false;
}
@Override
public boolean isRemoveSupported() {
return false;
}
@Override
public SortedMap<String, V> makeFullMap() {
final SortedMap<String, V> m = new PatriciaTrie<V>();
addSampleMappings(m);
return UnmodifiableSortedMap.unmodifiableSortedMap(m);
}
//-----------------------------------------------------------------------
public void testUnmodifiable() {
assertTrue(makeObject() instanceof Unmodifiable);
assertTrue(makeFullMap() instanceof Unmodifiable);
}
public void testDecorateFactory() {
final SortedMap<String, V> map = makeFullMap();
assertSame(map, UnmodifiableSortedMap.unmodifiableSortedMap(map));
try {
UnmodifiableSortedMap.unmodifiableSortedMap(null);
fail();
} catch (final IllegalArgumentException ex) {}
}
@Override
public String getCompatibilityVersion() {
return "4";
}
// public void testCreate() throws Exception {
// resetEmpty();
// writeExternalFormToDisk(
// (java.io.Serializable) map,
// "src/test/resources/data/test/UnmodifiableTrie.emptyCollection.version4.obj");
// resetFull();
// writeExternalFormToDisk(
// (java.io.Serializable) map,
// "src/test/resources/data/test/UnmodifiableTrie.fullCollection.version4.obj");
// }
}

View File

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