1 package jalview.io.cache;
3 import jalview.bin.Cache;
4 import jalview.util.MessageManager;
7 import java.awt.Dimension;
8 import java.awt.event.ActionEvent;
9 import java.awt.event.ActionListener;
10 import java.util.ArrayList;
11 import java.util.Arrays;
12 import java.util.Collections;
13 import java.util.LinkedHashSet;
14 import java.util.List;
17 import javax.swing.BorderFactory;
18 import javax.swing.JComboBox;
19 import javax.swing.JLabel;
20 import javax.swing.JMenuItem;
21 import javax.swing.JPanel;
22 import javax.swing.JPopupMenu;
23 import javax.swing.JTextField;
24 import javax.swing.SwingUtilities;
25 import javax.swing.text.AttributeSet;
26 import javax.swing.text.BadLocationException;
27 import javax.swing.text.PlainDocument;
29 public class JvCacheableInputBox<E> extends JComboBox<String>
32 private static final long serialVersionUID = 5774610435079326695L;
34 private static final int INPUT_LIMIT = 2;
36 private static final int LEFT_BOARDER_WIDTH = 2;
38 private String cacheKey;
40 private AppCache appCache;
42 private JPanel pnlDefaultCache = new JPanel();
44 private JLabel lblDefaultCacheSize = new JLabel();
46 private JTextField txtDefaultCacheSize = new JTextField();
48 private JPopupMenu popup = new JPopupMenu();
50 private JMenuItem menuItemClearCache = new JMenuItem();
52 public JvCacheableInputBox(String newCacheKey)
55 this.cacheKey = newCacheKey;
57 setPrototypeDisplayValue("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
58 appCache = AppCache.getInstance();
60 initCache(newCacheKey);
65 * Method for initialising cache items for a given cache key and populating
66 * the in-memory cache with persisted cache items
70 private void initCache(String cacheKey)
72 // obtain persisted cache items from properties file as a delimited string
73 String delimitedCacheStr = Cache.getProperty(cacheKey);
74 if (delimitedCacheStr == null || delimitedCacheStr.isEmpty())
78 // convert delimited cache items to a list of strings
79 List<String> persistedCacheItems = Arrays.asList(delimitedCacheStr
80 .split(AppCache.CACHE_DELIMITER));
82 LinkedHashSet<String> foundCacheItems = appCache
83 .getAllCachedItemsFor(cacheKey);// cacheItems.get(cacheKey);
84 if (foundCacheItems == null)
86 foundCacheItems = new LinkedHashSet<String>();
88 // populate memory cache
89 for (String cacheItem : persistedCacheItems)
91 foundCacheItems.add(cacheItem);
93 appCache.putCache(cacheKey, foundCacheItems);
97 * Initialise this cache's pop-up menu
99 private void initCachePopupMenu()
101 pnlDefaultCache.setBackground(Color.WHITE);
102 // pad panel so as to align with other menu items
103 pnlDefaultCache.setBorder(BorderFactory.createEmptyBorder(0,
104 LEFT_BOARDER_WIDTH, 0, 0));
105 txtDefaultCacheSize.setPreferredSize(new Dimension(45, 20));
106 lblDefaultCacheSize.setText(MessageManager
107 .getString("label.default_cache_size"));
108 // Force input to accept only Integer entries up to length - INPUT_LIMIT
109 txtDefaultCacheSize.setDocument(new PlainDocument()
111 private static final long serialVersionUID = 1L;
114 public void insertString(int offs, String str, AttributeSet a)
115 throws BadLocationException
117 if (getLength() + str.length() <= INPUT_LIMIT && isInteger(str))
119 super.insertString(offs, str, a);
123 txtDefaultCacheSize.setText(appCache.getCacheLimit(cacheKey));
124 pnlDefaultCache.add(lblDefaultCacheSize);
125 pnlDefaultCache.add(txtDefaultCacheSize);
126 menuItemClearCache.setText(MessageManager
127 .getString("action.clear_cached_items"));
128 menuItemClearCache.addActionListener(new ActionListener()
131 public void actionPerformed(ActionEvent e)
133 // System.out.println(">>>>> Clear cache items");
135 appCache.deleteCacheItems(cacheKey);
140 popup.insert(pnlDefaultCache, 0);
141 popup.add(menuItemClearCache);
142 setComponentPopupMenu(popup);
147 * Answers true if input text is an integer
152 static boolean isInteger(String text)
156 Integer.parseInt(text);
158 } catch (NumberFormatException e)
165 * Method called to update the cache with the last user input
167 public void updateCache()
169 SwingUtilities.invokeLater(new Runnable()
174 int userLimit = txtDefaultCacheSize.getText().trim().isEmpty() ? Integer
175 .valueOf(AppCache.DEFAULT_LIMIT) : Integer
176 .valueOf(txtDefaultCacheSize.getText());
177 int cacheLimit = appCache.updateCacheLimit(cacheKey, userLimit);
178 String userInput = getUserInput();
179 if (userInput != null && !userInput.isEmpty())
181 LinkedHashSet<String> foundCache = appCache
182 .getAllCachedItemsFor(cacheKey);
183 // remove old cache item so as to place current input at the top of
185 foundCache.remove(userInput);
186 foundCache.add(userInput);
187 appCache.putCache(cacheKey, foundCache);
190 String lastSearch = userInput;
191 if (getItemCount() > 0)
195 Set<String> cacheItems = appCache.getAllCachedItemsFor(cacheKey);
196 List<String> reversedCacheItems = new ArrayList<String>();
197 reversedCacheItems.addAll(cacheItems);
199 Collections.reverse(reversedCacheItems);
200 if (lastSearch.isEmpty())
205 if (reversedCacheItems != null && !reversedCacheItems.isEmpty())
207 LinkedHashSet<String> foundCache = appCache
208 .getAllCachedItemsFor(cacheKey);
209 boolean prune = reversedCacheItems.size() > cacheLimit;
211 boolean limitExceeded = false;
212 for (String cacheItem : reversedCacheItems)
214 limitExceeded = (count++ > cacheLimit);
219 foundCache.remove(cacheItem);
231 appCache.putCache(cacheKey, foundCache);
233 setSelectedItem(lastSearch.isEmpty() ? "" : lastSearch);
240 * This method should be called to persist the in-memory cache when this
241 * components parent frame is closed / exited
243 public void persistCache()
245 appCache.persistCache(cacheKey);
246 int userLimit = txtDefaultCacheSize.getText().trim().isEmpty() ? Integer
247 .valueOf(AppCache.DEFAULT_LIMIT) : Integer
248 .valueOf(txtDefaultCacheSize.getText());
249 appCache.updateCacheLimit(cacheKey, userLimit);
253 * Method to obtain input text from the cache box
257 public String getUserInput()
259 return getEditor().getItem() == null ? "" : getEditor().getItem()