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