--- /dev/null
+package org.apache.lucene.search.suggest.tst;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.lucene.search.suggest.Lookup;
+import org.apache.lucene.search.suggest.SortedTermFreqIteratorWrapper;
+import org.apache.lucene.search.spell.SortedIterator;
+import org.apache.lucene.search.spell.TermFreqIterator;
+
+public class TSTLookup extends Lookup {
+ TernaryTreeNode root = new TernaryTreeNode();
+ TSTAutocomplete autocomplete = new TSTAutocomplete();
+
+ @Override
+ public void build(TermFreqIterator tfit) throws IOException {
+ root = new TernaryTreeNode();
+ // buffer first
+ if (!(tfit instanceof SortedIterator)) {
+ // make sure it's sorted
+ tfit = new SortedTermFreqIteratorWrapper(tfit);
+ }
+
+ ArrayList<String> tokens = new ArrayList<String>();
+ ArrayList<Float> vals = new ArrayList<Float>();
+ while (tfit.hasNext()) {
+ tokens.add(tfit.next());
+ vals.add(new Float(tfit.freq()));
+ }
+ autocomplete.balancedTree(tokens.toArray(), vals.toArray(), 0, tokens.size() - 1, root);
+ }
+
+ @Override
+ public boolean add(String key, Object value) {
+ autocomplete.insert(root, key, value, 0);
+ // XXX we don't know if a new node was created
+ return true;
+ }
+
+ @Override
+ public Object get(String key) {
+ List<TernaryTreeNode> list = autocomplete.prefixCompletion(root, key, 0);
+ if (list == null || list.isEmpty()) {
+ return null;
+ }
+ for (TernaryTreeNode n : list) {
+ if (n.token.equals(key)) {
+ return n.val;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public List<LookupResult> lookup(String key, boolean onlyMorePopular, int num) {
+ List<TernaryTreeNode> list = autocomplete.prefixCompletion(root, key, 0);
+ List<LookupResult> res = new ArrayList<LookupResult>();
+ if (list == null || list.size() == 0) {
+ return res;
+ }
+ int maxCnt = Math.min(num, list.size());
+ if (onlyMorePopular) {
+ LookupPriorityQueue queue = new LookupPriorityQueue(num);
+ for (TernaryTreeNode ttn : list) {
+ queue.insertWithOverflow(new LookupResult(ttn.token, (Float)ttn.val));
+ }
+ for (LookupResult lr : queue.getResults()) {
+ res.add(lr);
+ }
+ } else {
+ for (int i = 0; i < maxCnt; i++) {
+ TernaryTreeNode ttn = list.get(i);
+ res.add(new LookupResult(ttn.token, (Float)ttn.val));
+ }
+ }
+ return res;
+ }
+
+ public static final String FILENAME = "tst.dat";
+
+ private static final byte LO_KID = 0x01;
+ private static final byte EQ_KID = 0x02;
+ private static final byte HI_KID = 0x04;
+ private static final byte HAS_TOKEN = 0x08;
+ private static final byte HAS_VALUE = 0x10;
+
+ @Override
+ public synchronized boolean load(File storeDir) throws IOException {
+ File data = new File(storeDir, FILENAME);
+ if (!data.exists() || !data.canRead()) {
+ return false;
+ }
+ DataInputStream in = new DataInputStream(new FileInputStream(data));
+ root = new TernaryTreeNode();
+ try {
+ readRecursively(in, root);
+ } finally {
+ in.close();
+ }
+ return true;
+ }
+
+ // pre-order traversal
+ private void readRecursively(DataInputStream in, TernaryTreeNode node) throws IOException {
+ node.splitchar = in.readChar();
+ byte mask = in.readByte();
+ if ((mask & HAS_TOKEN) != 0) {
+ node.token = in.readUTF();
+ }
+ if ((mask & HAS_VALUE) != 0) {
+ node.val = new Float(in.readFloat());
+ }
+ if ((mask & LO_KID) != 0) {
+ node.loKid = new TernaryTreeNode();
+ readRecursively(in, node.loKid);
+ }
+ if ((mask & EQ_KID) != 0) {
+ node.eqKid = new TernaryTreeNode();
+ readRecursively(in, node.eqKid);
+ }
+ if ((mask & HI_KID) != 0) {
+ node.hiKid = new TernaryTreeNode();
+ readRecursively(in, node.hiKid);
+ }
+ }
+
+ @Override
+ public synchronized boolean store(File storeDir) throws IOException {
+ if (!storeDir.exists() || !storeDir.isDirectory() || !storeDir.canWrite()) {
+ return false;
+ }
+ File data = new File(storeDir, FILENAME);
+ DataOutputStream out = new DataOutputStream(new FileOutputStream(data));
+ try {
+ writeRecursively(out, root);
+ out.flush();
+ } finally {
+ out.close();
+ }
+ return true;
+ }
+
+ // pre-order traversal
+ private void writeRecursively(DataOutputStream out, TernaryTreeNode node) throws IOException {
+ // write out the current node
+ out.writeChar(node.splitchar);
+ // prepare a mask of kids
+ byte mask = 0;
+ if (node.eqKid != null) mask |= EQ_KID;
+ if (node.loKid != null) mask |= LO_KID;
+ if (node.hiKid != null) mask |= HI_KID;
+ if (node.token != null) mask |= HAS_TOKEN;
+ if (node.val != null) mask |= HAS_VALUE;
+ out.writeByte(mask);
+ if (node.token != null) out.writeUTF(node.token);
+ if (node.val != null) out.writeFloat((Float)node.val);
+ // recurse and write kids
+ if (node.loKid != null) {
+ writeRecursively(out, node.loKid);
+ }
+ if (node.eqKid != null) {
+ writeRecursively(out, node.eqKid);
+ }
+ if (node.hiKid != null) {
+ writeRecursively(out, node.hiKid);
+ }
+ }
+}