X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fws%2Frest%2FRestJobThread.java;h=f32535c056f0a826a7f50250f996513f77a24996;hb=cb8e52fbbc5f725e3f7f48c672cdddb0690bd978;hp=c100a72034e0cff84eb0a1433994c0d91bb24767;hpb=9e28d56706fbc90d588b7089532af55077c5762e;p=jalview.git diff --git a/src/jalview/ws/rest/RestJobThread.java b/src/jalview/ws/rest/RestJobThread.java index c100a72..f32535c 100644 --- a/src/jalview/ws/rest/RestJobThread.java +++ b/src/jalview/ws/rest/RestJobThread.java @@ -1,24 +1,52 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ package jalview.ws.rest; -import jalview.bin.Cache; +import java.util.Locale; + +import jalview.bin.Console; +import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; -import jalview.datamodel.ColumnSelection; +import jalview.datamodel.AlignmentOrder; +import jalview.datamodel.Annotation; +import jalview.datamodel.HiddenColumns; +import jalview.datamodel.SequenceGroup; +import jalview.datamodel.SequenceI; import jalview.gui.AlignFrame; +import jalview.gui.Desktop; +import jalview.gui.PaintRefresher; import jalview.gui.WebserviceInfo; -import jalview.io.packed.DataProvider; +import jalview.io.NewickFile; import jalview.io.packed.JalviewDataset; import jalview.io.packed.JalviewDataset.AlignmentSet; -import jalview.io.packed.ParsePackedSet; -import jalview.io.packed.SimpleDataProvider; -import jalview.io.packed.DataProvider.JvDataType; +import jalview.util.MessageManager; import jalview.ws.AWSThread; import jalview.ws.AWsJob; -import java.awt.Desktop; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.util.ArrayList; +import java.util.Hashtable; import java.util.List; import java.util.Map.Entry; @@ -44,7 +72,7 @@ public class RestJobThread extends AWSThread SUBMIT, POLL } - protected RestClient restClient;; + protected RestClient restClient; public RestJobThread(RestClient restClient) { @@ -64,8 +92,8 @@ public class RestJobThread extends AWSThread { jobs = new RestJob[1]; jobs[0] = new RestJob(0, this, - restClient._input.getVisibleAlignment(restClient.service - .getGapCharacter()), + restClient._input.getVisibleAlignment( + restClient.service.getGapCharacter()), restClient._input.getVisibleContigs()); // need a function to get a range on a view/alignment and return both // annotation, groups and selection subsetted to just that region. @@ -75,15 +103,14 @@ public class RestJobThread extends AWSThread { int[] viscontig = restClient._input.getVisibleContigs(); AlignmentI[] viscontigals = restClient._input - .getVisibleContigAlignments(restClient.service - .getGapCharacter()); + .getVisibleContigAlignments( + restClient.service.getGapCharacter()); if (viscontigals != null && viscontigals.length > 0) { jobs = new RestJob[viscontigals.length]; for (int j = 0; j < jobs.length; j++) { - int[] visc = new int[] - { viscontig[j * 2], viscontig[j * 2 + 1] }; + int[] visc = new int[] { viscontig[j * 2], viscontig[j * 2 + 1] }; if (j != 0) { jobs[j] = new RestJob(j, this, viscontigals[j], visc); @@ -148,9 +175,13 @@ public class RestJobThread extends AWSThread private String getStage(Stage stg) { if (stg == Stage.SUBMIT) + { return "submitting "; + } if (stg == Stage.POLL) + { return "checking status of "; + } return (" being confused about "); } @@ -170,6 +201,7 @@ public class RestJobThread extends AWSThread { String postUrl = rj.getPostUrl(); doHttpReq(Stage.SUBMIT, rj, postUrl); + wsInfo.invalidate(); } /** @@ -203,8 +235,8 @@ public class RestJobThread extends AWSThread { if (input.getValue().validFor(rj)) { - postentity.addPart(input.getKey(), input.getValue() - .formatForInput(rj)); + postentity.addPart(input.getKey(), + input.getValue().formatForInput(rj)); } else { @@ -230,18 +262,22 @@ public class RestJobThread extends AWSThread response = httpclient.execute(request); } catch (ClientProtocolException he) { - rj.statMessage = "Web Protocol Exception when attempting to " - + getStage(stg) + "Job. See Console output for details."; + rj.statMessage = "Web Protocol Exception when " + getStage(stg) + + "Job.
Problematic url was " + request.getURI() + + "
See Console output for details."; rj.setAllowedServerExceptions(0);// unrecoverable; rj.error = true; - Cache.log.fatal("Unexpected REST Job " + getStage(stg) + Console.fatal("Unexpected REST Job " + getStage(stg) + "exception for URL " + rj.rsd.postUrl); throw (he); } catch (IOException e) { - rj.statMessage = "IO Exception when attempting to " - + getStage(stg) + "Job. See Console output for details."; - Cache.log.warn("IO Exception for REST Job " + getStage(stg) + rj.statMessage = "IO Exception when " + getStage(stg) + + "Job.
Problematic url was " + request.getURI() + + "
See Console output for details."; + Console.warn("IO Exception for REST Job " + getStage(stg) + "exception for URL " + rj.rsd.postUrl); throw (e); @@ -250,15 +286,12 @@ public class RestJobThread extends AWSThread { case 200: rj.running = false; - Cache.log.debug("Processing result set."); + Console.debug("Processing result set."); processResultSet(rj, response, request); break; case 202: rj.statMessage = "
Job submitted successfully. Results available at this URL:\n" - + "" - + rj.getJobId() + + "" + rj.getJobId() + "
"; rj.running = true; break; @@ -271,11 +304,9 @@ public class RestJobThread extends AWSThread { if (loc.length > 1) { - Cache.log - .warn("Ignoring additional " - + (loc.length - 1) - + " location(s) provided in response header ( next one is '" - + loc[1].getValue() + "' )"); + Console.warn("Ignoring additional " + (loc.length - 1) + + " location(s) provided in response header ( next one is '" + + loc[1].getValue() + "' )"); } rj.setJobId(loc[0].getValue()); rj.setSubmitted(true); @@ -289,20 +320,27 @@ public class RestJobThread extends AWSThread rj.setSubjobComplete(true); rj.error = true; rj.running = false; - completeStatus(rj, response, "" + getStage(stg) - + "failed. Reason below:\n"); + completeStatus(rj, response, + "" + getStage(stg) + "failed. Reason below:\n"); break; default: // Some other response. Probably need to pop up the content in a window. // TODO: deal with all other HTTP response codes from server. - Cache.log.warn("Unhandled response status when " + getStage(stg) + Console.warn("Unhandled response status when " + getStage(stg) + "for " + postUrl + ": " + response.getStatusLine()); + rj.error = true; + rj.setAllowedServerExceptions(0); + rj.setSubjobComplete(true); + rj.setSubmitted(true); try { - response.getEntity().consumeContent(); + completeStatus(rj, response, "" + getStage(stg) + + " resulted in an unexpected server response.
Url concerned was " + request.getURI() + + "
Filtered response content below:
"); } catch (IOException e) { - Cache.log.debug("IOException when consuming unhandled response", + Console.debug("IOException when consuming unhandled response", e); } ; @@ -365,11 +403,11 @@ public class RestJobThread extends AWSThread */ String f; StringBuffer content = new StringBuffer(f = EntityUtils.toString(en)); - f = f.toLowerCase(); + f = f.toLowerCase(Locale.ROOT); int body = f.indexOf(" -1) { - content.delete(0, f.indexOf(">", body)); + content.delete(0, f.indexOf(">", body) + 1); } if (body > -1 && sb.length() > 0) { @@ -397,11 +435,19 @@ public class RestJobThread extends AWSThread { System.err.println("Debug RestJob: Posting Job"); doPost((RestJob) job); + } catch (NoValidInputDataException erex) + { + job.setSubjobComplete(true); + job.setSubmitted(true); + ((RestJob) job).statMessage = "
It looks like there was a problem with the data sent to the service :
" + + erex.getMessage() + "\n"; + ((RestJob) job).error = true; + } catch (Exception ex) { job.setSubjobComplete(true); job.setAllowedServerExceptions(-1); - Cache.log.error("Exception when trying to start Rest Job.", ex); + Console.error("Exception when trying to start Rest Job.", ex); } } @@ -410,7 +456,7 @@ public class RestJobThread extends AWSThread { // crazy users will see this message // TODO: finish this! and remove the message below! - Cache.log.warn("Rest job result parser is currently INCOMPLETE!"); + Console.warn("Rest job result parser is currently INCOMPLETE!"); int validres = 0; for (RestJob rj : (RestJob[]) jobs) { @@ -419,24 +465,30 @@ public class RestJobThread extends AWSThread String ln = null; try { - Cache.log.debug("Parsing data for job " + rj.getJobId()); + Console.debug("Parsing data for job " + rj.getJobId()); rj.parseResultSet(); if (rj.hasResults()) { validres++; } - Cache.log.debug("Finished parsing data for job " + rj.getJobId()); + Console.debug("Finished parsing data for job " + rj.getJobId()); } catch (Error ex) { - Cache.log.warn("Failed to finish parsing data for job " - + rj.getJobId()); + Console.warn( + "Failed to finish parsing data for job " + rj.getJobId()); ex.printStackTrace(); } catch (Exception ex) { - Cache.log.warn("Failed to finish parsing data for job " - + rj.getJobId()); + Console.warn( + "Failed to finish parsing data for job " + rj.getJobId()); ex.printStackTrace(); + } finally + { + rj.error = true; + rj.statMessage = "Error whilst parsing data for this job.
URL for job response is :" + rj.resSet.getUrl() + + "
"; } } } @@ -452,7 +504,8 @@ public class RestJobThread extends AWSThread */ if (true) { - wsInfo.setViewResultsImmediatly(false); + // preserver current jalview behaviour + wsInfo.setViewResultsImmediatly(true); } else { @@ -488,9 +541,32 @@ public class RestJobThread extends AWSThread else { // tell the user nothing was returned. + wsInfo.setStatus(wsInfo.STATE_STOPPED_ERROR); + wsInfo.setFinishedNoResults(); } } + /** + * instructions for whether to create new alignment view on current alignment + * set, add to current set, or create new alignFrame + */ + private enum AddDataTo + { + /** + * add annotation, trees etc to current view + */ + currentView, + /** + * create a new view derived from current view and add data to that + */ + newView, + /** + * create a new alignment frame for the result set and add annotation to + * that. + */ + newAlignment + }; + public void realiseResults(boolean merge) { /* @@ -499,139 +575,683 @@ public class RestJobThread extends AWSThread * annotation/features. */ /** - * alignment panels derived from each alignment set returned by service. + * alignment panels derived from each alignment set returned by service. */ ArrayList destPanels = new ArrayList(); /** - * current pane being worked with + * list of instructions for how to process each distinct alignment set + * returned by the job set */ - jalview.gui.AlignmentPanel destPanel; + ArrayList resultDest = new ArrayList(); /** * when false, zeroth pane is panel derived from input deta. */ boolean newAlignment = false; - if (merge) - { - if (!restClient.isAlignmentModified()) - { - destPanel = restClient.recoverAlignPanelForView(); - if (restClient.isShowResultsInNewView()) - { - destPanel = destPanel.alignFrame.newView(false); - } - // add the destination panel to frame zero of result panel set - destPanels.add(destPanel); - } - } - if (destPanels.size()==0) - { - Object[] idat = input.getAlignmentAndColumnSelection(restClient.av - .getGapCharacter()); - AlignFrame af = new AlignFrame((AlignmentI) idat[0], - (ColumnSelection) idat[1], AlignFrame.DEFAULT_WIDTH, - AlignFrame.DEFAULT_HEIGHT); - jalview.gui.Desktop.addInternalFrame(af, - "Results for " + restClient.service.details.Name + " " - + restClient.service.details.Action + " on " - + restClient.af.getTitle(), AlignFrame.DEFAULT_WIDTH, - AlignFrame.DEFAULT_HEIGHT); - destPanel = af.alignPanel; - // create totally new alignment from stashed data/results - newAlignment = true; - } - // Now process results, adding/creating new views as necessary. + /** + * gap character to be used for alignment reconstruction + */ + char gapCharacter = restClient.av.getGapCharacter(); + // Now, iterate over all alignment sets returned from all jobs: + // first inspect jobs and collate results data in order to count alignments + // and other results + // then assemble results from decomposed (v followed by h-separated) jobs + // finally, new views and alignments will be created and displayed as + // necessary. + boolean hsepjobs = restClient.service.isHseparable(); + boolean vsepjobs = restClient.service.isVseparable(); + // total number of distinct alignment sets generated by job set. + int numAlSets = 0, als = 0; + List destAls = new ArrayList(); + List destColsel = new ArrayList(); + List> trees = new ArrayList>(); + + do { - boolean hsepjobs = restClient.service.isHseparable(); - boolean vsepjobs = restClient.service.isVseparable(); - // total number of distinct alignment sets generated by job set. - int totalSets = 0, numAlSets = 0; + // Step 1. + // iterate over each alignment set returned from each subjob. Treating + // each one returned in parallel with others. + // Result collation arrays + + /** + * mapping between index of sequence in alignment that was submitted to + * server and index of sequence in the input alignment + */ + int[][] ordermap = new int[jobs.length][]; + SequenceI[][] rseqs = new SequenceI[jobs.length][]; + AlignmentOrder[] orders = new AlignmentOrder[jobs.length]; + AlignmentAnnotation[][] alan = new AlignmentAnnotation[jobs.length][]; + SequenceGroup[][] sgrp = new SequenceGroup[jobs.length][]; + // Now collect all data for alignment Set als from job array for (int j = 0; j < jobs.length; j++) { RestJob rj = (RestJob) jobs[j]; if (rj.hasResults()) { JalviewDataset rset = rj.context; - numAlSets = rset.hasAlignments() ? 0 : rset.getAl().size(); - if (numAlSets > 0) + if (rset.hasAlignments()) + { + if (numAlSets < rset.getAl().size()) + { + numAlSets = rset.getAl().size(); + } + if (als < rset.getAl().size() + && rset.getAl().get(als).isModified()) + { + // Collate result data + // TODO: decide if all alignmentI should be collected rather than + // specific alignment data containers + // for moment, we just extract content, but this means any + // alignment properties may be lost. + AlignmentSet alset = rset.getAl().get(als); + alan[j] = alset.al.getAlignmentAnnotation(); + if (alset.al.getGroups() != null) + { + sgrp[j] = new SequenceGroup[alset.al.getGroups().size()]; + alset.al.getGroups().toArray(sgrp[j]); + } + else + { + sgrp[j] = null; + } + orders[j] = new AlignmentOrder(alset.al); + rseqs[j] = alset.al.getSequencesArray(); + ordermap[j] = rj.getOrderMap(); + // if (rj.isInputUniquified()) { + // jalview.analysis.AlignmentSorter.recoverOrder(rseqs[als]); + // } + + if (alset.trees != null) + { + trees.add(new ArrayList(alset.trees)); + } + else + { + trees.add(new ArrayList()); + } + } + } + } + } + // Now aggregate and present results from this frame of alignment data. + int nvertsep = 0, nvertseps = 1; + if (vsepjobs) + { + // Jobs relate to different rows of input alignment. + // Jobs are subdivided by rows before columns, + // so there will now be a number of subjobs according tohsep for each + // vertsep + // TODO: get vertical separation intervals for each job and set + // nvertseps + // TODO: merge data from each group/sequence onto whole + // alignment + } + /** + * index into rest jobs subdivided vertically + */ + int vrestjob = 0; + // Destination alignments for all result data. + ArrayList visgrps = new ArrayList(); + Hashtable groupNames = new Hashtable(); + ArrayList visAlAn = null; + for (nvertsep = 0; nvertsep < nvertseps; nvertsep++) + { + // TODO: set scope w.r.t. original alignment view for vertical + // separation. + { + // results for a job exclude hidden columns of input data, so map + // back on to all visible regions + /** + * rest job result we are working with + */ + int nrj = vrestjob; + + RestJob rj = (RestJob) jobs[nrj]; + int contigs[] = input.getVisibleContigs(); + AlignmentI destAl = null; + jalview.datamodel.HiddenColumns destHCs = null; + // Resolve destAl for this data. + if (als == 0 && rj.isInputContextModified()) { - for (int als = 0; als < numAlSets; als++) + // special case: transfer features, annotation, groups, etc, + // from input + // context to align panel derived from input data + if (destAls.size() > als) { - // gather data from context - if (vsepjobs) + destAl = destAls.get(als); + } + else + { + if (!restClient.isAlignmentModified() && merge) + { + destAl = restClient.av.getAlignment(); + destHCs = restClient.av.getAlignment().getHiddenColumns(); + resultDest.add(restClient.isShowResultsInNewView() + ? AddDataTo.newView + : AddDataTo.currentView); + destPanels.add(restClient.recoverAlignPanelForView()); + } + else + { + newAlignment = true; + // recreate the input alignment data + Object[] idat = input + .getAlignmentAndHiddenColumns(gapCharacter); + destAl = new Alignment((SequenceI[]) idat[0]); + destHCs = (HiddenColumns) idat[1]; + resultDest.add(AddDataTo.newAlignment); + // but do not add to the alignment panel list - since we need to + // create a whole new alignFrame set. + } + destAls.add(destAl); + destColsel.add(destHCs); + } + } + else + { + // alignment(s) returned by service is to be re-integrated and + // displayed + if (destAls.size() > als) + { + if (!vsepjobs) + { + // TODO: decide if multiple multiple alignments returned by + // non-vseparable services are allowed. + Console.warn( + "dealing with multiple alignment products returned by non-vertically separable service."); + } + // recover reference to last alignment created for this rest frame + // ready for extension + destAl = destAls.get(als); + destHCs = destColsel.get(als); + } + else + { + Object[] newview; + + if (!hsepjobs) { - // todo: merge data from each group/sequence onto whole - // alignment + // single alignment for any job that gets mapped back on to + // input data. Reconstruct by interleaving parts of returned + // alignment with hidden parts of input data. + SequenceI[][] nsq = splitSeqsOnVisibleContigs(rseqs[nrj], + contigs, gapCharacter); + AlignmentOrder alo[] = new AlignmentOrder[nsq.length]; + for (int no = 0; no < alo.length; no++) + { + alo[no] = new AlignmentOrder(orders[nrj].getOrder()); + } + newview = input.getUpdatedView(nsq, orders, gapCharacter); } else { - if (hsepjobs) + // each job maps to a single visible contig, and all need to be + // stitched back together. + // reconstruct using sub-region based MSA alignment construction + // mechanism + newview = input.getUpdatedView(rseqs, orders, gapCharacter); + } + destAl = new Alignment((SequenceI[]) newview[0]); + destHCs = (HiddenColumns) newview[1]; + newAlignment = true; + // TODO create alignment from result data with propagated + // references. + destAls.add(destAl); + destColsel.add(destHCs); + resultDest.add(AddDataTo.newAlignment); + throw new Error( + MessageManager.getString("error.implementation_error") + + "TODO: "); + } + } + /** + * save initial job in this set in case alignment is h-separable + */ + int initnrj = nrj; + // Now add in groups + for (int ncnt = 0; ncnt < contigs.length; ncnt += 2) + { + if (!hsepjobs) + { + // single alignment for any job that gets mapped back on to input + // data. + } + else + { + // each job maps to a single visible contig, and all need to be + // stitched back together. + if (ncnt > 0) + { + nrj++; + } + // TODO: apply options for group merging and annotation merging. + // If merging not supported, then either clear hashtables now or + // use them to rename the new annotation/groups for each contig if + // a conflict occurs. + } + if (sgrp[nrj] != null) + { + for (SequenceGroup sg : sgrp[nrj]) + { + boolean recovered = false; + SequenceGroup exsg = null; + if (sg.getName() != null) { - // map single result back on to all visible region of original alignment - if (als==0 && rj.isInputContextModified()) + exsg = groupNames.get(sg.getName()); + } + if (exsg == null) + { + exsg = new SequenceGroup(sg); + groupNames.put(exsg.getName(), exsg); + visgrps.add(exsg); + exsg.setStartRes(sg.getStartRes() + contigs[ncnt]); + exsg.setEndRes(sg.getEndRes() + contigs[ncnt]); + } + else + { + recovered = true; + } + // now replace any references from the result set with + // corresponding refs from alignment input set. + + // TODO: cope with recovering hidden sequences from + // resultContext + { + for (SequenceI oseq : sg.getSequences(null)) + { + SequenceI nseq = getNewSeq(oseq, rseqs[nrj], + ordermap[nrj], destAl); + if (nseq != null) + { + if (!recovered) + { + exsg.deleteSequence(oseq, false); + } + exsg.addSequence(nseq, false); + } + else + { + Console.warn( + "Couldn't resolve original sequence for new sequence."); + } + } + if (sg.hasSeqrep()) { - // transfer features, annotation, groups, etc, from input context to align panel derived from input data - new jalview.datamodel.Alignment(new jalview.datamodel.SequenceI[] {null}).getAlignmentAnnotation(); + if (exsg.getSeqrep() == sg.getSeqrep()) + { + // lift over sequence rep reference too + SequenceI oseq = sg.getSeqrep(); + SequenceI nseq = getNewSeq(oseq, rseqs[nrj], + ordermap[nrj], destAl); + if (nseq != null) + { + exsg.setSeqrep(nseq); + } + } } - } - else + if (recovered) { - // map result onto visible contigs. - AlignmentSet alset = rset.getAl().get(als); - if (als>0 ) - if (als == 0) + // adjust boundaries of recovered group w.r.t. new group being + // merged on to original alignment. + int start = sg.getStartRes() + contigs[ncnt], + end = sg.getEndRes() + contigs[ncnt]; + if (start < exsg.getStartRes()) + { + exsg.setStartRes(start); + } + if (end > exsg.getEndRes()) { - - // alignment is added straight to + exsg.setEndRes(end); } } } } } + // reset job counter + nrj = initnrj; + // and finally add in annotation and any trees for each job + for (int ncnt = 0; ncnt < contigs.length; ncnt += 2) + { + if (!hsepjobs) + { + // single alignment for any job that gets mapped back on to input + // data. + } + else + { + // each job maps to a single visible contig, and all need to be + // stitched back together. + if (ncnt > 0) + { + nrj++; + } + } + + // merge alignmentAnnotation into one row + if (alan[nrj] != null) + { + for (int an = 0; an < alan[nrj].length; an++) + { + SequenceI sqass = null; + SequenceGroup grass = null; + if (alan[nrj][an].sequenceRef != null) + { + // TODO: ensure this relocates sequence reference to local + // context. + sqass = getNewSeq(alan[nrj][an].sequenceRef, rseqs[nrj], + ordermap[nrj], destAl); + } + if (alan[nrj][an].groupRef != null) + { + // TODO: verify relocate group reference to local context + // works correctly + grass = groupNames.get(alan[nrj][an].groupRef.getName()); + if (grass == null) + { + Console.error( + "Couldn't relocate group referemce for group " + + alan[nrj][an].groupRef.getName()); + } + } + if (visAlAn == null) + { + visAlAn = new ArrayList(); + } + AlignmentAnnotation visan = null; + for (AlignmentAnnotation v : visAlAn) + { + if (v.label != null + && v.label.equals(alan[nrj][an].label)) + { + visan = v; + } + } + if (visan == null) + { + visan = new AlignmentAnnotation(alan[nrj][an]); + // copy annotations, and wipe out/update refs. + visan.annotations = new Annotation[input.getWidth()]; + visan.groupRef = grass; + visan.sequenceRef = sqass; + visAlAn.add(visan); + } + if (contigs[ncnt] + + alan[nrj][an].annotations.length > visan.annotations.length) + { + // increase width of annotation row + Annotation[] newannv = new Annotation[contigs[ncnt] + + alan[nrj][an].annotations.length]; + System.arraycopy(visan.annotations, 0, newannv, 0, + visan.annotations.length); + visan.annotations = newannv; + } + // now copy local annotation data into correct position + System.arraycopy(alan[nrj][an].annotations, 0, + visan.annotations, contigs[ncnt], + alan[nrj][an].annotations.length); + + } + } + // Trees + if (trees.get(nrj).size() > 0) + { + for (NewickFile nf : trees.get(nrj)) + { + // TODO: process each newick file, lifting over sequence refs to + // current alignment, if necessary. + Console.error( + "Tree recovery from restjob not yet implemented."); + } + } + } + } + } // end of vseps loops. + if (visAlAn != null) + { + for (AlignmentAnnotation v : visAlAn) + { + destAls.get(als).addAnnotation(v); + } + } + if (visgrps != null) + { + for (SequenceGroup sg : visgrps) + { + destAls.get(als).addGroup(sg); } - // transfer results onto panel + } + } while (++als < numAlSets); + // Finally, assemble each new alignment, and create new gui components to + // present it. + /** + * current AlignFrame where results will go. + */ + AlignFrame destaf = restClient.recoverAlignFrameForView(); + /** + * current pane being worked with + */ + jalview.gui.AlignmentPanel destPanel = restClient + .recoverAlignPanelForView(); + als = 0; + for (AddDataTo action : resultDest) + { + AlignmentI destal; + HiddenColumns destcs; + String alTitle = MessageManager + .formatMessage("label.webservice_job_title_on", new String[] + { restClient.service.details.Action, + restClient.service.details.Name, restClient.viewTitle }); + switch (action) + { + case newAlignment: + destal = destAls.get(als); + destcs = destColsel.get(als); + destaf = new AlignFrame(destal, destcs, AlignFrame.DEFAULT_WIDTH, + AlignFrame.DEFAULT_HEIGHT); + PaintRefresher.Refresh(destaf, + destaf.getViewport().getSequenceSetId()); + // todo transfer any feature settings and colouring + /* + * destaf.getFeatureRenderer().transferSettings(this.featureSettings); + * // update orders if (alorders.size() > 0) { if (alorders.size() == 1) + * { af.addSortByOrderMenuItem(WebServiceName + " Ordering", + * (AlignmentOrder) alorders.get(0)); } else { // construct a + * non-redundant ordering set Vector names = new Vector(); for (int i = + * 0, l = alorders.size(); i < l; i++) { String orderName = new + * String(" Region " + i); int j = i + 1; + * + * while (j < l) { if (((AlignmentOrder) alorders.get(i)) + * .equals(((AlignmentOrder) alorders.get(j)))) { alorders.remove(j); + * l--; orderName += "," + j; } else { j++; } } + * + * if (i == 0 && j == 1) { names.add(new String("")); } else { + * names.add(orderName); } } for (int i = 0, l = alorders.size(); i < l; + * i++) { af.addSortByOrderMenuItem( WebServiceName + ((String) + * names.get(i)) + " Ordering", (AlignmentOrder) alorders.get(i)); } } } + */ + // TODO: modify this and previous alignment's title if many alignments + // have been returned. + Desktop.addInternalFrame(destaf, alTitle, AlignFrame.DEFAULT_WIDTH, + AlignFrame.DEFAULT_HEIGHT); + break; + case newView: + // TODO: determine title for view + break; + case currentView: + break; } - /** - * alignments. New alignments are added to dataset, and subsequently - * annotated/visualised accordingly. 1. New alignment frame created for - * new alignment. Decide if any vis settings should be inherited from old - * alignment frame (e.g. sequence ordering ?). 2. Subsequent data added to - * alignment as below: - */ - /** - * annotation update to original/newly created context alignment: 1. - * identify alignment where annotation is to be loaded onto. 2. Add - * annotation, excluding any duplicates. 3. Ensure annotation is visible - * on alignment - honouring ordering given by file. - */ - /** - * features updated to original or newly created context alignment: 1. - * Features are(or were already) added to dataset. 2. Feature settings - * modified to ensure all features are displayed - honouring any ordering - * given by result file. Consider merging action with the code used by the - * DAS fetcher to update alignment views with new info. - */ - /** - * Seq associated data files (PDB files). 1. locate seq association in - * current dataset/alignment context and add file as normal - keep handle - * of any created ref objects. 2. decide if new data should be displayed : - * PDB display: if alignment has PDB display already, should new pdb files - * be aligned to it ? - * - */ + } + if (!newAlignment) + { + if (restClient.isShowResultsInNewView()) + { + // destPanel = destPanel.alignFrame.newView(false); + } + } + else + { } + /* + * if (als) // add the destination panel to frame zero of result panel set } + * } if (destPanels.size()==0) { AlignFrame af = new AlignFrame((AlignmentI) + * idat[0], (ColumnSelection) idat[1], AlignFrame.DEFAULT_WIDTH, + * AlignFrame.DEFAULT_HEIGHT); + * + * jalview.gui.Desktop.addInternalFrame(af, "Results for " + + * restClient.service.details.Name + " " + restClient.service.details.Action + * + " on " + restClient.af.getTitle(), AlignFrame.DEFAULT_WIDTH, + * AlignFrame.DEFAULT_HEIGHT); destPanel = af.alignPanel; // create totally + * new alignment from stashed data/results + */ + + /* + */ + + /** + * alignments. New alignments are added to dataset, and subsequently + * annotated/visualised accordingly. 1. New alignment frame created for new + * alignment. Decide if any vis settings should be inherited from old + * alignment frame (e.g. sequence ordering ?). 2. Subsequent data added to + * alignment as below: + */ + /** + * annotation update to original/newly created context alignment: 1. + * identify alignment where annotation is to be loaded onto. 2. Add + * annotation, excluding any duplicates. 3. Ensure annotation is visible on + * alignment - honouring ordering given by file. + */ + /** + * features updated to original or newly created context alignment: 1. + * Features are(or were already) added to dataset. 2. Feature settings + * modified to ensure all features are displayed - honouring any ordering + * given by result file. Consider merging action with the code used by the + * DAS fetcher to update alignment views with new info. + */ + /** + * Seq associated data files (PDB files). 1. locate seq association in + * current dataset/alignment context and add file as normal - keep handle of + * any created ref objects. 2. decide if new data should be displayed : PDB + * display: if alignment has PDB display already, should new pdb files be + * aligned to it ? + * + */ // destPanel.adjustAnnotationHeight(); } /** + * split the given array of sequences into blocks of subsequences + * corresponding to each visible contig + * + * @param sequenceIs + * @param contigs + * @param gapChar + * padding character for ragged ends of visible contig region. + * @return + */ + private SequenceI[][] splitSeqsOnVisibleContigs(SequenceI[] sequenceIs, + int[] contigs, char gapChar) + { + int nvc = contigs == null ? 1 : contigs.length / 2; + SequenceI[][] blocks = new SequenceI[nvc][]; + if (contigs == null) + { + blocks[0] = new SequenceI[sequenceIs.length]; + System.arraycopy(sequenceIs, 0, blocks[0], 0, sequenceIs.length); + } + else + { + // deja vu - have I written this before ?? propagateGaps does this in a + // way + char[] gapset = null; + int start = 0, width = 0; + for (int c = 0; c < nvc; c++) + { + width = contigs[c * 2 + 1] - contigs[c * 2] + 1; + for (int s = 0; s < sequenceIs.length; s++) + { + int end = sequenceIs[s].getLength(); + if (start < end) + { + if (start + width < end) + { + blocks[c][s] = sequenceIs[s].getSubSequence(start, + start + width); + } + else + { + blocks[c][s] = sequenceIs[s].getSubSequence(start, end); + String sq = blocks[c][s].getSequenceAsString(); + for (int n = start + width; n > end; n--) + { + sq += gapChar; + } + } + } + else + { + if (gapset == null || gapset.length < width) + { + char ng[] = new char[width]; + int gs = 0; + if (gapset != null) + { + System.arraycopy(gapset, 0, ng, 0, gs = gapset.length); + } + while (gs < ng.length) + { + ng[gs++] = gapChar; + } + } + blocks[c][s] = sequenceIs[s].getSubSequence(end, end); + blocks[c][s].setSequence(gapset.toString().substring(0, width)); + } + } + if (c > 0) + { + // adjust window for next visible segnment + start += contigs[c * 2 + 1] - contigs[c * 2]; + } + } + } + return blocks; + } + + /** + * recover corresponding sequence from original input data corresponding to + * sequence in a specific job's input data. + * + * @param oseq + * @param sequenceIs + * @param is + * @param destAl + * @return + */ + private SequenceI getNewSeq(SequenceI oseq, SequenceI[] sequenceIs, + int[] is, AlignmentI destAl) + { + int p = 0; + while (p < sequenceIs.length && sequenceIs[p] != oseq) + { + p++; + } + if (p < sequenceIs.length && p < destAl.getHeight()) + { + return destAl.getSequenceAt(is[p]); + } + return null; + } + + /** * * @return true if the run method is safe to call */ public boolean isValid() { + ArrayList _warnings = new ArrayList(); + boolean validt = true; if (jobs != null) { for (RestJob rj : (RestJob[]) jobs) @@ -640,14 +1260,48 @@ public class RestJobThread extends AWSThread { // invalid input for this job System.err.println("Job " + rj.getJobnum() - + " has invalid input."); - return false; + + " has invalid input. ( " + rj.getStatus() + ")"); + if (rj.hasStatus() && !_warnings.contains(rj.getStatus())) + { + _warnings.add(rj.getStatus()); + } + validt = false; } } - return true; } + if (!validt) + { + warnings = ""; + for (String st : _warnings) + { + if (warnings.length() > 0) + { + warnings += "\n"; + } + warnings += st; + + } + } + return validt; + } + + protected String warnings; + + public boolean hasWarnings() + { // TODO Auto-generated method stub - return false; + return warnings != null && warnings.length() > 0; + } + + /** + * get any informative messages about why the job thread couldn't start. + * + * @return + */ + public String getWarnings() + { + return isValid() ? "Job can be started. No warnings." + : hasWarnings() ? warnings : ""; } }