patch to fix occasional arrayoutofbounds exception when working with hidden columns...
[jalview.git] / src / jalview / util / UrlLink.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)\r
3  * Copyright (C) 2008 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
4  * \r
5  * This program is free software; you can redistribute it and/or\r
6  * modify it under the terms of the GNU General Public License\r
7  * as published by the Free Software Foundation; either version 2\r
8  * of the License, or (at your option) any later version.\r
9  * \r
10  * This program is distributed in the hope that it will be useful,\r
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13  * GNU General Public License for more details.\r
14  * \r
15  * You should have received a copy of the GNU General Public License\r
16  * along with this program; if not, write to the Free Software\r
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
18  */\r
19 package jalview.util;\r
20 \r
21 import java.util.Vector;\r
22 \r
23 public class UrlLink\r
24 {\r
25   /**\r
26    * helper class to parse URL Link strings taken from applet parameters or\r
27    * jalview properties file using the com.stevesoft.pat.Regex implementation.\r
28    * Jalview 2.4 extension allows regular expressions to be used to parse ID\r
29    * strings and replace the result in the URL. Regex's operate on the whole ID\r
30    * string given to the matchURL method, if no regex is supplied, then only\r
31    * text following the first pipe symbol will be susbstituted.\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    * \r
175    * @return true if URL string could not be parsed properly.\r
176    */\r
177   public boolean isValid()\r
178   {\r
179     return invalidMessage == null;\r
180   }\r
181 \r
182   /**\r
183    * return one or more URL strings by applying regex to the given idstring\r
184    * \r
185    * @param idstring\r
186    * @param onlyIfMatches -\r
187    *                when true url strings are only made if regex is defined and\r
188    *                matches\r
189    * @return String[] { part of idstring substituted, full substituted url , ..\r
190    *         next part, next url..}\r
191    */\r
192   public String[] makeUrls(String idstring, boolean onlyIfMatches)\r
193   {\r
194     if (dynamic)\r
195     {\r
196       if (regexReplace != null)\r
197       {\r
198         com.stevesoft.pat.Regex rg = com.stevesoft.pat.Regex.perlCode("/"\r
199                 + regexReplace + "/");\r
200         if (rg.search(idstring))\r
201         {\r
202           int ns = rg.numSubs();\r
203           if (ns == 0)\r
204           {\r
205             // take whole regex\r
206             return new String[]\r
207             { rg.stringMatched(),\r
208                 url_prefix + rg.stringMatched() + url_suffix };\r
209           } /*\r
210              * else if (ns==1) { // take only subgroup match return new String[] {\r
211              * rg.stringMatched(1), url_prefix+rg.stringMatched(1)+url_suffix }; }\r
212              */\r
213           else\r
214           {\r
215             // debug\r
216             for (int s = 0; s <= rg.numSubs(); s++)\r
217             {\r
218               System.err.println("Sub " + s + " : " + rg.matchedFrom(s)\r
219                       + " : " + rg.matchedTo(s) + " : '"\r
220                       + rg.stringMatched(s) + "'");\r
221             }\r
222             // try to collate subgroup matches\r
223             Vector subs = new Vector();\r
224             // have to loop through submatches, collating them at top level\r
225             // match\r
226             int s = 0; // 1;\r
227             while (s <= ns)\r
228             {\r
229               if (s + 1 <= ns && rg.matchedTo(s) > -1\r
230                       && rg.matchedTo(s + 1) > -1\r
231                       && rg.matchedTo(s + 1) < rg.matchedTo(s))\r
232               {\r
233                 // s is top level submatch. search for submatches enclosed by\r
234                 // this one\r
235                 int r = s + 1;\r
236                 String mtch = "";\r
237                 while (r <= ns && rg.matchedTo(r) <= rg.matchedTo(s))\r
238                 {\r
239                   if (rg.matchedFrom(r) > -1)\r
240                   {\r
241                     mtch += rg.stringMatched(r);\r
242                   }\r
243                   r++;\r
244                 }\r
245                 if (mtch.length() > 0)\r
246                 {\r
247                   subs.addElement(mtch);\r
248                   subs.addElement(url_prefix + mtch + url_suffix);\r
249                 }\r
250                 s = r;\r
251               }\r
252               else\r
253               {\r
254                 if (rg.matchedFrom(s) > -1)\r
255                 {\r
256                   subs.addElement(rg.stringMatched(s));\r
257                   subs.addElement(url_prefix + rg.stringMatched(s)\r
258                           + url_suffix);\r
259                 }\r
260                 s++;\r
261               }\r
262             }\r
263 \r
264             String[] res = new String[subs.size()];\r
265             for (int r = 0, rs = subs.size(); r < rs; r++)\r
266             {\r
267               res[r] = (String) subs.elementAt(r);\r
268             }\r
269             subs.removeAllElements();\r
270             return res;\r
271           }\r
272         }\r
273         if (onlyIfMatches)\r
274         {\r
275           return null;\r
276         }\r
277       }\r
278       /* Otherwise - trim off any 'prefix' - pre 2.4 Jalview behaviour */\r
279       if (idstring.indexOf("|") > -1)\r
280       {\r
281         idstring = idstring.substring(idstring.lastIndexOf("|") + 1);\r
282       }\r
283 \r
284       // just return simple url substitution.\r
285       return new String[]\r
286       { idstring, url_prefix + idstring + url_suffix };\r
287     }\r
288     else\r
289     {\r
290       return new String[]\r
291       { "", url_prefix };\r
292     }\r
293   }\r
294 \r
295   public String toString()\r
296   {\r
297     return label\r
298             + "|"\r
299             + url_prefix\r
300             + (dynamic ? ("$SEQUENCE_ID" + ((regexReplace != null) ? "="\r
301                     + regexReplace + "=$" : "$")) : "")\r
302             + ((url_suffix == null) ? "" : url_suffix);\r
303 \r
304   }\r
305 \r
306   private static void testUrls(UrlLink ul, String idstring, String[] urls)\r
307   {\r
308 \r
309     if (urls == null)\r
310     {\r
311       System.out.println("Created NO urls.");\r
312     }\r
313     else\r
314     {\r
315       System.out.println("Created " + (urls.length / 2) + " Urls.");\r
316       for (int uls = 0; uls < urls.length; uls += 2)\r
317       {\r
318         System.out.println("URL Replacement text : " + urls[uls]\r
319                 + " : URL : " + urls[uls + 1]);\r
320       }\r
321     }\r
322   }\r
323 \r
324   public static void main(String argv[])\r
325   {\r
326     String[] links = new String[]\r
327     {\r
328     /*\r
329      * "AlinkT|Target|http://foo.foo.soo/",\r
330      * "myUrl1|http://$SEQUENCE_ID=/[0-9]+/=$.someserver.org/foo",\r
331      * "myUrl2|http://$SEQUENCE_ID=/(([0-9]+).+([A-Za-z]+))/=$.someserver.org/foo",\r
332      * "myUrl3|http://$SEQUENCE_ID=/([0-9]+).+([A-Za-z]+)/=$.someserver.org/foo",\r
333      * "myUrl4|target|http://$SEQUENCE_ID$.someserver.org/foo|too",\r
334      * "PF1|http://us.expasy.org/cgi-bin/niceprot.pl?$SEQUENCE_ID=/(?:PFAM:)?(.+)/=$",\r
335      * "PF2|http://us.expasy.org/cgi-bin/niceprot.pl?$SEQUENCE_ID=/(PFAM:)?(.+)/=$",\r
336      * "PF3|http://us.expasy.org/cgi-bin/niceprot.pl?$SEQUENCE_ID=/PFAM:(.+)/=$",\r
337      * "NOTFER|http://notfer.org/$SEQUENCE_ID=/(?<!\\s)(.+)/=$",\r
338      */\r
339     "NESTED|http://nested/$SEQUENCE_ID=/^(?:Label:)?(?:(?:gi\\|(\\d+))|([^:]+))/=$/nested" };\r
340     String[] idstrings = new String[]\r
341     {\r
342     /*\r
343      * //"LGUL_human", //"QWIQW_123123", "uniprot|why_do+_12313_foo",\r
344      * //"123123312", "123123 ABCDE foo", "PFAM:PF23943",\r
345      */\r
346     "Label:gi|9234|pdb|102L|A" };\r
347     // TODO: test the setLabel method.\r
348     for (int i = 0; i < links.length; i++)\r
349     {\r
350       UrlLink ul = new UrlLink(links[i]);\r
351       if (ul.isValid())\r
352       {\r
353         System.out.println("\n\n\n");\r
354         System.out.println("Link " + i + " " + links[i] + " : "\r
355                 + ul.toString());\r
356         System.out.println(" pref : "\r
357                 + ul.getUrl_prefix()\r
358                 + "\n suf : "\r
359                 + ul.getUrl_suffix()\r
360                 + "\n : "\r
361                 + ((ul.getRegexReplace() != null) ? ul.getRegexReplace()\r
362                         : ""));\r
363         for (int ids = 0; ids < idstrings.length; ids++)\r
364         {\r
365           System.out.println("ID String : " + idstrings[ids]\r
366                   + "\nWithout onlyIfMatches:");\r
367           String[] urls = ul.makeUrls(idstrings[ids], false);\r
368           testUrls(ul, idstrings[ids], urls);\r
369           System.out.println("With onlyIfMatches set.");\r
370           urls = ul.makeUrls(idstrings[ids], true);\r
371           testUrls(ul, idstrings[ids], urls);\r
372         }\r
373       }\r
374       else\r
375       {\r
376         System.err.println("Invalid URLLink : " + links[i] + " : "\r
377                 + ul.getInvalidMessage());\r
378       }\r
379     }\r
380   }\r
381 \r
382   public boolean isDynamic()\r
383   {\r
384     // TODO Auto-generated method stub\r
385     return dynamic;\r
386   }\r
387 \r
388   public void setLabel(String newlabel)\r
389   {\r
390     this.label = newlabel;\r
391   }\r
392 }\r