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