--- /dev/null
+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<SequenceI> 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;
+ }
+
+ }
+
+ Vector<String[]> 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;
+ }
+
+}
\ No newline at end of file