2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.io.cache;
23 import jalview.bin.Cache;
24 import jalview.util.MessageManager;
26 import java.awt.Color;
27 import java.awt.Dimension;
28 import java.awt.event.ActionEvent;
29 import java.awt.event.ActionListener;
30 import java.awt.event.KeyEvent;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collections;
34 import java.util.LinkedHashSet;
35 import java.util.List;
38 import javax.swing.BorderFactory;
39 import javax.swing.JComboBox;
40 import javax.swing.JLabel;
41 import javax.swing.JMenuItem;
42 import javax.swing.JPanel;
43 import javax.swing.JPopupMenu;
44 import javax.swing.JTextField;
45 import javax.swing.SwingUtilities;
46 import javax.swing.text.AttributeSet;
47 import javax.swing.text.BadLocationException;
48 import javax.swing.text.PlainDocument;
50 public class JvCacheableInputBox<E> extends JComboBox<String>
53 private static final long serialVersionUID = 5774610435079326695L;
55 private static final int INPUT_LIMIT = 2;
57 private static final int LEFT_BOARDER_WIDTH = 16;
59 private String cacheKey;
61 private AppCache appCache;
63 private JPanel pnlDefaultCache = new JPanel();
65 private JLabel lblDefaultCacheSize = new JLabel();
67 private JTextField txtDefaultCacheSize = new JTextField();
69 private JPopupMenu popup = new JPopupMenu();
71 private JMenuItem menuItemClearCache = new JMenuItem();
73 public JvCacheableInputBox(String newCacheKey)
76 this.cacheKey = newCacheKey;
78 setPrototypeDisplayValue(
79 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
80 appCache = AppCache.getInstance();
82 initCache(newCacheKey);
87 * Method for initialising cache items for a given cache key and populating
88 * the in-memory cache with persisted cache items
92 private void initCache(String cacheKey)
94 // obtain persisted cache items from properties file as a delimited string
95 String delimitedCacheStr = Cache.getProperty(cacheKey);
96 if (delimitedCacheStr == null || delimitedCacheStr.isEmpty())
100 // convert delimited cache items to a list of strings
101 List<String> persistedCacheItems = Arrays
102 .asList(delimitedCacheStr.split(AppCache.CACHE_DELIMITER));
104 LinkedHashSet<String> foundCacheItems = appCache
105 .getAllCachedItemsFor(cacheKey);
106 if (foundCacheItems == null)
108 foundCacheItems = new LinkedHashSet<String>();
110 // populate memory cache
111 for (String cacheItem : persistedCacheItems)
113 foundCacheItems.add(cacheItem);
115 appCache.putCache(cacheKey, foundCacheItems);
119 * Initialise this cache's pop-up menu
121 private void initCachePopupMenu()
123 pnlDefaultCache.setBackground(Color.WHITE);
124 // pad panel so as to align with other menu items
125 pnlDefaultCache.setBorder(
126 BorderFactory.createEmptyBorder(0, LEFT_BOARDER_WIDTH, 0, 0));
127 txtDefaultCacheSize.setPreferredSize(new Dimension(45, 20));
128 txtDefaultCacheSize.setFont(new java.awt.Font("Verdana", 0, 12));
130 .setText(MessageManager.getString("label.default_cache_size"));
131 lblDefaultCacheSize.setFont(new java.awt.Font("Verdana", 0, 12));
132 // Force input to accept only Integer entries up to length - INPUT_LIMIT
133 txtDefaultCacheSize.setDocument(new PlainDocument()
135 private static final long serialVersionUID = 1L;
138 public void insertString(int offs, String str, AttributeSet a)
139 throws BadLocationException
141 if (getLength() + str.length() <= INPUT_LIMIT && isInteger(str))
143 super.insertString(offs, str, a);
147 txtDefaultCacheSize.addKeyListener(new java.awt.event.KeyAdapter()
150 public void keyPressed(KeyEvent e)
152 if (e.getKeyCode() == KeyEvent.VK_ENTER)
161 txtDefaultCacheSize.setText(appCache.getCacheLimit(cacheKey));
162 pnlDefaultCache.add(lblDefaultCacheSize);
163 menuItemClearCache.setFont(new java.awt.Font("Verdana", 0, 12));
164 pnlDefaultCache.add(txtDefaultCacheSize);
166 .setText(MessageManager.getString("action.clear_cached_items"));
167 menuItemClearCache.addActionListener(new ActionListener()
170 public void actionPerformed(ActionEvent e)
172 // System.out.println(">>>>> Clear cache items");
174 appCache.deleteCacheItems(cacheKey);
179 popup.insert(pnlDefaultCache, 0);
180 popup.add(menuItemClearCache);
181 setComponentPopupMenu(popup);
185 private void closePopup()
187 popup.setVisible(false);
188 popup.transferFocus();
192 * Answers true if input text is an integer
197 static boolean isInteger(String text)
201 Integer.parseInt(text);
203 } catch (NumberFormatException e)
210 * Method called to update the cache with the last user input
212 public void updateCache()
214 SwingUtilities.invokeLater(new Runnable()
219 int userLimit = txtDefaultCacheSize.getText().trim().isEmpty()
220 ? Integer.valueOf(AppCache.DEFAULT_LIMIT)
221 : Integer.valueOf(txtDefaultCacheSize.getText());
222 int cacheLimit = appCache.updateCacheLimit(cacheKey, userLimit);
223 String userInput = getUserInput();
224 if (userInput != null && !userInput.isEmpty())
226 LinkedHashSet<String> foundCache = appCache
227 .getAllCachedItemsFor(cacheKey);
228 // remove old cache item so as to place current input at the top of
230 foundCache.remove(userInput);
231 foundCache.add(userInput);
232 appCache.putCache(cacheKey, foundCache);
235 String lastSearch = userInput;
236 if (getItemCount() > 0)
240 Set<String> cacheItems = appCache.getAllCachedItemsFor(cacheKey);
241 List<String> reversedCacheItems = new ArrayList<String>();
242 reversedCacheItems.addAll(cacheItems);
244 Collections.reverse(reversedCacheItems);
245 if (lastSearch.isEmpty())
250 if (reversedCacheItems != null && !reversedCacheItems.isEmpty())
252 LinkedHashSet<String> foundCache = appCache
253 .getAllCachedItemsFor(cacheKey);
254 boolean prune = reversedCacheItems.size() > cacheLimit;
256 boolean limitExceeded = false;
257 for (String cacheItem : reversedCacheItems)
259 limitExceeded = (count++ > cacheLimit);
264 foundCache.remove(cacheItem);
276 appCache.putCache(cacheKey, foundCache);
278 setSelectedItem(lastSearch.isEmpty() ? "" : lastSearch);
284 * This method should be called to persist the in-memory cache when this
285 * components parent frame is closed / exited
287 public void persistCache()
289 appCache.persistCache(cacheKey);
290 int userLimit = txtDefaultCacheSize.getText().trim().isEmpty()
291 ? Integer.valueOf(AppCache.DEFAULT_LIMIT)
292 : Integer.valueOf(txtDefaultCacheSize.getText());
293 appCache.updateCacheLimit(cacheKey, userLimit);
297 * Method to obtain input text from the cache box
301 public String getUserInput()
303 return getEditor().getItem() == null ? ""
304 : getEditor().getItem().toString().trim();