1   package org.daisy.pipeline.nlp.impl;
2   
3   import java.util.Collection;
4   import java.util.TreeMap;
5   
6   /**
7    * Prefix matching strategy based on a naive trie structure.
8    * 
9    * It is not memory-efficient since it uses java objects (i.e. Character) as map
10   * keys, among other issues. A better way would be to perform binary searches on
11   * native char[].
12   */
13  public class PrefixMatchStringFinder implements IStringFinder {
14  
15  	static class Trie {
16  		TreeMap<Character, Trie> children = new TreeMap<Character, Trie>();
17  		boolean endOfWord = false;
18  	}
19  
20  	private Trie mTrieRoot;
21  
22  	@Override
23  	public void compile(Collection<String> matchable) {
24  		mTrieRoot = new Trie();
25  		for (String s : matchable) {
26  			Trie current = mTrieRoot;
27  			for (int k = 0; k < s.length(); ++k) {
28  				Character c = s.charAt(k);
29  				Trie next = current.children.get(c);
30  				if (next == null) {
31  					next = new Trie();
32  					current.children.put(c, next);
33  				}
34  				current = next;
35  			}
36  			current.endOfWord = true;
37  		}
38  	}
39  
40  	/**
41  	 * Match the beginning of @param input with the provided collection.
42  	 */
43  	@Override
44  	public String find(String input) {
45  		Trie current = mTrieRoot;
46  		int longestMatch = -1;
47  		for (int k = 0; k < input.length() && current != null; ++k) {
48  			if (current.endOfWord) {
49  				longestMatch = k;
50  			}
51  			current = current.children.get(input.charAt(k));
52  		}
53  		if (current != null && current.endOfWord) {
54  			return input;
55  		} else if (longestMatch > -1) {
56  			return input.substring(0, longestMatch);
57  		}
58  
59  		return null;
60  	}
61  
62  	@Override
63  	public boolean threadsafe() {
64  		return true;
65  	}
66  }