Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  2. Package jalview.io.cache

File JvCacheableInputBox.java

 

Coverage histogram

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

Code metrics

46
105
22
1
404
283
49
0.47
4.77
22
2.23

Classes

Class Line # Actions
JvCacheableInputBox 61 105 49
0.641618564.2%
 

Contributing tests

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