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.awt.event.KeyEvent;
11 import java.util.ArrayList;
12 import java.util.Arrays;
13 import java.util.Collections;
14 import java.util.LinkedHashSet;
15 import java.util.List;
18 import javax.swing.BorderFactory;
19 import javax.swing.JComboBox;
20 import javax.swing.JLabel;
21 import javax.swing.JMenuItem;
22 import javax.swing.JPanel;
23 import javax.swing.JPopupMenu;
24 import javax.swing.JTextField;
25 import javax.swing.SwingUtilities;
26 import javax.swing.text.AttributeSet;
27 import javax.swing.text.BadLocationException;
28 import javax.swing.text.PlainDocument;
30 public class JvCacheableInputBox<E> extends JComboBox<String>
33 private static final long serialVersionUID = 5774610435079326695L;
35 private static final int INPUT_LIMIT = 2;
37 private static final int LEFT_BOARDER_WIDTH = 16;
39 private String cacheKey;
41 private AppCache appCache;
43 private JPanel pnlDefaultCache = new JPanel();
45 private JLabel lblDefaultCacheSize = new JLabel();
47 private JTextField txtDefaultCacheSize = new JTextField();
49 private JPopupMenu popup = new JPopupMenu();
51 private JMenuItem menuItemClearCache = new JMenuItem();
53 public JvCacheableInputBox(String newCacheKey)
56 this.cacheKey = newCacheKey;
58 setPrototypeDisplayValue(
59 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
60 appCache = AppCache.getInstance();
62 initCache(newCacheKey);
67 * Method for initialising cache items for a given cache key and populating
68 * the in-memory cache with persisted cache items
72 private void initCache(String cacheKey)
74 // obtain persisted cache items from properties file as a delimited string
75 String delimitedCacheStr = Cache.getProperty(cacheKey);
76 if (delimitedCacheStr == null || delimitedCacheStr.isEmpty())
80 // convert delimited cache items to a list of strings
81 List<String> persistedCacheItems = Arrays
82 .asList(delimitedCacheStr.split(AppCache.CACHE_DELIMITER));
84 LinkedHashSet<String> foundCacheItems = appCache
85 .getAllCachedItemsFor(cacheKey);
86 if (foundCacheItems == null)
88 foundCacheItems = new LinkedHashSet<String>();
90 // populate memory cache
91 for (String cacheItem : persistedCacheItems)
93 foundCacheItems.add(cacheItem);
95 appCache.putCache(cacheKey, foundCacheItems);
99 * Initialise this cache's pop-up menu
101 private void initCachePopupMenu()
103 pnlDefaultCache.setBackground(Color.WHITE);
104 // pad panel so as to align with other menu items
105 pnlDefaultCache.setBorder(
106 BorderFactory.createEmptyBorder(0, LEFT_BOARDER_WIDTH, 0, 0));
107 txtDefaultCacheSize.setPreferredSize(new Dimension(45, 20));
108 txtDefaultCacheSize.setFont(new java.awt.Font("Verdana", 0, 12));
110 .setText(MessageManager.getString("label.default_cache_size"));
111 lblDefaultCacheSize.setFont(new java.awt.Font("Verdana", 0, 12));
112 // Force input to accept only Integer entries up to length - INPUT_LIMIT
113 txtDefaultCacheSize.setDocument(new PlainDocument()
115 private static final long serialVersionUID = 1L;
118 public void insertString(int offs, String str, AttributeSet a)
119 throws BadLocationException
121 if (getLength() + str.length() <= INPUT_LIMIT && isInteger(str))
123 super.insertString(offs, str, a);
127 txtDefaultCacheSize.addKeyListener(new java.awt.event.KeyAdapter()
130 public void keyPressed(KeyEvent e)
132 if (e.getKeyCode() == KeyEvent.VK_ENTER)
141 txtDefaultCacheSize.setText(appCache.getCacheLimit(cacheKey));
142 pnlDefaultCache.add(lblDefaultCacheSize);
143 menuItemClearCache.setFont(new java.awt.Font("Verdana", 0, 12));
144 pnlDefaultCache.add(txtDefaultCacheSize);
146 .setText(MessageManager.getString("action.clear_cached_items"));
147 menuItemClearCache.addActionListener(new ActionListener()
150 public void actionPerformed(ActionEvent e)
152 // System.out.println(">>>>> Clear cache items");
154 appCache.deleteCacheItems(cacheKey);
159 popup.insert(pnlDefaultCache, 0);
160 popup.add(menuItemClearCache);
161 setComponentPopupMenu(popup);
165 private void closePopup()
167 popup.setVisible(false);
168 popup.transferFocus();
172 * Answers true if input text is an integer
177 static boolean isInteger(String text)
181 Integer.parseInt(text);
183 } catch (NumberFormatException e)
190 * Method called to update the cache with the last user input
192 public void updateCache()
194 SwingUtilities.invokeLater(new Runnable()
199 int userLimit = txtDefaultCacheSize.getText().trim().isEmpty()
200 ? Integer.valueOf(AppCache.DEFAULT_LIMIT)
201 : Integer.valueOf(txtDefaultCacheSize.getText());
202 int cacheLimit = appCache.updateCacheLimit(cacheKey, userLimit);
203 String userInput = getUserInput();
204 if (userInput != null && !userInput.isEmpty())
206 LinkedHashSet<String> foundCache = appCache
207 .getAllCachedItemsFor(cacheKey);
208 // remove old cache item so as to place current input at the top of
210 foundCache.remove(userInput);
211 foundCache.add(userInput);
212 appCache.putCache(cacheKey, foundCache);
215 String lastSearch = userInput;
216 if (getItemCount() > 0)
220 Set<String> cacheItems = appCache.getAllCachedItemsFor(cacheKey);
221 List<String> reversedCacheItems = new ArrayList<String>();
222 reversedCacheItems.addAll(cacheItems);
224 Collections.reverse(reversedCacheItems);
225 if (lastSearch.isEmpty())
230 if (reversedCacheItems != null && !reversedCacheItems.isEmpty())
232 LinkedHashSet<String> foundCache = appCache
233 .getAllCachedItemsFor(cacheKey);
234 boolean prune = reversedCacheItems.size() > cacheLimit;
236 boolean limitExceeded = false;
237 for (String cacheItem : reversedCacheItems)
239 limitExceeded = (count++ > cacheLimit);
244 foundCache.remove(cacheItem);
256 appCache.putCache(cacheKey, foundCache);
258 setSelectedItem(lastSearch.isEmpty() ? "" : lastSearch);
264 * This method should be called to persist the in-memory cache when this
265 * components parent frame is closed / exited
267 public void persistCache()
269 appCache.persistCache(cacheKey);
270 int userLimit = txtDefaultCacheSize.getText().trim().isEmpty()
271 ? Integer.valueOf(AppCache.DEFAULT_LIMIT)
272 : Integer.valueOf(txtDefaultCacheSize.getText());
273 appCache.updateCacheLimit(cacheKey, userLimit);
277 * Method to obtain input text from the cache box
281 public String getUserInput()
283 return getEditor().getItem() == null ? ""
284 : getEditor().getItem().toString().trim();