|
|
|
@ -2,8 +2,8 @@ package de.anomic.tools;
|
|
|
|
|
|
|
|
|
|
import java.util.Collections;
|
|
|
|
|
import java.util.HashSet;
|
|
|
|
|
import java.util.LinkedList;
|
|
|
|
|
import java.util.Set;
|
|
|
|
|
import java.util.concurrent.LinkedBlockingQueue;
|
|
|
|
|
|
|
|
|
|
import de.anomic.kelondro.text.IndexCell;
|
|
|
|
|
import de.anomic.kelondro.text.referencePrototype.WordReference;
|
|
|
|
@ -23,69 +23,66 @@ public class DidYouMean {
|
|
|
|
|
'q','r','s','t','u','v','w','x','y','z','\u00e4','\u00f6','\u00fc','\u00df'};
|
|
|
|
|
private static final long TIMEOUT = 500;
|
|
|
|
|
|
|
|
|
|
final LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<String>();
|
|
|
|
|
|
|
|
|
|
private final Set<String> set;
|
|
|
|
|
private final IndexCell<WordReference> index;
|
|
|
|
|
private String word;
|
|
|
|
|
private int len;
|
|
|
|
|
|
|
|
|
|
private Thread ChangingOneLetter;
|
|
|
|
|
private Thread AddingOneLetter;
|
|
|
|
|
private Thread DeletingOneLetter;
|
|
|
|
|
private Thread ReversingTwoConsecutiveLetters;
|
|
|
|
|
|
|
|
|
|
private final BlockingQueue bq = new BlockingQueue();
|
|
|
|
|
|
|
|
|
|
public DidYouMean(final IndexCell<WordReference> index) {
|
|
|
|
|
// this.set = Collections.synchronizedSortedSet(new TreeSet<String>(new wordSizeComparator()));
|
|
|
|
|
this.set = Collections.synchronizedSet(new HashSet<String>());
|
|
|
|
|
this.word = "";
|
|
|
|
|
this.len = 0;
|
|
|
|
|
this.index = index;
|
|
|
|
|
|
|
|
|
|
this.ChangingOneLetter = new ChangingOneLetter();
|
|
|
|
|
this.AddingOneLetter = new AddingOneLetter();
|
|
|
|
|
this.DeletingOneLetter = new DeletingOneLetter();
|
|
|
|
|
this.ReversingTwoConsecutiveLetters = new ReversingTwoConsecutiveLetters();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Set<String> getSuggestion(final String word) {
|
|
|
|
|
long startTime = System.currentTimeMillis();
|
|
|
|
|
this.word = word.toLowerCase();
|
|
|
|
|
this.len = word.length();
|
|
|
|
|
|
|
|
|
|
// create worker threads
|
|
|
|
|
Thread[] workers = new Thread[8];
|
|
|
|
|
for (int i=0; i<workers.length; i++) {
|
|
|
|
|
workers[i] = new Worker("WorkerThread_"+i);
|
|
|
|
|
}
|
|
|
|
|
// first push the tasks for calculation of word variations on blocking queue
|
|
|
|
|
bq.push(this.ChangingOneLetter);
|
|
|
|
|
bq.push(this.AddingOneLetter);
|
|
|
|
|
bq.push(this.DeletingOneLetter);
|
|
|
|
|
bq.push(this.ReversingTwoConsecutiveLetters);
|
|
|
|
|
|
|
|
|
|
// check for timeout
|
|
|
|
|
boolean run = true;
|
|
|
|
|
while(run && (System.currentTimeMillis()-startTime) < TIMEOUT) {
|
|
|
|
|
if(bq.size() > 0) {
|
|
|
|
|
run = true;
|
|
|
|
|
} else {
|
|
|
|
|
run = false;
|
|
|
|
|
}
|
|
|
|
|
// create producers
|
|
|
|
|
Thread[] producers = new Thread[4];
|
|
|
|
|
producers[0] = new ChangingOneLetter();
|
|
|
|
|
producers[1] = new AddingOneLetter();
|
|
|
|
|
producers[2] = new DeletingOneLetter();
|
|
|
|
|
producers[3] = new ReversingTwoConsecutiveLetters();
|
|
|
|
|
|
|
|
|
|
// start producers
|
|
|
|
|
for (int i=0; i<producers.length; i++) {
|
|
|
|
|
producers[i].start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// create and start 8 consumers threads
|
|
|
|
|
Thread[] consumers = new Thread[8];
|
|
|
|
|
for (int i=0; i<consumers.length; i++) {
|
|
|
|
|
consumers[i] = new Consumer();
|
|
|
|
|
consumers[i].start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check if timeout has been reached
|
|
|
|
|
while(((System.currentTimeMillis()-startTime) < TIMEOUT) && queue.size()>0) {
|
|
|
|
|
// consume more ...
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// push "poison pill" for each worker thread
|
|
|
|
|
for (int i=0; i<workers.length; i++) {
|
|
|
|
|
bq.push(new Thread() {
|
|
|
|
|
public void run() {
|
|
|
|
|
Thread.currentThread().interrupt();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
// put "poison pill" for each consumer thread
|
|
|
|
|
for (int i=0; i<consumers.length; i++) {
|
|
|
|
|
try {
|
|
|
|
|
queue.put("\n");
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// interupt all remaining producer threads
|
|
|
|
|
for (int i=0; i<producers.length; i++) {
|
|
|
|
|
producers[i].interrupt();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.set.remove(word.toLowerCase());
|
|
|
|
|
Log.logInfo("DidYouMean", "found "+this.set.size()+" terms; execution time: "
|
|
|
|
|
+(System.currentTimeMillis()-startTime)+"ms"+ (run?"(timed out)":""));
|
|
|
|
|
+(System.currentTimeMillis()-startTime)+"ms"+ (queue.size()>0?"(timed out)":""));
|
|
|
|
|
|
|
|
|
|
return this.set;
|
|
|
|
|
|
|
|
|
@ -93,17 +90,17 @@ public class DidYouMean {
|
|
|
|
|
|
|
|
|
|
private class ChangingOneLetter extends Thread {
|
|
|
|
|
|
|
|
|
|
public ChangingOneLetter() {
|
|
|
|
|
this.setName("ChangingOneLetter");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// tests: alphabet.length * len
|
|
|
|
|
public void run() {
|
|
|
|
|
String s;
|
|
|
|
|
for(int i=0; i<len; i++) {
|
|
|
|
|
for(int j=0; j<alphabet.length; j++) {
|
|
|
|
|
s = word.substring(0, i) + alphabet[j] + word.substring(i+1);
|
|
|
|
|
bq.push(new Tester(s));
|
|
|
|
|
try {
|
|
|
|
|
queue.put(s);
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -111,104 +108,75 @@ public class DidYouMean {
|
|
|
|
|
|
|
|
|
|
private class DeletingOneLetter extends Thread {
|
|
|
|
|
|
|
|
|
|
public DeletingOneLetter() {
|
|
|
|
|
this.setName("DeletingOneLetter");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// tests: len
|
|
|
|
|
public void run() {
|
|
|
|
|
String s;
|
|
|
|
|
for(int i=0; i<len;i++) {
|
|
|
|
|
s = word.substring(0, i) + word.substring(i+1);
|
|
|
|
|
bq.push(new Tester(s));
|
|
|
|
|
try {
|
|
|
|
|
queue.put(s);
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class AddingOneLetter extends Thread {
|
|
|
|
|
|
|
|
|
|
public AddingOneLetter() {
|
|
|
|
|
this.setName("AddingOneLetter");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// tests: alphabet.length * len
|
|
|
|
|
public void run() {
|
|
|
|
|
String s;
|
|
|
|
|
for(int i=0; i<=len;i++) {
|
|
|
|
|
for(int j=0; j<alphabet.length; j++) {
|
|
|
|
|
s = word.substring(0, i) + alphabet[j] + word.substring(i);
|
|
|
|
|
bq.push(new Tester(s));
|
|
|
|
|
try {
|
|
|
|
|
queue.put(s);
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class ReversingTwoConsecutiveLetters extends Thread {
|
|
|
|
|
|
|
|
|
|
public ReversingTwoConsecutiveLetters() {
|
|
|
|
|
this.setName("ReversingTwoConsecutiveLetters");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// tests: (len - 1)
|
|
|
|
|
public void run() {
|
|
|
|
|
String s;
|
|
|
|
|
for(int i=0; i<len-1; i++) {
|
|
|
|
|
s = word.substring(0,i)+word.charAt(i+1)+word.charAt(i)+word.substring(i+2);
|
|
|
|
|
bq.push(new Tester(s));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class Tester extends Thread {
|
|
|
|
|
|
|
|
|
|
private String s;
|
|
|
|
|
|
|
|
|
|
public Tester(String s) {
|
|
|
|
|
this.s = s;
|
|
|
|
|
}
|
|
|
|
|
public void run() {
|
|
|
|
|
if (index.has(Word.word2hash(s))) {
|
|
|
|
|
set.add(s);
|
|
|
|
|
try {
|
|
|
|
|
queue.put(s);
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class Worker extends Thread {
|
|
|
|
|
public Worker(String name) {
|
|
|
|
|
super(name);
|
|
|
|
|
start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class Consumer extends Thread {
|
|
|
|
|
|
|
|
|
|
public void run() {
|
|
|
|
|
try {
|
|
|
|
|
while(!isInterrupted()) {
|
|
|
|
|
((Runnable)bq.pop()).run();
|
|
|
|
|
while(true) {
|
|
|
|
|
String s = (String)queue.take();
|
|
|
|
|
if(s.equals("\n"))
|
|
|
|
|
this.interrupt();
|
|
|
|
|
else
|
|
|
|
|
consume(s);
|
|
|
|
|
}
|
|
|
|
|
} catch(InterruptedException e) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class BlockingQueue {
|
|
|
|
|
private final LinkedList<Thread> queue = new LinkedList<Thread>();
|
|
|
|
|
|
|
|
|
|
public void push(Thread t) {
|
|
|
|
|
synchronized(queue) {
|
|
|
|
|
queue.add(t);
|
|
|
|
|
queue.notify();
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public Object pop() throws InterruptedException {
|
|
|
|
|
synchronized(queue) {
|
|
|
|
|
while (queue.isEmpty()) {
|
|
|
|
|
queue.wait();
|
|
|
|
|
}
|
|
|
|
|
return queue.removeFirst();
|
|
|
|
|
void consume(String s) {
|
|
|
|
|
if (index.has(Word.word2hash(s))) {
|
|
|
|
|
set.add(s);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public int size() {
|
|
|
|
|
return queue.size();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
private class wordSizeComparator implements Comparator<String> {
|
|
|
|
|