JAL-2484 adjusted default cache settings panel's left border width
[jalview.git] / src / jalview / io / cache / JvCacheableInputBox.java
index 55254c5..444670b 100644 (file)
 package jalview.io.cache;
 
+import jalview.bin.Cache;
+import jalview.util.MessageManager;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
+import javax.swing.BorderFactory;
 import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JMenuItem;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JTextField;
 import javax.swing.SwingUtilities;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.PlainDocument;
 
 public class JvCacheableInputBox<E> extends JComboBox<String>
 {
 
   private static final long serialVersionUID = 5774610435079326695L;
 
+  private static final int INPUT_LIMIT = 2;
+
+  private static final int LEFT_BOARDER_WIDTH = 16;
+
   private String cacheKey;
 
   private AppCache appCache;
 
-  public JvCacheableInputBox(String cacheKey)
+  private JPanel pnlDefaultCache = new JPanel();
+
+  private JLabel lblDefaultCacheSize = new JLabel();
+
+  private JTextField txtDefaultCacheSize = new JTextField();
+
+  private JPopupMenu popup = new JPopupMenu();
+
+  private JMenuItem menuItemClearCache = new JMenuItem();
+
+  public JvCacheableInputBox(String newCacheKey)
   {
     super();
-    this.cacheKey = cacheKey;
+    this.cacheKey = newCacheKey;
     setEditable(true);
     setPrototypeDisplayValue("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
     appCache = AppCache.getInstance();
-    appCache.initCache(cacheKey);
+    initCachePopupMenu();
+    initCache(newCacheKey);
     updateCache();
   }
 
+  /**
+   * Method for initialising cache items for a given cache key and populating
+   * the in-memory cache with persisted cache items
+   * 
+   * @param cacheKey
+   */
+  private void initCache(String cacheKey)
+  {
+    // obtain persisted cache items from properties file as a delimited string
+    String delimitedCacheStr = Cache.getProperty(cacheKey);
+    if (delimitedCacheStr == null || delimitedCacheStr.isEmpty())
+    {
+      return;
+    }
+    // convert delimited cache items to a list of strings
+    List<String> persistedCacheItems = Arrays.asList(delimitedCacheStr
+            .split(AppCache.CACHE_DELIMITER));
+
+    LinkedHashSet<String> foundCacheItems = appCache
+            .getAllCachedItemsFor(cacheKey);
+    if (foundCacheItems == null)
+    {
+      foundCacheItems = new LinkedHashSet<String>();
+    }
+    // populate memory cache
+    for (String cacheItem : persistedCacheItems)
+    {
+      foundCacheItems.add(cacheItem);
+    }
+    appCache.putCache(cacheKey, foundCacheItems);
+  }
+
+  /**
+   * Initialise this cache's pop-up menu
+   */
+  private void initCachePopupMenu()
+  {
+    pnlDefaultCache.setBackground(Color.WHITE);
+    // pad panel so as to align with other menu items
+    pnlDefaultCache.setBorder(BorderFactory.createEmptyBorder(0,
+            LEFT_BOARDER_WIDTH, 0, 0));
+    txtDefaultCacheSize.setPreferredSize(new Dimension(45, 20));
+    txtDefaultCacheSize.setFont(new java.awt.Font("Verdana", 0, 12));
+    lblDefaultCacheSize.setText(MessageManager
+            .getString("label.default_cache_size"));
+    lblDefaultCacheSize.setFont(new java.awt.Font("Verdana", 0, 12));
+    // Force input to accept only Integer entries up to length - INPUT_LIMIT
+    txtDefaultCacheSize.setDocument(new PlainDocument()
+    {
+      private static final long serialVersionUID = 1L;
+
+      @Override
+      public void insertString(int offs, String str, AttributeSet a)
+              throws BadLocationException
+      {
+        if (getLength() + str.length() <= INPUT_LIMIT && isInteger(str))
+        {
+          super.insertString(offs, str, a);
+        }
+      }
+    });
+    txtDefaultCacheSize.addKeyListener(new java.awt.event.KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent e)
+      {
+        if (e.getKeyCode() == KeyEvent.VK_ENTER)
+        {
+          e.consume();
+          updateCache();
+          closePopup();
+        }
+      }
+    });
+
+    txtDefaultCacheSize.setText(appCache.getCacheLimit(cacheKey));
+    pnlDefaultCache.add(lblDefaultCacheSize);
+    menuItemClearCache.setFont(new java.awt.Font("Verdana", 0, 12));
+    pnlDefaultCache.add(txtDefaultCacheSize);
+    menuItemClearCache.setText(MessageManager
+            .getString("action.clear_cached_items"));
+    menuItemClearCache.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        // System.out.println(">>>>> Clear cache items");
+        setSelectedItem("");
+        appCache.deleteCacheItems(cacheKey);
+        updateCache();
+      }
+    });
+
+    popup.insert(pnlDefaultCache, 0);
+    popup.add(menuItemClearCache);
+    setComponentPopupMenu(popup);
+    add(popup);
+  }
+
+  private void closePopup()
+  {
+    popup.setVisible(false);
+    popup.transferFocus();
+  }
 
   /**
    * Answers true if input text is an integer
@@ -36,7 +173,7 @@ public class JvCacheableInputBox<E> extends JComboBox<String>
    * @param text
    * @return
    */
-  private boolean isInteger(String text)
+  static boolean isInteger(String text)
   {
     try
     {
@@ -58,6 +195,10 @@ public class JvCacheableInputBox<E> extends JComboBox<String>
       @Override
       public void run()
       {
+        int userLimit = txtDefaultCacheSize.getText().trim().isEmpty() ? Integer
+                .valueOf(AppCache.DEFAULT_LIMIT) : Integer
+                .valueOf(txtDefaultCacheSize.getText());
+        int cacheLimit = appCache.updateCacheLimit(cacheKey, userLimit);
         String userInput = getUserInput();
         if (userInput != null && !userInput.isEmpty())
         {
@@ -84,12 +225,34 @@ public class JvCacheableInputBox<E> extends JComboBox<String>
         {
           addItem("");
         }
+
         if (reversedCacheItems != null && !reversedCacheItems.isEmpty())
         {
+          LinkedHashSet<String> foundCache = appCache
+                  .getAllCachedItemsFor(cacheKey);
+          boolean prune = reversedCacheItems.size() > cacheLimit;
+          int count = 1;
+          boolean limitExceeded = false;
           for (String cacheItem : reversedCacheItems)
           {
-            addItem(cacheItem);
+            limitExceeded = (count++ > cacheLimit);
+            if (prune)
+            {
+              if (limitExceeded)
+              {
+                foundCache.remove(cacheItem);
+              }
+              else
+              {
+                addItem(cacheItem);
+              }
+            }
+            else
+            {
+              addItem(cacheItem);
+            }
           }
+          appCache.putCache(cacheKey, foundCache);
         }
         setSelectedItem(lastSearch.isEmpty() ? "" : lastSearch);
       }
@@ -97,6 +260,24 @@ public class JvCacheableInputBox<E> extends JComboBox<String>
   }
 
 
+  /**
+   * This method should be called to persist the in-memory cache when this
+   * components parent frame is closed / exited
+   */
+  public void persistCache()
+  {
+    appCache.persistCache(cacheKey);
+    int userLimit = txtDefaultCacheSize.getText().trim().isEmpty() ? Integer
+            .valueOf(AppCache.DEFAULT_LIMIT) : Integer
+            .valueOf(txtDefaultCacheSize.getText());
+    appCache.updateCacheLimit(cacheKey, userLimit);
+  }
+
+  /**
+   * Method to obtain input text from the cache box
+   * 
+   * @return
+   */
   public String getUserInput()
   {
     return getEditor().getItem() == null ? "" : getEditor().getItem()