/* Copyright (C) 2003-2012 JabRef contributors. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package net.sf.jabref.autocompleter; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.SortedSet; import java.util.TreeSet; import javax.swing.text.JTextComponent; import net.sf.jabref.BibtexEntry; import net.sf.jabref.Globals; import net.sf.jabref.JabRefPreferences; /** * An autocompleter delivers possible completions for a given String. There are * different types of autocompleters for different use cases. * * Example: {@link NameFieldAutoCompleter}, {@link EntireFieldAutoCompleter} * * @author kahlert, cordes, olly98 * @see AutoCompleterFactory */ public abstract class AbstractAutoCompleter { public static int SHORTEST_TO_COMPLETE = Globals.prefs.getInt(JabRefPreferences.SHORTEST_TO_COMPLETE); public static final int SHORTEST_WORD = 4; // stores the strings as is private TreeSet<String> _index_casesensitive = new TreeSet<String>(); // stores strings in lowercase private TreeSet<String> _index_caseinsensitive = new TreeSet<String>(); // stores for a lowercase string the possible expanded strings private HashMap<String, TreeSet<String>> _possibleStringsForSearchString = new HashMap<String, TreeSet<String>>(); /** * Add a BibtexEntry to this autocompleter. The autocompleter (respectively * to the concrete implementations of {@link AbstractAutoCompleter}) itself * decides which information should be stored for later completion. * */ abstract public void addBibtexEntry(BibtexEntry entry); /** * States whether the field consists of multiple values (false) or of a single value (true) * * Symptom: if false, {@link net.sf.jabref.gui.AutoCompleteListener#getCurrentWord(JTextComponent comp)} * returns current word only, if true, it returns the text beginning from the buffer */ abstract public boolean isSingleUnitField(); /** * Returns one or more possible completions for a given String. The returned * completion depends on which informations were stored while adding * BibtexEntries by the used implementation of {@link AbstractAutoCompleter} * . * * @see AbstractAutoCompleter#addBibtexEntry(BibtexEntry) */ public String[] complete(String str) { if (stringMinLength(str)) return null; String lstr = str.toLowerCase(); if (lstr.equals(str)) { // user typed in lower case word -> we do an case-insenstive search String ender = incrementLastCharacter(lstr); SortedSet<String> subset = _index_caseinsensitive.subSet(lstr, ender); // As subset only contains lower case strings, // we have to to determine possible strings for each hit ArrayList<String> res = new ArrayList<String>(); for (String s: subset) { res.addAll(_possibleStringsForSearchString.get(s)); } return res.toArray(new String[0]); } else { // user typed in a mix of upper case and lower case, // we assume user wants to have exact search String ender = incrementLastCharacter(str); SortedSet<String> subset = _index_casesensitive.subSet(str, ender); return subset.toArray(new String[0]); } } /** * Increments the last character of a string. * * Example: incrementLastCharacter("abc") returns "abd". */ private static String incrementLastCharacter(String str) { char lastChar = str.charAt(str.length() - 1); String ender = str.substring(0, str.length() - 1) + Character.toString((char) (lastChar + 1)); return ender; } private static boolean stringMinLength(String str) { return str.length() < AbstractAutoCompleter.SHORTEST_TO_COMPLETE; } public void addWordToIndex(String word) { if (word.length() >= SHORTEST_WORD) { _index_casesensitive.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 word_lcase = word.toLowerCase(); _index_caseinsensitive.add(word_lcase); TreeSet<String> set = _possibleStringsForSearchString.get(word_lcase); if (set==null) { set = new TreeSet<String>(); } set.add(word); _possibleStringsForSearchString.put(word_lcase, set); } } public boolean indexContainsWord(String word) { return _index_caseinsensitive.contains(word.toLowerCase()); } public String getPrefix() { return ""; } }