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