From 2e14f847dd18b74f49c8fe397937e0ebe5e59594 Mon Sep 17 00:00:00 2001 From: Stefan Kolb Date: Mon, 4 Apr 2016 19:33:56 +0200 Subject: [PATCH] Refactor auto complete --- .../autocompleter/AutoCompleteListener.java | 424 +++++------------- .../gui/autocompleter/AutoCompleteState.java | 65 +++ .../sf/jabref/gui/fieldeditors/TextArea.java | 2 +- .../sf/jabref/gui/fieldeditors/TextField.java | 2 +- .../autocompleter/AbstractAutoCompleter.java | 56 +-- 5 files changed, 191 insertions(+), 358 deletions(-) create mode 100644 src/main/java/net/sf/jabref/gui/autocompleter/AutoCompleteState.java diff --git a/src/main/java/net/sf/jabref/gui/autocompleter/AutoCompleteListener.java b/src/main/java/net/sf/jabref/gui/autocompleter/AutoCompleteListener.java index 1bf03082a06..aa56081d84d 100644 --- a/src/main/java/net/sf/jabref/gui/autocompleter/AutoCompleteListener.java +++ b/src/main/java/net/sf/jabref/gui/autocompleter/AutoCompleteListener.java @@ -24,24 +24,18 @@ import java.awt.event.*; import java.util.List; +import java.util.Optional; public class AutoCompleteListener extends KeyAdapter implements FocusListener { private static final Log LOGGER = LogFactory.getLog(AutoCompleteListener.class); private final AutoCompleter completer; - - // substring of the completion to be inserted - // null indicates that there are no completions available - private String textToInsert; - // the letters, the user has typed until know - private String lastBeginning; - private int lastCaretPosition = -1; - private List lastCompletions; - private int lastShownCompletion; + private Optional currentCompletion; private FocusListener nextFocusListener; public AutoCompleteListener(AutoCompleter completer) { this.completer = completer; + currentCompletion = Optional.empty(); } /** @@ -56,309 +50,143 @@ public void setNextFocusListener(FocusListener listener) { @Override public void keyPressed(KeyEvent e) { - if ((textToInsert != null) && (e.getKeyCode() == KeyEvent.VK_ENTER)) { - JTextComponent comp = (JTextComponent) e.getSource(); + // no auto completions available + if (!currentCompletion.isPresent()) { + return; + } - // replace typed characters by characters from completion - lastBeginning = lastCompletions.get(lastShownCompletion); + AutoCompleteState completion = currentCompletion.get(); + int keyCode = e.getKeyCode(); + JTextComponent textField = (JTextComponent) e.getSource(); - int end = comp.getSelectionEnd(); - comp.select(end, end); - textToInsert = null; - e.consume(); - } - // Cycle through alternative completions when user presses PGUP/PGDN: - else if ((e.getKeyCode() == KeyEvent.VK_PAGE_DOWN) && (textToInsert != null)) { - cycleSuggestion((JTextComponent) e.getSource(), 1); - e.consume(); - } else if ((e.getKeyCode() == KeyEvent.VK_PAGE_UP) && (textToInsert != null)) { - cycleSuggestion((JTextComponent) e.getSource(), -1); - e.consume(); - } - // else if ((e.getKeyCode() == KeyEvent.VK_BACK_SPACE)) { - // StringBuffer currentword = getCurrentWord((JTextComponent) e.getSource()); - // // delete last char to obey semantics of back space - // currentword.deleteCharAt(currentword.length()-1); - // doCompletion(currentword, e); - // } - else if (e.getKeyChar() == KeyEvent.CHAR_UNDEFINED) { - if (e.getKeyCode() == KeyEvent.VK_SHIFT) { - // shift is OK, everything else leads to a reset - LOGGER.debug("Special case: shift pressed. No action."); - } else { - resetAutoCompletion(); - } + if (keyCode == KeyEvent.VK_ENTER) { + // ENTER inserts auto-completion + insertSuggestion(textField, e); + // reset state + clearAutoCompletion(textField); + } else if (keyCode == KeyEvent.VK_PAGE_DOWN) { + // Cycle through alternative completions + completion.previousSuggestion(); + insertSuggestion(textField, e); + } else if (keyCode == KeyEvent.VK_PAGE_UP) { + // Cycle through alternative completions + completion.nextSuggestion(); + insertSuggestion(textField, e); } } - private void cycleSuggestion(JTextComponent textField, int increment) { - assert (lastCompletions != null); - assert (!lastCompletions.isEmpty()); + @Override + public void keyTyped(KeyEvent e) { + char newChar = e.getKeyChar(); + JTextComponent textField = (JTextComponent) e.getSource(); - lastShownCompletion += increment; - if (lastShownCompletion >= lastCompletions.size()) { - lastShownCompletion = 0; - } else if (lastShownCompletion < 0) { - lastShownCompletion = lastCompletions.size() - 1; + // this case is handled at keyPressed(e) + if (newChar == KeyEvent.VK_ENTER) { + return; } - // new suggestion - String newSuggestion = lastCompletions.get(lastShownCompletion); - textToInsert = newSuggestion.substring(lastBeginning.length() - 1); - - // replace suggestion selection with new suggestion - StringBuilder text = new StringBuilder(textField.getText()); - - int oldSelectionStart = textField.getSelectionStart(); - int oldSelectionEnd = textField.getSelectionEnd(); - - // replace prefix with new prefix - int startPos = textField.getSelectionStart() - lastBeginning.length(); - text.delete(startPos, oldSelectionStart); - text.insert(startPos, newSuggestion.subSequence(0, lastBeginning.length())); - - // replace suffix with new suffix - text.delete(oldSelectionStart, oldSelectionEnd); - text.insert(oldSelectionStart, textToInsert.substring(1)); - - // set new suggestion - textField.setText(text.toString()); - // highlight auto-complete part - textField.select(oldSelectionStart, (oldSelectionStart + textToInsert.length()) - 1); - // update caret position - lastCaretPosition = textField.getCaretPosition(); - } + if ((e.getModifiers() | InputEvent.SHIFT_MASK) != InputEvent.SHIFT_MASK) { + // TODO: plain key or SHIFT + key is pressed, no handling of CTRL+key, META+key, ... + return; + } - /** - * If user cancels autocompletion by a) entering another letter than the completed word (and there is no other auto - * completion) b) space the casing of the letters has to be kept - * - * Global variable "lastBeginning" keeps track of typed letters. We rely on this variable to reconstruct the text - * - * @param wordSeperatorTyped indicates whether the user has typed a white space character or a - */ - private void setUnmodifiedTypedLetters(JTextComponent comp, boolean lastBeginningContainsTypedCharacter, - boolean wordSeperatorTyped) { - if (lastBeginning == null) { - LOGGER.debug("No last beginning found"); - // There was no previous input (if the user typed a word, where no autocompletion is available) - // Thus, there is nothing to replace + // don't do auto completion inside words + if (!atEndOfWord(textField)) { return; } - LOGGER.debug("lastBeginning: >" + lastBeginning + '<'); - if (comp.getSelectedText() == null) { - // if there is no selection - // the user has typed the complete word, but possibly with a different casing - // we need a replacement - if (wordSeperatorTyped) { - LOGGER.debug("Replacing complete word"); + + if (Character.isLetter(newChar) || Character.isDigit(newChar) || Character.isWhitespace(newChar)) { + // User continues on the word that was suggested. + if (currentCompletion.isPresent() && currentCompletion.get().continuesSuggestion(newChar)) { + // update currentWord inside auto-completion + currentCompletion.get().updateState(newChar); + // update highlighting + insertKeyAndSuggestion(textField, e); } else { - // if user did not press a white space character (space, ...), - // then we do not do anything - return; + // start new completion attempt + // delete selection if auto-completion is active + if (currentCompletion.isPresent()) { + textField.replaceSelection(""); + } + String word = getCurrentWord(textField).toString() + newChar; + startCompletion(word, textField); + insertKeyAndSuggestion(textField, e); } } else { - LOGGER.debug("Selected text " + comp.getSelectedText() + " will be removed"); - // remove completion suggestion - comp.replaceSelection(""); - } - - lastCaretPosition = comp.getCaretPosition(); - - int endIndex = lastCaretPosition - lastBeginning.length(); - if (lastBeginningContainsTypedCharacter) { - // the current letter is NOT contained in comp.getText(), but in lastBeginning - // thus lastBeginning.length() is one too large - endIndex++; + // Some other key like backspace has been entered, state may have changed + clearAutoCompletion(textField); } - String text = comp.getText(); - comp.setText(text.substring(0, endIndex).concat(lastBeginning).concat(text.substring(lastCaretPosition))); - if (lastBeginningContainsTypedCharacter) { - // the current letter is NOT contained in comp.getText() - // Thus, cursor position also did not get updated - lastCaretPosition++; - } - comp.setCaretPosition(lastCaretPosition); - lastBeginning = null; } - /** - * Start a new completion attempt (instead of treating a continuation of an existing word or an interrupt of the - * current word) - */ - private void startCompletion(String currentWord, KeyEvent e) { - JTextComponent textField = (JTextComponent) e.getSource(); + private void startCompletion(String currentWord, JTextComponent textField) { + // reset state + clearAutoCompletion(textField); + // try to find suggestions for current word List suggestions = completer.complete(currentWord); if (suggestions == null || suggestions.isEmpty()) { return; } - - lastShownCompletion = 0; - lastCompletions = suggestions; - // we use the first word inside the suggestions - String suggestedWord = suggestions.get(0); - - // these two lines obey the user's input - //textToInsert = Character.toString(ch); - //textToInsert = textToInsert.concat(suggestedWord.substring(word.length())); - // BUT we obey the completion - textToInsert = suggestedWord.substring(currentWord.length() - 1); - - StringBuilder alltext = new StringBuilder(textField.getText()); - int cp = textField.getCaretPosition(); - alltext.insert(cp, textToInsert); - textField.setText(alltext.toString()); - textField.setCaretPosition(cp); - textField.select(cp + 1, (cp + 1 + suggestedWord.length()) - currentWord.length()); - e.consume(); - lastCaretPosition = textField.getCaretPosition(); - char ch = e.getKeyChar(); - - LOGGER.debug("Appending >" + ch + '<'); - - if (currentWord.length() <= 1) { - lastBeginning = Character.toString(ch); - } else { - lastBeginning = currentWord.substring(0, currentWord.length() - 1).concat(Character.toString(ch)); - } + currentCompletion = Optional.of(new AutoCompleteState(currentWord, suggestions, textField.getCaretPosition())); } - @Override - public void keyTyped(KeyEvent e) { - char ch = e.getKeyChar(); - JTextComponent textField = (JTextComponent) e.getSource(); - - // this case is handled at keyPressed(e) - if (ch == KeyEvent.VK_ENTER) { + // DANGER: typed key will also be inserted here! + private void insertKeyAndSuggestion(JTextComponent textField, KeyEvent e) { + if (!currentCompletion.isPresent()) { return; } - if ((e.getModifiers() | InputEvent.SHIFT_MASK) == InputEvent.SHIFT_MASK) { - // plain key or SHIFT + key is pressed, no handling of CTRL+key, META+key, ... - if (Character.isLetter(ch) || Character.isDigit(ch) - || (Character.isWhitespace(ch) && completer.isSingleUnitField())) { - JTextComponent comp = (JTextComponent) e.getSource(); - - // don't do auto completion inside words - // TODO: ac not working as expected, e.g. Appstand, Application -> App -> Apps (no completion here) - try { - char pos = comp.getText().charAt(comp.getCaretPosition()); - if (!Character.isWhitespace(pos)) { - return; - } - } catch (IndexOutOfBoundsException ex) { - - } - - // The case-insensitive system is a bit tricky here - // If keyword is "TODO" and user types "tO", then this is treated as "continue" as the "O" matches the "O" - // If keyword is "TODO" and user types "To", then this is treated as "discont" as the "o" does NOT match the "O". - - if ((textToInsert != null) && (textToInsert.length() > 1) && (ch == textToInsert.charAt(1))) { - // User continues on the word that was suggested. - LOGGER.debug("cont"); - - textToInsert = textToInsert.substring(1); - if (!textToInsert.isEmpty()) { - int cp = comp.getCaretPosition(); - //comp.setCaretPosition(cp+1-textToInsert.); - comp.select((cp + 1) - textToInsert.length(), cp); - lastBeginning = lastBeginning + ch; - - e.consume(); - lastCaretPosition = comp.getCaretPosition(); - - lastCompletions = completer.complete(lastBeginning); - lastShownCompletion = 0; - for (int i = 0; i < lastCompletions.size(); i++) { - String lastCompletion = lastCompletions.get(i); - if (lastCompletion.endsWith(textToInsert)) { - lastShownCompletion = i; - break; - } - - } - if (textToInsert.length() < 2) { - // User typed the last character of the autocompleted word - // We have to replace the automcompletion word by the typed word. - // This helps if the user presses "space" after the completion - // "space" indicates that the user does NOT want the autocompletion, - // but the typed word - String text = comp.getText(); - comp.setText(text.substring(0, lastCaretPosition - lastBeginning.length()) + lastBeginning - + text.substring(lastCaretPosition)); - // there is no selected text, therefore we are not updating the selection - textToInsert = null; - } - return; - } - } - - if ((textToInsert != null) && ((textToInsert.length() <= 1) || (ch != textToInsert.charAt(1)))) { - // User discontinues the word that was suggested. - lastBeginning = lastBeginning + ch; - - LOGGER.debug("discont textToInsert: >" + textToInsert + "'<' lastBeginning: >" + lastBeginning + '<'); - - List completed = completer.complete(lastBeginning); - if ((completed != null) && (!completed.isEmpty())) { - lastShownCompletion = 0; - lastCompletions = completed; - String sno = completed.get(0); - // textToInsert = string used for autocompletion last time - // this string has to be removed - // lastCaretPosition is the position of the caret after textToInsert. - int lastLen = textToInsert.length() - 1; - textToInsert = sno.substring(lastBeginning.length() - 1); - String text = comp.getText(); - //we do not use textToInsert as we want to obey the casing of "sno" - comp.setText(text.substring(0, (lastCaretPosition - lastLen - lastBeginning.length()) + 1) + sno - + text.substring(lastCaretPosition)); - int startSelect = (lastCaretPosition + 1) - lastLen; - int endSelect = (lastCaretPosition + textToInsert.length()) - lastLen; - comp.select(startSelect, endSelect); - - lastCaretPosition = comp.getCaretPosition(); - e.consume(); - return; - } else { - setUnmodifiedTypedLetters(comp, true, false); - e.consume(); - textToInsert = null; - return; - } - } + AutoCompleteState completion = currentCompletion.get(); + + String suggestedWord = completion.getSuggestedWord(); + // remove old suggestion + textField.replaceSelection(""); + StringBuilder fieldText = new StringBuilder(textField.getText()); + // insert suggested substring + int caretPosition = currentCompletion.get().getRealCaretPosition(); + // DANGER: typed key will also be inserted here! + String keyAndWord = completion.getSuggestedWord().substring(completion.getCurrentWord().length() - 1); + fieldText.insert(caretPosition, keyAndWord); + textField.setText(fieldText.toString()); + // highlight suggested substring + textField.select(caretPosition + 1, (caretPosition + 1 + suggestedWord.length()) - completion.getCurrentWord().length()); + // prevent insertion of typed key + e.consume(); + } - comp.replaceSelection(""); + private void insertSuggestion(JTextComponent textField, KeyEvent e) { + if (!currentCompletion.isPresent()) { + return; + } - StringBuffer currentword = getCurrentWord(comp); + AutoCompleteState completion = currentCompletion.get(); + + String suggestedWord = completion.getSuggestedWord(); + // remove old suggestion + textField.replaceSelection(""); + StringBuilder fieldText = new StringBuilder(textField.getText()); + // insert suggested substring + int caretPosition = currentCompletion.get().getRealCaretPosition(); + String word = completion.getSuggestedWord().substring(completion.getCurrentWord().length()); + fieldText.insert(caretPosition + 1, word); + textField.setText(fieldText.toString()); + // highlight suggested substring + textField.select(caretPosition + 1, (caretPosition + 1 + suggestedWord.length()) - completion.getCurrentWord().length()); + // prevent usual action of ENTER/PGUP/PGDN + e.consume(); + } - // only "real characters" end up here - assert (!Character.isISOControl(ch)); - currentword.append(ch); - startCompletion(currentword.toString(), e); - return; - } else { - if (Character.isWhitespace(ch)) { - assert (!completer.isSingleUnitField()); - LOGGER.debug("whitespace && !singleUnitField"); - // start a new search if end-of-field is reached - - // replace displayed letters with typed letters - setUnmodifiedTypedLetters(textField, false, true); - resetAutoCompletion(); - return; - } + private boolean atEndOfWord(JTextComponent textField) { + int nextCharPosition = textField.getCaretPosition(); - LOGGER.debug("No letter/digit/whitespace or CHAR_UNDEFINED"); - // replace displayed letters with typed letters - setUnmodifiedTypedLetters(textField, false, !Character.isISOControl(ch)); - resetAutoCompletion(); - return; + // position not at the end of input + if(nextCharPosition < textField.getText().length()) { + char nextChar = textField.getText().charAt(nextCharPosition); + if (!Character.isWhitespace(nextChar)) { + return false; } } - resetAutoCompletion(); + return true; } private StringBuffer getCurrentWord(JTextComponent textField) { @@ -371,15 +199,14 @@ private StringBuffer getCurrentWord(JTextComponent textField) { String textBeforeCaret = textField.getText(0, caretPosition); // In most fields, we are only interested in the currently edited word, so we // seek from the textBeforeCaret backward to the closest space: - // TODO: should be the inverse here?! if (!completer.isSingleUnitField()) { - // textBeforeCaret is in the middle of the text AND current character is a whitespace + // textBeforeCaret is in the middle of the text AND last character is a whitespace // that means: a new word is started and there is no current word - if (caretPosition < textField.getText().length() && Character.isWhitespace(textField.getText().charAt(caretPosition))) { + if (caretPosition < textField.getText().length() && Character.isWhitespace(textField.getText().charAt(caretPosition - 1))) { return new StringBuffer(0); } - // TODO: this whitespace finder code must be refactored + // find word int piv = textBeforeCaret.length() - 1; while ((piv >= 0) && !Character.isWhitespace(textBeforeCaret.charAt(piv))) { piv--; @@ -392,7 +219,6 @@ private StringBuffer getCurrentWord(JTextComponent textField) { // text field content, so we skip the searching and keep the entire part up to the textBeforeCaret: result.append(textBeforeCaret); } - LOGGER.debug("AutoCompListener: " + result); } catch (BadLocationException ignore) { // potentially thrown by textField.getText() } @@ -409,34 +235,20 @@ public void focusGained(FocusEvent event) { @Override public void focusLost(FocusEvent event) { - if (textToInsert != null) { - JTextComponent comp = (JTextComponent) event.getSource(); - clearCurrentSuggestion(comp); + if (currentCompletion.isPresent()) { + // comment this when debugging! + clearAutoCompletion((JTextComponent) event.getSource()); } if (nextFocusListener != null) { nextFocusListener.focusLost(event); } } - /** - * Resets the auto completion data in a way that no leftovers are there - */ - private void resetAutoCompletion() { - LOGGER.debug("Resetting autocompletion"); - textToInsert = null; - lastBeginning = null; - } - - public void clearCurrentSuggestion(JTextComponent comp) { - if (textToInsert != null) { - int selStart = comp.getSelectionStart(); - String text = comp.getText(); - comp.setText(text.substring(0, selStart) + text.substring(comp.getSelectionEnd())); - comp.setCaretPosition(selStart); - lastCompletions = null; - lastShownCompletion = 0; - lastCaretPosition = -1; - textToInsert = null; + public void clearAutoCompletion(JTextComponent textField) { + if (currentCompletion.isPresent()) { + textField.replaceSelection(""); + textField.setCaretPosition(textField.getSelectionStart()); + currentCompletion = Optional.empty(); } } } diff --git a/src/main/java/net/sf/jabref/gui/autocompleter/AutoCompleteState.java b/src/main/java/net/sf/jabref/gui/autocompleter/AutoCompleteState.java new file mode 100644 index 00000000000..be1589744cc --- /dev/null +++ b/src/main/java/net/sf/jabref/gui/autocompleter/AutoCompleteState.java @@ -0,0 +1,65 @@ +package net.sf.jabref.gui.autocompleter; + +import java.util.List; + +public class AutoCompleteState { + private List suggestions; + private String currentWord; + private int currentCompletion; + private int realCaretPosition; + + public AutoCompleteState(String currentWord, List suggestions, int caretPosition) { + this.currentWord = currentWord; + this.suggestions = suggestions; + realCaretPosition = caretPosition; + currentCompletion = 0; + } + + public String nextSuggestion() { + return cycleSuggestion(1); + } + + public String previousSuggestion() { + return cycleSuggestion(-1); + } + + public String getCurrentWord() { + return currentWord; + } + + public boolean continuesSuggestion(char newChar) { + if (!getSuggestedWord().startsWith(currentWord + newChar)) { + return false; + } + return true; + } + + public void updateState(char newChar) { + if (continuesSuggestion(newChar)) { + currentWord = currentWord + newChar; + realCaretPosition++; + } else { + throw new IllegalArgumentException("Make sure continuesSuggestion(newChar) is true before using this method!"); + } + } + + public int getRealCaretPosition() { + return realCaretPosition; + } + + public String getSuggestedWord() { + return suggestions.get(currentCompletion); + } + + private String cycleSuggestion(int increment) { + currentCompletion += increment; + + if (currentCompletion >= suggestions.size()) { + currentCompletion = 0; + } else if (currentCompletion < 0) { + currentCompletion = suggestions.size() - 1; + } + + return getSuggestedWord(); + } +} diff --git a/src/main/java/net/sf/jabref/gui/fieldeditors/TextArea.java b/src/main/java/net/sf/jabref/gui/fieldeditors/TextArea.java index 236194daaea..974550af698 100644 --- a/src/main/java/net/sf/jabref/gui/fieldeditors/TextArea.java +++ b/src/main/java/net/sf/jabref/gui/fieldeditors/TextArea.java @@ -150,7 +150,7 @@ public void setAutoCompleteListener(AutoCompleteListener listener) { @Override public void clearAutoCompleteSuggestion() { if (autoCompleteListener != null) { - autoCompleteListener.clearCurrentSuggestion(this); + autoCompleteListener.clearAutoCompletion(this); } } } diff --git a/src/main/java/net/sf/jabref/gui/fieldeditors/TextField.java b/src/main/java/net/sf/jabref/gui/fieldeditors/TextField.java index 714509e601a..e018b737461 100644 --- a/src/main/java/net/sf/jabref/gui/fieldeditors/TextField.java +++ b/src/main/java/net/sf/jabref/gui/fieldeditors/TextField.java @@ -158,7 +158,7 @@ public void setAutoCompleteListener(AutoCompleteListener listener) { @Override public void clearAutoCompleteSuggestion() { if (autoCompleteListener != null) { - autoCompleteListener.clearCurrentSuggestion(this); + autoCompleteListener.clearAutoCompletion(this); } } diff --git a/src/main/java/net/sf/jabref/logic/autocompleter/AbstractAutoCompleter.java b/src/main/java/net/sf/jabref/logic/autocompleter/AbstractAutoCompleter.java index fee40589bf5..89480eb54ff 100644 --- a/src/main/java/net/sf/jabref/logic/autocompleter/AbstractAutoCompleter.java +++ b/src/main/java/net/sf/jabref/logic/autocompleter/AbstractAutoCompleter.java @@ -22,22 +22,9 @@ */ public abstract class AbstractAutoCompleter implements AutoCompleter { private static final int SHORTEST_WORD_TO_ADD = 4; - private final AutoCompletePreferences preferences; - /** - * Stores the strings as is. - */ - private final TreeSet indexCaseSensitive = new TreeSet<>(); - - /** - * Stores strings in lowercase. - */ - private final TreeSet indexCaseInsensitive = new TreeSet<>(); - - /** - * Stores for a lowercase string the possible expanded strings. - */ - private final Map> possibleStringsForLowercaseSearch = new HashMap<>(); + private final AutoCompletePreferences preferences; + private final TreeSet caseSensitiveIndex = new TreeSet<>(); public AbstractAutoCompleter(AutoCompletePreferences preferences) { this.preferences = Objects.requireNonNull(preferences); @@ -54,27 +41,9 @@ public List complete(String toComplete) { return Collections.emptyList(); } - String lowerCase = toComplete.toLowerCase(); - - // TODO: does this decision make sense? - // user typed in lower case word -> we do an case-insensitive search - if (lowerCase.equals(toComplete)) { - String ender = incrementLastCharacter(lowerCase); - SortedSet subset = indexCaseInsensitive.subSet(lowerCase, ender); - - // As subset only contains lower case strings, we have to to determine possible strings for each hit - ArrayList result = new ArrayList<>(); - for (String s : subset) { - result.addAll(possibleStringsForLowercaseSearch.get(s)); - } - return result; - } - // user typed in a mix of upper case and lower case, we assume user wants to have exact search - else { - String ender = incrementLastCharacter(toComplete); - SortedSet subset = indexCaseSensitive.subSet(toComplete, ender); - return new ArrayList<>(subset); - } + String nextWord = incrementLastCharacter(toComplete); + SortedSet matchingWords = caseSensitiveIndex.subSet(toComplete, nextWord); + return new ArrayList<>(matchingWords); } /** @@ -104,19 +73,7 @@ public void insertIntoIndex(String word) { return; } - indexCaseSensitive.add(word); - - // insensitive treatment - // first, add the lower cased word to search index - // second, add a mapping from the lower cased word to the real word - String lowerCase = word.toLowerCase(); - indexCaseInsensitive.add(lowerCase); - TreeSet set = possibleStringsForLowercaseSearch.get(lowerCase); - if (set == null) { - set = new TreeSet<>(); - } - set.add(word); - possibleStringsForLowercaseSearch.put(lowerCase, set); + caseSensitiveIndex.add(word); } @Override @@ -132,5 +89,4 @@ public String getAutoCompleteText(String item) { protected int getLengthOfShortestWordToAdd() { return SHORTEST_WORD_TO_ADD; } - }