urllength limit and more efficient url construction
[jalview.git] / src / jalview / util / GroupUrlLink.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)\r
3  * Copyright (C) 2009 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 jalview.datamodel.Sequence;\r
22 import jalview.datamodel.SequenceI;\r
23 \r
24 import java.util.Hashtable;\r
25 import java.util.Vector;\r
26 \r
27 public class GroupUrlLink\r
28 {\r
29   /**\r
30    * Helper class based on the UrlLink class which enables URLs to be\r
31    * constructed from sequences or IDs associated with a group of sequences. URL\r
32    * definitions consist of a pipe separated string containing a <label>|<url\r
33    * construct>|<separator character>[|<sequence separator character>]. The url\r
34    * construct includes regex qualified tokens which are replaced with seuqence\r
35    * IDs ($SEQUENCE_IDS$) and/or seuqence regions ($SEQUENCES$) that are\r
36    * extracted from the group. See <code>UrlLink</code> for more information\r
37    * about the approach, and the original implementation.\r
38    * Documentation to come. Note - groupUrls can be very big!\r
39    */\r
40   private String url_prefix, target, label;\r
41 \r
42   /**\r
43    * these are all filled in order of the occurence of each token in the url\r
44    * string template\r
45    */\r
46   private String url_suffix[], separators[], regexReplace[];\r
47 \r
48   private String invalidMessage = null;\r
49 \r
50   /**\r
51    * tokens that can be replaced in the URL.\r
52    */\r
53   private static String[] tokens;\r
54 \r
55   /**\r
56    * position of each token (which can appear once only) in the url\r
57    */\r
58   private int[] segs;\r
59 \r
60   /**\r
61    * contains tokens in the order they appear in the URL template.\r
62    */\r
63   private String[] mtch;\r
64   static\r
65   {\r
66     if (tokens == null)\r
67     {\r
68       tokens = new String[]\r
69       { "SEQUENCEIDS", "SEQUENCES", "DATASETID" };\r
70     }\r
71   }\r
72 \r
73   // private int idseg = -1, seqseg = -1;\r
74 \r
75   /**\r
76    * parse the given linkString of the form '<label>|<url>|separator\r
77    * char[|optional sequence separator char]' into parts. url may contain a\r
78    * string $SEQUENCEIDS<=optional regex=>$ where <=optional regex=> must be of\r
79    * the form =/<perl style regex>/=$ or $SEQUENCES<=optional regex=>$ or\r
80    * $SEQUENCES<=optional regex=>$.\r
81    * \r
82    * @param link\r
83    */\r
84   public GroupUrlLink(String link)\r
85   {\r
86     int sep = link.indexOf("|");\r
87     segs = new int[tokens.length];\r
88     int ntoks = 0;\r
89     for (int i = 0; i < segs.length; i++)\r
90     {\r
91       if ((segs[i] = link.indexOf("$" + tokens[i])) > -1)\r
92       {\r
93         ntoks++;\r
94       }\r
95     }\r
96     // expect at least one token\r
97     if (ntoks == 0)\r
98     {\r
99       invalidMessage = "Group URL string must contain at least one of ";\r
100       for (int i = 0; i < segs.length; i++)\r
101       {\r
102         invalidMessage += " '$" + tokens[i] + "[=/regex=/]$'";\r
103       }\r
104       return;\r
105     }\r
106 \r
107     int[] ptok = new int[ntoks + 1];\r
108     String[] tmtch = new String[ntoks + 1];\r
109     mtch = new String[ntoks];\r
110     for (int i = 0, t = 0; i < segs.length; i++)\r
111     {\r
112       if (segs[i] > -1)\r
113       {\r
114         ptok[t] = segs[i];\r
115         tmtch[t++] = tokens[i];\r
116       }\r
117     }\r
118     ptok[ntoks] = link.length();\r
119     tmtch[ntoks] = "$$$$$$$$$";\r
120     jalview.util.QuickSort.sort(ptok, tmtch);\r
121     for (int i = 0; i < ntoks; i++)\r
122     {\r
123       mtch[i] = tmtch[i]; // TODO: check order is ascending\r
124     }\r
125     /*\r
126      * replaces the specific code below {}; if (psqids > -1 && pseqs > -1) { if\r
127      * (psqids > pseqs) { idseg = 1; seqseg = 0;\r
128      * \r
129      * ptok = new int[] { pseqs, psqids, link.length() }; mtch = new String[] {\r
130      * "$SEQUENCES", "$SEQUENCEIDS" }; } else { idseg = 0; seqseg = 1; ptok =\r
131      * new int[] { psqids, pseqs, link.length() }; mtch = new String[] {\r
132      * "$SEQUENCEIDS", "$SEQUENCES" }; } } else { if (psqids != -1) { idseg = 0;\r
133      * ptok = new int[] { psqids, link.length() }; mtch = new String[] {\r
134      * "$SEQUENCEIDS" }; } else { seqseg = 0; ptok = new int[] { pseqs,\r
135      * link.length() }; mtch = new String[] { "$SEQUENCES" }; } }\r
136      */\r
137 \r
138     int p = sep;\r
139     // first get the label and target part before the first |\r
140     do\r
141     {\r
142       sep = p;\r
143       p = link.indexOf("|", sep + 1);\r
144     } while (p > sep && p < ptok[0]);\r
145     // Assuming that the URL itself does not contain any '|' symbols\r
146     // sep now contains last pipe symbol position prior to any regex symbols\r
147     label = link.substring(0, sep);\r
148     if (label.indexOf("|") > -1)\r
149     {\r
150       // | terminated database name / www target at start of Label\r
151       target = label.substring(0, label.indexOf("|"));\r
152     }\r
153     else if (label.indexOf(" ") > 2)\r
154     {\r
155       // space separated Label - matches database name\r
156       target = label.substring(0, label.indexOf(" "));\r
157     }\r
158     else\r
159     {\r
160       target = label;\r
161     }\r
162     // Now Parse URL : Whole URL string first\r
163     url_prefix = link.substring(sep + 1, ptok[0]);\r
164     url_suffix = new String[mtch.length];\r
165     regexReplace = new String[mtch.length];\r
166     // and loop through tokens\r
167     for (int pass = 0; pass < mtch.length; pass++)\r
168     {\r
169       int mlength = 3 + mtch[pass].length();\r
170       if (link.indexOf("$" + mtch[pass] + "=/") == ptok[pass]\r
171               && (p = link.indexOf("/=$", ptok[pass] + mlength)) > ptok[pass]\r
172                       + mlength)\r
173       {\r
174         // Extract Regex and suffix\r
175         if (ptok[pass + 1] < p + 3)\r
176         {\r
177           // tokens are not allowed inside other tokens - e.g. inserting a\r
178           // $sequences$ into the regex match for the sequenceid\r
179           invalidMessage = "Token regexes cannot contain other regexes (did you terminate the $"\r
180                   + mtch[pass] + " regex with a '/=$' ?";\r
181           return;\r
182         }\r
183         url_suffix[pass] = link.substring(p + 3, ptok[pass + 1]);\r
184         regexReplace[pass] = link.substring(ptok[pass] + mlength, p);\r
185         try\r
186         {\r
187           com.stevesoft.pat.Regex rg = com.stevesoft.pat.Regex.perlCode("/"\r
188                   + regexReplace[pass] + "/");\r
189           if (rg == null)\r
190           {\r
191             invalidMessage = "Invalid Regular Expression : '"\r
192                     + regexReplace[pass] + "'\n";\r
193           }\r
194         } catch (Exception e)\r
195         {\r
196           invalidMessage = "Invalid Regular Expression : '"\r
197                   + regexReplace[pass] + "'\n";\r
198         }\r
199       }\r
200       else\r
201       {\r
202         regexReplace[pass] = null;\r
203         // verify format is really correct.\r
204         if ((p = link.indexOf("$" + mtch[pass] + "$")) == ptok[pass])\r
205         {\r
206           url_suffix[pass] = link.substring(p + mtch[pass].length() + 2,\r
207                   ptok[pass + 1]);\r
208         }\r
209         else\r
210         {\r
211           invalidMessage = "Warning: invalid regex structure (after '"\r
212                   + mtch[0] + "') for URL link : " + link;\r
213         }\r
214       }\r
215     }\r
216     int pass = 0;\r
217     separators = new String[url_suffix.length];\r
218     String suffices = url_suffix[url_suffix.length - 1], lastsep = ",";\r
219     // have a look in the last suffix for any more separators.\r
220     while ((p = suffices.indexOf('|')) > -1)\r
221     {\r
222       separators[pass] = suffices.substring(p + 1);\r
223       if (pass == 0)\r
224       {\r
225         // trim the original suffix string\r
226         url_suffix[url_suffix.length - 1] = suffices.substring(0, p);\r
227       }\r
228       else\r
229       {\r
230         lastsep = (separators[pass - 1] = separators[pass - 1].substring(0,\r
231                 p));\r
232       }\r
233       suffices = separators[pass];\r
234       pass++;\r
235     }\r
236     if (pass > 0)\r
237     {\r
238       lastsep = separators[pass - 1];\r
239     }\r
240     // last separator is always used for all the remaining separators\r
241     while (pass < separators.length)\r
242     {\r
243       separators[pass++] = lastsep;\r
244     }\r
245   }\r
246 \r
247   /**\r
248    * @return the url_suffix\r
249    */\r
250   public String getUrl_suffix()\r
251   {\r
252     return url_suffix[url_suffix.length - 1];\r
253   }\r
254 \r
255   /**\r
256    * @return the url_prefix\r
257    */\r
258   public String getUrl_prefix()\r
259   {\r
260     return url_prefix;\r
261   }\r
262 \r
263   /**\r
264    * @return the target\r
265    */\r
266   public String getTarget()\r
267   {\r
268     return target;\r
269   }\r
270 \r
271   /**\r
272    * @return the label\r
273    */\r
274   public String getLabel()\r
275   {\r
276     return label;\r
277   }\r
278 \r
279   /**\r
280    * @return the sequence ID regexReplace\r
281    */\r
282   public String getIDRegexReplace()\r
283   {\r
284     return _replaceFor(tokens[0]);\r
285   }\r
286 \r
287   private String _replaceFor(String token)\r
288   {\r
289     for (int i = 0; i < mtch.length; i++)\r
290       if (segs[i] > -1 && mtch[i].equals(token))\r
291       {\r
292         return regexReplace[i];\r
293       }\r
294     return null;\r
295   }\r
296 \r
297   /**\r
298    * @return the sequence ID regexReplace\r
299    */\r
300   public String getSeqRegexReplace()\r
301   {\r
302     return _replaceFor(tokens[1]);\r
303   }\r
304 \r
305   /**\r
306    * @return the invalidMessage\r
307    */\r
308   public String getInvalidMessage()\r
309   {\r
310     return invalidMessage;\r
311   }\r
312 \r
313   /**\r
314    * Check if URL string was parsed properly.\r
315    * \r
316    * @return boolean - if false then <code>getInvalidMessage</code> returns an\r
317    *         error message\r
318    */\r
319   public boolean isValid()\r
320   {\r
321     return invalidMessage == null;\r
322   }\r
323 \r
324   /**\r
325    * return one or more URL strings by applying regex to the given idstring\r
326    * \r
327    * @param idstrings\r
328    *          array of id strings to pass to service\r
329    * @param seqstrings\r
330    *          array of seq strings to pass to service\r
331    * @param onlyIfMatches\r
332    *          - when true url strings are only made if regex is defined and\r
333    *          matches for all qualified tokens in groupURL - TODO: consider if\r
334    *          onlyIfMatches is really a useful parameter!\r
335    * @return null or Object[] { int[] { number of seqs substituted},boolean[] {\r
336    *         which seqs were substituted }, StringBuffer[] { substituted lists\r
337    *         for each token }, String[] { url } }\r
338    */\r
339   public Object[] makeUrls(String[] idstrings, String[] seqstrings,\r
340           String dsstring, boolean onlyIfMatches)\r
341   {\r
342     Hashtable rstrings = replacementArgs(idstrings, seqstrings, dsstring);\r
343     return makeUrls(rstrings, onlyIfMatches);\r
344   }\r
345 \r
346   /**\r
347    * gathers input into a hashtable\r
348    * @param idstrings\r
349    * @param seqstrings\r
350    * @param dsstring\r
351    * @return\r
352    */\r
353   private Hashtable replacementArgs(String[] idstrings,\r
354           String[] seqstrings, String dsstring)\r
355   {\r
356     Hashtable rstrings = new Hashtable();\r
357     rstrings.put(tokens[0], idstrings);\r
358     rstrings.put(tokens[1], seqstrings);\r
359     rstrings.put(tokens[2], new String[]\r
360     { dsstring });\r
361     if (idstrings.length != seqstrings.length)\r
362     {\r
363       throw new Error(\r
364               "idstrings and seqstrings contain one string each per sequence.");\r
365     }\r
366     return rstrings;\r
367   }\r
368 \r
369   public Object[] makeUrls(Hashtable repstrings, boolean onlyIfMatches)\r
370   {\r
371     return makeUrlsIf(true,repstrings,onlyIfMatches);\r
372   }\r
373   /**\r
374    * \r
375    * @param ids\r
376    * @param seqstr\r
377    * @param string\r
378    * @param b\r
379    * @return URL stub objects ready to pass to constructFrom  \r
380    */\r
381   public Object[] makeUrlStubs(String[] ids, String[] seqstr,\r
382           String string, boolean b)\r
383   {\r
384     Hashtable rstrings = replacementArgs(ids, seqstr, string);\r
385     Object[] stubs = makeUrlsIf(false, rstrings, b);\r
386     if (stubs!=null)\r
387     {\r
388       return new Object[] { stubs[0], stubs[1], rstrings, new boolean[] { b } };\r
389     }\r
390     // TODO Auto-generated method stub\r
391     return null;\r
392   }\r
393 \r
394   /**\r
395    * generate the URL for the given URL stub object array returned from makeUrlStubs\r
396    * @param stubs\r
397    * @return URL string.\r
398    */\r
399   public String constructFrom(Object[] stubs)\r
400   {\r
401     Object[] results = makeUrlsIf(true, (Hashtable) stubs[2], ((boolean[])stubs[3])[0]);    \r
402     return ((String[]) results[3])[0];\r
403   }\r
404   /**\r
405    * conditionally generate urls or stubs for a given input.\r
406    * @param createFullUrl set to false if you only want to test if URLs would be generated.\r
407    * @param repstrings\r
408    * @param onlyIfMatches\r
409    * @return null if no url is generated. Object[] { int[] { number of matches seqs }, boolean[] { which matched }, (if createFullUrl also has StringBuffer[] { segment generated from inputs that is used in URL }, String[] { url })}\r
410    */\r
411   protected Object[] makeUrlsIf(boolean createFullUrl, Hashtable repstrings, boolean onlyIfMatches)\r
412   {\r
413     int pass = 0;\r
414     \r
415     // prepare string arrays in correct order to be assembled into URL input\r
416     String[][] idseq = new String[mtch.length][]; // indexed by pass\r
417     int mins = 0, maxs = 0; // allowed two values, 1 or n-sequences.\r
418     for (int i = 0; i < mtch.length; i++)\r
419     {\r
420       idseq[i] = (String[]) repstrings.get(mtch[i]);\r
421       if (idseq[i].length >= 1)\r
422       {\r
423         if (mins == 0 && idseq[i].length == 1)\r
424         {\r
425           mins = 1;\r
426         }\r
427         if (maxs < 2)\r
428         {\r
429           maxs = idseq[i].length;\r
430         }\r
431         else\r
432         {\r
433           if (maxs != idseq[i].length)\r
434           {\r
435             throw new Error(\r
436                     "Cannot have mixed length replacement vectors. Replacement vector for "\r
437                             + (mtch[i]) + " is " + idseq[i].length\r
438                             + " strings long, and have already seen a "\r
439                             + maxs + " length vector.");\r
440           }\r
441         }\r
442       }\r
443       else\r
444       {\r
445         throw new Error(\r
446                 "Cannot have zero length vector of replacement strings - either 1 value or n values.");\r
447       }\r
448     }\r
449     // iterate through input, collating segments to be inserted into url\r
450     StringBuffer matched[] = new StringBuffer[idseq.length];\r
451     // and precompile regexes\r
452     com.stevesoft.pat.Regex[] rgxs = new com.stevesoft.pat.Regex[matched.length];\r
453     for (pass = 0; pass < matched.length; pass++)\r
454     {\r
455       matched[pass] = new StringBuffer();\r
456       if (regexReplace[pass] != null)\r
457       {\r
458         rgxs[pass] = com.stevesoft.pat.Regex.perlCode("/" + regexReplace[pass]\r
459                 + "/");\r
460       }\r
461       else\r
462       {\r
463         rgxs[pass] = null;\r
464       }\r
465     }\r
466     // tot up the invariant lengths for this url\r
467     int urllength = url_prefix.length();\r
468     for (pass=0;pass<matched.length; pass++)\r
469     {\r
470       urllength+=url_suffix[pass].length();\r
471     }\r
472 \r
473     // flags to record which of the input sequences were actually used to generate the\r
474     // url\r
475     boolean[] thismatched = new boolean[maxs];\r
476     int seqsmatched = 0;\r
477     for (int sq = 0; sq < maxs; sq++)\r
478     {\r
479       // initialise flag for match\r
480       thismatched[sq] = false;\r
481       StringBuffer[] thematches = new StringBuffer[rgxs.length];\r
482       for (pass = 0; pass < rgxs.length; pass++)\r
483       {\r
484         thematches[pass] = new StringBuffer(); // initialise - in case there are no more\r
485         // matches.\r
486         // if a regex is provided, then it must match for all sequences in all\r
487         // tokens for it to be considered.\r
488         if (idseq[pass].length <= sq)\r
489         {\r
490           // no more replacement strings to try for this token\r
491           continue;\r
492         }\r
493         if (rgxs[pass] != null)\r
494         {\r
495           com.stevesoft.pat.Regex rg = rgxs[pass];\r
496           int rematchat = 0;\r
497           // concatenate all matches of re in the given string!\r
498           while (rg.searchFrom(idseq[pass][sq], rematchat))\r
499           {\r
500             rematchat = rg.matchedTo();\r
501             thismatched[sq] |= true;\r
502             urllength+=rg.charsMatched(); // count length\r
503             if (!createFullUrl)\r
504             {\r
505               continue; // don't bother making the URL replacement text.\r
506             }\r
507             // do we take the cartesian products of the substituents ?\r
508             int ns = rg.numSubs();\r
509             if (ns == 0)\r
510             {\r
511               thematches[pass].append(rg.stringMatched());// take whole regex\r
512             }\r
513             /*\r
514              * else if (ns==1) { // take only subgroup match return new String[]\r
515              * { rg.stringMatched(1), url_prefix+rg.stringMatched(1)+url_suffix\r
516              * }; }\r
517              */\r
518             // deal with multiple submatch case - for moment we do the simplest\r
519             // - concatenate the matched regions, instead of creating a complete\r
520             // list for each alternate match over all sequences.\r
521             // TODO: specify a 'replace pattern' - next refinement\r
522             else\r
523             {\r
524               // debug\r
525               /*for (int s = 0; s <= rg.numSubs(); s++)\r
526               {\r
527                 System.err.println("Sub " + s + " : " + rg.matchedFrom(s)\r
528                         + " : " + rg.matchedTo(s) + " : '"\r
529                         + rg.stringMatched(s) + "'");\r
530               }*/\r
531               // try to collate subgroup matches\r
532               StringBuffer subs = new StringBuffer();\r
533               // have to loop through submatches, collating them at top level\r
534               // match\r
535               int s = 0; // 1;\r
536               while (s <= ns)\r
537               {\r
538                 if (s + 1 <= ns && rg.matchedTo(s) > -1\r
539                         && rg.matchedTo(s + 1) > -1\r
540                         && rg.matchedTo(s + 1) < rg.matchedTo(s))\r
541                 {\r
542                   // s is top level submatch. search for submatches enclosed by\r
543                   // this one\r
544                   int r = s + 1;\r
545                   StringBuffer rmtch = new StringBuffer();\r
546                   while (r <= ns && rg.matchedTo(r) <= rg.matchedTo(s))\r
547                   {\r
548                     if (rg.matchedFrom(r) > -1)\r
549                     {\r
550                       rmtch.append(rg.stringMatched(r));\r
551                     }\r
552                     r++;\r
553                   }\r
554                   if (rmtch.length() > 0)\r
555                   {\r
556                     subs.append(rmtch); // simply concatenate\r
557                   }\r
558                   s = r;\r
559                 }\r
560                 else\r
561                 {\r
562                   if (rg.matchedFrom(s) > -1)\r
563                   {\r
564                     subs.append(rg.stringMatched(s)); // concatenate\r
565                   }\r
566                   s++;\r
567                 }\r
568               }\r
569               thematches[pass].append(subs);\r
570             }\r
571           }\r
572         }\r
573         else\r
574         {\r
575           // are we only supposed to take regex matches ?\r
576           if (!onlyIfMatches)\r
577           {\r
578             thismatched[sq] |= true;\r
579             urllength+=idseq[pass][sq].length(); // tot up length\r
580             if (createFullUrl)\r
581             {\r
582             thematches[pass] = new StringBuffer(idseq[pass][sq]); // take whole string -\r
583             // regardless - probably not a\r
584             // good idea!\r
585             /*\r
586              * TODO: do some boilerplate trimming of the fields to make them\r
587              * sensible e.g. trim off any 'prefix' in the id string (see UrlLink\r
588              * for the below) - pre 2.4 Jalview behaviour if\r
589              * (idstring.indexOf("|") > -1) { idstring =\r
590              * idstring.substring(idstring.lastIndexOf("|") + 1); }\r
591              */\r
592             }\r
593             \r
594           }\r
595         }\r
596       }\r
597 \r
598       // check if we are going to add this sequence's results ? all token\r
599       // replacements must be valid for this to happen!\r
600       // (including single value replacements - eg. dataset name)\r
601       if (thismatched[sq])\r
602       {\r
603         if (createFullUrl) {\r
604           for (pass = 0; pass < matched.length; pass++)\r
605           {\r
606           if (idseq[pass].length > 1 && matched[pass].length() > 0)\r
607           {\r
608             matched[pass].append(separators[pass]);\r
609           }\r
610             matched[pass].append(thematches[pass]);\r
611           }\r
612         }\r
613         seqsmatched++;\r
614       }\r
615     }\r
616     // finally, if any sequences matched, then form the URL and return\r
617     if (seqsmatched==0 || (createFullUrl && matched[0].length() == 0))\r
618     {\r
619       // no matches - no url generated\r
620       return null;\r
621     }\r
622     // check if we are beyond the feasible command line string limit for this platform\r
623     if ((urllength+32)>Platform.getMaxCommandLineLength())\r
624     {\r
625       System.err.println("URL estimated to be too long "+urllength);\r
626       return null;\r
627     }\r
628     if (!createFullUrl)\r
629     {\r
630       // just return the essential info about what the URL would be generated from\r
631       return new Object[]\r
632                         { new int[]\r
633                         { seqsmatched }, thismatched};\r
634     }\r
635     // otherwise, create the URL completely.\r
636     \r
637     StringBuffer submiturl = new StringBuffer();\r
638     submiturl.append(url_prefix);\r
639     for (pass = 0; pass < matched.length; pass++)\r
640     {\r
641       submiturl.append(matched[pass]);\r
642       if (url_suffix[pass] != null)\r
643       {\r
644         submiturl.append(url_suffix[pass]);\r
645       }\r
646     }\r
647 \r
648     return new Object[]\r
649     { new int[]\r
650     { seqsmatched }, thismatched, matched, new String[]\r
651     { submiturl.toString() } };\r
652   }\r
653 \r
654   /**\r
655    * \r
656    * @param urlstub\r
657    * @return number of distinct sequence (id or seuqence) replacements predicted for this stub\r
658    */\r
659   public int getNumberInvolved(Object[] urlstub)\r
660   {\r
661     return ((int[])urlstub[0])[0]; // returns seqsmatched from makeUrlsIf(false,...)\r
662   }\r
663 \r
664   /**\r
665    * get token types present in this url as a bitfield indicating presence of each token from tokens (LSB->MSB).\r
666    * @return groupURL class as integer\r
667    */\r
668   public int getGroupURLType()\r
669   {\r
670     int r = 0;\r
671     for (int pass = 0; pass < tokens.length; pass++)\r
672     {\r
673       for (int i = 0; i < mtch.length; i++)\r
674       {\r
675         if (mtch[i].equals(tokens[pass]))\r
676         {\r
677           r += 1 << pass;\r
678         }\r
679       }\r
680     }\r
681     return r;\r
682   }\r
683 \r
684   public String toString()\r
685   {\r
686     StringBuffer result = new StringBuffer();\r
687     result.append(label + "|" + url_prefix);\r
688     int r;\r
689     for (r = 0; r < url_suffix.length; r++)\r
690     {\r
691       result.append("$");\r
692       result.append(mtch[r]);\r
693       if (regexReplace[r] != null)\r
694       {\r
695         result.append("=/");\r
696         result.append(regexReplace[r]);\r
697         result.append("/=");\r
698       }\r
699       result.append("$");\r
700       result.append(url_suffix[r]);\r
701     }\r
702     for (r = 0; r < separators.length; r++)\r
703     {\r
704       result.append("|");\r
705       result.append(separators[r]);\r
706     }\r
707     return result.toString();\r
708   }\r
709 \r
710   /**\r
711    * report stats about the generated url string given an input set\r
712    * \r
713    * @param ul\r
714    * @param idstring\r
715    * @param url\r
716    */\r
717   private static void testUrls(GroupUrlLink ul, String[][] idstring,\r
718           Object[] url)\r
719   {\r
720 \r
721     if (url == null)\r
722     {\r
723       System.out.println("Created NO urls.");\r
724     }\r
725     else\r
726     {\r
727       System.out.println("Created a url from " + ((int[]) url[0])[0]\r
728               + "out of " + idstring[0].length + " sequences.");\r
729       System.out.println("Sequences that did not match:");\r
730       for (int sq = 0; sq < idstring[0].length; sq++)\r
731       {\r
732         if (!((boolean[]) url[1])[sq])\r
733         {\r
734           System.out.println("Seq " + sq + ": " + idstring[0][sq] + "\t: "\r
735                   + idstring[1][sq]);\r
736         }\r
737       }\r
738       System.out.println("Sequences that DID match:");\r
739       for (int sq = 0; sq < idstring[0].length; sq++)\r
740       {\r
741         if (((boolean[]) url[1])[sq])\r
742         {\r
743           System.out.println("Seq " + sq + ": " + idstring[0][sq] + "\t: "\r
744                   + idstring[1][sq]);\r
745         }\r
746       }\r
747       System.out.println("The generated URL:");\r
748       System.out.println(((String[]) url[3])[0]);\r
749     }\r
750   }\r
751 \r
752   public static void main(String argv[])\r
753   {\r
754     String[] links = new String[]\r
755     {\r
756         "EnVision2|IDS|http://www.ebi.ac.uk/enfin-srv/envision2/pages/linkin.jsf?workflow=Enfin%20Default%20Workflow&datasetName=linkInDatasetFromJalview&input=$SEQUENCEIDS$&inputType=0|,",\r
757         "EnVision2|Seqs|http://www.ebi.ac.uk/enfin-srv/envision2/pages/linkin.jsf?workflow=Enfin%20Default%20Workflow&datasetName=linkInDatasetFromJalview&input=$SEQUENCES$&inputType=1|,",\r
758         "EnVision2|IDS|http://www.ebi.ac.uk/enfin-srv/envision2/pages/linkin.jsf?workflow=Enfin%20Default%20Workflow&datasetName=$DATASETID$&input=$SEQUENCEIDS$&inputType=0|,",\r
759         "EnVision2|Seqs|http://www.ebi.ac.uk/enfin-srv/envision2/pages/linkin.jsf?workflow=Enfin%20Default%20Workflow&datasetName=$DATASETID$&input=$SEQUENCES$&inputType=1|,",\r
760         "EnVision2|IDS|http://www.ebi.ac.uk/enfin-srv/envision2/pages/linkin.jsf?workflow=$SEQUENCEIDS$&datasetName=linkInDatasetFromJalview&input=$SEQUENCEIDS$&inputType=0|,",\r
761         "EnVision2|Seqs|http://www.ebi.ac.uk/enfin-srv/envision2/pages/linkin.jsf?workflow=$SEQUENCEIDS$&datasetName=$DATASETID$&input=$SEQUENCES$&inputType=1|,",\r
762         "EnVision2 Seqs|http://www.ebi.ac.uk/enfin-srv/envision2/pages/linkin.jsf?workflow=Default&datasetName=JalviewSeqs$DATASETID$&input=$SEQUENCES=/([a-zA-Z]+)/=$&inputType=1|,",\r
763         "EnVision2 Seqs|http://www.ebi.ac.uk/enfin-srv/envision2/pages/linkin.jsf?workflow=Default&datasetName=JalviewSeqs$DATASETID$&input=$SEQUENCES=/[A-Za-z]+/=$&inputType=1|,"\r
764     /*\r
765      * http://www.ebi.ac.uk/enfin-srv/envision2/pages/linkin.jsf?input=P38389,P38398\r
766      * &inputType=0&workflow=Enfin%20Default%20Workflow&datasetName=\r
767      * linkInDatasetFromPRIDE\r
768      */\r
769     };\r
770 \r
771     SequenceI[] seqs = new SequenceI[]\r
772     { new Sequence("StupidLabel:gi|9234|pdb|102L|A",\r
773             "asdiasdpasdpadpwpadasdpaspdw"), };\r
774     String[][] seqsandids = formStrings(seqs);\r
775     for (int i = 0; i < links.length; i++)\r
776     {\r
777       GroupUrlLink ul = new GroupUrlLink(links[i]);\r
778       if (ul.isValid())\r
779       {\r
780         System.out.println("\n\n\n");\r
781         System.out.println("Link " + i + " " + links[i] + " : "\r
782                 + ul.toString());\r
783         System.out.println(" pref : " + ul.getUrl_prefix());\r
784         System.out.println(" IdReplace : " + ul.getIDRegexReplace());\r
785         System.out.println(" SeqReplace : " + ul.getSeqRegexReplace());\r
786         System.out.println(" Suffixes : " + ul.getUrl_suffix());\r
787 \r
788         System.out\r
789                 .println("<insert input id and sequence strings here> Without onlyIfMatches:");\r
790         Object[] urls = ul.makeUrls(seqsandids[0], seqsandids[1],\r
791                 "mydataset", false);\r
792         testUrls(ul, seqsandids, urls);\r
793         System.out\r
794                 .println("<insert input id and sequence strings here> With onlyIfMatches set:");\r
795         urls = ul.makeUrls(seqsandids[0], seqsandids[1], "mydataset", true);\r
796         testUrls(ul, seqsandids, urls);\r
797       }\r
798       else\r
799       {\r
800         System.err.println("Invalid URLLink : " + links[i] + " : "\r
801                 + ul.getInvalidMessage());\r
802       }\r
803     }\r
804   }\r
805 \r
806   /**\r
807    * covenience method to generate the id and sequence string vector from a set\r
808    * of seuqences using each sequence's getName() and getSequenceAsString()\r
809    * method\r
810    * \r
811    * @param seqs\r
812    * @return String[][] {{sequence ids},{sequence strings}}\r
813    */\r
814   public static String[][] formStrings(SequenceI[] seqs)\r
815   {\r
816     String[][] idset = new String[2][seqs.length];\r
817     for (int i = 0; i < seqs.length; i++)\r
818     {\r
819       idset[0][i] = seqs[i].getName();\r
820       idset[1][i] = seqs[i].getSequenceAsString();\r
821     }\r
822     return idset;\r
823   }\r
824 \r
825   public void setLabel(String newlabel)\r
826   {\r
827     this.label = newlabel;\r
828   }\r
829 \r
830 \r
831 }\r