/*
* Concept profile generation tool suite
* Copyright (C) 2015 Biosemantics Group, Erasmus University Medical Center,
* Rotterdam, The Netherlands
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package org.erasmusmc.collections;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Trie<T> {
private TrieNode root;
private CharIterator charIterator;
private int direction;
public static final int FORWARD = 0;
public static final int BACKWARD = 1;
public Trie(int direction){
root = new TrieNode();
this.direction = direction;
if (direction == FORWARD)
charIterator = new ForwardCharIterator();
else
charIterator = new BackwardCharIterator();
}
/**
* Add a key to the trie, and link it to the value
* @param key
* @param value
*/
public void put(String key, T value){
TrieNode node = root;
charIterator.setString(key);
while (charIterator.hasNext()){
char ch = charIterator.next();
int index = node.binarySearch(ch);
if (index < node.size() && node.getKeyForIndex(index) == ch){ //Entry with char already exists
Object object = node.getObjectForIndex(index);
if (object instanceof TrieNode){
node = (TrieNode) object;
if (!charIterator.hasNext()) //end of key
node.value = value;
} else {
if (!charIterator.hasNext()) //end of key
node.setObject(index, value);
else {
TrieNode tempNode = new TrieNode();
tempNode.value = object;
node.setObject(index, tempNode);
node = tempNode;
}
}
} else { //Entry with char does not exist
if (!charIterator.hasNext()) //end of key
node.insert(index, ch, value);
else {
TrieNode tempNode = new TrieNode();
node.insert(index, ch, tempNode);
node = tempNode;
}
}
}
}
/**
* Get the longest key found in the string (at the beginning or end)
* @param string
* @return
*/
@SuppressWarnings("unchecked")
public T getLongest(String string){
TrieNode node = root;
charIterator.setString(string);
T result = null;
while (charIterator.hasNext()){
char ch = charIterator.next();
int index = node.indexOf(ch);
if (index == -1)
return result;
else {
Object object = node.getObjectForIndex(index);
if (object instanceof TrieNode){
node = (TrieNode)object;
if (node.value != null)
result = (T)node.value;
} else
return (T)object;
}
}
return result;
}
/**
* Get all the keys found in the string (at the beginning or end)
* @param string
* @return
*/
@SuppressWarnings("unchecked")
public List<T> getAll(String string){
TrieNode node = root;
charIterator.setString(string);
List<T> result = new ArrayList<T>();
while (charIterator.hasNext()){
char ch = charIterator.next();
int index = node.indexOf(ch);
if (index == -1)
return result;
else {
Object object = node.getObjectForIndex(index);
if (object instanceof TrieNode){
node = (TrieNode)object;
if (node.value != null)
result.add((T)node.value);
} else {
result.add((T)object);
return result;
}
}
}
return result;
}
@SuppressWarnings("unchecked")
public T getFullMatch(String key){
TrieNode node = root;
charIterator.setString(key);
while (charIterator.hasNext()){
char ch = charIterator.next();
int index = node.indexOf(ch);
if (index == -1)
return null;
else {
Object object = node.getObjectForIndex(index);
if (object instanceof TrieNode){
node = (TrieNode)object;
if (!charIterator.hasNext())
return (T)node.value;
} else {
if (!charIterator.hasNext())
return (T)object;
return null;
}
}
}
return null;
}
public int size(){
return countValues(root);
}
private int countValues(TrieNode node) {
int count = 0;
if (node.value != null)
count++;
for (int i = 0; i < node.size(); i++){
Object object = node.getObjectForIndex(i);
if (object instanceof TrieNode)
count += countValues((TrieNode)object);
else
count++;
}
return count;
}
public Iterator<Pair<String,T>>getEntryIterator() {
return new EntryIterator();
}
private class EntryIterator implements Iterator<Pair<String,T>>{
private IntList path;
private Pair<String,T> buffer;
public EntryIterator(){
path = new IntList();
buffer = findNext();
}
public boolean hasNext() {
return buffer != null;
}
public Pair<String,T> next() {
Pair<String,T> result = buffer;
buffer = findNext();
return result;
}
@SuppressWarnings("unchecked")
private Pair<String,T> findNext() {
Object currentObject = traversePath(0);
int skip = 0;
if (!(currentObject instanceof TrieNode)){
skip++;
currentObject = traversePath(skip);
}
while (currentObject != null){
int index;
if (skip == 0)
index = 0; //New at this node: start at begin
else
index = path.getInt(path.size()-skip) + 1; //One further
if (index == ((TrieNode)currentObject).size()){ //This node is finished
skip++;
if (skip > path.size())
return null;
currentObject = traversePath(skip);
} else {
//Construct the path to the new node:
path = path.subList(0, path.size()-skip);
path.add(index);
skip = 0;
//Fetch the new node:
currentObject = ((TrieNode)currentObject).getObjectForIndex(index);
if (currentObject instanceof TrieNode){
if (((TrieNode)currentObject).value != null)
return new Pair<String, T>(reconstructKey(),(T)((TrieNode)currentObject).value);
} else {
return new Pair<String, T>(reconstructKey(),(T)currentObject);
}
}
}
return null;
}
private Object traversePath(int skip) {
Object node = root;
for (int i = 0; i < path.size()-skip; i++)
node = ((TrieNode)node).getObjectForIndex(path.getInt(i));
return node;
}
private String reconstructKey(){
TrieNode node = root;
StringBuilder result = new StringBuilder();
for (int i = 0; i < path.size(); i++){
int index = path.getInt(i);
result.append(node.getKeyForIndex(index));
if (i < path.size()-1)
node = (TrieNode)node.getObjectForIndex(index);
}
if (direction == FORWARD)
return result.toString();
else
return result.reverse().toString();
}
public void remove() {
System.err.println("Calling unimplemented remove method");
}
}
private interface CharIterator {
public boolean hasNext();
public char next();
public void setString(String string);
}
private class ForwardCharIterator implements CharIterator {
private String string;
private int index;
public void setString(String string){
this.string = string;
index = 0;
}
public boolean hasNext() {
return (index < string.length());
}
public char next() {
return string.charAt(index++);
}
}
private class BackwardCharIterator implements CharIterator {
private String string;
private int index;
public void setString(String string){
this.string = string;
index = string.length()-1;
}
public boolean hasNext() {
return (index > -1);
}
public char next() {
return string.charAt(index--);
}
}
}