Clover icon

jalviewX

  1. Project Clover database Wed Oct 31 2018 15:13:58 GMT
  2. Package jalview.ws.rest

File RestJobThread.java

 

Coverage histogram

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

Code metrics

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

Classes

Class Line # Actions
RestJobThread 66 399 143 512
0.1537190115.4%
RestJobThread.Stage 68 0 0 0
-1.0 -
RestJobThread.AddDataTo 551 0 0 0
-1.0 -
 

Contributing tests

No tests hitting this source file were found.

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