regular expression based URL link generation
authorjprocter <Jim Procter>
Mon, 11 Aug 2008 14:56:31 +0000 (14:56 +0000)
committerjprocter <Jim Procter>
Mon, 11 Aug 2008 14:56:31 +0000 (14:56 +0000)
src/jalview/appletgui/APopupMenu.java
src/jalview/gui/PopupMenu.java
src/jalview/jbgui/GSequenceLink.java
src/jalview/util/UrlLink.java [new file with mode: 0644]

index ff1c3c8..7c1ea3f 100755 (executable)
@@ -28,6 +28,7 @@ import jalview.analysis.*;
 import jalview.commands.*;
 import jalview.datamodel.*;
 import jalview.schemes.*;
+import jalview.util.UrlLink;
 import jalview.io.AppletFormatAdapter;
 
 public class APopupMenu
@@ -134,10 +135,59 @@ public class APopupMenu
       for (int i = 0; i < links.size(); i++)
       {
         link = links.elementAt(i).toString();
-        final String target = link.substring(0, link.indexOf("|"));
-        
-        final String url;
+        UrlLink urlLink = new UrlLink(link);
+        if (!urlLink.isValid())
+        {
+          System.err.println(urlLink.getInvalidMessage());
+          continue;
+        }
+        final String target = urlLink.getTarget(); // link.substring(0, link.indexOf("|"));
+        final String label = urlLink.getLabel();
+        if (urlLink.isDynamic())
+        {
 
+          // collect matching db-refs
+          DBRefEntry[] dbr = jalview.util.DBRefUtils.selectRefs(seq.getDBRef(), new String[]{target});
+          // collect id string too
+          String id = seq.getName();
+          if (dbr!=null)
+          {
+            for (int r=0;r<dbr.length; r++)
+            {
+              if (id!=null && dbr[r].getAccessionId().equals(id))
+              {
+                // suppress duplicate link creation for the bare sequence ID string with this link
+                id = null;
+              }
+              // create Bare ID link for this RUL
+              String[] urls = urlLink.makeUrls(dbr[r].getAccessionId(), true);
+              if (urls!=null)
+              {
+                for (int u=0; u<urls.length; u+=2)
+                {
+                  addshowLink(linkMenu, label+"|"+urls[u],urls[u+1]);
+                }
+              }
+            }
+          }
+          if (id!=null)
+          {
+            // create Bare ID link for this RUL
+            String[] urls = urlLink.makeUrls(id, true);
+            if (urls!=null)
+            {
+              for (int u=0; u<urls.length; u+=2)
+              {
+                addshowLink(linkMenu, label,urls[u+1]);
+              }
+            }
+            // addshowLink(linkMenu, target,  url_pref + id + url_suff);
+          }
+        } else {
+          addshowLink(linkMenu, target, urlLink.getUrl_prefix()); // link.substring(link.lastIndexOf("|")+1));
+        }
+        /*final String url;
+        
         if (link.indexOf("$SEQUENCE_ID$") > -1)
         {
           // Substitute SEQUENCE_ID string and any matching database reference accessions
@@ -174,7 +224,7 @@ public class APopupMenu
           }
         } else {
           addshowLink(linkMenu, target, link.substring(link.lastIndexOf("|")+1));
-        }
+        } */
       }
     
       if (seq != null)
index 546ce65..97f7cba 100755 (executable)
@@ -30,6 +30,7 @@ import jalview.commands.*;
 import jalview.datamodel.*;
 import jalview.io.*;
 import jalview.schemes.*;
+import jalview.util.UrlLink;
 
 /**
  * DOCUMENT ME!
@@ -365,49 +366,56 @@ public class PopupMenu
       for (int i = 0; i < links.size(); i++)
       {
         String link = links.elementAt(i).toString();
-        final String label = link.substring(0, link.indexOf("|"));
-        
-        
-        if (link.indexOf("$SEQUENCE_ID$") > -1)
+        UrlLink urlLink = new UrlLink(link);
+        if (!urlLink.isValid())
+        {
+          jalview.bin.Cache.log.error(urlLink.getInvalidMessage());
+          continue;
+        }
+        final String label = urlLink.getLabel();
+        if (urlLink.isDynamic())
         {
-          // Substitute SEQUENCE_ID string and any matching database reference accessions
-          String url_pref = link.substring(link.indexOf("|") + 1,
-                  link.indexOf("$SEQUENCE_ID$"));
-          
-          String url_suff = link.substring(link.indexOf("$SEQUENCE_ID$") + 13); 
 
           // collect matching db-refs
-          DBRefEntry[] dbr = jalview.util.DBRefUtils.selectRefs(seq.getDBRef(), new String[]{label});
+          DBRefEntry[] dbr = jalview.util.DBRefUtils.selectRefs(seq.getDBRef(), new String[]{urlLink.getTarget()});
           // collect id string too
           String id = seq.getName();
-          if (id.indexOf("|") > -1)
-          {
-            id = id.substring(id.lastIndexOf("|") + 1);
-          }
-
           if (dbr!=null)
           {
             for (int r=0;r<dbr.length; r++)
             {
-              if (dbr[r].getAccessionId().equals(id))
+              if (id!=null && dbr[r].getAccessionId().equals(id))
               {
                 // suppress duplicate link creation for the bare sequence ID string with this link
                 id = null;
               }
-              addshowLink(linkMenu, dbr[r].getSource()+"|"+dbr[r].getAccessionId(), 
-                      url_pref+dbr[r].getAccessionId()+url_suff);
+              // create Bare ID link for this RUL
+              String[] urls = urlLink.makeUrls(dbr[r].getAccessionId(), true);
+              if (urls!=null)
+              {
+                for (int u=0; u<urls.length; u+=2)
+                {
+                  addshowLink(linkMenu, label+"|"+urls[u],urls[u+1]); 
+                }
+              }
             }
           }
           if (id!=null)
           {
             // create Bare ID link for this RUL
-            addshowLink(linkMenu, label,  url_pref + id + url_suff);
+            String[] urls = urlLink.makeUrls(id, true);
+            if (urls!=null)
+            {
+              for (int u=0; u<urls.length; u+=2)
+              {
+                addshowLink(linkMenu, label,  urls[u+1]);
+              }
+            }
           }
-        }
-        else
+        } else
         {
           // Add a non-dynamic link
-          addshowLink(linkMenu, label, link.substring(link.lastIndexOf("|") + 1));
+          addshowLink(linkMenu, label, urlLink.getUrl_prefix());
         }
       }
       if (sequence != null)
@@ -430,6 +438,7 @@ public class PopupMenu
   private void addshowLink(JMenu linkMenu, String label, final String url)
   {
     JMenuItem item = new JMenuItem(label);
+    item.setToolTipText("open URL: "+url);
     item.addActionListener(new java.awt.event.ActionListener()
     {
       public void actionPerformed(ActionEvent e)
index 19740fa..712354a 100755 (executable)
-/*\r
- * Jalview - A Sequence Alignment Editor and Viewer\r
- * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
- *\r
- * This program is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU General Public License\r
- * as published by the Free Software Foundation; either version 2\r
- * of the License, or (at your option) any later version.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program; if not, write to the Free Software\r
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
- */\r
-package jalview.jbgui;\r
-\r
-import java.awt.*;\r
-import java.awt.event.*;\r
-import javax.swing.*;\r
-\r
-public class GSequenceLink\r
-    extends Panel\r
-{\r
-  public GSequenceLink()\r
-  {\r
-    try\r
-    {\r
-      jbInit();\r
-    }\r
-    catch (Exception ex)\r
-    {\r
-      ex.printStackTrace();\r
-    }\r
-  }\r
-\r
-  private void jbInit()\r
-      throws Exception\r
-  {\r
-    this.setLayout(gridBagLayout1);\r
-    nameTB.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));\r
-    nameTB.setBounds(new Rectangle(77, 10, 310, 23));\r
-    nameTB.addKeyListener(new KeyAdapter()\r
-    {\r
-      public void keyTyped(KeyEvent e)\r
-      {\r
-        nameTB_keyTyped(e);\r
-      }\r
-    });\r
-    urlTB.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));\r
-    urlTB.setText("http://www.");\r
-    urlTB.setBounds(new Rectangle(78, 40, 309, 23));\r
-    urlTB.addKeyListener(new KeyAdapter()\r
-    {\r
-      public void keyTyped(KeyEvent e)\r
-      {\r
-        urlTB_keyTyped(e);\r
-      }\r
-    });\r
-    jLabel1.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));\r
-    jLabel1.setHorizontalAlignment(SwingConstants.TRAILING);\r
-    jLabel1.setText("Link Name");\r
-    jLabel1.setBounds(new Rectangle(4, 10, 71, 24));\r
-    jLabel2.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));\r
-    jLabel2.setHorizontalAlignment(SwingConstants.TRAILING);\r
-    jLabel2.setText("URL");\r
-    jLabel2.setBounds(new Rectangle(17, 37, 54, 27));\r
-    jLabel3.setFont(new java.awt.Font("Verdana", Font.ITALIC, 11));\r
-    jLabel3.setText("Use $SEQUENCE_ID$ to specify where sequence id is in URL");\r
-    jLabel3.setBounds(new Rectangle(21, 72, 351, 15));\r
-    jPanel1.setBorder(BorderFactory.createEtchedBorder());\r
-    jPanel1.setLayout(null);\r
-    jPanel1.add(jLabel1);\r
-    jPanel1.add(nameTB);\r
-    jPanel1.add(urlTB);\r
-    jPanel1.add(jLabel2);\r
-    jPanel1.add(jLabel3);\r
-    this.add(jPanel1, new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0\r
-                                             , GridBagConstraints.CENTER,\r
-                                             GridBagConstraints.BOTH,\r
-                                             new Insets(5, 4, 6, 5), 390, 100));\r
-  }\r
-\r
-  public void setName(String name)\r
-  {\r
-    nameTB.setText(name);\r
-  }\r
-\r
-  public void setURL(String url)\r
-  {\r
-    urlTB.setText(url);\r
-  }\r
-\r
-  public String getName()\r
-  {\r
-    return nameTB.getText();\r
-  }\r
-\r
-  public String getURL()\r
-  {\r
-    return urlTB.getText();\r
-  }\r
-\r
-  public boolean checkValid()\r
-  {\r
-    if (urlTB.getText().indexOf("$SEQUENCE_ID$") == -1)\r
-    {\r
-      JOptionPane.showInternalMessageDialog(jalview.gui.Desktop.desktop,\r
-                                            "Sequence URL must contain $SEQUENCE_ID$",\r
-                                            "URL not valid",\r
-                                            JOptionPane.WARNING_MESSAGE);\r
-      return false;\r
-    }\r
-    return true;\r
-  }\r
-\r
-  JTextField nameTB = new JTextField();\r
-  JTextField urlTB = new JTextField();\r
-  JLabel jLabel1 = new JLabel();\r
-  JLabel jLabel2 = new JLabel();\r
-  JLabel jLabel3 = new JLabel();\r
-  JPanel jPanel1 = new JPanel();\r
-  GridBagLayout gridBagLayout1 = new GridBagLayout();\r
-  public void nameTB_keyTyped(KeyEvent e)\r
-  {\r
-    if (e.getKeyChar() == '|')\r
-    {\r
-      e.consume();\r
-    }\r
-  }\r
-\r
-  public void urlTB_keyTyped(KeyEvent e)\r
-  {\r
-    if (e.getKeyChar() == '|' || e.getKeyChar() == ' ')\r
-    {\r
-      e.consume();\r
-    }\r
-\r
-  }\r
-}\r
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer
+ * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+package jalview.jbgui;
+
+import jalview.util.UrlLink;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+
+public class GSequenceLink
+    extends Panel
+{
+  public GSequenceLink()
+  {
+    try
+    {
+      jbInit();
+    }
+    catch (Exception ex)
+    {
+      ex.printStackTrace();
+    }
+  }
+
+  private void jbInit()
+      throws Exception
+  {
+    this.setLayout(gridBagLayout1);
+    nameTB.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
+    nameTB.setBounds(new Rectangle(77, 10, 310, 23));
+    nameTB.addKeyListener(new KeyAdapter()
+    {
+      public void keyTyped(KeyEvent e)
+      {
+        nameTB_keyTyped(e);
+      }
+    });
+    urlTB.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
+    urlTB.setText("http://www.");
+    urlTB.setBounds(new Rectangle(78, 40, 309, 23));
+    urlTB.addKeyListener(new KeyAdapter()
+    {
+      public void keyTyped(KeyEvent e)
+      {
+        urlTB_keyTyped(e);
+      }
+    });
+    jLabel1.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
+    jLabel1.setHorizontalAlignment(SwingConstants.TRAILING);
+    jLabel1.setText("Link Name");
+    jLabel1.setBounds(new Rectangle(4, 10, 71, 24));
+    jLabel2.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
+    jLabel2.setHorizontalAlignment(SwingConstants.TRAILING);
+    jLabel2.setText("URL");
+    jLabel2.setBounds(new Rectangle(17, 37, 54, 27));
+    jLabel3.setFont(new java.awt.Font("Verdana", Font.ITALIC, 11));
+    jLabel3.setText("Use $SEQUENCE_ID$ to specify where sequence id is in URL");
+    jLabel3.setBounds(new Rectangle(21, 72, 351, 15));
+    jPanel1.setBorder(BorderFactory.createEtchedBorder());
+    jPanel1.setLayout(null);
+    jPanel1.add(jLabel1);
+    jPanel1.add(nameTB);
+    jPanel1.add(urlTB);
+    jPanel1.add(jLabel2);
+    jPanel1.add(jLabel3);
+    this.add(jPanel1, new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0
+                                             , GridBagConstraints.CENTER,
+                                             GridBagConstraints.BOTH,
+                                             new Insets(5, 4, 6, 5), 390, 100));
+  }
+
+  public void setName(String name)
+  {
+    nameTB.setText(name);
+  }
+
+  public void setURL(String url)
+  {
+    urlTB.setText(url);
+  }
+
+  public String getName()
+  {
+    return nameTB.getText();
+  }
+
+  public String getURL()
+  {
+    return urlTB.getText();
+  }
+
+  public boolean checkValid()
+  {
+    UrlLink ul = new UrlLink("foo|"+urlTB.getText().trim());
+    if (ul.isValid()&&ul.isDynamic())
+    {
+      return true;
+    }
+    
+    JOptionPane.showInternalMessageDialog(jalview.gui.Desktop.desktop,
+            "Sequence URL must contain $SEQUENCE_ID$ or a regex $SEQUENCE_ID=/<regex>/=$",
+            "URL not valid",
+            JOptionPane.WARNING_MESSAGE);
+    return false;
+  }
+
+  JTextField nameTB = new JTextField();
+  JTextField urlTB = new JTextField();
+  JLabel jLabel1 = new JLabel();
+  JLabel jLabel2 = new JLabel();
+  JLabel jLabel3 = new JLabel();
+  JPanel jPanel1 = new JPanel();
+  GridBagLayout gridBagLayout1 = new GridBagLayout();
+  public void nameTB_keyTyped(KeyEvent e)
+  {
+    if (e.getKeyChar() == '|')
+    {
+      e.consume();
+    }
+  }
+
+  public void urlTB_keyTyped(KeyEvent e)
+  {
+    if (e.getKeyChar() == '|' || e.getKeyChar() == ' ')
+    {
+      e.consume();
+    }
+
+  }
+}
diff --git a/src/jalview/util/UrlLink.java b/src/jalview/util/UrlLink.java
new file mode 100644 (file)
index 0000000..21dee38
--- /dev/null
@@ -0,0 +1,369 @@
+package jalview.util;\r
+\r
+import java.util.Vector;\r
+\r
+public class UrlLink\r
+{\r
+  /**\r
+   * helper class to parse URL Link strings taken from applet parameters or\r
+   * jalview properties file using the com.stevesoft.pat.Regex implementation.\r
+   * Jalview 2.4 extension allows regular expressions to be used to parse ID\r
+   * strings and replace the result in the URL. Regex's operate on the whole ID\r
+   * string given to the matchURL method, if no regex is supplied, then only\r
+   * text following the first pipe symbol will be susbstituted.\r
+   */\r
+  private String url_suffix, url_prefix, target, label, regexReplace;\r
+\r
+  private boolean dynamic = false;\r
+\r
+  private String invalidMessage = null;\r
+\r
+  /**\r
+   * parse the given linkString of the form '<label>|<url>' into parts url may\r
+   * contain a string $SEQUENCE_ID<=optional regex=>$ where <=optional regex=>\r
+   * must be of the form =/<perl style regex>/=$\r
+   * \r
+   * @param link\r
+   */\r
+  public UrlLink(String link)\r
+  {\r
+    int sep = link.indexOf("|"), psqid = link.indexOf("$SEQUENCE_ID");\r
+    if (psqid > -1)\r
+    {\r
+      dynamic = true;\r
+      int p = sep;\r
+      do\r
+      {\r
+        sep = p;\r
+        p = link.indexOf("|", sep + 1);\r
+      } while (p > sep && p < psqid);\r
+      // Assuming that the URL itself does not contain any '|' symbols\r
+      // sep now contains last pipe symbol position prior to any regex symbols\r
+      label = link.substring(0, sep);\r
+      if (label.indexOf("|") > -1)\r
+      {\r
+        // | terminated database name / www target at start of Label\r
+        target = label.substring(0, label.indexOf("|"));\r
+      }\r
+      else if (label.indexOf(" ") > 2)\r
+      {\r
+        // space separated Label - matches database name\r
+        target = label.substring(0, label.indexOf(" "));\r
+      }\r
+      else\r
+      {\r
+        target = label;\r
+      }\r
+      // Parse URL : Whole URL string first\r
+      url_prefix = link.substring(sep + 1, psqid);\r
+      if (link.indexOf("$SEQUENCE_ID=/") == psqid\r
+              && (p = link.indexOf("/=$", psqid + 14)) > psqid + 14)\r
+      {\r
+        // Extract Regex and suffix\r
+        url_suffix = link.substring(p + 3);\r
+        regexReplace = link.substring(psqid + 14, p);\r
+        try\r
+        {\r
+          com.stevesoft.pat.Regex rg = com.stevesoft.pat.Regex.perlCode("/"\r
+                  + regexReplace + "/");\r
+          if (rg == null)\r
+          {\r
+            invalidMessage = "Invalid Regular Expression : '"\r
+                    + regexReplace + "'\n";\r
+          }\r
+        } catch (Exception e)\r
+        {\r
+          invalidMessage = "Invalid Regular Expression : '" + regexReplace\r
+                  + "'\n";\r
+        }\r
+      }\r
+      else\r
+      {\r
+        regexReplace = null;\r
+        // verify format is really correct.\r
+        if (link.indexOf("$SEQUENCE_ID$") == psqid)\r
+        {\r
+          url_suffix = link.substring(psqid + 13);\r
+          regexReplace = null;\r
+        }\r
+        else\r
+        {\r
+          invalidMessage = "Warning: invalid regex structure for URL link : "\r
+                  + link;\r
+        }\r
+      }\r
+    }\r
+    else\r
+    {\r
+      target = link.substring(0, sep);\r
+      label = link.substring(0, sep = link.lastIndexOf("|"));\r
+      url_prefix = link.substring(sep + 1);\r
+      regexReplace = null; // implies we trim any prefix if necessary //\r
+                            // regexReplace=".*\\|?(.*)";\r
+      url_suffix = null;\r
+    }\r
+  }\r
+\r
+  /**\r
+   * @return the url_suffix\r
+   */\r
+  public String getUrl_suffix()\r
+  {\r
+    return url_suffix;\r
+  }\r
+\r
+  /**\r
+   * @return the url_prefix\r
+   */\r
+  public String getUrl_prefix()\r
+  {\r
+    return url_prefix;\r
+  }\r
+\r
+  /**\r
+   * @return the target\r
+   */\r
+  public String getTarget()\r
+  {\r
+    return target;\r
+  }\r
+\r
+  /**\r
+   * @return the label\r
+   */\r
+  public String getLabel()\r
+  {\r
+    return label;\r
+  }\r
+\r
+  /**\r
+   * @return the regexReplace\r
+   */\r
+  public String getRegexReplace()\r
+  {\r
+    return regexReplace;\r
+  }\r
+\r
+  /**\r
+   * @return the invalidMessage\r
+   */\r
+  public String getInvalidMessage()\r
+  {\r
+    return invalidMessage;\r
+  }\r
+\r
+  /**\r
+   * \r
+   * @return true if URL string could not be parsed properly.\r
+   */\r
+  public boolean isValid()\r
+  {\r
+    return invalidMessage == null;\r
+  }\r
+\r
+  /**\r
+   * return one or more URL strings by applying regex to the given idstring\r
+   * \r
+   * @param idstring\r
+   * @param onlyIfMatches -\r
+   *                when true url strings are only made if regex is defined and\r
+   *                matches\r
+   * @return String[] { part of idstring substituted, full substituted url , ..\r
+   *         next part, next url..}\r
+   */\r
+  public String[] makeUrls(String idstring, boolean onlyIfMatches)\r
+  {\r
+    if (dynamic)\r
+    {\r
+      if (regexReplace != null)\r
+      {\r
+        com.stevesoft.pat.Regex rg = com.stevesoft.pat.Regex.perlCode("/"\r
+                + regexReplace + "/");\r
+        if (rg.search(idstring))\r
+        {\r
+          int ns = rg.numSubs();\r
+          if (ns == 0)\r
+          {\r
+            // take whole regex\r
+            return new String[]\r
+            { rg.stringMatched(),\r
+                url_prefix + rg.stringMatched() + url_suffix };\r
+          } /*\r
+             * else if (ns==1) { // take only subgroup match return new String[] {\r
+             * rg.stringMatched(1), url_prefix+rg.stringMatched(1)+url_suffix }; }\r
+             */\r
+          else\r
+          {\r
+            // debug\r
+            for (int s = 0; s <= rg.numSubs(); s++)\r
+            {\r
+              System.err.println("Sub " + s + " : " + rg.matchedFrom(s)\r
+                      + " : " + rg.matchedTo(s) + " : '"\r
+                      + rg.stringMatched(s) + "'");\r
+            }\r
+            // try to collate subgroup matches\r
+            Vector subs = new Vector();\r
+            // have to loop through submatches, collating them at top level\r
+            // match\r
+            int s = 0; // 1;\r
+            while (s <= ns)\r
+            {\r
+              if (s + 1 <= ns && rg.matchedTo(s) > -1\r
+                      && rg.matchedTo(s + 1) > -1\r
+                      && rg.matchedTo(s + 1) < rg.matchedTo(s))\r
+              {\r
+                // s is top level submatch. search for submatches enclosed by\r
+                // this one\r
+                int r = s + 1;\r
+                String mtch = "";\r
+                while (r <= ns && rg.matchedTo(r) <= rg.matchedTo(s))\r
+                {\r
+                  if (rg.matchedFrom(r) > -1)\r
+                  {\r
+                    mtch += rg.stringMatched(r);\r
+                  }\r
+                  r++;\r
+                }\r
+                if (mtch.length() > 0)\r
+                {\r
+                  subs.addElement(mtch);\r
+                  subs.addElement(url_prefix + mtch + url_suffix);\r
+                }\r
+                s = r;\r
+              }\r
+              else\r
+              {\r
+                if (rg.matchedFrom(s) > -1)\r
+                {\r
+                  subs.addElement(rg.stringMatched(s));\r
+                  subs.addElement(url_prefix + rg.stringMatched(s)\r
+                          + url_suffix);\r
+                }\r
+                s++;\r
+              }\r
+            }\r
+\r
+            String[] res = new String[subs.size()];\r
+            for (int r = 0, rs = subs.size(); r < rs; r++)\r
+            {\r
+              res[r] = (String) subs.elementAt(r);\r
+            }\r
+            subs.removeAllElements();\r
+            return res;\r
+          }\r
+        }\r
+        if (onlyIfMatches)\r
+        {\r
+          return null;\r
+        }\r
+      }\r
+      /* Otherwise - trim off any 'prefix' - pre 2.4 Jalview behaviour */\r
+      if (idstring.indexOf("|") > -1)\r
+      {\r
+        idstring = idstring.substring(idstring.lastIndexOf("|") + 1);\r
+      }\r
+\r
+      // just return simple url substitution.\r
+      return new String[]\r
+      { idstring, url_prefix + idstring + url_suffix };\r
+    }\r
+    else\r
+    {\r
+      return new String[]\r
+      { "", url_prefix };\r
+    }\r
+  }\r
+\r
+  public String toString()\r
+  {\r
+    return label\r
+            + "|"\r
+            + url_prefix\r
+            + (dynamic ? ("$SEQUENCE_ID" + ((regexReplace != null) ? "="\r
+                    + regexReplace + "=$" : "$")) : "")\r
+            + ((url_suffix == null) ? "" : url_suffix);\r
+\r
+  }\r
+\r
+  private static void testUrls(UrlLink ul, String idstring, String[] urls)\r
+  {\r
+\r
+    if (urls == null)\r
+    {\r
+      System.out.println("Created NO urls.");\r
+    }\r
+    else\r
+    {\r
+      System.out.println("Created " + (urls.length / 2) + " Urls.");\r
+      for (int uls = 0; uls < urls.length; uls += 2)\r
+      {\r
+        System.out.println("URL Replacement text : " + urls[uls]\r
+                + " : URL : " + urls[uls + 1]);\r
+      }\r
+    }\r
+  }\r
+\r
+  public static void main(String argv[])\r
+  {\r
+    String[] links = new String[]\r
+    {\r
+    /*\r
+     * "AlinkT|Target|http://foo.foo.soo/",\r
+     * "myUrl1|http://$SEQUENCE_ID=/[0-9]+/=$.someserver.org/foo",\r
+     * "myUrl2|http://$SEQUENCE_ID=/(([0-9]+).+([A-Za-z]+))/=$.someserver.org/foo",\r
+     * "myUrl3|http://$SEQUENCE_ID=/([0-9]+).+([A-Za-z]+)/=$.someserver.org/foo",\r
+     * "myUrl4|target|http://$SEQUENCE_ID$.someserver.org/foo|too",\r
+     * "PF1|http://us.expasy.org/cgi-bin/niceprot.pl?$SEQUENCE_ID=/(?:PFAM:)?(.+)/=$",\r
+     * "PF2|http://us.expasy.org/cgi-bin/niceprot.pl?$SEQUENCE_ID=/(PFAM:)?(.+)/=$",\r
+     * "PF3|http://us.expasy.org/cgi-bin/niceprot.pl?$SEQUENCE_ID=/PFAM:(.+)/=$",\r
+     * "NOTFER|http://notfer.org/$SEQUENCE_ID=/(?<!\\s)(.+)/=$",\r
+     */\r
+    "NESTED|http://nested/$SEQUENCE_ID=/^(?:Label:)?(?:(?:gi\\|(\\d+))|([^:]+))/=$/nested" };\r
+    String[] idstrings = new String[]\r
+    {\r
+    /*\r
+     * //"LGUL_human", //"QWIQW_123123", "uniprot|why_do+_12313_foo",\r
+     * //"123123312", "123123 ABCDE foo", "PFAM:PF23943",\r
+     */\r
+    "Label:gi|9234|pdb|102L|A" };\r
+\r
+    for (int i = 0; i < links.length; i++)\r
+    {\r
+      UrlLink ul = new UrlLink(links[i]);\r
+      if (ul.isValid())\r
+      {\r
+        System.out.println("\n\n\n");\r
+        System.out.println("Link " + i + " " + links[i] + " : "\r
+                + ul.toString());\r
+        System.out.println(" pref : "\r
+                + ul.getUrl_prefix()\r
+                + "\n suf : "\r
+                + ul.getUrl_suffix()\r
+                + "\n : "\r
+                + ((ul.getRegexReplace() != null) ? ul.getRegexReplace()\r
+                        : ""));\r
+        for (int ids = 0; ids < idstrings.length; ids++)\r
+        {\r
+          System.out.println("ID String : " + idstrings[ids]\r
+                  + "\nWithout onlyIfMatches:");\r
+          String[] urls = ul.makeUrls(idstrings[ids], false);\r
+          testUrls(ul, idstrings[ids], urls);\r
+          System.out.println("With onlyIfMatches set.");\r
+          urls = ul.makeUrls(idstrings[ids], true);\r
+          testUrls(ul, idstrings[ids], urls);\r
+        }\r
+      }\r
+      else\r
+      {\r
+        System.err.println("Invalid URLLink : " + links[i] + " : "\r
+                + ul.getInvalidMessage());\r
+      }\r
+    }\r
+  }\r
+\r
+  public boolean isDynamic()\r
+  {\r
+    // TODO Auto-generated method stub\r
+    return dynamic;\r
+  }\r
+}\r