Clover icon

Coverage Report

  1. Project Clover database Thu Dec 4 2025 14:43:25 GMT
  2. Package jalview.io.cache

File JvCacheableInputBox.java

 

Coverage histogram

../../../img/srcFileCovDistChart7.png
30% of files have more coverage

Code metrics

50
114
22
1
422
299
51
0.45
5.18
22
2.32

Classes

Class Line # Actions
JvCacheableInputBox 63 114 51
0.645161364.5%
 

Contributing tests

This file is covered by 7 tests. .

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
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.
11    *
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.
16    *
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.
20    */
21    package jalview.io.cache;
22   
23    import java.awt.Dimension;
24    import java.awt.FontMetrics;
25    import java.awt.event.ActionEvent;
26    import java.awt.event.ActionListener;
27    import java.awt.event.FocusListener;
28    import java.awt.event.KeyAdapter;
29    import java.awt.event.KeyEvent;
30    import java.awt.event.KeyListener;
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;
36    import java.util.Set;
37   
38    import javax.swing.JComboBox;
39    import javax.swing.JComponent;
40    import javax.swing.JMenuItem;
41    import javax.swing.JPopupMenu;
42    import javax.swing.JTextField;
43    import javax.swing.SwingUtilities;
44    import javax.swing.event.CaretListener;
45    import javax.swing.event.DocumentListener;
46    import javax.swing.text.JTextComponent;
47   
48    import jalview.bin.Cache;
49    import jalview.util.MessageManager;
50    import jalview.util.Platform;
51   
52    /**
53    * A class that provides an editable combobox with a memory of previous entries
54    * that may be persisted
55    *
56    * @author tcofoegbu
57    *
58    * @param <E>
59    */
60    /*
61    * (temporary?) patches to wrap a JTextField instead when running as Javascript
62    */
 
63    public class JvCacheableInputBox<E>
64    {
65    protected JComboBox<String> comboBox; // used for Jalview
66   
67    protected JTextField textField; // used for JalviewJS
68   
69    protected JTextComponent textComponent; // used for both
70   
71    protected String cacheKey;
72   
73    protected AppCache appCache;
74   
75    private JPopupMenu popup = new JPopupMenu();
76   
77    private JMenuItem menuItemClearCache = new JMenuItem();
78   
79    volatile boolean enterWasPressed = false;
80   
81    private String prototypeDisplayValue;
82   
83    /**
84    * @return flag indicating if the most recent keypress was enter
85    */
 
86  0 toggle public boolean wasEnterPressed()
87    {
88  0 return enterWasPressed;
89    }
90   
91    /**
92    * Constructor given the key to cached values, and the (approximate) length in
93    * characters of the input field
94    *
95    * @param newCacheKey
96    * @param length
97    */
 
98  8 toggle public JvCacheableInputBox(String newCacheKey, int length)
99    {
100    // super();
101  8 cacheKey = newCacheKey;
102  8 prototypeDisplayValue = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
103  8 if (length > 0)
104    {
105  8 StringBuilder sb = new StringBuilder();
106  224 for (int i = 0; i < length; i++)
107    {
108  216 sb.append("X");
109    }
110  8 setPrototypeDisplayValue(sb.toString());
111    }
112  8 boolean useTextField = Platform.isJS();
113    // BH 2019.03 only switch for JavaScript here
114    // SwingJS TODO implement editable combo box
115  8 if (useTextField)
116    {
117  0 appCache = null;
118  0 textComponent = textField = new JTextField();
119  0 FontMetrics fm = textField.getFontMetrics(textField.getFont());
120  0 textField.setPreferredSize(new Dimension(
121    fm.stringWidth(prototypeDisplayValue), fm.getHeight() + 4));
122    // {
123    // @Override
124    // public Dimension getPreferredSize() {
125    // return super.getPreferredSize();
126    //// FontMetrics fm = getFontMetrics(getFont());
127    //// return new Dimension(fm.stringWidth(prototypeDisplayValue),
128    // fm.getHeight());
129    // }
130    // };
131    }
132    else
133    {
134  8 appCache = AppCache.getInstance();
135  8 comboBox = new JComboBox<>();
136  8 textComponent = (JTextComponent) comboBox.getEditor()
137    .getEditorComponent();
138  8 comboBox.setEditable(true);
139  8 comboBox.addKeyListener(new KeyAdapter()
140    {
 
141  0 toggle @Override
142    public void keyTyped(KeyEvent e)
143    {
144  0 enterWasPressed = false;
145  0 if (e.getKeyCode() == KeyEvent.VK_ENTER)
146    {
147  0 enterWasPressed = true;
148    }
149    // let event bubble up
150    }
151    });
152  8 comboBox.setPrototypeDisplayValue(prototypeDisplayValue);
153  8 initCachePopupMenu();
154  8 initCache(newCacheKey);
155  8 updateCache();
156    }
157    }
158   
159    /**
160    * Method for initialising cache items for a given cache key and populating
161    * the in-memory cache with persisted cache items
162    *
163    * @param cacheKey
164    */
 
165  8 toggle private void initCache(String cacheKey)
166    {
167  8 if (appCache == null)
168    {
169  0 return;
170    }
171    // obtain persisted cache items from properties file as a delimited string
172  8 String delimitedCacheStr = Cache.getProperty(cacheKey);
173  8 if (delimitedCacheStr == null || delimitedCacheStr.isEmpty())
174    {
175  8 return;
176    }
177    // convert delimited cache items to a list of strings
178  0 List<String> persistedCacheItems = Arrays
179    .asList(delimitedCacheStr.split(AppCache.CACHE_DELIMITER));
180   
181  0 LinkedHashSet<String> foundCacheItems = appCache
182    .getAllCachedItemsFor(cacheKey);
183  0 if (foundCacheItems == null)
184    {
185  0 foundCacheItems = new LinkedHashSet<>();
186    }
187    // populate memory cache
188  0 for (String cacheItem : persistedCacheItems)
189    {
190  0 foundCacheItems.add(cacheItem);
191    }
192  0 appCache.putCache(cacheKey, foundCacheItems);
193    }
194   
195    /**
196    * Initialise this cache's pop-up menu
197    */
 
198  8 toggle private void initCachePopupMenu()
199    {
200  8 if (appCache == null)
201    {
202  0 return;
203    }
204  8 menuItemClearCache.setFont(new java.awt.Font("Verdana", 0, 12));
205  8 menuItemClearCache
206    .setText(MessageManager.getString("action.clear_cached_items"));
207  8 menuItemClearCache.addActionListener(new ActionListener()
208    {
 
209  0 toggle @Override
210    public void actionPerformed(ActionEvent e)
211    {
212    // jalview.bin.Console.outPrintln(">>>>> Clear cache items");
213  0 setSelectedItem("");
214  0 appCache.deleteCacheItems(cacheKey);
215  0 updateCache();
216    }
217    });
218   
219  8 popup.add(menuItemClearCache);
220  8 comboBox.setComponentPopupMenu(popup);
221  8 comboBox.add(popup);
222    }
223   
224    /**
225    * Answers true if input text is an integer
226    *
227    * @param text
228    * @return
229    */
 
230  0 toggle static boolean isInteger(String text)
231    {
232  0 try
233    {
234  0 Integer.parseInt(text);
235  0 return true;
236    } catch (NumberFormatException e)
237    {
238  0 return false;
239    }
240    }
241   
242    /**
243    * Method called to update the cache with the last user input
244    */
 
245  11 toggle public void updateCache()
246    {
247  11 if (appCache == null)
248    {
249  0 return;
250    }
251  11 SwingUtilities.invokeLater(new Runnable()
252    {
 
253  11 toggle @Override
254    public void run()
255    {
256  11 comboBox.setEnabled(false);
257  11 int cacheLimit = Integer.parseInt(appCache.getCacheLimit(cacheKey));
258  11 String userInput = getUserInput();
259  11 if (userInput != null && !userInput.isEmpty())
260    {
261  1 LinkedHashSet<String> foundCache = appCache
262    .getAllCachedItemsFor(cacheKey);
263    // remove old cache item so as to place current input at the top of
264    // the result
265  1 foundCache.remove(userInput);
266  1 foundCache.add(userInput);
267  1 appCache.putCache(cacheKey, foundCache);
268    }
269   
270  11 String lastSearch = userInput;
271  11 if (comboBox.getItemCount() > 0)
272    {
273  3 comboBox.removeAllItems();
274    }
275  11 Set<String> cacheItems = appCache.getAllCachedItemsFor(cacheKey);
276  11 List<String> reversedCacheItems = new ArrayList<>();
277  11 reversedCacheItems.addAll(cacheItems);
278  11 cacheItems = null;
279  11 Collections.reverse(reversedCacheItems);
280  11 if (lastSearch.isEmpty())
281    {
282  10 comboBox.addItem("");
283    }
284   
285  11 if (reversedCacheItems != null && !reversedCacheItems.isEmpty())
286    {
287  1 LinkedHashSet<String> foundCache = appCache
288    .getAllCachedItemsFor(cacheKey);
289  1 boolean prune = reversedCacheItems.size() > cacheLimit;
290  1 int count = 1;
291  1 boolean limitExceeded = false;
292  1 for (String cacheItem : reversedCacheItems)
293    {
294  1 limitExceeded = (count++ > cacheLimit);
295  1 if (prune)
296    {
297  0 if (limitExceeded)
298    {
299  0 foundCache.remove(cacheItem);
300    }
301    else
302    {
303  0 comboBox.addItem(cacheItem);
304    }
305    }
306    else
307    {
308  1 comboBox.addItem(cacheItem);
309    }
310    }
311  1 appCache.putCache(cacheKey, foundCache);
312    }
313  11 setSelectedItem(lastSearch.isEmpty() ? "" : lastSearch);
314  11 comboBox.setEnabled(true);
315    }
316    });
317    }
318   
319    /**
320    * This method should be called to persist the in-memory cache when this
321    * components parent frame is closed / exited
322    */
 
323  4 toggle public void persistCache()
324    {
325  4 if (appCache == null)
326    {
327  0 return;
328    }
329  4 appCache.persistCache(cacheKey);
330    }
331   
332    /**
333    * Returns the trimmed text in the input field
334    *
335    * @return
336    */
 
337  15 toggle public String getUserInput()
338    {
339  15 if (comboBox == null)
340    {
341  0 return textField.getText().trim();
342    }
343  15 Object item = comboBox.getEditor().getItem();
344  15 return item == null ? "" : item.toString().trim();
345    }
346   
 
347  10 toggle public JComponent getComponent()
348    {
349  10 return (comboBox == null ? textField : comboBox);
350    }
351   
 
352  2 toggle public void addActionListener(ActionListener actionListener)
353    {
354  2 if (comboBox == null)
355    {
356  0 textField.addActionListener(actionListener);
357    }
358    else
359    {
360  2 comboBox.addActionListener(actionListener);
361    }
362    }
363   
 
364  2 toggle public void addDocumentListener(DocumentListener listener)
365    {
366  2 textComponent.getDocument().addDocumentListener(listener);
367    }
368   
 
369  4 toggle public void addFocusListener(FocusListener focusListener)
370    {
371  4 textComponent.addFocusListener(focusListener);
372    }
373   
 
374  4 toggle public void addKeyListener(KeyListener kl)
375    {
376  4 textComponent.addKeyListener(kl);
377    }
378   
 
379  0 toggle public void addCaretListener(CaretListener caretListener)
380    {
381  0 textComponent.addCaretListener(caretListener);
382    }
383   
 
384  0 toggle public void setEditable(boolean b)
385    {
386  0 if (comboBox != null)
387    {
388  0 comboBox.setEditable(b);
389    }
390    }
391   
 
392  8 toggle public void setPrototypeDisplayValue(String string)
393    {
394  8 prototypeDisplayValue = string;
395  8 if (comboBox != null)
396    {
397  0 comboBox.setPrototypeDisplayValue(string);
398    }
399    }
400   
 
401  13 toggle public void setSelectedItem(String userInput)
402    {
403  13 if (comboBox != null)
404    {
405  13 comboBox.setSelectedItem(userInput);
406    }
407    }
408   
 
409  0 toggle public boolean isPopupVisible()
410    {
411  0 return (comboBox != null && comboBox.isPopupVisible());
412    }
413   
 
414  2 toggle public void addItem(String item)
415    {
416  2 if (comboBox != null)
417    {
418  2 comboBox.addItem(item);
419    }
420    }
421   
422    }