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