JAL-1620 version bump and release notes
[jalview.git] / src / jalview / util / UrlLink.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2b1)
3  * Copyright (C) 2014 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.util;
22
23 import java.util.Vector;
24
25 public class UrlLink
26 {
27   /**
28    * helper class to parse URL Link strings taken from applet parameters or
29    * jalview properties file using the com.stevesoft.pat.Regex implementation.
30    * Jalview 2.4 extension allows regular expressions to be used to parse ID
31    * strings and replace the result in the URL. Regex's operate on the whole ID
32    * string given to the matchURL method, if no regex is supplied, then only
33    * text following the first pipe symbol will be susbstituted. Usage
34    * documentation todo.
35    */
36   private String url_suffix, url_prefix, target, label, regexReplace;
37
38   private boolean dynamic = false;
39
40   private String invalidMessage = null;
41
42   /**
43    * parse the given linkString of the form '<label>|<url>' into parts url may
44    * contain a string $SEQUENCE_ID<=optional regex=>$ where <=optional regex=>
45    * must be of the form =/<perl style regex>/=$
46    * 
47    * @param link
48    */
49   public UrlLink(String link)
50   {
51     int sep = link.indexOf("|"), psqid = link.indexOf("$SEQUENCE_ID");
52     if (psqid > -1)
53     {
54       dynamic = true;
55       int p = sep;
56       do
57       {
58         sep = p;
59         p = link.indexOf("|", sep + 1);
60       } while (p > sep && p < psqid);
61       // Assuming that the URL itself does not contain any '|' symbols
62       // sep now contains last pipe symbol position prior to any regex symbols
63       label = link.substring(0, sep);
64       if (label.indexOf("|") > -1)
65       {
66         // | terminated database name / www target at start of Label
67         target = label.substring(0, label.indexOf("|"));
68       }
69       else if (label.indexOf(" ") > 2)
70       {
71         // space separated Label - matches database name
72         target = label.substring(0, label.indexOf(" "));
73       }
74       else
75       {
76         target = label;
77       }
78       // Parse URL : Whole URL string first
79       url_prefix = link.substring(sep + 1, psqid);
80       if (link.indexOf("$SEQUENCE_ID=/") == psqid
81               && (p = link.indexOf("/=$", psqid + 14)) > psqid + 14)
82       {
83         // Extract Regex and suffix
84         url_suffix = link.substring(p + 3);
85         regexReplace = link.substring(psqid + 14, p);
86         try
87         {
88           com.stevesoft.pat.Regex rg = com.stevesoft.pat.Regex.perlCode("/"
89                   + regexReplace + "/");
90           if (rg == null)
91           {
92             invalidMessage = "Invalid Regular Expression : '"
93                     + regexReplace + "'\n";
94           }
95         } catch (Exception e)
96         {
97           invalidMessage = "Invalid Regular Expression : '" + regexReplace
98                   + "'\n";
99         }
100       }
101       else
102       {
103         regexReplace = null;
104         // verify format is really correct.
105         if (link.indexOf("$SEQUENCE_ID$") == psqid)
106         {
107           url_suffix = link.substring(psqid + 13);
108           regexReplace = null;
109         }
110         else
111         {
112           invalidMessage = "Warning: invalid regex structure for URL link : "
113                   + link;
114         }
115       }
116     }
117     else
118     {
119       target = link.substring(0, sep);
120       label = link.substring(0, sep = link.lastIndexOf("|"));
121       url_prefix = link.substring(sep + 1);
122       regexReplace = null; // implies we trim any prefix if necessary //
123       // regexReplace=".*\\|?(.*)";
124       url_suffix = null;
125     }
126   }
127
128   /**
129    * @return the url_suffix
130    */
131   public String getUrl_suffix()
132   {
133     return url_suffix;
134   }
135
136   /**
137    * @return the url_prefix
138    */
139   public String getUrl_prefix()
140   {
141     return url_prefix;
142   }
143
144   /**
145    * @return the target
146    */
147   public String getTarget()
148   {
149     return target;
150   }
151
152   /**
153    * @return the label
154    */
155   public String getLabel()
156   {
157     return label;
158   }
159
160   /**
161    * @return the regexReplace
162    */
163   public String getRegexReplace()
164   {
165     return regexReplace;
166   }
167
168   /**
169    * @return the invalidMessage
170    */
171   public String getInvalidMessage()
172   {
173     return invalidMessage;
174   }
175
176   /**
177    * Check if URL string was parsed properly.
178    * 
179    * @return boolean - if false then <code>getInvalidMessage</code> returns an
180    *         error message
181    */
182   public boolean isValid()
183   {
184     return invalidMessage == null;
185   }
186
187   /**
188    * return one or more URL strings by applying regex to the given idstring
189    * 
190    * @param idstring
191    * @param onlyIfMatches
192    *          - when true url strings are only made if regex is defined and
193    *          matches
194    * @return String[] { part of idstring substituted, full substituted url , ..
195    *         next part, next url..}
196    */
197   public String[] makeUrls(String idstring, boolean onlyIfMatches)
198   {
199     if (dynamic)
200     {
201       if (regexReplace != null)
202       {
203         com.stevesoft.pat.Regex rg = com.stevesoft.pat.Regex.perlCode("/"
204                 + regexReplace + "/");
205         if (rg.search(idstring))
206         {
207           int ns = rg.numSubs();
208           if (ns == 0)
209           {
210             // take whole regex
211             return new String[]
212             { rg.stringMatched(),
213                 url_prefix + rg.stringMatched() + url_suffix };
214           } /*
215              * else if (ns==1) { // take only subgroup match return new String[]
216              * { rg.stringMatched(1), url_prefix+rg.stringMatched(1)+url_suffix
217              * }; }
218              */
219           else
220           {
221             // debug
222             for (int s = 0; s <= rg.numSubs(); s++)
223             {
224               System.err.println("Sub " + s + " : " + rg.matchedFrom(s)
225                       + " : " + rg.matchedTo(s) + " : '"
226                       + rg.stringMatched(s) + "'");
227             }
228             // try to collate subgroup matches
229             Vector subs = new Vector();
230             // have to loop through submatches, collating them at top level
231             // match
232             int s = 0; // 1;
233             while (s <= ns)
234             {
235               if (s + 1 <= ns && rg.matchedTo(s) > -1
236                       && rg.matchedTo(s + 1) > -1
237                       && rg.matchedTo(s + 1) < rg.matchedTo(s))
238               {
239                 // s is top level submatch. search for submatches enclosed by
240                 // this one
241                 int r = s + 1;
242                 String mtch = "";
243                 while (r <= ns && rg.matchedTo(r) <= rg.matchedTo(s))
244                 {
245                   if (rg.matchedFrom(r) > -1)
246                   {
247                     mtch += rg.stringMatched(r);
248                   }
249                   r++;
250                 }
251                 if (mtch.length() > 0)
252                 {
253                   subs.addElement(mtch);
254                   subs.addElement(url_prefix + mtch + url_suffix);
255                 }
256                 s = r;
257               }
258               else
259               {
260                 if (rg.matchedFrom(s) > -1)
261                 {
262                   subs.addElement(rg.stringMatched(s));
263                   subs.addElement(url_prefix + rg.stringMatched(s)
264                           + url_suffix);
265                 }
266                 s++;
267               }
268             }
269
270             String[] res = new String[subs.size()];
271             for (int r = 0, rs = subs.size(); r < rs; r++)
272             {
273               res[r] = (String) subs.elementAt(r);
274             }
275             subs.removeAllElements();
276             return res;
277           }
278         }
279         if (onlyIfMatches)
280         {
281           return null;
282         }
283       }
284       /* Otherwise - trim off any 'prefix' - pre 2.4 Jalview behaviour */
285       if (idstring.indexOf("|") > -1)
286       {
287         idstring = idstring.substring(idstring.lastIndexOf("|") + 1);
288       }
289
290       // just return simple url substitution.
291       return new String[]
292       { idstring, url_prefix + idstring + url_suffix };
293     }
294     else
295     {
296       return new String[]
297       { "", url_prefix };
298     }
299   }
300
301   public String toString()
302   {
303     return label
304             + "|"
305             + url_prefix
306             + (dynamic ? ("$SEQUENCE_ID" + ((regexReplace != null) ? "="
307                     + regexReplace + "=$" : "$")) : "")
308             + ((url_suffix == null) ? "" : url_suffix);
309
310   }
311
312   private static void testUrls(UrlLink ul, String idstring, String[] urls)
313   {
314
315     if (urls == null)
316     {
317       System.out.println("Created NO urls.");
318     }
319     else
320     {
321       System.out.println("Created " + (urls.length / 2) + " Urls.");
322       for (int uls = 0; uls < urls.length; uls += 2)
323       {
324         System.out.println("URL Replacement text : " + urls[uls]
325                 + " : URL : " + urls[uls + 1]);
326       }
327     }
328   }
329
330   public static void main(String argv[])
331   {
332     String[] links = new String[]
333     {
334     /*
335      * "AlinkT|Target|http://foo.foo.soo/",
336      * "myUrl1|http://$SEQUENCE_ID=/[0-9]+/=$.someserver.org/foo",
337      * "myUrl2|http://$SEQUENCE_ID=/(([0-9]+).+([A-Za-z]+))/=$.someserver.org/foo"
338      * ,
339      * "myUrl3|http://$SEQUENCE_ID=/([0-9]+).+([A-Za-z]+)/=$.someserver.org/foo"
340      * , "myUrl4|target|http://$SEQUENCE_ID$.someserver.org/foo|too",
341      * "PF1|http://us.expasy.org/cgi-bin/niceprot.pl?$SEQUENCE_ID=/(?:PFAM:)?(.+)/=$"
342      * ,
343      * "PF2|http://us.expasy.org/cgi-bin/niceprot.pl?$SEQUENCE_ID=/(PFAM:)?(.+)/=$"
344      * ,
345      * "PF3|http://us.expasy.org/cgi-bin/niceprot.pl?$SEQUENCE_ID=/PFAM:(.+)/=$"
346      * , "NOTFER|http://notfer.org/$SEQUENCE_ID=/(?<!\\s)(.+)/=$",
347      */
348     "NESTED|http://nested/$SEQUENCE_ID=/^(?:Label:)?(?:(?:gi\\|(\\d+))|([^:]+))/=$/nested" };
349     String[] idstrings = new String[]
350     {
351     /*
352      * //"LGUL_human", //"QWIQW_123123", "uniprot|why_do+_12313_foo",
353      * //"123123312", "123123 ABCDE foo", "PFAM:PF23943",
354      */
355     "Label:gi|9234|pdb|102L|A" };
356     // TODO: test the setLabel method.
357     for (int i = 0; i < links.length; i++)
358     {
359       UrlLink ul = new UrlLink(links[i]);
360       if (ul.isValid())
361       {
362         System.out.println("\n\n\n");
363         System.out.println("Link " + i + " " + links[i] + " : "
364                 + ul.toString());
365         System.out.println(" pref : "
366                 + ul.getUrl_prefix()
367                 + "\n suf : "
368                 + ul.getUrl_suffix()
369                 + "\n : "
370                 + ((ul.getRegexReplace() != null) ? ul.getRegexReplace()
371                         : ""));
372         for (int ids = 0; ids < idstrings.length; ids++)
373         {
374           System.out.println("ID String : " + idstrings[ids]
375                   + "\nWithout onlyIfMatches:");
376           String[] urls = ul.makeUrls(idstrings[ids], false);
377           testUrls(ul, idstrings[ids], urls);
378           System.out.println("With onlyIfMatches set.");
379           urls = ul.makeUrls(idstrings[ids], true);
380           testUrls(ul, idstrings[ids], urls);
381         }
382       }
383       else
384       {
385         System.err.println("Invalid URLLink : " + links[i] + " : "
386                 + ul.getInvalidMessage());
387       }
388     }
389   }
390
391   public boolean isDynamic()
392   {
393     // TODO Auto-generated method stub
394     return dynamic;
395   }
396
397   public void setLabel(String newlabel)
398   {
399     this.label = newlabel;
400   }
401 }