package jalview.ws.gui; import jalview.analysis.AlignSeq; import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentOrder; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceI; import jalview.util.MessageManager; import jalview.ws.api.JobId; import jalview.ws.jws2.dm.JabaWsParamSet; import jalview.ws.params.ArgumentI; import java.util.ArrayList; import java.util.Vector; class MsaWSJob extends WsJob { /** * holds basic MSA analysis configuration - todo - encapsulate */ private final MsaWSThread msaWSThread; long lastChunk = 0; /** * input */ ArrayList seqs = new ArrayList<>(); /** * output */ AlignmentI alignment; // set if the job didn't get run - then the input is simply returned to the // user private boolean returnInput = false; /** * MsaWSJob * * @param jobNum * int * @param msaWSThread * TODO - abstract the properties provided by the thread * @param jobId * String */ public MsaWSJob(MsaWSThread msaWSThread, int jobNum, SequenceI[] inSeqs) { this.msaWSThread = msaWSThread; this.jobnum = jobNum; if (!prepareInput(inSeqs, 2)) { submitted = true; subjobComplete = true; returnInput = true; } else { validInput = true; } } Vector emptySeqs = new Vector(); /** * prepare input sequences for MsaWS service * * @param seqs * jalview sequences to be prepared * @param minlen * minimum number of residues required for this MsaWS service * @return true if seqs contains sequences to be submitted to service. */ // TODO: return compbio.seqs list or nothing to indicate validity. private boolean prepareInput(SequenceI[] seqs, int minlen) { // TODO: service specific input data is generated in this method - for // JABAWS it is client-side // prepared, but for Slivka it could be uploaded at this stage. int nseqs = 0; if (minlen < 0) { throw new Error(MessageManager.getString( "error.implementation_error_minlen_must_be_greater_zero")); } for (int i = 0; i < seqs.length; i++) { if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1) { nseqs++; } } boolean valid = nseqs > 1; // need at least two seqs Sequence seq; for (int i = 0, n = 0; i < seqs.length; i++) { String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same // for // any // subjob SeqNames.put(newname, jalview.analysis.SeqsetUtils.SeqCharacterHash(seqs[i])); if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1) { // make new input sequence with or without gaps seq = new Sequence(newname, (this.msaWSThread.submitGaps) ? seqs[i].getSequenceAsString() : AlignSeq.extractGaps( jalview.util.Comparison.GapChars, seqs[i].getSequenceAsString())); this.seqs.add(seq); } else { String empty = null; if (seqs[i].getEnd() >= seqs[i].getStart()) { empty = (this.msaWSThread.submitGaps) ? seqs[i].getSequenceAsString() : AlignSeq.extractGaps(jalview.util.Comparison.GapChars, seqs[i].getSequenceAsString()); } emptySeqs.add(new String[] { newname, empty }); } } return valid; } /** * * @return true if getAlignment will return a valid alignment result. */ @Override public boolean hasResults() { if (subjobComplete && isFinished() && (alignment != null || (emptySeqs != null && emptySeqs.size() > 0))) { return true; } return false; } /** * * get the alignment including any empty sequences in the original order * with original ids. Caller must access the alignment.getMetadata() object * to annotate the final result passsed to the user. * * @return { SequenceI[], AlignmentOrder } */ public Object[] getAlignment() { // TODO: make this generic based on MsaResultI // TODO: decide if the data loss for this return signature is avoidable // (ie should we just return AlignmentI instead ?) if (hasResults()) { SequenceI[] alseqs = null; char alseq_gapchar = '-'; int alseq_l = 0; alseqs = new SequenceI[alignment.getSequences().size()]; if (alignment.getSequences().size() > 0) { for (SequenceI seq : alignment .getSequences()) { alseqs[alseq_l++] = new Sequence(seq); } alseq_gapchar = alignment.getGapCharacter(); } // add in the empty seqs. if (emptySeqs.size() > 0) { SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()]; // get width int i, w = 0; if (alseq_l > 0) { for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++) { if (w < alseqs[i].getLength()) { w = alseqs[i].getLength(); } t_alseqs[i] = alseqs[i]; alseqs[i] = null; } } // check that aligned width is at least as wide as emptySeqs width. int ow = w, nw = w; for (i = 0, w = emptySeqs.size(); i < w; i++) { String[] es = emptySeqs.get(i); if (es != null && es[1] != null) { int sw = es[1].length(); if (nw < sw) { nw = sw; } } } // make a gapped string. StringBuffer insbuff = new StringBuffer(w); for (i = 0; i < nw; i++) { insbuff.append(alseq_gapchar); } if (ow < nw) { for (i = 0; i < alseq_l; i++) { int sw = t_alseqs[i].getLength(); if (nw > sw) { // pad at end alseqs[i].setSequence(t_alseqs[i].getSequenceAsString() + insbuff.substring(0, sw - nw)); } } } for (i = 0, w = emptySeqs.size(); i < w; i++) { String[] es = emptySeqs.get(i); if (es[1] == null) { t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(es[0], insbuff.toString(), 1, 0); } else { if (es[1].length() < nw) { t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence( es[0], es[1] + insbuff.substring(0, nw - es[1].length()), 1, 1 + es[1].length()); } else { t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence( es[0], es[1]); } } } alseqs = t_alseqs; } AlignmentOrder msaorder = new AlignmentOrder(alseqs); // always recover the order - makes parseResult()'s life easier. jalview.analysis.AlignmentSorter.recoverOrder(alseqs); // account for any missing sequences jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs); return new Object[] { alseqs, msaorder }; } return null; } /** * mark subjob as cancelled and set result object appropriatly */ void cancel() { cancelled = true; subjobComplete = true; alignment = null; } /** * * @return boolean true if job can be submitted. */ @Override public boolean hasValidInput() { // TODO: get attributes for this MsaWS instance to check if it can do two // sequence alignment. if (seqs != null && seqs.size() >= 2) // two or more sequences is valid ? { return true; } return false; } StringBuffer jobProgress = new StringBuffer(); @Override public void setStatus(String string) { jobProgress.setLength(0); jobProgress.append(string); } @Override public String getStatus() { return jobProgress.toString(); } @Override public boolean hasStatus() { return jobProgress != null; } /** * @return the lastChunk */ public long getLastChunk() { return lastChunk; } /** * @param lastChunk * the lastChunk to set */ public void setLastChunk(long lastChunk) { this.lastChunk = lastChunk; } String alignmentProgram = null; public String getAlignmentProgram() { return alignmentProgram; } public boolean hasArguments() { return (arguments != null && arguments.size() > 0) || (preset != null && preset instanceof JabaWsParamSet); } /** * add a progess header to status string containing presets/args used */ public void addInitialStatus() { // TODO: decide if it is useful to report 'JABAWS format' argument lists // rather than generic Jalview service arguments if (preset != null) { jobProgress.append( "Using " + (preset.isModifiable() ? "Server" : "User") + "Preset: " + preset.getName()); for (ArgumentI opt : preset.getArguments()) { jobProgress.append(opt.getName() + " " + opt.getValue() + "\n"); } } else { if (arguments != null && arguments.size() > 0) { jobProgress.append("With custom parameters : \n"); // merge arguments with preset's own arguments. for (ArgumentI opt : arguments) { jobProgress.append(opt.getName() + " " + opt.getValue() + "\n"); } } jobProgress.append("\nJob Output:\n"); } } JobId jobHandle = null; public void setJobHandle(JobId align) { jobHandle = align; setJobId(jobHandle.getJobId()); } public JobId getJobHandle() { return jobHandle; } }