package jalview.ws; import java.util.*; import javax.swing.*; import vamsas.objects.simple.JpredResult; import jalview.analysis.*; import jalview.bin.*; import jalview.datamodel.*; import jalview.gui.*; import jalview.io.*; import jalview.util.*; class JPredThread extends WSThread implements WSClientI { // TODO: put mapping between JPredJob input and input data here - JNetAnnotation adding is done after result parsing. class JPredJob extends WSThread.WSJob { // TODO: make JPredJob deal only with what was sent to and received from a JNet service int[] predMap=null; // mapping from sequence(i) to the original sequence(predMap[i]) being predicted on vamsas.objects.simple.Sequence sequence; vamsas.objects.simple.Msfalignment msa; java.util.Hashtable SequenceInfo = null; int msaIndex=0; // the position of the original sequence in the array of Sequences in the input object that this job holds a prediction for /** * * @return true if getResultSet will return a valid alignment and prediction result. */ public boolean hasResults() { if (subjobComplete && result != null && result.isFinished() && ( (JpredResult) result).getPredfile() != null && ( (JpredResult) result).getAligfile() != null) { return true; } return false; } boolean hasValidInput() { if (sequence != null) { return true; } return false; } /** * * @return null or Object[] { annotated alignment for this prediction, ColumnSelection for this prediction} or null if no results available. * @throws Exception */ public Object[] getResultSet() throws Exception { if (result == null || !result.isFinished()) { return null; } Alignment al = null; ColumnSelection alcsel=null; int FirstSeq = -1; // the position of the query sequence in Alignment al JpredResult result = (JpredResult)this.result; jalview.bin.Cache.log.debug("Parsing output from JNet job."); // JPredFile prediction = new JPredFile("C:/JalviewX/files/jpred.txt", "File"); jalview.io.JPredFile prediction = new jalview.io.JPredFile(result. getPredfile(), "Paste"); SequenceI[] preds = prediction.getSeqsAsArray(); jalview.bin.Cache.log.debug("Got prediction profile."); if ( (this.msa != null) && (result.getAligfile() != null)) { jalview.bin.Cache.log.debug("Getting associated alignment."); // we ignore the returned alignment if we only predicted on a single sequence String format = new jalview.io.IdentifyFile().Identify(result. getAligfile(), "Paste"); if (jalview.io.FormatAdapter.isValidFormat(format)) { SequenceI sqs[]; if (predMap!=null) { Object[] alandcolsel = input.getAlignmentAndColumnSelection(alignFrame.getViewport().getGapCharacter()); sqs = (SequenceI[]) alandcolsel[0]; al = new Alignment(sqs); alcsel=(ColumnSelection) alandcolsel[1]; } else { al = new FormatAdapter().readFile(result.getAligfile(), "Paste", format); sqs = new SequenceI[al.getHeight()]; for (int i = 0, j = al.getHeight(); i < j; i++) { sqs[i] = al.getSequenceAt(i); } if (!jalview.analysis.SeqsetUtils.deuniquify( (Hashtable) SequenceInfo, sqs)) { throw (new Exception( "Couldn't recover sequence properties for alignment.")); } } FirstSeq = 0; al.setDataset(null); jalview.io.JnetAnnotationMaker.add_annotation(prediction, al, FirstSeq, false,predMap); } else { throw (new Exception( "Unknown format "+format+" for file : \n" + result.getAligfile())); } } else { al = new Alignment(preds); FirstSeq = prediction.getQuerySeqPosition(); if (predMap!=null) { char gc = alignFrame.getViewport().getGapCharacter(); SequenceI[] sqs = (SequenceI[]) ((java.lang.Object[]) input.getAlignmentAndColumnSelection(gc))[0]; if (this.msaIndex>=sqs.length) throw new Error("Implementation Error! Invalid msaIndex for JPredJob on parent MSA input object!"); ///// //Uses RemoveGapsCommand ///// new jalview.commands.RemoveGapsCommand("Remove Gaps", new SequenceI[] {sqs[msaIndex]}, alignFrame.getCurrentView().getAlignment()); SequenceI profileseq=al.getSequenceAt(FirstSeq); profileseq.setSequence(sqs[msaIndex].getSequenceAsString()); } if (!jalview.analysis.SeqsetUtils.SeqCharacterUnhash( al.getSequenceAt(FirstSeq), SequenceInfo)) { throw (new Exception( "Couldn't recover sequence properties for JNet Query sequence!")); } else { al.setDataset(null); jalview.io.JnetAnnotationMaker.add_annotation(prediction, al, FirstSeq, true, predMap); SequenceI profileseq=al.getSequenceAt(0); // this includes any gaps. alignToProfileSeq(al, profileseq); if (predMap!=null) { // Adjust input view for gaps // propagate insertions into profile alcsel=propagateInsertions(profileseq, al, input); } } } return new Object[] { al, alcsel}; // , FirstSeq, noMsa}; } /** * Given an alignment where all other sequences except profileseq are aligned to the ungapped profileseq, insert gaps in the other sequences to realign them with the residues in profileseq * @param al * @param profileseq */ private void alignToProfileSeq(Alignment al, SequenceI profileseq) { char gc = al.getGapCharacter(); int[] gapMap = profileseq.gapMap(); // insert gaps into profile for (int lp=0,r=0; r1) { StringBuffer sb=new StringBuffer(); for (int s=0, ns=gapMap[r]-lp; s0) { // pad gaps sq=sq+sb; while ((diff=gapMap[r]-sq.length())>0) { sq=sq+((diff>=sb.length()) ? sb.toString() : sb.substring(0, diff)); } al.getSequenceAt(s).setSequence(sq); } else { al.getSequenceAt(s).setSequence(sq.substring(0,gapMap[r])+sb.toString()+sq.substring(gapMap[r])); } } } lp=gapMap[r]; } } /** * Add gaps into the sequences aligned to profileseq under the given AlignmentView * @param profileseq * @param al * @param input */ private ColumnSelection propagateInsertions(SequenceI profileseq, Alignment al, AlignmentView input) { char gc = al.getGapCharacter(); Object[] alandcolsel = input.getAlignmentAndColumnSelection(gc); ColumnSelection nview = (ColumnSelection) alandcolsel[1]; SequenceI origseq; nview.pruneDeletions(ShiftList.parseMap((origseq=((SequenceI[]) alandcolsel[0])[0]).gapMap())); // recover original prediction sequence's mapping to view. int[] viscontigs=nview.getVisibleContigs(0, profileseq.getLength()); int spos=0; int offset=0; // input.pruneDeletions(ShiftList.parseMap(((SequenceI[]) alandcolsel[0])[0].gapMap())) // add profile to visible contigs for (int v=0; vspos) { StringBuffer sb=new StringBuffer(); for (int s=0, ns=viscontigs[v]-spos; s0) { // pad gaps sq=sq+sb; while ((diff=spos+offset-sq.length()-1)>0) { sq=sq+((diff>=sb.length()) ? sb.toString() : sb.substring(0, diff)); } } sq+=sb.toString(); } else { al.getSequenceAt(s).setSequence(sq.substring(0,spos+offset)+sb.toString()+sq.substring(spos+offset)); } } } //offset+=sb.length(); } spos = viscontigs[v+1]+1; } if ((offset+spos)0) { sq=sq+((diff>=sb.length()) ? sb.toString() : sb.substring(0, diff)); diff=origseq.getLength()-sq.length(); } } } return nview; } public JPredJob(Hashtable SequenceInfo, SequenceI seq, int[] delMap) { super(); this.predMap = delMap; String sq = AlignSeq.extractGaps(Comparison.GapChars, seq.getSequenceAsString()); if (sq.length() >= 20) { this.SequenceInfo = SequenceInfo; sequence = new vamsas.objects.simple.Sequence(); sequence.setId(seq.getName()); sequence.setSeq(sq); } } public JPredJob(Hashtable SequenceInfo, SequenceI[] msf, int[] delMap) { this(SequenceInfo, msf[0], delMap); if (sequence != null) { if (msf.length > 1) { msa = new vamsas.objects.simple.Msfalignment(); jalview.io.PileUpfile pileup = new jalview.io.PileUpfile(); msa.setMsf(pileup.print(msf)); } } } } ext.vamsas.Jpred server; String altitle = ""; JPredThread(WebserviceInfo wsinfo, String altitle, ext.vamsas.Jpred server, String wsurl, AlignmentView alview, AlignFrame alframe) { super(); this.altitle = altitle; this.server = server; this.wsInfo = wsinfo; this.input = alview; this.alignFrame = alframe; WsUrl = wsurl; } JPredThread(WebserviceInfo wsinfo, String altitle, ext.vamsas.Jpred server, String wsurl, Hashtable SequenceInfo,SequenceI seq, int[] delMap, AlignmentView alview, AlignFrame alframe) { this(wsinfo, altitle, server,wsurl, alview, alframe); JPredJob job = new JPredJob(SequenceInfo, seq, delMap); if (job.hasValidInput()) { OutputHeader = wsInfo.getProgressText(); jobs = new WSJob[] { job}; job.jobnum = 0; } } JPredThread(WebserviceInfo wsinfo, String altitle, ext.vamsas.Jpred server, Hashtable SequenceInfo, SequenceI[] msf, int[] delMap, AlignmentView alview, AlignFrame alframe, String wsurl) { this(wsinfo, altitle, server,wsurl, alview, alframe); JPredJob job = new JPredJob(SequenceInfo, msf, delMap); if (job.hasValidInput()) { jobs = new WSJob[] { job}; OutputHeader = wsInfo.getProgressText(); job.jobnum = 0; } } void StartJob(WSJob j) { if (! (j instanceof JPredJob)) { throw new Error("Implementation error - StartJob(JpredJob) called on " + j.getClass()); } try { JPredJob job = (JPredJob) j; if (job.msa != null) { job.jobId = server.predictOnMsa(job.msa); } else if (job.sequence!=null) { job.jobId = server.predict(job.sequence); // debug like : job.jobId = "/jobs/www-jpred/jp_Yatat29";// } if (job.jobId != null) { if (job.jobId.startsWith("Broken")) { job.result = (vamsas.objects.simple.Result)new JpredResult(); job.result.setInvalid(true); job.result.setStatus("Submission " + job.jobId); } else { job.submitted = true; job.subjobComplete = false; Cache.log.info(WsUrl + " Job Id '" + job.jobId + "'"); } } else { throw new Exception("Server timed out - try again later\n"); } } catch (Exception e) { if (e.getMessage().indexOf("Exception") > -1) { wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR); wsInfo.setProgressText(j.jobnum, "Failed to submit the prediction. (Just close the window)\n" + "It is most likely that there is a problem with the server.\n"); System.err.println( "JPredWS Client: Failed to submit the prediction. Quite possibly because of a server error - see below)\n" + e.getMessage() + "\n"); jalview.bin.Cache.log.warn("Server Exception", e); } else { wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR); // JBPNote - this could be a popup informing the user of the problem. wsInfo.appendProgressText(j.jobnum, "Failed to submit the prediction:\n" + e.getMessage() + wsInfo.getProgressText()); jalview.bin.Cache.log.debug("Failed Submission of job " + j.jobnum, e); } j.allowedServerExceptions = -1; j.subjobComplete = true; } } void parseResult() { int results = 0; // number of result sets received JobStateSummary finalState = new JobStateSummary(); try { for (int j = 0; j < jobs.length; j++) { finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]); if (jobs[j].submitted && jobs[j].subjobComplete && jobs[j].hasResults()) { results++; } } } catch (Exception ex) { Cache.log.error("Unexpected exception when processing results for " + altitle, ex); wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR); } if (results > 0) { wsInfo.showResultsNewFrame .addActionListener(new java.awt.event.ActionListener() { public void actionPerformed( java.awt.event.ActionEvent evt) { displayResults(true); } }); wsInfo.mergeResults .addActionListener(new java.awt.event.ActionListener() { public void actionPerformed( java.awt.event.ActionEvent evt) { displayResults(false); } }); wsInfo.setResultsReady(); } else { wsInfo.setFinishedNoResults(); } } void displayResults(boolean newWindow) { // TODO: cope with multiple subjobs. if (jobs != null) { Object[] res = null; boolean msa=false; for (int jn = 0; jn < jobs.length; jn++) { Object[] jobres = null; JPredJob j = (JPredJob) jobs[jn]; if (j.hasResults()) { // hack - we only deal with all single seuqence predictions or all profile predictions msa = (j.msa!=null) ? true : msa; try { jalview.bin.Cache.log.debug("Parsing output of job " + jn); jobres = j.getResultSet(); jalview.bin.Cache.log.debug("Finished parsing output."); if (jobs.length==1) res = jobres; else { // do merge with other job results throw new Error("Multiple JNet subjob merging not yet implemented."); } } catch (Exception e) { jalview.bin.Cache.log.error( "JNet Client: JPred Annotation Parse Error", e); wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR); wsInfo.appendProgressText(j.jobnum, OutputHeader + "\n" + j.result.getStatus() + "\nInvalid JNet job result data!\n" + e.getMessage()); j.result.setBroken(true); } } } if (res != null) { if (newWindow) { AlignFrame af; if (input==null) { if (res[1]!=null) { af = new AlignFrame((Alignment)res[0], (ColumnSelection) res[1], AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); } else { af = new AlignFrame((Alignment)res[0], AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); } } else { /*java.lang.Object[] alandcolsel = input.getAlignmentAndColumnSelection(alignFrame.getViewport().getGapCharacter()); if (((SequenceI[])alandcolsel[0])[0].getLength()!=res.getWidth()) { if (msa) { throw new Error("Implementation Error! ColumnSelection from input alignment will not map to result alignment!"); } } if (!msa) { // update hidden regions to account for loss of gaps in profile. - if any // gapMap returns insert list, interpreted as delete list by pruneDeletions //((ColumnSelection) alandcolsel[1]).pruneDeletions(ShiftList.parseMap(((SequenceI[]) alandcolsel[0])[0].gapMap())); }*/ af = new AlignFrame((Alignment) res[0], (ColumnSelection) res[1],AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); } Desktop.addInternalFrame(af, altitle, AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); } else { Cache.log.info("Append results onto existing alignment."); } } } } void pollJob(WSJob job) throws Exception { job.result = server.getresult(job.jobId); } public boolean isCancellable() { return false; } public void cancelJob() { throw new Error("Implementation error!"); } public boolean canMergeResults() { return false; } }