JAL-2484 minor house-keeping
[jalview.git] / src / jalview / io / cache / JvCacheableInputBox.java
1 package jalview.io.cache;
2
3 import jalview.util.MessageManager;
4
5 import java.awt.Color;
6 import java.awt.Dimension;
7 import java.awt.event.ActionEvent;
8 import java.awt.event.ActionListener;
9 import java.util.ArrayList;
10 import java.util.Collections;
11 import java.util.LinkedHashSet;
12 import java.util.List;
13 import java.util.Set;
14
15 import javax.swing.BorderFactory;
16 import javax.swing.JComboBox;
17 import javax.swing.JLabel;
18 import javax.swing.JMenuItem;
19 import javax.swing.JPanel;
20 import javax.swing.JPopupMenu;
21 import javax.swing.JTextField;
22 import javax.swing.SwingUtilities;
23 import javax.swing.text.AttributeSet;
24 import javax.swing.text.BadLocationException;
25 import javax.swing.text.PlainDocument;
26
27 public class JvCacheableInputBox<E> extends JComboBox<String>
28 {
29
30   private static final long serialVersionUID = 5774610435079326695L;
31
32   private String cacheKey;
33
34   private AppCache appCache;
35
36   JPanel pnlDefaultCache = new JPanel();
37
38   JLabel lblDefaultCacheSize = new JLabel();
39
40   JTextField txtDefaultCacheSize = new JTextField();
41
42   JPopupMenu popup = new JPopupMenu();
43
44   JMenuItem menuItemClearCache = new JMenuItem();
45
46   final static int INPUT_LIMIT = 2;
47
48
49   public JvCacheableInputBox(String cacheKey)
50   {
51     super();
52     this.cacheKey = cacheKey;
53     setEditable(true);
54     setPrototypeDisplayValue("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
55     appCache = AppCache.getInstance();
56     initCachePopupMenu();
57     appCache.initCache(cacheKey);
58     updateCache();
59   }
60
61
62   /**
63    * Initialise this cache's pop-up menu
64    */
65   private void initCachePopupMenu()
66   {
67     pnlDefaultCache.setBackground(Color.WHITE);
68     pnlDefaultCache.setBorder(BorderFactory.createEmptyBorder(0, 19, 0, 0));
69     txtDefaultCacheSize.setPreferredSize(new Dimension(45, 20));
70     lblDefaultCacheSize.setText(MessageManager
71             .getString("label.default_cache_size"));
72     // Force input to accept only Integer entries up to length - INPUT_LIMIT
73     txtDefaultCacheSize.setDocument(new PlainDocument()
74     {
75       private static final long serialVersionUID = 1L;
76
77       @Override
78       public void insertString(int offs, String str, AttributeSet a)
79               throws BadLocationException
80       {
81         if (getLength() + str.length() <= INPUT_LIMIT && isInteger(str))
82         {
83           super.insertString(offs, str, a);
84         }
85       }
86     });
87     txtDefaultCacheSize.setText(appCache.getCacheLmit(cacheKey));
88     pnlDefaultCache.add(lblDefaultCacheSize);
89     pnlDefaultCache.add(txtDefaultCacheSize);
90     menuItemClearCache.setText(MessageManager
91             .getString("action.clear_cached_items"));
92     menuItemClearCache.addActionListener(new ActionListener()
93     {
94       @Override
95       public void actionPerformed(ActionEvent e)
96       {
97         System.out.println(">>>>> Clear cache items");
98         setSelectedItem("");
99         appCache.deleteCacheItems(cacheKey);
100         updateCache();
101       }
102     });
103
104     popup.insert(pnlDefaultCache, 0);
105     popup.add(menuItemClearCache);
106     setComponentPopupMenu(popup);
107     add(popup);
108   }
109
110   /**
111    * Answers true if input text is an integer
112    * 
113    * @param text
114    * @return
115    */
116   private boolean isInteger(String text)
117   {
118     try
119     {
120       Integer.parseInt(text);
121       return true;
122     } catch (NumberFormatException e)
123     {
124       return false;
125     }
126   }
127
128   /**
129    * Method called to update the cache with the last user input
130    */
131   public void updateCache()
132   {
133     SwingUtilities.invokeLater(new Runnable()
134     {
135       @Override
136       public void run()
137       {
138         int cacheLimit = appCache.updateCacheLimit(cacheKey,
139                 txtDefaultCacheSize.getText());
140         String userInput = getUserInput();
141         if (userInput != null && !userInput.isEmpty())
142         {
143           LinkedHashSet<String> foundCache = appCache
144                   .getAllCachedItemsFor(cacheKey);
145           // remove old cache item so as to place current input at the top of
146           // the result
147           foundCache.remove(userInput);
148           foundCache.add(userInput);
149           appCache.putCache(cacheKey, foundCache);
150         }
151
152         String lastSearch = userInput;
153         if (getItemCount() > 0)
154         {
155           removeAllItems();
156         }
157         Set<String> cacheItems = appCache.getAllCachedItemsFor(cacheKey);
158         List<String> reversedCacheItems = new ArrayList<String>();
159         reversedCacheItems.addAll(cacheItems);
160         cacheItems = null;
161         Collections.reverse(reversedCacheItems);
162         if (lastSearch.isEmpty())
163         {
164           addItem("");
165         }
166
167         if (reversedCacheItems != null && !reversedCacheItems.isEmpty())
168         {
169           LinkedHashSet<String> foundCache = appCache
170                   .getAllCachedItemsFor(cacheKey);
171           boolean prune = reversedCacheItems.size() > cacheLimit;
172           int count = 1;
173           boolean limitExceeded = false;
174           for (String cacheItem : reversedCacheItems)
175           {
176             limitExceeded = (count++ > cacheLimit);
177             if (prune)
178             {
179               if (limitExceeded)
180               {
181                 foundCache.remove(cacheItem);
182               }
183               else
184               {
185                 addItem(cacheItem);
186               }
187             }
188             else
189             {
190               addItem(cacheItem);
191             }
192           }
193           appCache.putCache(cacheKey, foundCache);
194         }
195         setSelectedItem(lastSearch.isEmpty() ? "" : lastSearch);
196       }
197     });
198   }
199
200
201   /**
202    * This method should be called to persist the in-memory cache when this
203    * components parent frame is closed / exited
204    */
205   public void persistCache()
206   {
207     appCache.persistCache(cacheKey);
208     appCache.updateCacheLimit(cacheKey, txtDefaultCacheSize.getText());
209   }
210
211   /**
212    * Method to obtain input text from the cache box
213    * 
214    * @return
215    */
216   public String getUserInput()
217   {
218     return getEditor().getItem() == null ? "" : getEditor().getItem()
219             .toString().trim();
220   }
221
222 }