Clover icon

Coverage Report

  1. Project Clover database Thu Nov 28 2024 18:06:40 GMT
  2. Package jalview.ws.rest

File RestJobThread.java

 

Coverage histogram

../../../img/srcFileCovDistChart2.png
57% of files have more coverage

Code metrics

186
399
20
3
1,306
903
143
0.36
19.95
6.67
7.15

Classes

Class Line # Actions
RestJobThread 68 399 143
0.1537190115.4%
RestJobThread.Stage 70 0 0
-1.0 -
RestJobThread.AddDataTo 552 0 0
-1.0 -
 

Contributing tests

This file is covered by 1 test. .

Source view

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.ws.rest;
22   
23    import java.util.Locale;
24   
25    import jalview.bin.Console;
26    import jalview.datamodel.Alignment;
27    import jalview.datamodel.AlignmentAnnotation;
28    import jalview.datamodel.AlignmentI;
29    import jalview.datamodel.AlignmentOrder;
30    import jalview.datamodel.Annotation;
31    import jalview.datamodel.HiddenColumns;
32    import jalview.datamodel.SequenceGroup;
33    import jalview.datamodel.SequenceI;
34    import jalview.gui.AlignFrame;
35    import jalview.gui.Desktop;
36    import jalview.gui.PaintRefresher;
37    import jalview.gui.WebserviceInfo;
38    import jalview.io.NewickFile;
39    import jalview.io.packed.JalviewDataset;
40    import jalview.io.packed.JalviewDataset.AlignmentSet;
41    import jalview.util.MessageManager;
42    import jalview.ws.AWSThread;
43    import jalview.ws.AWsJob;
44   
45    import java.awt.event.ActionEvent;
46    import java.awt.event.ActionListener;
47    import java.io.IOException;
48    import java.util.ArrayList;
49    import java.util.Hashtable;
50    import java.util.List;
51    import java.util.Map.Entry;
52   
53    import org.apache.axis.transport.http.HTTPConstants;
54    import org.apache.http.Header;
55    import org.apache.http.HttpEntity;
56    import org.apache.http.HttpResponse;
57    import org.apache.http.client.ClientProtocolException;
58    import org.apache.http.client.methods.HttpGet;
59    import org.apache.http.client.methods.HttpPost;
60    import org.apache.http.client.methods.HttpRequestBase;
61    import org.apache.http.entity.mime.HttpMultipartMode;
62    import org.apache.http.entity.mime.MultipartEntity;
63    import org.apache.http.impl.client.DefaultHttpClient;
64    import org.apache.http.protocol.BasicHttpContext;
65    import org.apache.http.protocol.HttpContext;
66    import org.apache.http.util.EntityUtils;
67   
 
68    public class RestJobThread extends AWSThread
69    {
 
70    enum Stage
71    {
72    SUBMIT, POLL
73    }
74   
75    protected RestClient restClient;
76   
 
77  2 toggle public RestJobThread(RestClient restClient)
78    {
79  2 super(restClient.af, null, restClient._input,
80    restClient.service.postUrl);
81  2 this.restClient = restClient; // may not be needed
82    // Test Code
83    // minimal job - submit given input and parse result onto alignment as
84    // annotation/whatever
85   
86    // look for tree data, etc.
87   
88    // for moment do following job type only
89    // input=visiblealignment,groupsindex
90    // ie one subjob using groups defined on alignment.
91  2 if (!restClient.service.isHseparable())
92    {
93  0 jobs = new RestJob[1];
94  0 jobs[0] = new RestJob(0, this,
95    restClient._input.getVisibleAlignment(
96    restClient.service.getGapCharacter()),
97    restClient._input.getVisibleContigs());
98    // need a function to get a range on a view/alignment and return both
99    // annotation, groups and selection subsetted to just that region.
100   
101    }
102    else
103    {
104  2 int[] viscontig = restClient._input.getVisibleContigs();
105  2 AlignmentI[] viscontigals = restClient._input
106    .getVisibleContigAlignments(
107    restClient.service.getGapCharacter());
108  2 if (viscontigals != null && viscontigals.length > 0)
109    {
110  2 jobs = new RestJob[viscontigals.length];
111  4 for (int j = 0; j < jobs.length; j++)
112    {
113  2 int[] visc = new int[] { viscontig[j * 2], viscontig[j * 2 + 1] };
114  2 if (j != 0)
115    {
116  0 jobs[j] = new RestJob(j, this, viscontigals[j], visc);
117    }
118    else
119    {
120  2 jobs[j] = new RestJob(0, this, viscontigals[j], visc);
121    }
122    }
123    }
124    }
125    // end Test Code
126    /**
127    * subjob types row based: per sequence in alignment/selected region { input
128    * is one sequence or sequence ID } per alignment/selected region { input is
129    * set of sequences, alignment, one or more sets of sequence IDs,
130    */
131   
132  2 if (!restClient.service.isHseparable())
133    {
134   
135    // create a bunch of subjobs per visible contig to ensure result honours
136    // hidden boundaries
137    // TODO: determine if a 'soft' hSeperable property is needed - e.g. if
138    // user does SS-pred on sequence with big hidden regions, its going to be
139    // less reliable.
140    }
141    else
142    {
143    // create a single subjob for the visible/selected input
144   
145    }
146    // TODO: decide if vSeperable exists: eg. column wide analysis where hidden
147    // rows do not affect output - generally no analysis that generates
148    // alignment annotation is vSeparable -
149    }
150   
151    /**
152    * create gui components for monitoring jobs
153    *
154    * @param webserviceInfo
155    */
 
156  0 toggle public void setWebServiceInfo(WebserviceInfo webserviceInfo)
157    {
158  0 wsInfo = webserviceInfo;
159  0 for (int j = 0; j < jobs.length; j++)
160    {
161  0 wsInfo.addJobPane();
162    // Copy over any per job params
163  0 if (jobs.length > 1)
164    {
165  0 wsInfo.setProgressName("region " + jobs[j].getJobnum(),
166    jobs[j].getJobnum());
167    }
168    else
169    {
170  0 wsInfo.setProgressText(jobs[j].getJobnum(), OutputHeader);
171    }
172    }
173    }
174   
 
175  0 toggle private String getStage(Stage stg)
176    {
177  0 if (stg == Stage.SUBMIT)
178    {
179  0 return "submitting ";
180    }
181  0 if (stg == Stage.POLL)
182    {
183  0 return "checking status of ";
184    }
185   
186  0 return (" being confused about ");
187    }
188   
 
189  0 toggle private void doPoll(RestJob rj) throws Exception
190    {
191  0 String postUrl = rj.getPollUrl();
192  0 doHttpReq(Stage.POLL, rj, postUrl);
193    }
194   
195    /**
196    * construct the post and handle the response.
197    *
198    * @throws Exception
199    */
 
200  1 toggle public void doPost(RestJob rj) throws Exception
201    {
202  1 String postUrl = rj.getPostUrl();
203  1 doHttpReq(Stage.SUBMIT, rj, postUrl);
204  1 wsInfo.invalidate();
205    }
206   
207    /**
208    * do the HTTP request - and respond/set statuses appropriate to the current
209    * stage.
210    *
211    * @param stg
212    * @param rj
213    * - provides any data needed for posting and used to record state
214    * @param postUrl
215    * - actual URL to post/get from
216    * @throws Exception
217    */
 
218  1 toggle protected void doHttpReq(Stage stg, RestJob rj, String postUrl)
219    throws Exception
220    {
221  1 StringBuffer respText = new StringBuffer();
222    // con.setContentHandlerFactory(new
223    // jalview.ws.io.mime.HttpContentHandler());
224  1 HttpRequestBase request = null;
225  1 String messages = "";
226  1 if (stg == Stage.SUBMIT)
227    {
228    // Got this from
229    // http://evgenyg.wordpress.com/2010/05/01/uploading-files-multipart-post-apache/
230   
231  1 HttpPost htpost = new HttpPost(postUrl);
232  1 MultipartEntity postentity = new MultipartEntity(
233    HttpMultipartMode.STRICT);
234  1 for (Entry<String, InputType> input : rj.getInputParams())
235    {
236  2 if (input.getValue().validFor(rj))
237    {
238  2 postentity.addPart(input.getKey(),
239    input.getValue().formatForInput(rj));
240    }
241    else
242    {
243  0 messages += "Skipped an input (" + input.getKey()
244    + ") - Couldn't generate it from available data.";
245    }
246    }
247  1 htpost.setEntity(postentity);
248  1 request = htpost;
249    }
250    else
251    {
252  0 request = new HttpGet(postUrl);
253    }
254  1 if (request != null)
255    {
256  1 DefaultHttpClient httpclient = new DefaultHttpClient();
257   
258  1 HttpContext localContext = new BasicHttpContext();
259  1 HttpResponse response = null;
260  1 try
261    {
262  1 response = httpclient.execute(request);
263    } catch (ClientProtocolException he)
264    {
265  0 rj.statMessage = "Web Protocol Exception when " + getStage(stg)
266    + "Job. <br>Problematic url was <a href=\""
267    + request.getURI() + "\">" + request.getURI()
268    + "</a><br>See Console output for details.";
269  0 rj.setAllowedServerExceptions(0);// unrecoverable;
270  0 rj.error = true;
271  0 Console.fatal("Unexpected REST Job " + getStage(stg)
272    + "exception for URL " + rj.rsd.postUrl);
273  0 throw (he);
274    } catch (IOException e)
275    {
276  0 rj.statMessage = "IO Exception when " + getStage(stg)
277    + "Job. <br>Problematic url was <a href=\""
278    + request.getURI() + "\">" + request.getURI()
279    + "</a><br>See Console output for details.";
280  0 Console.warn("IO Exception for REST Job " + getStage(stg)
281    + "exception for URL " + rj.rsd.postUrl);
282   
283  0 throw (e);
284    }
285  1 switch (response.getStatusLine().getStatusCode())
286    {
287  0 case 200:
288  0 rj.running = false;
289  0 Console.debug("Processing result set.");
290  0 processResultSet(rj, response, request);
291  0 break;
292  0 case 202:
293  0 rj.statMessage = "<br>Job submitted successfully. Results available at this URL:\n"
294    + "<a href=" + rj.getJobId() + "\">" + rj.getJobId()
295    + "</a><br>";
296  0 rj.running = true;
297  0 break;
298  1 case 302:
299  1 Header[] loc;
300  ? if (!rj.isSubmitted()
301    && (loc = response
302    .getHeaders(HTTPConstants.HEADER_LOCATION)) != null
303    && loc.length > 0)
304    {
305  1 if (loc.length > 1)
306    {
307  0 Console.warn("Ignoring additional " + (loc.length - 1)
308    + " location(s) provided in response header ( next one is '"
309    + loc[1].getValue() + "' )");
310    }
311  1 rj.setJobId(loc[0].getValue());
312  1 rj.setSubmitted(true);
313    }
314  1 completeStatus(rj, response);
315  1 break;
316  0 case 500:
317    // Failed.
318  0 rj.setSubmitted(true);
319  0 rj.setAllowedServerExceptions(0);
320  0 rj.setSubjobComplete(true);
321  0 rj.error = true;
322  0 rj.running = false;
323  0 completeStatus(rj, response,
324    "" + getStage(stg) + "failed. Reason below:\n");
325  0 break;
326  0 default:
327    // Some other response. Probably need to pop up the content in a window.
328    // TODO: deal with all other HTTP response codes from server.
329  0 Console.warn("Unhandled response status when " + getStage(stg)
330    + "for " + postUrl + ": " + response.getStatusLine());
331  0 rj.error = true;
332  0 rj.setAllowedServerExceptions(0);
333  0 rj.setSubjobComplete(true);
334  0 rj.setSubmitted(true);
335  0 try
336    {
337  0 completeStatus(rj, response, "" + getStage(stg)
338    + " resulted in an unexpected server response.<br/>Url concerned was <a href=\""
339    + request.getURI() + "\">" + request.getURI()
340    + "</a><br/>Filtered response content below:<br/>");
341    } catch (IOException e)
342    {
343  0 Console.debug("IOException when consuming unhandled response", e);
344    }
345  0 ;
346    }
347    }
348    }
349   
350    /**
351    * job has completed. Something valid should be available from con
352    *
353    * @param rj
354    * @param con
355    * @param req
356    * is a stateless request - expected to return the same data
357    * regardless of how many times its called.
358    */
 
359  0 toggle private void processResultSet(RestJob rj, HttpResponse con,
360    HttpRequestBase req)
361    {
362  0 if (rj.statMessage == null)
363    {
364  0 rj.statMessage = "";
365    }
366  0 rj.statMessage += "Job Complete.\n";
367  0 try
368    {
369  0 rj.resSet = new HttpResultSet(rj, con, req);
370  0 rj.gotresult = true;
371    } catch (IOException e)
372    {
373  0 rj.statMessage += "Couldn't parse results. Failed.";
374  0 rj.error = true;
375  0 rj.gotresult = false;
376    }
377    }
378   
 
379  1 toggle private void completeStatus(RestJob rj, HttpResponse con)
380    throws IOException
381    {
382  1 completeStatus(rj, con, null);
383   
384    }
385   
 
386  1 toggle private void completeStatus(RestJob rj, HttpResponse con, String prefix)
387    throws IOException
388    {
389  1 StringBuffer sb = new StringBuffer();
390  1 if (prefix != null)
391    {
392  0 sb.append(prefix);
393    }
394  1 ;
395  1 if (rj.statMessage != null && rj.statMessage.length() > 0)
396    {
397  0 sb.append(rj.statMessage);
398    }
399  1 HttpEntity en = con.getEntity();
400    /*
401    * Just append the content as a string.
402    */
403  1 String f;
404  1 StringBuffer content = new StringBuffer(f = EntityUtils.toString(en));
405  1 f = f.toLowerCase(Locale.ROOT);
406  1 int body = f.indexOf("<body");
407  1 if (body > -1)
408    {
409  0 content.delete(0, f.indexOf(">", body) + 1);
410    }
411  1 if (body > -1 && sb.length() > 0)
412    {
413  0 sb.append("\n");
414  0 content.insert(0, sb);
415  0 sb = null;
416    }
417  1 f = null;
418  1 rj.statMessage = content.toString();
419    }
420   
 
421  0 toggle @Override
422    public void pollJob(AWsJob job) throws Exception
423    {
424  0 assert (job instanceof RestJob);
425  0 jalview.bin.Console.errPrintln("Debug RestJob: Polling Job");
426  0 doPoll((RestJob) job);
427    }
428   
 
429  1 toggle @Override
430    public void StartJob(AWsJob job)
431    {
432  1 assert (job instanceof RestJob);
433  1 try
434    {
435  1 jalview.bin.Console.errPrintln("Debug RestJob: Posting Job");
436  1 doPost((RestJob) job);
437    } catch (NoValidInputDataException erex)
438    {
439  0 job.setSubjobComplete(true);
440  0 job.setSubmitted(true);
441  0 ((RestJob) job).statMessage = "<br>It looks like there was a problem with the data sent to the service :<br>"
442    + erex.getMessage() + "\n";
443  0 ((RestJob) job).error = true;
444   
445    } catch (Exception ex)
446    {
447  1 job.setSubjobComplete(true);
448  1 job.setAllowedServerExceptions(-1);
449  1 Console.error("Exception when trying to start Rest Job.", ex);
450    }
451    }
452   
 
453  0 toggle @Override
454    public void parseResult()
455    {
456    // crazy users will see this message
457    // TODO: finish this! and remove the message below!
458  0 Console.warn("Rest job result parser is currently INCOMPLETE!");
459  0 int validres = 0;
460  0 for (RestJob rj : (RestJob[]) jobs)
461    {
462  0 if (rj.hasResponse() && rj.resSet != null && rj.resSet.isValid())
463    {
464  0 String ln = null;
465  0 try
466    {
467  0 Console.debug("Parsing data for job " + rj.getJobId());
468  0 rj.parseResultSet();
469  0 if (rj.hasResults())
470    {
471  0 validres++;
472    }
473  0 Console.debug("Finished parsing data for job " + rj.getJobId());
474   
475    } catch (Error ex)
476    {
477  0 Console.warn(
478    "Failed to finish parsing data for job " + rj.getJobId());
479  0 ex.printStackTrace();
480    } catch (Exception ex)
481    {
482  0 Console.warn(
483    "Failed to finish parsing data for job " + rj.getJobId());
484  0 ex.printStackTrace();
485    } finally
486    {
487  0 rj.error = true;
488  0 rj.statMessage = "Error whilst parsing data for this job.<br>URL for job response is :<a href=\""
489    + rj.resSet.getUrl() + "\">" + rj.resSet.getUrl()
490    + "</a><br>";
491    }
492    }
493    }
494  0 if (validres > 0)
495    {
496    // add listeners and activate result display gui elements
497    /**
498    * decisions based on job result content + state of alignFrame that
499    * originated the job:
500    */
501    /*
502    * 1. Can/Should this job automatically open a new window for results
503    */
504  0 if (true)
505    {
506    // preserver current jalview behaviour
507  0 wsInfo.setViewResultsImmediatly(true);
508    }
509    else
510    {
511    // realiseResults(true, true);
512    }
513    // otherwise, should automatically view results
514   
515    // TODO: check if at least one or more contexts are valid - if so, enable
516    // gui
517  0 wsInfo.showResultsNewFrame.addActionListener(new ActionListener()
518    {
519   
 
520  0 toggle @Override
521    public void actionPerformed(ActionEvent e)
522    {
523  0 realiseResults(false);
524    }
525   
526    });
527  0 wsInfo.mergeResults.addActionListener(new ActionListener()
528    {
529   
 
530  0 toggle @Override
531    public void actionPerformed(ActionEvent e)
532    {
533  0 realiseResults(true);
534    }
535   
536    });
537   
538  0 wsInfo.setResultsReady();
539    }
540    else
541    {
542    // tell the user nothing was returned.
543  0 wsInfo.setStatus(wsInfo.STATE_STOPPED_ERROR);
544  0 wsInfo.setFinishedNoResults();
545    }
546    }
547   
548    /**
549    * instructions for whether to create new alignment view on current alignment
550    * set, add to current set, or create new alignFrame
551    */
 
552    private enum AddDataTo
553    {
554    /**
555    * add annotation, trees etc to current view
556    */
557    currentView,
558    /**
559    * create a new view derived from current view and add data to that
560    */
561    newView,
562    /**
563    * create a new alignment frame for the result set and add annotation to
564    * that.
565    */
566    newAlignment
567    };
568   
 
569  0 toggle public void realiseResults(boolean merge)
570    {
571    /*
572    * 2. Should the job modify the parent alignment frame/view(s) (if they
573    * still exist and the alignment hasn't been edited) in order to display new
574    * annotation/features.
575    */
576    /**
577    * alignment panels derived from each alignment set returned by service.
578    */
579  0 ArrayList<jalview.gui.AlignmentPanel> destPanels = new ArrayList<jalview.gui.AlignmentPanel>();
580    /**
581    * list of instructions for how to process each distinct alignment set
582    * returned by the job set
583    */
584  0 ArrayList<AddDataTo> resultDest = new ArrayList<AddDataTo>();
585    /**
586    * when false, zeroth pane is panel derived from input deta.
587    */
588  0 boolean newAlignment = false;
589    /**
590    * gap character to be used for alignment reconstruction
591    */
592  0 char gapCharacter = restClient.av.getGapCharacter();
593    // Now, iterate over all alignment sets returned from all jobs:
594    // first inspect jobs and collate results data in order to count alignments
595    // and other results
596    // then assemble results from decomposed (v followed by h-separated) jobs
597    // finally, new views and alignments will be created and displayed as
598    // necessary.
599  0 boolean hsepjobs = restClient.service.isHseparable();
600  0 boolean vsepjobs = restClient.service.isVseparable();
601    // total number of distinct alignment sets generated by job set.
602  0 int numAlSets = 0, als = 0;
603  0 List<AlignmentI> destAls = new ArrayList<AlignmentI>();
604  0 List<jalview.datamodel.HiddenColumns> destColsel = new ArrayList<jalview.datamodel.HiddenColumns>();
605  0 List<List<NewickFile>> trees = new ArrayList<List<NewickFile>>();
606   
607  0 do
608    {
609    // Step 1.
610    // iterate over each alignment set returned from each subjob. Treating
611    // each one returned in parallel with others.
612    // Result collation arrays
613   
614    /**
615    * mapping between index of sequence in alignment that was submitted to
616    * server and index of sequence in the input alignment
617    */
618  0 int[][] ordermap = new int[jobs.length][];
619  0 SequenceI[][] rseqs = new SequenceI[jobs.length][];
620  0 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
621  0 AlignmentAnnotation[][] alan = new AlignmentAnnotation[jobs.length][];
622  0 SequenceGroup[][] sgrp = new SequenceGroup[jobs.length][];
623    // Now collect all data for alignment Set als from job array
624  0 for (int j = 0; j < jobs.length; j++)
625    {
626  0 RestJob rj = (RestJob) jobs[j];
627  0 if (rj.hasResults())
628    {
629  0 JalviewDataset rset = rj.context;
630  0 if (rset.hasAlignments())
631    {
632  0 if (numAlSets < rset.getAl().size())
633    {
634  0 numAlSets = rset.getAl().size();
635    }
636  0 if (als < rset.getAl().size()
637    && rset.getAl().get(als).isModified())
638    {
639    // Collate result data
640    // TODO: decide if all alignmentI should be collected rather than
641    // specific alignment data containers
642    // for moment, we just extract content, but this means any
643    // alignment properties may be lost.
644  0 AlignmentSet alset = rset.getAl().get(als);
645  0 alan[j] = alset.al.getAlignmentAnnotation();
646  0 if (alset.al.getGroups() != null)
647    {
648  0 sgrp[j] = new SequenceGroup[alset.al.getGroups().size()];
649  0 alset.al.getGroups().toArray(sgrp[j]);
650    }
651    else
652    {
653  0 sgrp[j] = null;
654    }
655  0 orders[j] = new AlignmentOrder(alset.al);
656  0 rseqs[j] = alset.al.getSequencesArray();
657  0 ordermap[j] = rj.getOrderMap();
658    // if (rj.isInputUniquified()) {
659    // jalview.analysis.AlignmentSorter.recoverOrder(rseqs[als]);
660    // }
661   
662  0 if (alset.trees != null)
663    {
664  0 trees.add(new ArrayList<NewickFile>(alset.trees));
665    }
666    else
667    {
668  0 trees.add(new ArrayList<NewickFile>());
669    }
670    }
671    }
672    }
673    }
674    // Now aggregate and present results from this frame of alignment data.
675  0 int nvertsep = 0, nvertseps = 1;
676  0 if (vsepjobs)
677    {
678    // Jobs relate to different rows of input alignment.
679    // Jobs are subdivided by rows before columns,
680    // so there will now be a number of subjobs according tohsep for each
681    // vertsep
682    // TODO: get vertical separation intervals for each job and set
683    // nvertseps
684    // TODO: merge data from each group/sequence onto whole
685    // alignment
686    }
687    /**
688    * index into rest jobs subdivided vertically
689    */
690  0 int vrestjob = 0;
691    // Destination alignments for all result data.
692  0 ArrayList<SequenceGroup> visgrps = new ArrayList<SequenceGroup>();
693  0 Hashtable<String, SequenceGroup> groupNames = new Hashtable<String, SequenceGroup>();
694  0 ArrayList<AlignmentAnnotation> visAlAn = null;
695  0 for (nvertsep = 0; nvertsep < nvertseps; nvertsep++)
696    {
697    // TODO: set scope w.r.t. original alignment view for vertical
698    // separation.
699    {
700    // results for a job exclude hidden columns of input data, so map
701    // back on to all visible regions
702    /**
703    * rest job result we are working with
704    */
705  0 int nrj = vrestjob;
706   
707  0 RestJob rj = (RestJob) jobs[nrj];
708  0 int contigs[] = input.getVisibleContigs();
709  0 AlignmentI destAl = null;
710  0 jalview.datamodel.HiddenColumns destHCs = null;
711    // Resolve destAl for this data.
712  0 if (als == 0 && rj.isInputContextModified())
713    {
714    // special case: transfer features, annotation, groups, etc,
715    // from input
716    // context to align panel derived from input data
717  0 if (destAls.size() > als)
718    {
719  0 destAl = destAls.get(als);
720    }
721    else
722    {
723  0 if (!restClient.isAlignmentModified() && merge)
724    {
725  0 destAl = restClient.av.getAlignment();
726  0 destHCs = restClient.av.getAlignment().getHiddenColumns();
727  0 resultDest.add(restClient.isShowResultsInNewView()
728    ? AddDataTo.newView
729    : AddDataTo.currentView);
730  0 destPanels.add(restClient.recoverAlignPanelForView());
731    }
732    else
733    {
734  0 newAlignment = true;
735    // recreate the input alignment data
736  0 Object[] idat = input
737    .getAlignmentAndHiddenColumns(gapCharacter);
738  0 destAl = new Alignment((SequenceI[]) idat[0]);
739  0 destHCs = (HiddenColumns) idat[1];
740  0 resultDest.add(AddDataTo.newAlignment);
741    // but do not add to the alignment panel list - since we need to
742    // create a whole new alignFrame set.
743    }
744  0 destAls.add(destAl);
745  0 destColsel.add(destHCs);
746    }
747    }
748    else
749    {
750    // alignment(s) returned by service is to be re-integrated and
751    // displayed
752  0 if (destAls.size() > als)
753    {
754  0 if (!vsepjobs)
755    {
756    // TODO: decide if multiple multiple alignments returned by
757    // non-vseparable services are allowed.
758  0 Console.warn(
759    "dealing with multiple alignment products returned by non-vertically separable service.");
760    }
761    // recover reference to last alignment created for this rest frame
762    // ready for extension
763  0 destAl = destAls.get(als);
764  0 destHCs = destColsel.get(als);
765    }
766    else
767    {
768  0 Object[] newview;
769   
770  0 if (!hsepjobs)
771    {
772    // single alignment for any job that gets mapped back on to
773    // input data. Reconstruct by interleaving parts of returned
774    // alignment with hidden parts of input data.
775  0 SequenceI[][] nsq = splitSeqsOnVisibleContigs(rseqs[nrj],
776    contigs, gapCharacter);
777  0 AlignmentOrder alo[] = new AlignmentOrder[nsq.length];
778  0 for (int no = 0; no < alo.length; no++)
779    {
780  0 alo[no] = new AlignmentOrder(orders[nrj].getOrder());
781    }
782  0 newview = input.getUpdatedView(nsq, orders, gapCharacter);
783    }
784    else
785    {
786    // each job maps to a single visible contig, and all need to be
787    // stitched back together.
788    // reconstruct using sub-region based MSA alignment construction
789    // mechanism
790  0 newview = input.getUpdatedView(rseqs, orders, gapCharacter);
791    }
792  0 destAl = new Alignment((SequenceI[]) newview[0]);
793  0 destHCs = (HiddenColumns) newview[1];
794  0 newAlignment = true;
795    // TODO create alignment from result data with propagated
796    // references.
797  0 destAls.add(destAl);
798  0 destColsel.add(destHCs);
799  0 resultDest.add(AddDataTo.newAlignment);
800  0 throw new Error(
801    MessageManager.getString("error.implementation_error")
802    + "TODO: ");
803    }
804    }
805    /**
806    * save initial job in this set in case alignment is h-separable
807    */
808  0 int initnrj = nrj;
809    // Now add in groups
810  0 for (int ncnt = 0; ncnt < contigs.length; ncnt += 2)
811    {
812  0 if (!hsepjobs)
813    {
814    // single alignment for any job that gets mapped back on to input
815    // data.
816    }
817    else
818    {
819    // each job maps to a single visible contig, and all need to be
820    // stitched back together.
821  0 if (ncnt > 0)
822    {
823  0 nrj++;
824    }
825    // TODO: apply options for group merging and annotation merging.
826    // If merging not supported, then either clear hashtables now or
827    // use them to rename the new annotation/groups for each contig if
828    // a conflict occurs.
829    }
830  0 if (sgrp[nrj] != null)
831    {
832  0 for (SequenceGroup sg : sgrp[nrj])
833    {
834  0 boolean recovered = false;
835  0 SequenceGroup exsg = null;
836  0 if (sg.getName() != null)
837    {
838  0 exsg = groupNames.get(sg.getName());
839    }
840  0 if (exsg == null)
841    {
842  0 exsg = new SequenceGroup(sg);
843  0 groupNames.put(exsg.getName(), exsg);
844  0 visgrps.add(exsg);
845  0 exsg.setStartRes(sg.getStartRes() + contigs[ncnt]);
846  0 exsg.setEndRes(sg.getEndRes() + contigs[ncnt]);
847    }
848    else
849    {
850  0 recovered = true;
851    }
852    // now replace any references from the result set with
853    // corresponding refs from alignment input set.
854   
855    // TODO: cope with recovering hidden sequences from
856    // resultContext
857    {
858  0 for (SequenceI oseq : sg.getSequences(null))
859    {
860  0 SequenceI nseq = getNewSeq(oseq, rseqs[nrj],
861    ordermap[nrj], destAl);
862  0 if (nseq != null)
863    {
864  0 if (!recovered)
865    {
866  0 exsg.deleteSequence(oseq, false);
867    }
868  0 exsg.addSequence(nseq, false);
869    }
870    else
871    {
872  0 Console.warn(
873    "Couldn't resolve original sequence for new sequence.");
874    }
875    }
876  0 if (sg.hasSeqrep())
877    {
878  0 if (exsg.getSeqrep() == sg.getSeqrep())
879    {
880    // lift over sequence rep reference too
881  0 SequenceI oseq = sg.getSeqrep();
882  0 SequenceI nseq = getNewSeq(oseq, rseqs[nrj],
883    ordermap[nrj], destAl);
884  0 if (nseq != null)
885    {
886  0 exsg.setSeqrep(nseq);
887    }
888    }
889    }
890    }
891  0 if (recovered)
892    {
893    // adjust boundaries of recovered group w.r.t. new group being
894    // merged on to original alignment.
895  0 int start = sg.getStartRes() + contigs[ncnt],
896    end = sg.getEndRes() + contigs[ncnt];
897  0 if (start < exsg.getStartRes())
898    {
899  0 exsg.setStartRes(start);
900    }
901  0 if (end > exsg.getEndRes())
902    {
903  0 exsg.setEndRes(end);
904    }
905    }
906    }
907    }
908    }
909    // reset job counter
910  0 nrj = initnrj;
911    // and finally add in annotation and any trees for each job
912  0 for (int ncnt = 0; ncnt < contigs.length; ncnt += 2)
913    {
914  0 if (!hsepjobs)
915    {
916    // single alignment for any job that gets mapped back on to input
917    // data.
918    }
919    else
920    {
921    // each job maps to a single visible contig, and all need to be
922    // stitched back together.
923  0 if (ncnt > 0)
924    {
925  0 nrj++;
926    }
927    }
928   
929    // merge alignmentAnnotation into one row
930  0 if (alan[nrj] != null)
931    {
932  0 for (int an = 0; an < alan[nrj].length; an++)
933    {
934  0 SequenceI sqass = null;
935  0 SequenceGroup grass = null;
936  0 if (alan[nrj][an].sequenceRef != null)
937    {
938    // TODO: ensure this relocates sequence reference to local
939    // context.
940  0 sqass = getNewSeq(alan[nrj][an].sequenceRef, rseqs[nrj],
941    ordermap[nrj], destAl);
942    }
943  0 if (alan[nrj][an].groupRef != null)
944    {
945    // TODO: verify relocate group reference to local context
946    // works correctly
947  0 grass = groupNames.get(alan[nrj][an].groupRef.getName());
948  0 if (grass == null)
949    {
950  0 Console.error(
951    "Couldn't relocate group referemce for group "
952    + alan[nrj][an].groupRef.getName());
953    }
954    }
955  0 if (visAlAn == null)
956    {
957  0 visAlAn = new ArrayList<AlignmentAnnotation>();
958    }
959  0 AlignmentAnnotation visan = null;
960  0 for (AlignmentAnnotation v : visAlAn)
961    {
962  0 if (v.label != null
963    && v.label.equals(alan[nrj][an].label))
964    {
965  0 visan = v;
966    }
967    }
968  0 if (visan == null)
969    {
970  0 visan = new AlignmentAnnotation(alan[nrj][an]);
971    // copy annotations, and wipe out/update refs.
972  0 visan.annotations = new Annotation[input.getWidth()];
973  0 visan.groupRef = grass;
974  0 visan.sequenceRef = sqass;
975  0 visAlAn.add(visan);
976    }
977  0 if (contigs[ncnt]
978    + alan[nrj][an].annotations.length > visan.annotations.length)
979    {
980    // increase width of annotation row
981  0 Annotation[] newannv = new Annotation[contigs[ncnt]
982    + alan[nrj][an].annotations.length];
983  0 System.arraycopy(visan.annotations, 0, newannv, 0,
984    visan.annotations.length);
985  0 visan.annotations = newannv;
986    }
987    // now copy local annotation data into correct position
988  0 System.arraycopy(alan[nrj][an].annotations, 0,
989    visan.annotations, contigs[ncnt],
990    alan[nrj][an].annotations.length);
991   
992    }
993    }
994    // Trees
995  0 if (trees.get(nrj).size() > 0)
996    {
997  0 for (NewickFile nf : trees.get(nrj))
998    {
999    // TODO: process each newick file, lifting over sequence refs to
1000    // current alignment, if necessary.
1001  0 Console.error(
1002    "Tree recovery from restjob not yet implemented.");
1003    }
1004    }
1005    }
1006    }
1007    } // end of vseps loops.
1008  0 if (visAlAn != null)
1009    {
1010  0 for (AlignmentAnnotation v : visAlAn)
1011    {
1012  0 destAls.get(als).addAnnotation(v);
1013    }
1014    }
1015  0 if (visgrps != null)
1016    {
1017  0 for (SequenceGroup sg : visgrps)
1018    {
1019  0 destAls.get(als).addGroup(sg);
1020    }
1021    }
1022  0 } while (++als < numAlSets);
1023    // Finally, assemble each new alignment, and create new gui components to
1024    // present it.
1025    /**
1026    * current AlignFrame where results will go.
1027    */
1028  0 AlignFrame destaf = restClient.recoverAlignFrameForView();
1029    /**
1030    * current pane being worked with
1031    */
1032  0 jalview.gui.AlignmentPanel destPanel = restClient
1033    .recoverAlignPanelForView();
1034  0 als = 0;
1035  0 for (AddDataTo action : resultDest)
1036    {
1037  0 AlignmentI destal;
1038  0 HiddenColumns destcs;
1039  0 String alTitle = MessageManager
1040    .formatMessage("label.webservice_job_title_on", new String[]
1041    { restClient.service.details.Action,
1042    restClient.service.details.Name, restClient.viewTitle });
1043  0 switch (action)
1044    {
1045  0 case newAlignment:
1046  0 destal = destAls.get(als);
1047  0 destcs = destColsel.get(als);
1048  0 destaf = new AlignFrame(destal, destcs, AlignFrame.DEFAULT_WIDTH,
1049    AlignFrame.DEFAULT_HEIGHT);
1050  0 PaintRefresher.Refresh(destaf,
1051    destaf.getViewport().getSequenceSetId());
1052    // todo transfer any feature settings and colouring
1053    /*
1054    * destaf.getFeatureRenderer().transferSettings(this.featureSettings);
1055    * // update orders if (alorders.size() > 0) { if (alorders.size() == 1)
1056    * { af.addSortByOrderMenuItem(WebServiceName + " Ordering",
1057    * (AlignmentOrder) alorders.get(0)); } else { // construct a
1058    * non-redundant ordering set Vector names = new Vector(); for (int i =
1059    * 0, l = alorders.size(); i < l; i++) { String orderName = new
1060    * String(" Region " + i); int j = i + 1;
1061    *
1062    * while (j < l) { if (((AlignmentOrder) alorders.get(i))
1063    * .equals(((AlignmentOrder) alorders.get(j)))) { alorders.remove(j);
1064    * l--; orderName += "," + j; } else { j++; } }
1065    *
1066    * if (i == 0 && j == 1) { names.add(new String("")); } else {
1067    * names.add(orderName); } } for (int i = 0, l = alorders.size(); i < l;
1068    * i++) { af.addSortByOrderMenuItem( WebServiceName + ((String)
1069    * names.get(i)) + " Ordering", (AlignmentOrder) alorders.get(i)); } } }
1070    */
1071    // TODO: modify this and previous alignment's title if many alignments
1072    // have been returned.
1073  0 Desktop.addInternalFrame(destaf, alTitle, AlignFrame.DEFAULT_WIDTH,
1074    AlignFrame.DEFAULT_HEIGHT);
1075   
1076  0 break;
1077  0 case newView:
1078    // TODO: determine title for view
1079  0 break;
1080  0 case currentView:
1081  0 break;
1082    }
1083    }
1084  0 if (!newAlignment)
1085    {
1086  0 if (restClient.isShowResultsInNewView())
1087    {
1088    // destPanel = destPanel.alignFrame.newView(false);
1089    }
1090    }
1091    else
1092    {
1093   
1094    }
1095    /*
1096    * if (als) // add the destination panel to frame zero of result panel set }
1097    * } if (destPanels.size()==0) { AlignFrame af = new AlignFrame((AlignmentI)
1098    * idat[0], (ColumnSelection) idat[1], AlignFrame.DEFAULT_WIDTH,
1099    * AlignFrame.DEFAULT_HEIGHT);
1100    *
1101    * jalview.gui.Desktop.addInternalFrame(af, "Results for " +
1102    * restClient.service.details.Name + " " + restClient.service.details.Action
1103    * + " on " + restClient.af.getTitle(), AlignFrame.DEFAULT_WIDTH,
1104    * AlignFrame.DEFAULT_HEIGHT); destPanel = af.alignPanel; // create totally
1105    * new alignment from stashed data/results
1106    */
1107   
1108    /*
1109    */
1110   
1111    /**
1112    * alignments. New alignments are added to dataset, and subsequently
1113    * annotated/visualised accordingly. 1. New alignment frame created for new
1114    * alignment. Decide if any vis settings should be inherited from old
1115    * alignment frame (e.g. sequence ordering ?). 2. Subsequent data added to
1116    * alignment as below:
1117    */
1118    /**
1119    * annotation update to original/newly created context alignment: 1.
1120    * identify alignment where annotation is to be loaded onto. 2. Add
1121    * annotation, excluding any duplicates. 3. Ensure annotation is visible on
1122    * alignment - honouring ordering given by file.
1123    */
1124    /**
1125    * features updated to original or newly created context alignment: 1.
1126    * Features are(or were already) added to dataset. 2. Feature settings
1127    * modified to ensure all features are displayed - honouring any ordering
1128    * given by result file. Consider merging action with the code used by the
1129    * DAS fetcher to update alignment views with new info.
1130    */
1131    /**
1132    * Seq associated data files (PDB files). 1. locate seq association in
1133    * current dataset/alignment context and add file as normal - keep handle of
1134    * any created ref objects. 2. decide if new data should be displayed : PDB
1135    * display: if alignment has PDB display already, should new pdb files be
1136    * aligned to it ?
1137    *
1138    */
1139    // destPanel.adjustAnnotationHeight();
1140   
1141    }
1142   
1143    /**
1144    * split the given array of sequences into blocks of subsequences
1145    * corresponding to each visible contig
1146    *
1147    * @param sequenceIs
1148    * @param contigs
1149    * @param gapChar
1150    * padding character for ragged ends of visible contig region.
1151    * @return
1152    */
 
1153  0 toggle private SequenceI[][] splitSeqsOnVisibleContigs(SequenceI[] sequenceIs,
1154    int[] contigs, char gapChar)
1155    {
1156  0 int nvc = contigs == null ? 1 : contigs.length / 2;
1157  0 SequenceI[][] blocks = new SequenceI[nvc][];
1158  0 if (contigs == null)
1159    {
1160  0 blocks[0] = new SequenceI[sequenceIs.length];
1161  0 System.arraycopy(sequenceIs, 0, blocks[0], 0, sequenceIs.length);
1162    }
1163    else
1164    {
1165    // deja vu - have I written this before ?? propagateGaps does this in a
1166    // way
1167  0 char[] gapset = null;
1168  0 int start = 0, width = 0;
1169  0 for (int c = 0; c < nvc; c++)
1170    {
1171  0 width = contigs[c * 2 + 1] - contigs[c * 2] + 1;
1172  0 for (int s = 0; s < sequenceIs.length; s++)
1173    {
1174  0 int end = sequenceIs[s].getLength();
1175  0 if (start < end)
1176    {
1177  0 if (start + width < end)
1178    {
1179  0 blocks[c][s] = sequenceIs[s].getSubSequence(start,
1180    start + width);
1181    }
1182    else
1183    {
1184  0 blocks[c][s] = sequenceIs[s].getSubSequence(start, end);
1185  0 String sq = blocks[c][s].getSequenceAsString();
1186  0 for (int n = start + width; n > end; n--)
1187    {
1188  0 sq += gapChar;
1189    }
1190    }
1191    }
1192    else
1193    {
1194  0 if (gapset == null || gapset.length < width)
1195    {
1196  0 char ng[] = new char[width];
1197  0 int gs = 0;
1198  0 if (gapset != null)
1199    {
1200  0 System.arraycopy(gapset, 0, ng, 0, gs = gapset.length);
1201    }
1202  0 while (gs < ng.length)
1203    {
1204  0 ng[gs++] = gapChar;
1205    }
1206    }
1207  0 blocks[c][s] = sequenceIs[s].getSubSequence(end, end);
1208  0 blocks[c][s].setSequence(gapset.toString().substring(0, width));
1209    }
1210    }
1211  0 if (c > 0)
1212    {
1213    // adjust window for next visible segnment
1214  0 start += contigs[c * 2 + 1] - contigs[c * 2];
1215    }
1216    }
1217    }
1218  0 return blocks;
1219    }
1220   
1221    /**
1222    * recover corresponding sequence from original input data corresponding to
1223    * sequence in a specific job's input data.
1224    *
1225    * @param oseq
1226    * @param sequenceIs
1227    * @param is
1228    * @param destAl
1229    * @return
1230    */
 
1231  0 toggle private SequenceI getNewSeq(SequenceI oseq, SequenceI[] sequenceIs,
1232    int[] is, AlignmentI destAl)
1233    {
1234  0 int p = 0;
1235  0 while (p < sequenceIs.length && sequenceIs[p] != oseq)
1236    {
1237  0 p++;
1238    }
1239  0 if (p < sequenceIs.length && p < destAl.getHeight())
1240    {
1241  0 return destAl.getSequenceAt(is[p]);
1242    }
1243  0 return null;
1244    }
1245   
1246    /**
1247    *
1248    * @return true if the run method is safe to call
1249    */
 
1250  1 toggle public boolean isValid()
1251    {
1252  1 ArrayList<String> _warnings = new ArrayList<String>();
1253  1 boolean validt = true;
1254  1 if (jobs != null)
1255    {
1256  1 for (RestJob rj : (RestJob[]) jobs)
1257    {
1258  1 if (!rj.hasValidInput())
1259    {
1260    // invalid input for this job
1261  0 jalview.bin.Console.errPrintln("Job " + rj.getJobnum()
1262    + " has invalid input. ( " + rj.getStatus() + ")");
1263  0 if (rj.hasStatus() && !_warnings.contains(rj.getStatus()))
1264    {
1265  0 _warnings.add(rj.getStatus());
1266    }
1267  0 validt = false;
1268    }
1269    }
1270    }
1271  1 if (!validt)
1272    {
1273  0 warnings = "";
1274  0 for (String st : _warnings)
1275    {
1276  0 if (warnings.length() > 0)
1277    {
1278  0 warnings += "\n";
1279    }
1280  0 warnings += st;
1281   
1282    }
1283    }
1284  1 return validt;
1285    }
1286   
1287    protected String warnings;
1288   
 
1289  0 toggle public boolean hasWarnings()
1290    {
1291    // TODO Auto-generated method stub
1292  0 return warnings != null && warnings.length() > 0;
1293    }
1294   
1295    /**
1296    * get any informative messages about why the job thread couldn't start.
1297    *
1298    * @return
1299    */
 
1300  0 toggle public String getWarnings()
1301    {
1302  0 return isValid() ? "Job can be started. No warnings."
1303  0 : hasWarnings() ? warnings : "";
1304    }
1305   
1306    }