package org.geogebra.common.util; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; /** * A default implementation of the autocomplete dictionary. This implementation * is based upon the TreeSet collection class to provide quick lookups and * default sorting. All lookups are case insensitive! */ public class LowerCaseDictionary extends HashMap<String, String> implements AutoCompleteDictionary { private static final long serialVersionUID = 1L; private TreeSet<String> treeSet = new TreeSet<String>(); private transient NormalizerMinimal normalizer; /** * constructor * * @param normalizer * normalizer used for dict entries */ public LowerCaseDictionary(NormalizerMinimal normalizer) { this.normalizer = normalizer; } private static final String greatestCommonPrefix(String possiblyNull, String notNull) { if (possiblyNull == null) { return null; } int minLength = Math.min(possiblyNull.length(), notNull.length()); for (int i = 0; i < minLength; i++) { if (possiblyNull.charAt(i) != notNull.charAt(i)) { return possiblyNull.substring(0, i); } } return possiblyNull.substring(0, minLength); } /** * Adds an entry to the dictionary. * * @param s * The string to add to the dictionary. */ @Override public void addEntry(final String s) { String lowerCase = normalizer.transform(s); put(lowerCase, s); treeSet.add(lowerCase); } /** * Removes an entry from the dictionary. * * @param s * The string to remove to the dictionary. * @return True if successful, false if the string is not contained or * cannot be removed. */ @Override public boolean removeEntry(String s) { String lowerCase = s.toLowerCase(); remove(lowerCase); return treeSet.remove(lowerCase); } @Override public Iterator<String> getIterator() { return treeSet.iterator(); } /** * Perform a lookup. This routine returns the closest matching string that * completely starts with the given string, or null if none is found. Note: * the lookup is NOT case sensitive. * * @param curr * The string to use as the base for the lookup. * @return curr The closest matching string that completely contains the * given string. */ @Override public String lookup(final String curr) { if (curr == null || "".equals(curr)) { return null; } String currLowerCase = curr.toLowerCase(); try { SortedSet<String> tailSet = treeSet.tailSet(currLowerCase); if (tailSet != null) { String firstObj = tailSet.first(); if (firstObj != null) { String first = firstObj; if (first.startsWith(currLowerCase)) { String ret = get(first); return ret; } } } } catch (Exception e) { return null; } return null; } /** * Find all possible completions for the string curr; return null if none * exists * * @param curr * The string to use as the base for the lookup * @return a list of strings containing all completions or null if none * exists */ @Override public List<String> getCompletions(final String curr) { if (curr == null || "".equals(curr)) { return null; } String currLowerCase = normalizer.transform(curr); getGreatestCommonPrefix(currLowerCase); try { SortedSet<String> tailSet = treeSet.tailSet(currLowerCase); ArrayList<String> completions = new ArrayList<String>(); Iterator<String> compIter = tailSet.iterator(); while (compIter.hasNext()) { String comp = compIter.next(); if (!comp.startsWith(currLowerCase)) { break; } completions.add(get(comp)); } if (completions.isEmpty()) { return null; } return completions; } catch (Exception e) { return null; } } /** * Calculate greatest prefix common to curr. Then return list of all * elements matching this prefix. Return null if none exists * * @param curr * The string to use as the base for the lookup * @return the greatest common prefix */ public String setMatchingGreatestPrefix(final String curr, ArrayList<String> completions) { if (curr == null || "".equals(curr)) { return ""; } String prefixLowerCase = getGreatestCommonPrefix( normalizer.transform(curr)); if (prefixLowerCase == null || "".equals(prefixLowerCase)) { return ""; // no common prefix } try { SortedSet<String> tailSet = treeSet.tailSet(prefixLowerCase); Iterator<String> compIter = tailSet.iterator(); while (compIter.hasNext()) { String comp = compIter.next(); if (!comp.startsWith(prefixLowerCase)) { break; } completions.add(get(comp)); } return prefixLowerCase; } catch (Exception e) { return ""; } } private String getGreatestCommonPrefix(final String curr) { String prefixBefore = greatestCommonPrefix(treeSet.floor(curr), curr); String prefixAfter = greatestCommonPrefix(treeSet.ceiling(curr), curr); if (prefixBefore == null) { return prefixAfter; } if (prefixAfter == null) { return prefixBefore; } if (prefixBefore.length() > prefixAfter.length()) { return prefixBefore; } return prefixAfter; } @Override public List<String> getCompletionsKorean(final String curr) { if (curr == null || "".equals(curr)) { return null; } ArrayList<String> completions = new ArrayList<String>(); String koreanCurr = Korean.flattenKorean(curr); Iterator<String> it = getIterator(); while (it.hasNext()) { String str = it.next(); if (Korean.flattenKorean(str).startsWith(koreanCurr)) { completions.add(Korean.unflattenKorean(str).toString()); } } return completions.isEmpty() ? null : completions; } @Override public void clear() { super.clear(); this.treeSet.clear(); } public ArrayList<String> getAllCommands() { ArrayList<String> ret = new ArrayList<String>(); Iterator<String> compIter = treeSet.iterator(); while (compIter.hasNext()) { ret.add(get(compIter.next())); } if (ret.isEmpty()) { return null; } return ret; } }