Merge branch 'feature/JAL-3181linkOrdering' into develop
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 28 Mar 2019 07:56:40 +0000 (07:56 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 28 Mar 2019 07:56:40 +0000 (07:56 +0000)
src/jalview/gui/PopupMenu.java
src/jalview/urls/CustomUrlProvider.java
src/jalview/util/UrlLink.java
test/jalview/gui/PopupMenuTest.java
test/jalview/util/UrlLinkTest.java

index 86febed..702773b 100644 (file)
@@ -78,10 +78,8 @@ import javax.swing.JPopupMenu;
 import javax.swing.JRadioButtonMenuItem;
 
 /**
- * DOCUMENT ME!
- * 
- * @author $author$
- * @version $Revision: 1.118 $
+ * The popup menu that is displayed on right-click on a sequence id, or in the
+ * sequence alignment.
  */
 public class PopupMenu extends JPopupMenu implements ColourChangeListener
 {
@@ -178,6 +176,187 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
   JMenuItem hideInsertions = new JMenuItem();
 
   /**
+   * Constructs a menu with sub-menu items for any hyperlinks for the sequence
+   * and/or features provided. Hyperlinks may include a lookup by sequence id,
+   * or database cross-references, depending on which links are enabled in user
+   * preferences.
+   * 
+   * @param seq
+   * @param features
+   * @return
+   */
+  static JMenu buildLinkMenu(final SequenceI seq,
+          List<SequenceFeature> features)
+  {
+    JMenu linkMenu = new JMenu(MessageManager.getString("action.link"));
+
+    List<String> nlinks = null;
+    if (seq != null)
+    {
+      nlinks = Preferences.sequenceUrlLinks.getLinksForMenu();
+      UrlLink.sort(nlinks);
+    }
+    else
+    {
+      nlinks = new ArrayList<>();
+    }
+
+    if (features != null)
+    {
+      for (SequenceFeature sf : features)
+      {
+        if (sf.links != null)
+        {
+          for (String link : sf.links)
+          {
+            nlinks.add(link);
+          }
+        }
+      }
+    }
+
+    /*
+     * instantiate the hyperlinklink templates from sequence data;
+     * note the order of the templates is preserved in the map
+     */
+    Map<String, List<String>> linkset = new LinkedHashMap<>();
+    for (String link : nlinks)
+    {
+      UrlLink urlLink = null;
+      try
+      {
+        urlLink = new UrlLink(link);
+      } catch (Exception foo)
+      {
+        Cache.log.error("Exception for URLLink '" + link + "'", foo);
+        continue;
+      }
+
+      if (!urlLink.isValid())
+      {
+        Cache.log.error(urlLink.getInvalidMessage());
+        continue;
+      }
+
+      urlLink.createLinksFromSeq(seq, linkset);
+    }
+
+    /*
+     * construct menu items for the hyperlinks (still preserving
+     * the order of the sorted templates)
+     */
+    addUrlLinks(linkMenu, linkset.values());
+
+    return linkMenu;
+  }
+
+  /**
+   * A helper method that builds menu items from the given links, with action
+   * handlers to open the link URL, and adds them to the linkMenu. Each provided
+   * link should be a list whose second item is the menu text, and whose fourth
+   * item is the URL to open when the menu item is selected.
+   * 
+   * @param linkMenu
+   * @param linkset
+   */
+  static private void addUrlLinks(JMenu linkMenu,
+          Collection<List<String>> linkset)
+  {
+    for (List<String> linkstrset : linkset)
+    {
+      final String url = linkstrset.get(3);
+      JMenuItem item = new JMenuItem(linkstrset.get(1));
+      item.setToolTipText(MessageManager
+              .formatMessage("label.open_url_param", new Object[]
+              { url }));
+      item.addActionListener(new ActionListener()
+      {
+        @Override
+        public void actionPerformed(ActionEvent e)
+        {
+          new Thread(new Runnable()
+          {
+            @Override
+            public void run()
+            {
+              showLink(url);
+            }
+          }).start();
+        }
+      });
+      linkMenu.add(item);
+    }
+  }
+
+  /**
+   * Opens the provided url in the default web browser, or shows an error
+   * message if this fails
+   * 
+   * @param url
+   */
+  static void showLink(String url)
+  {
+    try
+    {
+      jalview.util.BrowserLauncher.openURL(url);
+    } catch (Exception ex)
+    {
+      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+              MessageManager.getString("label.web_browser_not_found_unix"),
+              MessageManager.getString("label.web_browser_not_found"),
+              JvOptionPane.WARNING_MESSAGE);
+
+      ex.printStackTrace();
+    }
+  }
+
+  /**
+   * add a late bound groupURL item to the given linkMenu
+   * 
+   * @param linkMenu
+   * @param label
+   *          - menu label string
+   * @param urlgenerator
+   *          GroupURLLink used to generate URL
+   * @param urlstub
+   *          Object array returned from the makeUrlStubs function.
+   */
+  static void addshowLink(JMenu linkMenu, String label,
+          final GroupUrlLink urlgenerator, final Object[] urlstub)
+  {
+    JMenuItem item = new JMenuItem(label);
+    item.setToolTipText(MessageManager
+            .formatMessage("label.open_url_seqs_param", new Object[]
+            { urlgenerator.getUrl_prefix(),
+                urlgenerator.getNumberInvolved(urlstub) }));
+    // TODO: put in info about what is being sent.
+    item.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        new Thread(new Runnable()
+        {
+
+          @Override
+          public void run()
+          {
+            try
+            {
+              showLink(urlgenerator.constructFrom(urlstub));
+            } catch (UrlStringTooLongException e2)
+            {
+            }
+          }
+
+        }).start();
+      }
+    });
+
+    linkMenu.add(item);
+  }
+
+  /**
    * Creates a new PopupMenu object.
    * 
    * @param ap
@@ -620,63 +799,14 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
    * When seq is not null, these are links for the sequence id, which may be to
    * external web sites for the sequence accession, and/or links embedded in
    * non-positional features. When seq is null, only links embedded in the
-   * provided features are added.
+   * provided features are added. If no links are found, the menu is not added.
    * 
    * @param seq
    * @param features
    */
   void addLinks(final SequenceI seq, List<SequenceFeature> features)
   {
-    JMenu linkMenu = new JMenu(MessageManager.getString("action.link"));
-
-    List<String> nlinks = null;
-    if (seq != null)
-    {
-      nlinks = Preferences.sequenceUrlLinks.getLinksForMenu();
-    }
-    else
-    {
-      nlinks = new ArrayList<>();
-    }
-
-    if (features != null)
-    {
-      for (SequenceFeature sf : features)
-      {
-        if (sf.links != null)
-        {
-          for (String link : sf.links)
-          {
-            nlinks.add(link);
-          }
-        }
-      }
-    }
-
-    Map<String, List<String>> linkset = new LinkedHashMap<>();
-
-    for (String link : nlinks)
-    {
-      UrlLink urlLink = null;
-      try
-      {
-        urlLink = new UrlLink(link);
-      } catch (Exception foo)
-      {
-        Cache.log.error("Exception for URLLink '" + link + "'", foo);
-        continue;
-      }
-
-      if (!urlLink.isValid())
-      {
-        Cache.log.error(urlLink.getInvalidMessage());
-        continue;
-      }
-
-      urlLink.createLinksFromSeq(seq, linkset);
-    }
-
-    addshowLinks(linkMenu, linkset.values());
+    JMenu linkMenu = buildLinkMenu(seq, features);
 
     // only add link menu if it has entries
     if (linkMenu.getItemCount() > 0)
@@ -969,98 +1099,6 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
     }
   }
 
-  private void addshowLinks(JMenu linkMenu,
-          Collection<List<String>> linkset)
-  {
-    for (List<String> linkstrset : linkset)
-    {
-      // split linkstr into label and url
-      addshowLink(linkMenu, linkstrset.get(1), linkstrset.get(3));
-    }
-  }
-
-  /**
-   * add a show URL menu item to the given linkMenu
-   * 
-   * @param linkMenu
-   * @param label
-   *          - menu label string
-   * @param url
-   *          - url to open
-   */
-  private void addshowLink(JMenu linkMenu, String label, final String url)
-  {
-    JMenuItem item = new JMenuItem(label);
-    item.setToolTipText(MessageManager.formatMessage("label.open_url_param",
-            new Object[]
-            { url }));
-    item.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        new Thread(new Runnable()
-        {
-
-          @Override
-          public void run()
-          {
-            showLink(url);
-          }
-
-        }).start();
-      }
-    });
-
-    linkMenu.add(item);
-  }
-
-  /**
-   * add a late bound groupURL item to the given linkMenu
-   * 
-   * @param linkMenu
-   * @param label
-   *          - menu label string
-   * @param urlgenerator
-   *          GroupURLLink used to generate URL
-   * @param urlstub
-   *          Object array returned from the makeUrlStubs function.
-   */
-  private void addshowLink(JMenu linkMenu, String label,
-          final GroupUrlLink urlgenerator, final Object[] urlstub)
-  {
-    JMenuItem item = new JMenuItem(label);
-    item.setToolTipText(MessageManager
-            .formatMessage("label.open_url_seqs_param", new Object[]
-            { urlgenerator.getUrl_prefix(),
-                urlgenerator.getNumberInvolved(urlstub) }));
-    // TODO: put in info about what is being sent.
-    item.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        new Thread(new Runnable()
-        {
-
-          @Override
-          public void run()
-          {
-            try
-            {
-              showLink(urlgenerator.constructFrom(urlstub));
-            } catch (UrlStringTooLongException e2)
-            {
-            }
-          }
-
-        }).start();
-      }
-    });
-
-    linkMenu.add(item);
-  }
-
   /**
    * DOCUMENT ME!
    * 
@@ -1940,22 +1978,6 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
     refresh();
   }
 
-  public void showLink(String url)
-  {
-    try
-    {
-      jalview.util.BrowserLauncher.openURL(url);
-    } catch (Exception ex)
-    {
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
-              MessageManager.getString("label.web_browser_not_found_unix"),
-              MessageManager.getString("label.web_browser_not_found"),
-              JvOptionPane.WARNING_MESSAGE);
-
-      ex.printStackTrace();
-    }
-  }
-
   void hideSequences(boolean representGroup)
   {
     ap.av.hideSequences(sequence, representGroup);
index 86d5660..d23f771 100644 (file)
@@ -107,12 +107,12 @@ public class CustomUrlProvider extends UrlProviderImpl
   {
     // cachedUrlList is in form <label>|<url>|<label>|<url>...
     // parse cachedUrlList into labels (used as id) and url links
-    HashMap<String, UrlLink> urls = new HashMap<String, UrlLink>();
+    HashMap<String, UrlLink> urls = new HashMap<>();
 
     StringTokenizer st = new StringTokenizer(urlStrings, SEP);
     while (st.hasMoreElements())
     {
-      String name = st.nextToken();
+      String name = st.nextToken().trim();
 
       if (!isMiriamId(name))
       {
@@ -129,6 +129,7 @@ public class CustomUrlProvider extends UrlProviderImpl
         {
           url = url + SEP + st.nextToken();
         }
+        url = url.trim();
         urls.put(name, new UrlLink(name, url, name));
       }
     }
@@ -138,7 +139,7 @@ public class CustomUrlProvider extends UrlProviderImpl
 
   private HashMap<String, UrlLink> parseUrlList(Map<String, String> urlList)
   {
-    HashMap<String, UrlLink> urls = new HashMap<String, UrlLink>();
+    HashMap<String, UrlLink> urls = new HashMap<>();
     if (urlList == null)
     {
       return urls;
@@ -187,7 +188,7 @@ public class CustomUrlProvider extends UrlProviderImpl
   @Override
   public List<String> getLinksForMenu()
   {
-    List<String> links = new ArrayList<String>();
+    List<String> links = new ArrayList<>();
     Iterator<Map.Entry<String, UrlLink>> it = selectedUrls.entrySet()
             .iterator();
     while (it.hasNext())
@@ -201,7 +202,7 @@ public class CustomUrlProvider extends UrlProviderImpl
   @Override
   public List<UrlLinkDisplay> getLinksForTable()
   {
-    ArrayList<UrlLinkDisplay> displayLinks = new ArrayList<UrlLinkDisplay>();
+    ArrayList<UrlLinkDisplay> displayLinks = new ArrayList<>();
     displayLinks = getLinksForTable(selectedUrls, true);
     displayLinks.addAll(getLinksForTable(nonselectedUrls, false));
     return displayLinks;
@@ -289,8 +290,8 @@ public class CustomUrlProvider extends UrlProviderImpl
   @Override
   public void setUrlData(List<UrlLinkDisplay> links)
   {
-    HashMap<String, UrlLink> unselurls = new HashMap<String, UrlLink>();
-    HashMap<String, UrlLink> selurls = new HashMap<String, UrlLink>();
+    HashMap<String, UrlLink> unselurls = new HashMap<>();
+    HashMap<String, UrlLink> selurls = new HashMap<>();
 
     Iterator<UrlLinkDisplay> it = links.iterator();
     while (it.hasNext())
index 007da86..18ee9b6 100644 (file)
@@ -29,21 +29,57 @@ import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.SequenceI;
 
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.Vector;
 
+/**
+ * A helper class to parse URL Link strings taken from applet parameters or
+ * jalview properties file using the com.stevesoft.pat.Regex implementation.
+ * Jalview 2.4 extension allows regular expressions to be used to parse ID
+ * strings and replace the result in the URL. Regex's operate on the whole ID
+ * string given to the matchURL method, if no regex is supplied, then only text
+ * following the first pipe symbol will be substituted. Usage documentation
+ * todo.
+ */
 public class UrlLink
 {
+  private static final String SEQUENCEID_PLACEHOLDER = DELIM + SEQUENCE_ID
+          + DELIM;
+
+  private static final String ACCESSION_PLACEHOLDER = DELIM + DB_ACCESSION
+          + DELIM;
+
   /**
-   * helper class to parse URL Link strings taken from applet parameters or
-   * jalview properties file using the com.stevesoft.pat.Regex implementation.
-   * Jalview 2.4 extension allows regular expressions to be used to parse ID
-   * strings and replace the result in the URL. Regex's operate on the whole ID
-   * string given to the matchURL method, if no regex is supplied, then only
-   * text following the first pipe symbol will be substituted. Usage
-   * documentation todo.
+   * A comparator that puts SEQUENCE_ID template links before DB_ACCESSION
+   * links, and otherwise orders by link name + url (not case sensitive). It
+   * expects to compare strings formatted as "Name|URLTemplate" where the
+   * template may include $SEQUENCE_ID$ or $DB_ACCESSION$ or neither.
    */
+  public static final Comparator<String> LINK_COMPARATOR = new Comparator<String>()
+  {
+    @Override
+    public int compare(String link1, String link2)
+    {
+      if (link1 == null || link2 == null)
+      {
+        return 0; // for failsafe only
+      }
+      if (link1.contains(SEQUENCEID_PLACEHOLDER)
+              && link2.contains(ACCESSION_PLACEHOLDER))
+      {
+        return -1;
+      }
+      if (link2.contains(SEQUENCEID_PLACEHOLDER)
+              && link1.contains(ACCESSION_PLACEHOLDER))
+      {
+        return 1;
+      }
+      return String.CASE_INSENSITIVE_ORDER.compare(link1, link2);
+    }
+  };
 
   private static final String EQUALS = "=";
 
@@ -291,7 +327,7 @@ public class UrlLink
                       + rg.stringMatched(s) + "'");
             }
             // try to collate subgroup matches
-            Vector<String> subs = new Vector<String>();
+            Vector<String> subs = new Vector<>();
             // have to loop through submatches, collating them at top level
             // match
             int s = 0; // 1;
@@ -632,4 +668,20 @@ public class UrlLink
       }
     }
   }
+
+  /**
+   * Sorts links (formatted as LinkName|LinkPattern) suitable for display in a
+   * menu
+   * <ul>
+   * <li>SEQUENCE_ID links precede DB_ACCESSION links (i.e. canonical lookup
+   * before cross-references)</li>
+   * <li>otherwise by Link name (case insensitive)</li>
+   * </ul>
+   * 
+   * @param nlinks
+   */
+  public static void sort(List<String> nlinks)
+  {
+    Collections.sort(nlinks, LINK_COMPARATOR);
+  }
 }
index 6f60588..df30935 100644 (file)
@@ -489,115 +489,76 @@ public class PopupMenuTest
   }
 
   /**
-   * Test for adding feature links
+   * Test for adding sequence id, dbref and feature links
    */
   @Test(groups = { "Functional" })
-  public void testAddFeatureLinks()
+  public void testBuildLinkMenu()
   {
-    // sequences from the alignment
     List<SequenceI> seqs = parentPanel.getAlignment().getSequences();
-
-    // create list of links and list of DBRefs
-    List<String> links = new ArrayList<>();
-    List<DBRefEntry> refs = new ArrayList<>();
-
-    // links as might be added into Preferences | Connections dialog
-    links.add("EMBL-EBI Search | http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$"
-            + SEQUENCE_ID + "$");
-    links.add("UNIPROT | http://www.uniprot.org/uniprot/$" + DB_ACCESSION
-            + "$");
-    links.add("INTERPRO | http://www.ebi.ac.uk/interpro/entry/$"
-            + DB_ACCESSION + "$");
-    // Gene3D entry tests for case (in)sensitivity
-    links.add("Gene3D | http://gene3d.biochem.ucl.ac.uk/Gene3D/search?sterm=$"
-            + DB_ACCESSION + "$&mode=protein");
-
-    // make seq0 dbrefs
-    refs.add(new DBRefEntry(DBRefSource.UNIPROT, "1", "P83527"));
-    refs.add(new DBRefEntry("INTERPRO", "1", "IPR001041"));
-    refs.add(new DBRefEntry("INTERPRO", "1", "IPR006058"));
-    refs.add(new DBRefEntry("INTERPRO", "1", "IPR012675"));
-    
-    // make seq1 dbrefs
-    refs.add(new DBRefEntry(DBRefSource.UNIPROT, "1", "Q9ZTS2"));
-    refs.add(new DBRefEntry("GENE3D", "1", "3.10.20.30"));
-
-    // add all the dbrefs to the sequences: Uniprot 1 each, Interpro all 3 to
-    // seq0, Gene3D to seq1
-    SequenceI seq = seqs.get(0);
-    seq.addDBRef(refs.get(0));
-
-    seq.addDBRef(refs.get(1));
-    seq.addDBRef(refs.get(2));
-    seq.addDBRef(refs.get(3));
+    final SequenceI seq0 = seqs.get(0);
+    final SequenceI seq1 = seqs.get(1);
+    final List<SequenceFeature> noFeatures = Collections
+            .<SequenceFeature> emptyList();
+    final String linkText = MessageManager.getString("action.link");
+
+    seq0.addDBRef(new DBRefEntry(DBRefSource.UNIPROT, "1", "P83527"));
+    seq0.addDBRef(new DBRefEntry("INTERPRO", "1", "IPR001041"));
+    seq0.addDBRef(new DBRefEntry("INTERPRO", "1", "IPR012675"));
+    seq0.addDBRef(new DBRefEntry("INTERPRO", "1", "IPR006058"));
+    seq1.addDBRef(new DBRefEntry(DBRefSource.UNIPROT, "1", "Q9ZTS2"));
+    seq1.addDBRef(new DBRefEntry("GENE3D", "1", "3.10.20.30"));
     
-    seqs.get(1).addDBRef(refs.get(4));
-    seqs.get(1).addDBRef(refs.get(5));
-    
-    // get the Popup Menu for first sequence
-    List<SequenceFeature> noFeatures = Collections.<SequenceFeature> emptyList();
-    testee = new PopupMenu(parentPanel, seq, noFeatures);
-    Component[] seqItems = testee.sequenceMenu.getMenuComponents();
-    JMenu linkMenu = (JMenu) seqItems[6];
+    /*
+     * check the Link Menu for the first sequence
+     */
+    JMenu linkMenu = PopupMenu.buildLinkMenu(seq0, noFeatures);
+    assertEquals(linkText, linkMenu.getText());
     Component[] linkItems = linkMenu.getMenuComponents();
     
-    // check the number of links are the expected number
+    /*
+     * menu items are ordered: SEQUENCE_ID search first, then dbrefs in order
+     * of database name (and within that by order of dbref addition)
+     */
     assertEquals(5, linkItems.length);
-
-    // first entry is EMBL-EBI which just uses sequence id not accession id?
     assertEquals("EMBL-EBI Search", ((JMenuItem) linkItems[0]).getText());
+    assertEquals("INTERPRO|IPR001041",
+            ((JMenuItem) linkItems[1]).getText());
+    assertEquals("INTERPRO|IPR012675",
+            ((JMenuItem) linkItems[2]).getText());
+    assertEquals("INTERPRO|IPR006058",
+            ((JMenuItem) linkItems[3]).getText());
+    assertEquals("UNIPROT|P83527", ((JMenuItem) linkItems[4]).getText());
 
-    // sequence id for each link should match corresponding DB accession id
-    for (int i = 1; i < 4; i++)
-    {
-      String msg = seq.getName() + " link[" + i + "]";
-      assertEquals(msg, refs.get(i - 1).getSource(),
-              ((JMenuItem) linkItems[i])
-              .getText().split("\\|")[0]);
-      assertEquals(msg, refs.get(i - 1).getAccessionId(),
-              ((JMenuItem) linkItems[i])
-              .getText().split("\\|")[1]);
-    }
-
-    // get the Popup Menu for second sequence
-    seq = seqs.get(1);
-    testee = new PopupMenu(parentPanel, seq, noFeatures);
-    seqItems = testee.sequenceMenu.getMenuComponents();
-    linkMenu = (JMenu) seqItems[6];
+    /*
+     * check the Link Menu for the second sequence
+     * note dbref GENE3D is matched to link Gene3D, the latter is displayed
+     */
+    linkMenu = PopupMenu.buildLinkMenu(seq1, noFeatures);
+    assertEquals(linkText, linkMenu.getText());
     linkItems = linkMenu.getMenuComponents();
-    
-    // check the number of links are the expected number
     assertEquals(3, linkItems.length);
-
-    // first entry is EMBL-EBI which just uses sequence id not accession id?
     assertEquals("EMBL-EBI Search", ((JMenuItem) linkItems[0]).getText());
+    assertEquals("Gene3D|3.10.20.30", ((JMenuItem) linkItems[1]).getText());
+    assertEquals("UNIPROT|Q9ZTS2", ((JMenuItem) linkItems[2]).getText());
 
-    // sequence id for each link should match corresponding DB accession id
-    for (int i = 1; i < 3; i++)
-    {
-      String msg = seq.getName() + " link[" + i + "]";
-      assertEquals(msg, refs.get(i + 3).getSource(),
-              ((JMenuItem) linkItems[i])
-              .getText().split("\\|")[0].toUpperCase());
-      assertEquals(msg, refs.get(i + 3).getAccessionId(),
-              ((JMenuItem) linkItems[i]).getText().split("\\|")[1]);
-    }
-
-    // if there are no valid links the Links submenu is disabled
-    List<String> nomatchlinks = new ArrayList<>();
-    nomatchlinks.add("NOMATCH | http://www.uniprot.org/uniprot/$"
-            + DB_ACCESSION + "$");
-
-    testee = new PopupMenu(parentPanel, seq, noFeatures);
-    seqItems = testee.sequenceMenu.getMenuComponents();
-    linkMenu = (JMenu) seqItems[6];
-    assertFalse(linkMenu.isEnabled());
+    /*
+     * if there are no valid links the Links submenu is still shown, but
+     * reduced to the EMBL-EBI lookup only (inserted by 
+     * CustomUrlProvider.choosePrimaryUrl())
+     */
+    String unmatched = "NOMATCH|http://www.uniprot.org/uniprot/$"
+            + DB_ACCESSION + "$";
+    UrlProviderFactoryI factory = new DesktopUrlProviderFactory(null,
+            unmatched, "");
+    Preferences.sequenceUrlLinks = factory.createUrlProvider();
 
+    linkMenu = PopupMenu.buildLinkMenu(seq1, noFeatures);
+    assertEquals(linkText, linkMenu.getText());
+    linkItems = linkMenu.getMenuComponents();
+    assertEquals(1, linkItems.length);
+    assertEquals("EMBL-EBI Search", ((JMenuItem) linkItems[0]).getText());
   }
 
-  /**
-   * Test for adding feature links
-   */
   @Test(groups = { "Functional" })
   public void testHideInsertions()
   {
index 4092cf2..c7d37b0 100644 (file)
@@ -33,6 +33,7 @@ import jalview.datamodel.Sequence;
 import jalview.gui.JvOptionPane;
 
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -271,7 +272,7 @@ public class UrlLinkTest
     UrlLink ul = new UrlLink(DB + SEP + URL_PREFIX + DELIM + SEQUENCE_ID
             + DELIM + URL_SUFFIX);
 
-    Map<String, List<String>> linkset = new LinkedHashMap<String, List<String>>();
+    Map<String, List<String>> linkset = new LinkedHashMap<>();
     ul.createLinksFromSeq(null, linkset);
 
     String key = DB + SEP + URL_PREFIX;
@@ -291,7 +292,7 @@ public class UrlLinkTest
   {
     UrlLink ul = new UrlLink(DB + SEP + URL_PREFIX + URL_SUFFIX);
 
-    Map<String, List<String>> linkset = new LinkedHashMap<String, List<String>>();
+    Map<String, List<String>> linkset = new LinkedHashMap<>();
     ul.createLinksFromSeq(null, linkset);
 
     String key = DB + SEP + URL_PREFIX + URL_SUFFIX;
@@ -311,8 +312,8 @@ public class UrlLinkTest
   {
 
     // create list of links and list of DBRefs
-    List<String> links = new ArrayList<String>();
-    List<DBRefEntry> refs = new ArrayList<DBRefEntry>();
+    List<String> links = new ArrayList<>();
+    List<DBRefEntry> refs = new ArrayList<>();
 
     // links as might be added into Preferences | Connections dialog
     links.add("EMBL-EBI Search | http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$"
@@ -341,7 +342,7 @@ public class UrlLinkTest
     UrlLink ul = new UrlLink(DB + SEP + URL_PREFIX + DELIM + SEQUENCE_ID
             + DELIM + URL_SUFFIX);
 
-    Map<String, List<String>> linkset = new LinkedHashMap<String, List<String>>();
+    Map<String, List<String>> linkset = new LinkedHashMap<>();
     ul.createLinksFromSeq(seq0, linkset);
 
     String key = seq0.getName() + SEP + URL_PREFIX + seq0.getName()
@@ -356,7 +357,7 @@ public class UrlLinkTest
 
     // Test where link takes a db annotation id and only has one dbref
     ul = new UrlLink(links.get(1));
-    linkset = new LinkedHashMap<String, List<String>>();
+    linkset = new LinkedHashMap<>();
     ul.createLinksFromSeq(seq0, linkset);
 
     key = "P83527|http://www.uniprot.org/uniprot/P83527";
@@ -371,7 +372,7 @@ public class UrlLinkTest
 
     // Test where link takes a db annotation id and has multiple dbrefs
     ul = new UrlLink(links.get(2));
-    linkset = new LinkedHashMap<String, List<String>>();
+    linkset = new LinkedHashMap<>();
     ul.createLinksFromSeq(seq0, linkset);
     assertEquals(3, linkset.size());
 
@@ -403,7 +404,7 @@ public class UrlLinkTest
     // Test where there are no matching dbrefs for the link
     ul = new UrlLink(DB + SEP + URL_PREFIX + DELIM + DB_ACCESSION + DELIM
             + URL_SUFFIX);
-    linkset = new LinkedHashMap<String, List<String>>();
+    linkset = new LinkedHashMap<>();
     ul.createLinksFromSeq(seq0, linkset);
     assertTrue(linkset.isEmpty());
   }
@@ -439,4 +440,25 @@ public class UrlLinkTest
 
   }
 
+  @Test(groups = { "Functional" })
+  public void testLinkComparator()
+  {
+    Comparator<String> c = UrlLink.LINK_COMPARATOR;
+    assertEquals(0, c.compare(null, null));
+    assertEquals(0, c.compare(null, "x"));
+    assertEquals(0, c.compare("y", null));
+
+    /*
+     * SEQUENCE_ID templates should come before DB_ACCESSION templates
+     */
+    String dbRefUrl = "Cath|http://www.cathdb.info/version/v4_2_0/superfamily/$DB_ACCESSION$";
+    String seqIdUrl = "EBI|https://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$";
+    assertTrue(c.compare(seqIdUrl, dbRefUrl) < 0);
+    assertTrue(c.compare(dbRefUrl, seqIdUrl) > 0);
+
+    String interpro = "Interpro|https://www.ebi.ac.uk/interpro/entry/$DB_ACCESSION$";
+    String prosite = "ProSite|https://prosite.expasy.org/PS00197";
+    assertTrue(c.compare(interpro, prosite) < 0);
+    assertTrue(c.compare(prosite, interpro) > 0);
+  }
 }