2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
21 package jalview.ws.rest;
23 import java.util.Locale;
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;
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;
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.util.EntityUtils;
66 public class RestJobThread extends AWSThread
73 protected RestClient restClient;
75 public RestJobThread(RestClient restClient)
77 super(restClient.af, null, restClient._input,
78 restClient.service.postUrl);
79 this.restClient = restClient; // may not be needed
81 // minimal job - submit given input and parse result onto alignment as
82 // annotation/whatever
84 // look for tree data, etc.
86 // for moment do following job type only
87 // input=visiblealignment,groupsindex
88 // ie one subjob using groups defined on alignment.
89 if (!restClient.service.isHseparable())
91 jobs = new RestJob[1];
92 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.
102 int[] viscontig = restClient._input.getVisibleContigs();
103 AlignmentI[] viscontigals = restClient._input
104 .getVisibleContigAlignments(
105 restClient.service.getGapCharacter());
106 if (viscontigals != null && viscontigals.length > 0)
108 jobs = new RestJob[viscontigals.length];
109 for (int j = 0; j < jobs.length; j++)
111 int[] visc = new int[] { viscontig[j * 2], viscontig[j * 2 + 1] };
114 jobs[j] = new RestJob(j, this, viscontigals[j], visc);
118 jobs[j] = new RestJob(0, this, viscontigals[j], visc);
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,
130 if (!restClient.service.isHseparable())
133 // create a bunch of subjobs per visible contig to ensure result honours
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
141 // create a single subjob for the visible/selected input
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 -
150 * create gui components for monitoring jobs
152 * @param webserviceInfo
154 public void setWebServiceInfo(WebserviceInfo webserviceInfo)
156 wsInfo = webserviceInfo;
157 for (int j = 0; j < jobs.length; j++)
160 // Copy over any per job params
163 wsInfo.setProgressName("region " + jobs[j].getJobnum(),
164 jobs[j].getJobnum());
168 wsInfo.setProgressText(jobs[j].getJobnum(), OutputHeader);
173 private String getStage(Stage stg)
175 if (stg == Stage.SUBMIT)
177 return "submitting ";
179 if (stg == Stage.POLL)
181 return "checking status of ";
184 return (" being confused about ");
187 private void doPoll(RestJob rj) throws Exception
189 String postUrl = rj.getPollUrl();
190 doHttpReq(Stage.POLL, rj, postUrl);
194 * construct the post and handle the response.
198 public void doPost(RestJob rj) throws Exception
200 String postUrl = rj.getPostUrl();
201 doHttpReq(Stage.SUBMIT, rj, postUrl);
206 * do the HTTP request - and respond/set statuses appropriate to the current
211 * - provides any data needed for posting and used to record state
213 * - actual URL to post/get from
216 protected void doHttpReq(Stage stg, RestJob rj, String postUrl)
219 HttpRequestBase request = null;
220 String messages = "";
221 if (stg == Stage.SUBMIT)
224 // http://evgenyg.wordpress.com/2010/05/01/uploading-files-multipart-post-apache/
226 HttpPost htpost = new HttpPost(postUrl);
227 MultipartEntity postentity = new MultipartEntity(
228 HttpMultipartMode.STRICT);
229 for (Entry<String, InputType> input : rj.getInputParams())
231 if (input.getValue().validFor(rj))
233 postentity.addPart(input.getKey(),
234 input.getValue().formatForInput(rj));
238 messages += "Skipped an input (" + input.getKey()
239 + ") - Couldn't generate it from available data.";
242 htpost.setEntity(postentity);
247 request = new HttpGet(postUrl);
251 DefaultHttpClient httpclient = new DefaultHttpClient();
253 HttpResponse response = null;
256 response = httpclient.execute(request);
257 } catch (ClientProtocolException he)
259 rj.statMessage = "Web Protocol Exception when " + getStage(stg)
260 + "Job. <br>Problematic url was <a href=\""
261 + request.getURI() + "\">" + request.getURI()
262 + "</a><br>See Console output for details.";
263 rj.setAllowedServerExceptions(0);// unrecoverable;
265 Console.fatal("Unexpected REST Job " + getStage(stg)
266 + "exception for URL " + rj.rsd.postUrl);
268 } catch (IOException e)
270 rj.statMessage = "IO Exception when " + getStage(stg)
271 + "Job. <br>Problematic url was <a href=\""
272 + request.getURI() + "\">" + request.getURI()
273 + "</a><br>See Console output for details.";
274 Console.warn("IO Exception for REST Job " + getStage(stg)
275 + "exception for URL " + rj.rsd.postUrl);
279 switch (response.getStatusLine().getStatusCode())
283 Console.debug("Processing result set.");
284 processResultSet(rj, response, request);
287 markJobAsRunning(rj);
290 // Created - redirect may be present. Fallthrough to 302
292 extractJobId(rj, response);
293 completeStatus(rj, response);
296 markAsFailed(rj, response);
297 completeStatus(rj, response, "" + getStage(stg)
298 + "failed. Reason below:\n");
301 // Some other response. Probably need to pop up the content in a window.
302 // TODO: deal with all other HTTP response codes from server.
303 Console.warn("Unhandled response status when " + getStage(stg)
304 + "for " + postUrl + ": " + response.getStatusLine());
306 rj.setAllowedServerExceptions(0);
307 rj.setSubjobComplete(true);
308 rj.setSubmitted(true);
311 completeStatus(rj, response, "" + getStage(stg)
312 + " resulted in an unexpected server response.<br/>Url concerned was <a href=\""
313 + request.getURI() + "\">" + request.getURI()
314 + "</a><br/>Filtered response content below:<br/>");
315 } catch (IOException e)
317 Console.debug("IOException when consuming unhandled response", e);
324 private void markAsFailed(RestJob rj, HttpResponse response)
327 rj.setSubmitted(true);
328 rj.setAllowedServerExceptions(0);
329 rj.setSubjobComplete(true);
335 * set the jobRunning flag and post a link to the physical result page encoded
340 private void markJobAsRunning(RestJob rj)
342 rj.statMessage = "<br>Job submitted successfully. Results available at this URL:\n"
352 * extract the job ID URL from the redirect page. Does nothing if job is
358 private void extractJobId(RestJob rj, HttpResponse response)
361 if (!rj.isSubmitted())
364 // redirect URL - typical for IBIVU type jobs.
365 if ((loc = response.getHeaders(HTTPConstants.HEADER_LOCATION)) != null
370 Console.warn("Ignoring additional "
372 + " location(s) provided in response header ( next one is '"
373 + loc[1].getValue() + "' )");
375 rj.setJobId(loc[0].getValue());
376 rj.setSubmitted(true);
381 * job has completed. Something valid should be available from con
386 * is a stateless request - expected to return the same data
387 * regardless of how many times its called.
389 private void processResultSet(RestJob rj, HttpResponse con,
392 if (rj.statMessage == null)
396 rj.statMessage += "Job Complete.\n";
399 rj.resSet = new HttpResultSet(rj, con, req);
401 } catch (IOException e)
403 rj.statMessage += "Couldn't parse results. Failed.";
405 rj.gotresult = false;
409 private void completeStatus(RestJob rj, HttpResponse con)
412 completeStatus(rj, con, null);
416 private void completeStatus(RestJob rj, HttpResponse con, String prefix)
419 StringBuffer sb = new StringBuffer();
425 if (rj.statMessage != null && rj.statMessage.length() > 0)
427 sb.append(rj.statMessage);
429 HttpEntity en = con.getEntity();
431 * Just append the content as a string.
434 StringBuffer content = new StringBuffer(f = EntityUtils.toString(en));
435 f = f.toLowerCase(Locale.ROOT);
436 int body = f.indexOf("<body");
439 content.delete(0, f.indexOf(">", body) + 1);
441 if (body > -1 && sb.length() > 0)
444 content.insert(0, sb);
448 rj.statMessage = content.toString();
452 public void pollJob(AWsJob job) throws Exception
454 assert (job instanceof RestJob);
455 System.err.println("Debug RestJob: Polling Job");
456 doPoll((RestJob) job);
460 public void StartJob(AWsJob job)
462 assert (job instanceof RestJob);
465 System.err.println("Debug RestJob: Posting Job");
466 doPost((RestJob) job);
467 } catch (NoValidInputDataException erex)
469 job.setSubjobComplete(true);
470 job.setSubmitted(true);
471 ((RestJob) job).statMessage = "<br>It looks like there was a problem with the data sent to the service :<br>"
472 + erex.getMessage() + "\n";
473 ((RestJob) job).error = true;
475 } catch (Exception ex)
477 job.setSubjobComplete(true);
478 job.setAllowedServerExceptions(-1);
479 Console.error("Exception when trying to start Rest Job.", ex);
484 public void parseResult()
486 // crazy users will see this message
487 // TODO: finish this! and remove the message below!
488 Console.warn("Rest job result parser is currently INCOMPLETE!");
490 for (RestJob rj : (RestJob[]) jobs)
492 if (rj.hasResponse() && rj.resSet != null && rj.resSet.isValid())
497 Console.debug("Parsing data for job " + rj.getJobId());
503 Console.debug("Finished parsing data for job " + rj.getJobId());
508 "Failed to finish parsing data for job " + rj.getJobId());
509 ex.printStackTrace();
510 } catch (Exception ex)
513 "Failed to finish parsing data for job " + rj.getJobId());
514 ex.printStackTrace();
518 rj.statMessage = "Error whilst parsing data for this job.<br>URL for job response is :<a href=\""
519 + rj.resSet.getUrl() + "\">" + rj.resSet.getUrl()
526 // add listeners and activate result display gui elements
528 * decisions based on job result content + state of alignFrame that
529 * originated the job:
532 * 1. Can/Should this job automatically open a new window for results
536 // preserver current jalview behaviour
537 wsInfo.setViewResultsImmediatly(true);
541 // realiseResults(true, true);
543 // otherwise, should automatically view results
545 // TODO: check if at least one or more contexts are valid - if so, enable
547 wsInfo.showResultsNewFrame.addActionListener(new ActionListener()
551 public void actionPerformed(ActionEvent e)
553 realiseResults(false);
557 wsInfo.mergeResults.addActionListener(new ActionListener()
561 public void actionPerformed(ActionEvent e)
563 realiseResults(true);
568 wsInfo.setResultsReady();
572 // tell the user nothing was returned.
573 wsInfo.setStatus(wsInfo.STATE_STOPPED_ERROR);
574 wsInfo.setFinishedNoResults();
579 * instructions for whether to create new alignment view on current alignment
580 * set, add to current set, or create new alignFrame
582 private enum AddDataTo
585 * add annotation, trees etc to current view
589 * create a new view derived from current view and add data to that
593 * create a new alignment frame for the result set and add annotation to
599 public void realiseResults(boolean merge)
602 * 2. Should the job modify the parent alignment frame/view(s) (if they
603 * still exist and the alignment hasn't been edited) in order to display new
604 * annotation/features.
607 * alignment panels derived from each alignment set returned by service.
609 ArrayList<jalview.gui.AlignmentPanel> destPanels = new ArrayList<jalview.gui.AlignmentPanel>();
611 * list of instructions for how to process each distinct alignment set
612 * returned by the job set
614 ArrayList<AddDataTo> resultDest = new ArrayList<AddDataTo>();
616 * when false, zeroth pane is panel derived from input deta.
618 boolean newAlignment = false;
620 * gap character to be used for alignment reconstruction
622 char gapCharacter = restClient.av.getGapCharacter();
623 // Now, iterate over all alignment sets returned from all jobs:
624 // first inspect jobs and collate results data in order to count alignments
626 // then assemble results from decomposed (v followed by h-separated) jobs
627 // finally, new views and alignments will be created and displayed as
629 boolean hsepjobs = restClient.service.isHseparable();
630 boolean vsepjobs = restClient.service.isVseparable();
631 // total number of distinct alignment sets generated by job set.
632 int numAlSets = 0, als = 0;
633 List<AlignmentI> destAls = new ArrayList<AlignmentI>();
634 List<jalview.datamodel.HiddenColumns> destColsel = new ArrayList<jalview.datamodel.HiddenColumns>();
635 List<List<NewickFile>> trees = new ArrayList<List<NewickFile>>();
640 // iterate over each alignment set returned from each subjob. Treating
641 // each one returned in parallel with others.
642 // Result collation arrays
645 * mapping between index of sequence in alignment that was submitted to
646 * server and index of sequence in the input alignment
648 int[][] ordermap = new int[jobs.length][];
649 SequenceI[][] rseqs = new SequenceI[jobs.length][];
650 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
651 AlignmentAnnotation[][] alan = new AlignmentAnnotation[jobs.length][];
652 SequenceGroup[][] sgrp = new SequenceGroup[jobs.length][];
653 // Now collect all data for alignment Set als from job array
654 for (int j = 0; j < jobs.length; j++)
656 RestJob rj = (RestJob) jobs[j];
659 JalviewDataset rset = rj.context;
660 if (rset.hasAlignments())
662 if (numAlSets < rset.getAl().size())
664 numAlSets = rset.getAl().size();
666 if (als < rset.getAl().size()
667 && rset.getAl().get(als).isModified())
669 // Collate result data
670 // TODO: decide if all alignmentI should be collected rather than
671 // specific alignment data containers
672 // for moment, we just extract content, but this means any
673 // alignment properties may be lost.
674 AlignmentSet alset = rset.getAl().get(als);
675 alan[j] = alset.al.getAlignmentAnnotation();
676 if (alset.al.getGroups() != null)
678 sgrp[j] = new SequenceGroup[alset.al.getGroups().size()];
679 alset.al.getGroups().toArray(sgrp[j]);
685 orders[j] = new AlignmentOrder(alset.al);
686 rseqs[j] = alset.al.getSequencesArray();
687 ordermap[j] = rj.getOrderMap();
688 // if (rj.isInputUniquified()) {
689 // jalview.analysis.AlignmentSorter.recoverOrder(rseqs[als]);
692 if (alset.trees != null)
694 trees.add(new ArrayList<NewickFile>(alset.trees));
698 trees.add(new ArrayList<NewickFile>());
704 // Now aggregate and present results from this frame of alignment data.
705 int nvertsep = 0, nvertseps = 1;
708 // Jobs relate to different rows of input alignment.
709 // Jobs are subdivided by rows before columns,
710 // so there will now be a number of subjobs according tohsep for each
712 // TODO: get vertical separation intervals for each job and set
714 // TODO: merge data from each group/sequence onto whole
718 * index into rest jobs subdivided vertically
721 // Destination alignments for all result data.
722 ArrayList<SequenceGroup> visgrps = new ArrayList<SequenceGroup>();
723 Hashtable<String, SequenceGroup> groupNames = new Hashtable<String, SequenceGroup>();
724 ArrayList<AlignmentAnnotation> visAlAn = null;
725 for (nvertsep = 0; nvertsep < nvertseps; nvertsep++)
727 // TODO: set scope w.r.t. original alignment view for vertical
730 // results for a job exclude hidden columns of input data, so map
731 // back on to all visible regions
733 * rest job result we are working with
737 RestJob rj = (RestJob) jobs[nrj];
738 int contigs[] = input.getVisibleContigs();
739 AlignmentI destAl = null;
740 jalview.datamodel.HiddenColumns destHCs = null;
741 // Resolve destAl for this data.
742 if (als == 0 && rj.isInputContextModified())
744 // special case: transfer features, annotation, groups, etc,
746 // context to align panel derived from input data
747 if (destAls.size() > als)
749 destAl = destAls.get(als);
753 if (!restClient.isAlignmentModified() && merge)
755 destAl = restClient.av.getAlignment();
756 destHCs = restClient.av.getAlignment().getHiddenColumns();
757 resultDest.add(restClient.isShowResultsInNewView()
759 : AddDataTo.currentView);
760 destPanels.add(restClient.recoverAlignPanelForView());
765 // recreate the input alignment data
766 Object[] idat = input
767 .getAlignmentAndHiddenColumns(gapCharacter);
768 destAl = new Alignment((SequenceI[]) idat[0]);
769 destHCs = (HiddenColumns) idat[1];
770 resultDest.add(AddDataTo.newAlignment);
771 // but do not add to the alignment panel list - since we need to
772 // create a whole new alignFrame set.
775 destColsel.add(destHCs);
780 // alignment(s) returned by service is to be re-integrated and
782 if (destAls.size() > als)
786 // TODO: decide if multiple multiple alignments returned by
787 // non-vseparable services are allowed.
789 "dealing with multiple alignment products returned by non-vertically separable service.");
791 // recover reference to last alignment created for this rest frame
792 // ready for extension
793 destAl = destAls.get(als);
794 destHCs = destColsel.get(als);
802 // single alignment for any job that gets mapped back on to
803 // input data. Reconstruct by interleaving parts of returned
804 // alignment with hidden parts of input data.
805 SequenceI[][] nsq = splitSeqsOnVisibleContigs(rseqs[nrj],
806 contigs, gapCharacter);
807 AlignmentOrder alo[] = new AlignmentOrder[nsq.length];
808 for (int no = 0; no < alo.length; no++)
810 alo[no] = new AlignmentOrder(orders[nrj].getOrder());
812 newview = input.getUpdatedView(nsq, orders, gapCharacter);
816 // each job maps to a single visible contig, and all need to be
817 // stitched back together.
818 // reconstruct using sub-region based MSA alignment construction
820 newview = input.getUpdatedView(rseqs, orders, gapCharacter);
822 destAl = new Alignment((SequenceI[]) newview[0]);
823 destHCs = (HiddenColumns) newview[1];
825 // TODO create alignment from result data with propagated
828 destColsel.add(destHCs);
829 resultDest.add(AddDataTo.newAlignment);
831 MessageManager.getString("error.implementation_error")
836 * save initial job in this set in case alignment is h-separable
840 for (int ncnt = 0; ncnt < contigs.length; ncnt += 2)
844 // single alignment for any job that gets mapped back on to input
849 // each job maps to a single visible contig, and all need to be
850 // stitched back together.
855 // TODO: apply options for group merging and annotation merging.
856 // If merging not supported, then either clear hashtables now or
857 // use them to rename the new annotation/groups for each contig if
858 // a conflict occurs.
860 if (sgrp[nrj] != null)
862 for (SequenceGroup sg : sgrp[nrj])
864 boolean recovered = false;
865 SequenceGroup exsg = null;
866 if (sg.getName() != null)
868 exsg = groupNames.get(sg.getName());
872 exsg = new SequenceGroup(sg);
873 groupNames.put(exsg.getName(), exsg);
875 exsg.setStartRes(sg.getStartRes() + contigs[ncnt]);
876 exsg.setEndRes(sg.getEndRes() + contigs[ncnt]);
882 // now replace any references from the result set with
883 // corresponding refs from alignment input set.
885 // TODO: cope with recovering hidden sequences from
888 for (SequenceI oseq : sg.getSequences(null))
890 SequenceI nseq = getNewSeq(oseq, rseqs[nrj],
891 ordermap[nrj], destAl);
896 exsg.deleteSequence(oseq, false);
898 exsg.addSequence(nseq, false);
903 "Couldn't resolve original sequence for new sequence.");
908 if (exsg.getSeqrep() == sg.getSeqrep())
910 // lift over sequence rep reference too
911 SequenceI oseq = sg.getSeqrep();
912 SequenceI nseq = getNewSeq(oseq, rseqs[nrj],
913 ordermap[nrj], destAl);
916 exsg.setSeqrep(nseq);
923 // adjust boundaries of recovered group w.r.t. new group being
924 // merged on to original alignment.
925 int start = sg.getStartRes() + contigs[ncnt],
926 end = sg.getEndRes() + contigs[ncnt];
927 if (start < exsg.getStartRes())
929 exsg.setStartRes(start);
931 if (end > exsg.getEndRes())
941 // and finally add in annotation and any trees for each job
942 for (int ncnt = 0; ncnt < contigs.length; ncnt += 2)
946 // single alignment for any job that gets mapped back on to input
951 // each job maps to a single visible contig, and all need to be
952 // stitched back together.
959 // merge alignmentAnnotation into one row
960 if (alan[nrj] != null)
962 for (int an = 0; an < alan[nrj].length; an++)
964 SequenceI sqass = null;
965 SequenceGroup grass = null;
966 if (alan[nrj][an].sequenceRef != null)
968 // TODO: ensure this relocates sequence reference to local
970 sqass = getNewSeq(alan[nrj][an].sequenceRef, rseqs[nrj],
971 ordermap[nrj], destAl);
973 if (alan[nrj][an].groupRef != null)
975 // TODO: verify relocate group reference to local context
977 grass = groupNames.get(alan[nrj][an].groupRef.getName());
981 "Couldn't relocate group referemce for group "
982 + alan[nrj][an].groupRef.getName());
987 visAlAn = new ArrayList<AlignmentAnnotation>();
989 AlignmentAnnotation visan = null;
990 for (AlignmentAnnotation v : visAlAn)
993 && v.label.equals(alan[nrj][an].label))
1000 visan = new AlignmentAnnotation(alan[nrj][an]);
1001 // copy annotations, and wipe out/update refs.
1002 visan.annotations = new Annotation[input.getWidth()];
1003 visan.groupRef = grass;
1004 visan.sequenceRef = sqass;
1008 + alan[nrj][an].annotations.length > visan.annotations.length)
1010 // increase width of annotation row
1011 Annotation[] newannv = new Annotation[contigs[ncnt]
1012 + alan[nrj][an].annotations.length];
1013 System.arraycopy(visan.annotations, 0, newannv, 0,
1014 visan.annotations.length);
1015 visan.annotations = newannv;
1017 // now copy local annotation data into correct position
1018 System.arraycopy(alan[nrj][an].annotations, 0,
1019 visan.annotations, contigs[ncnt],
1020 alan[nrj][an].annotations.length);
1025 if (trees.get(nrj).size() > 0)
1027 for (NewickFile nf : trees.get(nrj))
1029 // TODO: process each newick file, lifting over sequence refs to
1030 // current alignment, if necessary.
1032 "Tree recovery from restjob not yet implemented.");
1037 } // end of vseps loops.
1038 if (visAlAn != null)
1040 for (AlignmentAnnotation v : visAlAn)
1042 destAls.get(als).addAnnotation(v);
1045 if (visgrps != null)
1047 for (SequenceGroup sg : visgrps)
1049 destAls.get(als).addGroup(sg);
1052 } while (++als < numAlSets);
1053 // Finally, assemble each new alignment, and create new gui components to
1056 * current AlignFrame where results will go.
1058 AlignFrame destaf = restClient.recoverAlignFrameForView();
1060 * current pane being worked with
1062 jalview.gui.AlignmentPanel destPanel = restClient
1063 .recoverAlignPanelForView();
1065 for (AddDataTo action : resultDest)
1068 HiddenColumns destcs;
1069 String alTitle = MessageManager
1070 .formatMessage("label.webservice_job_title_on", new String[]
1071 { restClient.service.details.getAction(),
1072 restClient.service.details.getName(),
1073 restClient.viewTitle });
1077 destal = destAls.get(als);
1078 destcs = destColsel.get(als);
1079 destaf = new AlignFrame(destal, destcs, AlignFrame.DEFAULT_WIDTH,
1080 AlignFrame.DEFAULT_HEIGHT);
1081 PaintRefresher.Refresh(destaf,
1082 destaf.getViewport().getSequenceSetId());
1083 // todo transfer any feature settings and colouring
1085 * destaf.getFeatureRenderer().transferSettings(this.featureSettings);
1086 * // update orders if (alorders.size() > 0) { if (alorders.size() == 1)
1087 * { af.addSortByOrderMenuItem(WebServiceName + " Ordering",
1088 * (AlignmentOrder) alorders.get(0)); } else { // construct a
1089 * non-redundant ordering set Vector names = new Vector(); for (int i =
1090 * 0, l = alorders.size(); i < l; i++) { String orderName = new
1091 * String(" Region " + i); int j = i + 1;
1093 * while (j < l) { if (((AlignmentOrder) alorders.get(i))
1094 * .equals(((AlignmentOrder) alorders.get(j)))) { alorders.remove(j);
1095 * l--; orderName += "," + j; } else { j++; } }
1097 * if (i == 0 && j == 1) { names.add(new String("")); } else {
1098 * names.add(orderName); } } for (int i = 0, l = alorders.size(); i < l;
1099 * i++) { af.addSortByOrderMenuItem( WebServiceName + ((String)
1100 * names.get(i)) + " Ordering", (AlignmentOrder) alorders.get(i)); } } }
1102 // TODO: modify this and previous alignment's title if many alignments
1103 // have been returned.
1104 Desktop.addInternalFrame(destaf, alTitle, AlignFrame.DEFAULT_WIDTH,
1105 AlignFrame.DEFAULT_HEIGHT);
1109 // TODO: determine title for view
1117 if (restClient.isShowResultsInNewView())
1119 // destPanel = destPanel.alignFrame.newView(false);
1127 * if (als) // add the destination panel to frame zero of result panel set }
1128 * } if (destPanels.size()==0) { AlignFrame af = new AlignFrame((AlignmentI)
1129 * idat[0], (ColumnSelection) idat[1], AlignFrame.DEFAULT_WIDTH,
1130 * AlignFrame.DEFAULT_HEIGHT);
1132 * jalview.gui.Desktop.addInternalFrame(af, "Results for " +
1133 * restClient.service.details.Name + " " + restClient.service.details.Action
1134 * + " on " + restClient.af.getTitle(), AlignFrame.DEFAULT_WIDTH,
1135 * AlignFrame.DEFAULT_HEIGHT); destPanel = af.alignPanel; // create totally
1136 * new alignment from stashed data/results
1143 * alignments. New alignments are added to dataset, and subsequently
1144 * annotated/visualised accordingly. 1. New alignment frame created for new
1145 * alignment. Decide if any vis settings should be inherited from old
1146 * alignment frame (e.g. sequence ordering ?). 2. Subsequent data added to
1147 * alignment as below:
1150 * annotation update to original/newly created context alignment: 1.
1151 * identify alignment where annotation is to be loaded onto. 2. Add
1152 * annotation, excluding any duplicates. 3. Ensure annotation is visible on
1153 * alignment - honouring ordering given by file.
1156 * features updated to original or newly created context alignment: 1.
1157 * Features are(or were already) added to dataset. 2. Feature settings
1158 * modified to ensure all features are displayed - honouring any ordering
1159 * given by result file. Consider merging action with the code used by the
1160 * DAS fetcher to update alignment views with new info.
1163 * Seq associated data files (PDB files). 1. locate seq association in
1164 * current dataset/alignment context and add file as normal - keep handle of
1165 * any created ref objects. 2. decide if new data should be displayed : PDB
1166 * display: if alignment has PDB display already, should new pdb files be
1170 // destPanel.adjustAnnotationHeight();
1175 * split the given array of sequences into blocks of subsequences
1176 * corresponding to each visible contig
1181 * padding character for ragged ends of visible contig region.
1184 private SequenceI[][] splitSeqsOnVisibleContigs(SequenceI[] sequenceIs,
1185 int[] contigs, char gapChar)
1187 int nvc = contigs == null ? 1 : contigs.length / 2;
1188 SequenceI[][] blocks = new SequenceI[nvc][];
1189 if (contigs == null)
1191 blocks[0] = new SequenceI[sequenceIs.length];
1192 System.arraycopy(sequenceIs, 0, blocks[0], 0, sequenceIs.length);
1196 // deja vu - have I written this before ?? propagateGaps does this in a
1198 char[] gapset = null;
1199 int start = 0, width = 0;
1200 for (int c = 0; c < nvc; c++)
1202 width = contigs[c * 2 + 1] - contigs[c * 2] + 1;
1203 for (int s = 0; s < sequenceIs.length; s++)
1205 int end = sequenceIs[s].getLength();
1208 if (start + width < end)
1210 blocks[c][s] = sequenceIs[s].getSubSequence(start,
1215 blocks[c][s] = sequenceIs[s].getSubSequence(start, end);
1216 String sq = blocks[c][s].getSequenceAsString();
1217 for (int n = start + width; n > end; n--)
1225 if (gapset == null || gapset.length < width)
1227 char ng[] = new char[width];
1231 System.arraycopy(gapset, 0, ng, 0, gs = gapset.length);
1233 while (gs < ng.length)
1238 blocks[c][s] = sequenceIs[s].getSubSequence(end, end);
1239 blocks[c][s].setSequence(gapset.toString().substring(0, width));
1244 // adjust window for next visible segnment
1245 start += contigs[c * 2 + 1] - contigs[c * 2];
1253 * recover corresponding sequence from original input data corresponding to
1254 * sequence in a specific job's input data.
1262 private SequenceI getNewSeq(SequenceI oseq, SequenceI[] sequenceIs,
1263 int[] is, AlignmentI destAl)
1266 while (p < sequenceIs.length && sequenceIs[p] != oseq)
1270 if (p < sequenceIs.length && p < destAl.getHeight())
1272 return destAl.getSequenceAt(is[p]);
1279 * @return true if the run method is safe to call
1281 public boolean isValid()
1283 ArrayList<String> _warnings = new ArrayList<String>();
1284 boolean validt = true;
1287 for (RestJob rj : (RestJob[]) jobs)
1289 if (!rj.hasValidInput())
1291 // invalid input for this job
1292 System.err.println("Job " + rj.getJobnum()
1293 + " has invalid input. ( " + rj.getStatus() + ")");
1294 if (rj.hasStatus() && !_warnings.contains(rj.getStatus()))
1296 _warnings.add(rj.getStatus());
1305 for (String st : _warnings)
1307 if (warnings.length() > 0)
1318 protected String warnings;
1320 public boolean hasWarnings()
1322 // TODO Auto-generated method stub
1323 return warnings != null && warnings.length() > 0;
1327 * get any informative messages about why the job thread couldn't start.
1331 public String getWarnings()
1333 return isValid() ? "Job can be started. No warnings."
1334 : hasWarnings() ? warnings : "";