beef3e77a2df3a8b7e5d6e96ad348290ba68f0a1
[jalview.git] / src / jalview / io / cache / JvCacheableInputBox.java
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
26 import java.awt.event.ActionEvent;
27 import java.awt.event.ActionListener;
28 import java.awt.event.KeyEvent;
29 import java.awt.event.KeyListener;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.Collections;
33 import java.util.LinkedHashSet;
34 import java.util.List;
35 import java.util.Set;
36
37 import javax.swing.JComboBox;
38 import javax.swing.JMenuItem;
39 import javax.swing.JPopupMenu;
40 import javax.swing.SwingUtilities;
41
42 public class JvCacheableInputBox<E> extends JComboBox<String>
43 {
44
45   private static final long serialVersionUID = 5774610435079326695L;
46
47   private static final int LEFT_BOARDER_WIDTH = 16;
48
49   private String cacheKey;
50
51   private AppCache appCache;
52
53   private JPopupMenu popup = new JPopupMenu();
54
55   private JMenuItem menuItemClearCache = new JMenuItem();
56
57   volatile boolean enterWasPressed = false;
58
59   /**
60    * @return flag indicating if the most recent keypress was enter
61    */
62   public boolean wasEnterPressed()
63   {
64     return enterWasPressed;
65   }
66
67   public JvCacheableInputBox(String newCacheKey)
68   {
69     super();
70     this.cacheKey = newCacheKey;
71     setEditable(true);
72     addKeyListener(new KeyListener()
73     {
74
75       @Override
76       public void keyTyped(KeyEvent e)
77       {
78         enterWasPressed = false;
79         if (e.getKeyCode() == KeyEvent.VK_ENTER)
80         {
81           enterWasPressed = true;
82         }
83         // let event bubble up
84       }
85
86       @Override
87       public void keyReleased(KeyEvent e)
88       {
89         // TODO Auto-generated method stub
90
91       }
92
93       @Override
94       public void keyPressed(KeyEvent e)
95       {
96         // TODO Auto-generated method stub
97
98       }
99     });
100     setPrototypeDisplayValue(
101             "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
102     appCache = AppCache.getInstance();
103     initCachePopupMenu();
104     initCache(newCacheKey);
105     updateCache();
106   }
107
108   /**
109    * Method for initialising cache items for a given cache key and populating the
110    * in-memory cache with persisted cache items
111    * 
112    * @param cacheKey
113    */
114   private void initCache(String cacheKey)
115   {
116     // obtain persisted cache items from properties file as a delimited string
117     String delimitedCacheStr = Cache.getProperty(cacheKey);
118     if (delimitedCacheStr == null || delimitedCacheStr.isEmpty())
119     {
120       return;
121     }
122     // convert delimited cache items to a list of strings
123     List<String> persistedCacheItems = Arrays
124             .asList(delimitedCacheStr.split(AppCache.CACHE_DELIMITER));
125
126     LinkedHashSet<String> foundCacheItems = appCache
127             .getAllCachedItemsFor(cacheKey);
128     if (foundCacheItems == null)
129     {
130       foundCacheItems = new LinkedHashSet<>();
131     }
132     // populate memory cache
133     for (String cacheItem : persistedCacheItems)
134     {
135       foundCacheItems.add(cacheItem);
136     }
137     appCache.putCache(cacheKey, foundCacheItems);
138   }
139
140   /**
141    * Initialise this cache's pop-up menu
142    */
143   private void initCachePopupMenu()
144   {
145     menuItemClearCache.setFont(new java.awt.Font("Verdana", 0, 12));
146     menuItemClearCache
147             .setText(MessageManager.getString("action.clear_cached_items"));
148     menuItemClearCache.addActionListener(new ActionListener()
149     {
150       @Override
151       public void actionPerformed(ActionEvent e)
152       {
153         // System.out.println(">>>>> Clear cache items");
154         setSelectedItem("");
155         appCache.deleteCacheItems(cacheKey);
156         updateCache();
157       }
158     });
159
160     popup.add(menuItemClearCache);
161     setComponentPopupMenu(popup);
162     add(popup);
163   }
164
165   /**
166    * Answers true if input text is an integer
167    * 
168    * @param text
169    * @return
170    */
171   static boolean isInteger(String text)
172   {
173     try
174     {
175       Integer.parseInt(text);
176       return true;
177     } catch (NumberFormatException e)
178     {
179       return false;
180     }
181   }
182
183   /**
184    * Method called to update the cache with the last user input
185    */
186   public void updateCache()
187   {
188     SwingUtilities.invokeLater(new Runnable()
189     {
190       @Override
191       public void run()
192       {
193         int cacheLimit = Integer.parseInt(appCache.getCacheLimit(cacheKey));
194         String userInput = getUserInput();
195         if (userInput != null && !userInput.isEmpty())
196         {
197           LinkedHashSet<String> foundCache = appCache
198                   .getAllCachedItemsFor(cacheKey);
199           // remove old cache item so as to place current input at the top of
200           // the result
201           foundCache.remove(userInput);
202           foundCache.add(userInput);
203           appCache.putCache(cacheKey, foundCache);
204         }
205
206         String lastSearch = userInput;
207         if (getItemCount() > 0)
208         {
209           removeAllItems();
210         }
211         Set<String> cacheItems = appCache.getAllCachedItemsFor(cacheKey);
212         List<String> reversedCacheItems = new ArrayList<>();
213         reversedCacheItems.addAll(cacheItems);
214         cacheItems = null;
215         Collections.reverse(reversedCacheItems);
216         if (lastSearch.isEmpty())
217         {
218           addItem("");
219         }
220
221         if (reversedCacheItems != null && !reversedCacheItems.isEmpty())
222         {
223           LinkedHashSet<String> foundCache = appCache
224                   .getAllCachedItemsFor(cacheKey);
225           boolean prune = reversedCacheItems.size() > cacheLimit;
226           int count = 1;
227           boolean limitExceeded = false;
228           for (String cacheItem : reversedCacheItems)
229           {
230             limitExceeded = (count++ > cacheLimit);
231             if (prune)
232             {
233               if (limitExceeded)
234               {
235                 foundCache.remove(cacheItem);
236               }
237               else
238               {
239                 addItem(cacheItem);
240               }
241             }
242             else
243             {
244               addItem(cacheItem);
245             }
246           }
247           appCache.putCache(cacheKey, foundCache);
248         }
249         setSelectedItem(lastSearch.isEmpty() ? "" : lastSearch);
250       }
251     });
252   }
253
254   /**
255    * This method should be called to persist the in-memory cache when this
256    * components parent frame is closed / exited
257    */
258   public void persistCache()
259   {
260     appCache.persistCache(cacheKey);
261   }
262
263   /**
264    * Method to obtain input text from the cache box
265    * 
266    * @return
267    */
268   public String getUserInput()
269   {
270     return getEditor().getItem() == null ? ""
271             : getEditor().getItem().toString().trim();
272   }
273
274 }