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