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