
com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of concurrent-trees Show documentation
Show all versions of concurrent-trees Show documentation
Concurrent Radix Trees and Concurrent Suffix Trees for Java.
/**
* Copyright 2012 Niall Gallagher
*
* Licensed 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 com.googlecode.concurrenttrees.radixinverted;
import com.googlecode.concurrenttrees.common.CharSequenceUtil;
import com.googlecode.concurrenttrees.common.KeyValuePair;
import com.googlecode.concurrenttrees.radix.ConcurrentRadixTree;
import com.googlecode.concurrenttrees.radix.node.Node;
import com.googlecode.concurrenttrees.radix.node.NodeFactory;
import com.googlecode.concurrenttrees.radix.node.util.PrettyPrintable;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* An implementation of {@link InvertedRadixTree} which supports lock-free concurrent reads, and allows items to be
* added to and to be removed from the tree atomically by background thread(s), without blocking reads.
*
* This implementation is based on {@link ConcurrentRadixTree}.
*
* @author Niall Gallagher
*/
public class ConcurrentInvertedRadixTree implements InvertedRadixTree, PrettyPrintable {
static class ConcurrentInvertedRadixTreeImpl extends ConcurrentRadixTree {
public ConcurrentInvertedRadixTreeImpl(NodeFactory nodeFactory) {
super(nodeFactory);
}
public ConcurrentInvertedRadixTreeImpl(NodeFactory nodeFactory, boolean restrictConcurrency) {
super(nodeFactory, restrictConcurrency);
}
/**
* Traverses the tree based on characters in the given input, and for each node traversed which encodes a key
* in the tree, invokes the given {@link KeyValueHandler} supplying it the key which matched that node and
* the value from the node.
*
* @param input A sequence of characters which controls traversal of the tree
* @param keyValueHandler An object which will be notified of every key and value encountered in the input
*/
protected void scanForKeysAtStartOfInput(CharSequence input, KeyValueHandler keyValueHandler) {
Node currentNode = super.root;
int charsMatched = 0;
final int documentLength = input.length();
outer_loop: while (charsMatched < documentLength) {
Node nextNode = currentNode.getOutgoingEdge(input.charAt(charsMatched));
if (nextNode == null) {
// Next node is a dead end...
//noinspection UnnecessaryLabelOnBreakStatement
break outer_loop;
}
currentNode = nextNode;
CharSequence currentNodeEdgeCharacters = currentNode.getIncomingEdge();
int charsMatchedThisEdge = 0;
for (int i = 0, j = Math.min(currentNodeEdgeCharacters.length(), documentLength - charsMatched); i < j; i++) {
if (currentNodeEdgeCharacters.charAt(i) != input.charAt(charsMatched + i)) {
// Found a difference in chars between character in key and a character in current node.
// Current node is the deepest match (inexact match)....
break outer_loop;
}
charsMatchedThisEdge++;
}
if (charsMatchedThisEdge == currentNodeEdgeCharacters.length()) {
// All characters in the current edge matched, add this number to total chars matched...
charsMatched += charsMatchedThisEdge;
}
if (currentNode.getValue() != null) {
keyValueHandler.handle(input.subSequence(0, charsMatched), currentNode.getValue());
}
}
}
interface KeyValueHandler {
void handle(CharSequence key, Object value);
}
}
private final ConcurrentInvertedRadixTreeImpl radixTree;
/**
* Creates a new {@link ConcurrentInvertedRadixTree} which will use the given {@link NodeFactory} to create nodes.
*
* @param nodeFactory An object which creates {@link Node} objects on-demand, and which might return node
* implementations optimized for storing the values supplied to it for the creation of each node
*/
public ConcurrentInvertedRadixTree(NodeFactory nodeFactory) {
this.radixTree = new ConcurrentInvertedRadixTreeImpl(nodeFactory);
}
/**
* Creates a new {@link ConcurrentInvertedRadixTree} which will use the given {@link NodeFactory} to create nodes.
*
* @param nodeFactory An object which creates {@link Node} objects on-demand, and which might return node
* implementations optimized for storing the values supplied to it for the creation of each node
* @param restrictConcurrency If true, configures use of a {@link java.util.concurrent.locks.ReadWriteLock} allowing
* concurrent reads, except when writes are being performed by other threads, in which case writes block all reads;
* if false, configures lock-free reads; allows concurrent non-blocking reads, even if writes are being performed
* by other threads
*/
public ConcurrentInvertedRadixTree(NodeFactory nodeFactory, boolean restrictConcurrency) {
this.radixTree = new ConcurrentInvertedRadixTreeImpl(nodeFactory, restrictConcurrency);
}
/**
* {@inheritDoc}
*/
@Override
public O put(CharSequence key, O value) {
return radixTree.put(key, value);
}
/**
* {@inheritDoc}
*/
@Override
public O putIfAbsent(CharSequence key, O value) {
return radixTree.putIfAbsent(key, value);
}
/**
* {@inheritDoc}
*/
@Override
public boolean remove(CharSequence key) {
return radixTree.remove(key);
}
/**
* {@inheritDoc}
*/
@Override
public O getValueForExactKey(CharSequence key) {
return radixTree.getValueForExactKey(key);
}
/**
* {@inheritDoc}
*/
@Override
public Set getKeysContainedIn(CharSequence document) {
Iterable documentSuffixes = CharSequenceUtil.generateSuffixes(document);
final Set results = new LinkedHashSet();
for (CharSequence documentSuffix : documentSuffixes) {
radixTree.scanForKeysAtStartOfInput(documentSuffix, new ConcurrentInvertedRadixTreeImpl.KeyValueHandler() {
@Override
public void handle(CharSequence key, Object value) {
String keyString = CharSequenceUtil.toString(key);
results.add(keyString);
}
});
}
return results;
}
/**
* {@inheritDoc}
*/
@Override
public Set getValuesForKeysContainedIn(CharSequence document) {
Iterable documentSuffixes = CharSequenceUtil.generateSuffixes(document);
final Set results = new LinkedHashSet();
for (CharSequence documentSuffix : documentSuffixes) {
radixTree.scanForKeysAtStartOfInput(documentSuffix, new ConcurrentInvertedRadixTreeImpl.KeyValueHandler() {
@Override
public void handle(CharSequence key, Object value) {
@SuppressWarnings({"unchecked"})
O valueTyped = (O)value;
results.add(valueTyped);
}
});
}
return results;
}
/**
* {@inheritDoc}
*/
@Override
public Set> getKeyValuePairsForKeysContainedIn(CharSequence document) {
Iterable documentSuffixes = CharSequenceUtil.generateSuffixes(document);
final Set> results = new LinkedHashSet>();
for (CharSequence documentSuffix : documentSuffixes) {
radixTree.scanForKeysAtStartOfInput(documentSuffix, new ConcurrentInvertedRadixTreeImpl.KeyValueHandler() {
@Override
public void handle(CharSequence key, Object value) {
@SuppressWarnings({"unchecked"})
O valueTyped = (O)value;
String keyString = CharSequenceUtil.toString(key);
results.add(new ConcurrentRadixTree.KeyValuePairImpl(keyString, valueTyped));
}
});
}
return results;
}
@Override
public Node getNode() {
return radixTree.getNode();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy