/*******************************************************************************
* /***
* *
* * Copyright 2013 Netflix, Inc.
* *
* * 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.netflix.paas;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.lang.StringUtils;
import org.junit.Test;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
public class TrieTest {
public static interface TrieNodeVisitor {
void visit(TrieNode node);
}
public static interface TrieNode {
public TrieNode getOrCreateChild(Character c);
public TrieNode getChild(Character c);
public Character getCharacter();
public void setIsWord(boolean isWord);
public boolean isWord();
public void accept(TrieNodeVisitor visitor);
}
public static class HashMapTrieNode implements TrieNode {
private final ConcurrentMap<Character, TrieNode> children = Maps.newConcurrentMap();
private final Character character;
private volatile boolean isWord = false;
public HashMapTrieNode(Character ch) {
this.character = ch;
}
public TrieNode getChild(Character c) {
return children.get(c);
}
public TrieNode getOrCreateChild(Character c) {
TrieNode node = children.get(c);
if (node == null) {
TrieNode newNode = new HashMapTrieNode(c);
System.out.println("Create child : " + c);
node = children.putIfAbsent(c, newNode);
if (node == null)
return newNode;
}
return node;
}
public void setIsWord(boolean isWord) {
this.isWord = isWord;
}
public boolean isWord() {
return isWord;
}
@Override
public Character getCharacter() {
return this.character;
}
public void accept(TrieNodeVisitor visitor) {
List<TrieNode> nodes = Lists.newArrayList(children.values());
Collections.sort(nodes, new Comparator<TrieNode>() {
@Override
public int compare(TrieNode arg0, TrieNode arg1) {
return arg0.getCharacter().compareTo(arg1.getCharacter());
}
});
for (TrieNode node : nodes) {
visitor.visit(node);
}
}
}
public static class AtomicTrieNode implements TrieNode {
private final AtomicReference<Map<Character, TrieNode>> children = new AtomicReference<Map<Character, TrieNode>>();
private final Character character;
private volatile boolean isWord = false;
public AtomicTrieNode(Character ch) {
this.children.set(new HashMap<Character, TrieNode>());
this.character = ch;
}
public TrieNode getChild(Character c) {
return children.get().get(c);
}
public TrieNode getOrCreateChild(Character c) {
TrieNode node = children.get().get(c);
if (node == null) {
Map<Character, TrieNode> newChs;
do {
Map<Character, TrieNode> chs = children.get();
node = chs.get(c);
if (node != null) {
break;
}
newChs = Maps.newHashMap(chs);
node = new AtomicTrieNode(c);
newChs.put(c, node);
if (children.compareAndSet(chs, newChs)) {
break;
}
}
while (true);
}
return node;
}
public void setIsWord(boolean isWord) {
this.isWord = isWord;
}
public boolean isWord() {
return isWord;
}
@Override
public Character getCharacter() {
return this.character;
}
public void accept(TrieNodeVisitor visitor) {
List<TrieNode> nodes = Lists.newArrayList(children.get().values());
Collections.sort(nodes, new Comparator<TrieNode>() {
@Override
public int compare(TrieNode arg0, TrieNode arg1) {
return arg0.getCharacter().compareTo(arg1.getCharacter());
}
});
for (TrieNode node : nodes) {
visitor.visit(node);
}
}
}
public static class Trie {
private TrieNode root = new AtomicTrieNode(null);
public boolean addWord(String word) {
word = word.toUpperCase();
StringCharacterIterator iter = new StringCharacterIterator(word);
TrieNode current = root;
for (Character ch = iter.first(); ch != CharacterIterator.DONE; ch = iter.next()) {
current = current.getOrCreateChild(ch);
}
current.setIsWord(true);
return true;
}
public boolean containsWord(String word) {
word = word.toUpperCase();
StringCharacterIterator iter = new StringCharacterIterator(word);
TrieNode current = root;
for (Character ch = iter.first(); ch != CharacterIterator.DONE; ch = iter.next()) {
current = current.getChild(ch);
if (current == null)
return false;
}
return current.isWord();
}
public void accept(TrieNodeVisitor visitor) {
visitor.visit(root);
}
}
public static class SimpleTriePrinter implements TrieNodeVisitor {
private String prefix = "";
@Override
public void visit(TrieNode node) {
System.out.println(prefix + node.getCharacter());
prefix += " ";
node.accept(this);
prefix = StringUtils.substring(prefix, 1);
}
}
@Test
public void testTrie() {
String[] common = {"the","of","and","a","to","in","is","you","that","it","he","was","for","on","are","as","with","his","they","I","at","be","this","have","from","or","one","had","by","word","but","not","what","all","were","we","when","your","can","said","there","use","an","each","which","she","do","how","their","if","will","up","other","about","out","many","then","them","these","so","some","her","would","make","like","him","into","time","has","look","two","more","write","go","see","number","no","way","could","people","my","than","first","water","been","call","who","oil","its","now","find","long","down","day","did","get","come","made","may","part"};
Trie trie = new Trie();
for (String word : common) {
trie.addWord(word);
}
trie.accept(new SimpleTriePrinter());
}
}