X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fws%2FMsaWSThread.java;h=fb82808641adbdaa7f88e0add4a8cc204ba15dd8;hb=174230b4233d9ce80f94527768d2cd2f76da11ab;hp=d51bc7640b1e48be0796ff2a3e8b13236382ffc3;hpb=c431a65bbe794a0dd27f3f806d3c5cf37c53279c;p=jalview.git diff --git a/src/jalview/ws/MsaWSThread.java b/src/jalview/ws/MsaWSThread.java index d51bc76..fb82808 100644 --- a/src/jalview/ws/MsaWSThread.java +++ b/src/jalview/ws/MsaWSThread.java @@ -1,199 +1,311 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer + * Copyright (C) 2006 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ package jalview.ws; -import jalview.datamodel.AlignmentView; -import jalview.datamodel.AlignmentOrder; -import jalview.datamodel.ColumnSelection; -import jalview.gui.WebserviceInfo; -import jalview.analysis.AlignSeq; -import jalview.bin.Cache; -import jalview.gui.AlignFrame; -import javax.swing.JOptionPane; +import java.util.*; -import vamsas.objects.simple.MsaResult; -import vamsas.objects.simple.Result; +import jalview.analysis.*; +import jalview.bin.*; +import jalview.datamodel.*; import jalview.datamodel.Alignment; -import jalview.datamodel.SeqCigar; -import jalview.gui.Desktop; -import jalview.datamodel.SequenceI; -import java.util.Hashtable; -import java.util.Vector; +import jalview.datamodel.Sequence; +import jalview.gui.*; +import vamsas.objects.simple.MsaResult; /** *

* Title: *

- * + * *

* Description: *

- * + * *

* Copyright: Copyright (c) 2004 *

- * + * *

* Company: Dundee University *

- * + * * @author not attributable * @version 1.0 */ -class MsaWSThread extends Thread implements WSClientI { - jalview.gui.AlignFrame alignFrame; - - WebserviceInfo wsInfo = null; - - String WebServiceName = null; - - String OutputHeader; - AlignmentView input; +class MsaWSThread + extends WSThread implements WSClientI +{ boolean submitGaps = false; // pass sequences including gaps to alignment - + // service - + boolean preserveOrder = true; // and always store and recover sequence - + // order - - class MsaWSJob { - int jobnum = 0; // WebServiceInfo pane for this job - - String jobId; // ws job ticket - - vamsas.objects.simple.MsaResult result = null; - - vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.SequenceSet(); - + + class MsaWSJob extends WSThread.WSJob + { + // hold special input for this + vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple. + SequenceSet(); + /** * MsaWSJob - * + * * @param jobNum * int * @param jobId * String */ - public MsaWSJob(int jobNum, SequenceI[] inSeqs) { + public MsaWSJob(int jobNum, SequenceI[] inSeqs) + { this.jobnum = jobNum; - prepareInput(inSeqs); + if (!prepareInput(inSeqs, 2)) + { + submitted = true; + subjobComplete = true; + result = new MsaResult(); + result.setFinished(true); + result.setStatus("Job never ran - input returned to user."); + } + } - - int allowedServerExceptions = 3; // thread dies if too many - - // exceptions. - boolean submitted=false; - boolean subjobComplete = false; - + Hashtable SeqNames = new Hashtable(); Vector emptySeqs = new Vector(); - private void prepareInput(SequenceI[] seqs) { + /** + * 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. + */ + private boolean prepareInput(SequenceI[] seqs, int minlen) + { int nseqs = 0; - for (int i = 0; i < seqs.length; i++) { - if (seqs[i].getStart() < seqs[i].getEnd()) { + if (minlen < 0) + { + throw new Error("Implementation error: minlen must be zero or more."); + } + for (int i = 0; i < seqs.length; i++) + { + if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1) + { nseqs++; } } - vamsas.objects.simple.Sequence[] seqarray = - (nseqs>0) - ? new vamsas.objects.simple.Sequence[nseqs] - :null; - 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 (seqs[i].getStart() < seqs[i].getEnd()) { - seqarray[n] = new vamsas.objects.simple.Sequence(); - seqarray[n].setId(newname); - seqarray[n++].setSeq((submitGaps) ? seqs[i].getSequence() + boolean valid = nseqs > 1; // need at least two seqs + vamsas.objects.simple.Sequence[] seqarray = + (valid) + ? new vamsas.objects.simple.Sequence[nseqs] + : null; + 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) + { + seqarray[n] = new vamsas.objects.simple.Sequence(); + seqarray[n].setId(newname); + seqarray[n++].setSeq( (submitGaps) ? seqs[i].getSequence() + : AlignSeq.extractGaps( + jalview.util.Comparison.GapChars, seqs[i] + .getSequence())); + } + else + { + String empty = null; + if (seqs[i].getEnd() >= seqs[i].getStart()) + { + empty = (submitGaps) ? seqs[i].getSequence() : AlignSeq.extractGaps( jalview.util.Comparison.GapChars, seqs[i] - .getSequence())); - } else { - emptySeqs.add(newname); + .getSequence()); } + emptySeqs.add(new String[] + {newname, empty}); } - this.seqs = new vamsas.objects.simple.SequenceSet(); - this.seqs.setSeqs(seqarray); + } + this.seqs = new vamsas.objects.simple.SequenceSet(); + this.seqs.setSeqs(seqarray); + return valid; } + /** - * + * * @return true if getAlignment will return a valid alignment result. */ - public boolean hasResults() { - if (subjobComplete && result!=null && jobs[0].result.isFinished()) + public boolean hasResults() + { + if (subjobComplete && result != null && result.isFinished() + && ((MsaResult) result).getMsa() != null && ((MsaResult) result).getMsa().getSeqs() != null) + { return true; + } return false; } - public Object[] getAlignment() { - - if (result!=null && result.isFinished()) { - SequenceI[] alseqs=null; - char alseq_gapchar='-'; - int alseq_l=0; - if (result.getMsa() != null) { - alseqs = getVamsasAlignment(result.getMsa()); - alseq_gapchar=result.getMsa().getGapchar().charAt(0); + + public Object[] getAlignment() + { + + if (result != null && result.isFinished()) + { + SequenceI[] alseqs = null; + char alseq_gapchar = '-'; + int alseq_l = 0; + if (((MsaResult) result).getMsa() != null) + { + alseqs = getVamsasAlignment(((MsaResult) result).getMsa()); + alseq_gapchar = ((MsaResult) result).getMsa().getGapchar().charAt(0); alseq_l = alseqs.length; } - if (emptySeqs.size()>0) { - SequenceI[] t_alseqs = new SequenceI[alseq_l+emptySeqs.size()]; + 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 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 = (String[]) 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 sw) + { + // pad at end + alseqs[i].setSequence(t_alseqs[i].getSequence() + + insbuff.substring(0, sw - nw)); + } + } + } + for (i = 0, w = emptySeqs.size(); i < w; i++) + { + String[] es = (String[]) 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 new Object[] + { + alseqs, msaorder}; } return null; } + /** + * mark subjob as cancelled and set result object appropriatly + */ + void cancel() { + cancelled=true; + subjobComplete = true; + result = null; + } + /** + * + * @return boolean true if job can be submitted. + */ + boolean hasValidInput() { + if (seqs.getSeqs()!=null) + return true; + return false; + } } - MsaWSJob jobs[] = null; - + + String alTitle; // name which will be used to form new alignment window. - - boolean jobComplete = false; - Alignment dataset; // dataset to which the new alignment will be - + // associated. - + ext.vamsas.MuscleWS server = null; - - String WsUrl = null; - /** * set basic options for this (group) of Msa jobs - * + * * @param subgaps * boolean * @param presorder * boolean */ MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl, - WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame, AlignmentView alview, - String wsname, boolean subgaps, boolean presorder) { + WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame, + AlignmentView alview, + String wsname, boolean subgaps, boolean presorder) + { this.server = server; this.WsUrl = wsUrl; this.wsInfo = wsinfo; @@ -203,10 +315,10 @@ class MsaWSThread extends Thread implements WSClientI { this.preserveOrder = presorder; this.alignFrame = alFrame; } - + /** * create one or more Msa jobs to align visible seuqences in _msa - * + * * @param title * String * @param _msa @@ -219,496 +331,365 @@ class MsaWSThread extends Thread implements WSClientI { * Alignment */ MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl, - WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame, - String wsname, String title, AlignmentView _msa, boolean subgaps, - boolean presorder, Alignment seqset) { + WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame, + String wsname, String title, AlignmentView _msa, boolean subgaps, + boolean presorder, Alignment seqset) + { this(server, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder); OutputHeader = wsInfo.getProgressText(); alTitle = title; dataset = seqset; - SeqCigar[] msa = _msa.getSequences(); - int[] contigs = _msa.getContigs(); - int njobs=1; - if (contigs != null && contigs.length > 0) { - int start = 0; - njobs=0; - int width = _msa.getWidth(); - for (int contig = 0; contig < contigs.length; contig += 3) { - if ((contigs[contig+1] - start) > 0) { - njobs++; - } - width+=contigs[contig+2];// end up with full region width (including hidden regions) - start = contigs[contig+1] + contigs[contig + 2]; - } - if (start 0) { - SequenceI mseq[] = new SequenceI[msa.length]; - for (int s = 0; s < mseq.length; s++) { - mseq[s] = msa[s].getSeq('-').getSubSequence(start, - contigs[contig+1]); - } - if (j!=0) { - jobs[j] = new MsaWSJob(wsinfo.addJobPane(), mseq); - } else { - jobs[j] = new MsaWSJob(0, mseq); - } - wsinfo.setProgressName("region "+jobs[j].jobnum,jobs[j].jobnum); - wsinfo.setProgressText(jobs[j].jobnum, OutputHeader); - j++; - } - start = contigs[contig+1] + contigs[contig + 2]; - } - if (start 0) + wsinfo.setProgressName("region " + jobs[j].jobnum, jobs[j].jobnum); wsinfo.setProgressText(jobs[j].jobnum, OutputHeader); - j++; } - } else { - SequenceI mseq[] = new SequenceI[msa.length]; - for (int s = 0; s < mseq.length; s++) { - mseq[s] = msa[s].getSeq('-'); - } - jobs = new MsaWSJob[1]; - wsinfo.setProgressText(OutputHeader); // ensures default text - jobs[0] = new MsaWSJob(0, mseq); } } - - public boolean isCancellable() { + public boolean isCancellable() + { return true; } - - public void cancelJob() { - if (!jobComplete && jobs != null) { + + public void cancelJob() + { + if (!jobComplete && jobs != null) + { boolean cancelled = true; - for (int job = 0; job < jobs.length; job++) { + for (int job = 0; job < jobs.length; job++) + { if (jobs[job].submitted && !jobs[job].subjobComplete) - { + { String cancelledMessage = ""; - try { + try + { vamsas.objects.simple.WsJobId cancelledJob = server - .cancel(jobs[job].jobId); - if (cancelledJob.getStatus() == 2) { + .cancel(jobs[job].jobId); + if (cancelledJob.getStatus() == 2) + { // CANCELLED_JOB cancelledMessage = "Job cancelled."; - jobs[job].subjobComplete = true; - jobs[job].result = null; - wsInfo.setStatus(jobs[job].jobnum, WebserviceInfo.STATE_CANCELLED_OK); - } else if (cancelledJob.getStatus() == 3) { + ((MsaWSJob) jobs[job]).cancel(); + wsInfo.setStatus(jobs[job].jobnum, + WebserviceInfo.STATE_CANCELLED_OK); + } + else if (cancelledJob.getStatus() == 3) + { // VALID UNSTOPPABLE JOB - cancelledMessage += "Server cannot cancel this job. just close the window.\n"; + cancelledMessage += + "Server cannot cancel this job. just close the window.\n"; cancelled = false; - wsInfo.setStatus(jobs[job].jobnum, WebserviceInfo.STATE_CANCELLED_OK); + // wsInfo.setStatus(jobs[job].jobnum, + // WebserviceInfo.STATE_RUNNING); } - - if (cancelledJob.getJobId() != null) { + + if (cancelledJob.getJobId() != null) + { cancelledMessage += ("[" + cancelledJob.getJobId() + "]"); } - + cancelledMessage += "\n"; - } catch (Exception exc) { - cancelledMessage += ("\nProblems cancelling the job : Exception received...\n" - + exc + "\n"); - exc.printStackTrace(); + } + catch (Exception exc) + { + cancelledMessage += + ("\nProblems cancelling the job : Exception received...\n" + + exc + "\n"); + Cache.log.warn("Exception whilst cancelling "+jobs[job].jobId,exc); } wsInfo.setProgressText(jobs[job].jobnum, OutputHeader - + cancelledMessage + "\n"); + + cancelledMessage + "\n"); } } - if (cancelled) { + if (cancelled) + { wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK); jobComplete = true; } - } else { - if (!jobComplete) { + this.interrupt(); // kick thread to update job states. + } + else + { + if (!jobComplete) + { wsInfo - .setProgressText(OutputHeader - + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n"); + .setProgressText(OutputHeader + + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n"); } } } - - public void run() { - - while (!jobComplete) { - int running=0; - int queuing=0; - int finished=0; - int error=0; - int serror=0; - for (int j=0; j 0) { - jobs[j].allowedServerExceptions--; - Cache.log.debug("Sleeping after a server exception."); - try { - Thread.sleep(5000); - } - catch (InterruptedException ex1) { - } - } else { - Cache.log.warn("Dropping job "+j+" "+jobs[j].jobId); - jobs[j].subjobComplete=true; - wsInfo.setStatus(jobs[j].jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR); - } - } - catch (OutOfMemoryError er) { - jobComplete = true; - jobs[j].subjobComplete=true; - jobs[j].result=null; // may contain out of date result object - wsInfo.setStatus(jobs[j].jobnum, - WebserviceInfo.STATE_STOPPED_ERROR); - JOptionPane - .showInternalMessageDialog( - Desktop.desktop, - "Out of memory handling result for job !!" - + "\nSee help files for increasing Java Virtual Machine memory.", - "Out of memory", JOptionPane.WARNING_MESSAGE); - Cache.log.error("Out of memory when retrieving Job "+j+" id:" + WsUrl+"/"+jobs[j].jobId, er); - System.gc(); - } - } - if (jobs[j].result!=null) { - String progheader=""; - // Parse state of job[j] - if (jobs[j].result.isRunning()) { - running++; - wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_RUNNING); - } else if (jobs[j].result.isQueued()) { - queuing++; - wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_QUEUING); - } else if (jobs[j].result.isFinished()) { - finished++; - jobs[j].subjobComplete = true; - wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_STOPPED_OK); - } else if (jobs[j].result.isFailed()) { - progheader += "Job failed.\n"; - jobs[j].subjobComplete=true; - wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_STOPPED_ERROR); - error++; - } else if (jobs[j].result.isServerError()) { - serror++; - jobs[j].subjobComplete = true; - wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_STOPPED_SERVERERROR); - } else if (jobs[j].result.isBroken() || jobs[j].result.isFailed()) { - error++; - jobs[j].subjobComplete=true; - wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_STOPPED_ERROR); - } - // and pass on any sub-job messages to the user - wsInfo.setProgressText(jobs[j].jobnum, OutputHeader); - wsInfo.appendProgressText(jobs[j].jobnum, progheader); - if (jobs[j].result.getStatus() != null) { - wsInfo.appendProgressText(jobs[j].jobnum, jobs[j].result.getStatus()); - } - } else { - if (jobs[j].submitted && jobs[j].subjobComplete) { - if (jobs[j].allowedServerExceptions==0) { - serror++; - } else if (jobs[j].result==null) { - error++; - } - } - } - } - // Decide on overall state based on collected jobs[] states - if (running>0) { - wsInfo.setStatus(WebserviceInfo.STATE_RUNNING); - } else if (queuing>0) { - wsInfo.setStatus(WebserviceInfo.STATE_QUEUING); - } else { - jobComplete=true; - if (finished>0) { - wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_OK); - } else if (error>0) { - wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR); - } else if (serror>0) { - wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR); - } - } - if (!jobComplete) { - try { - Thread.sleep(5000); - } - catch (InterruptedException e) { - Cache.log.debug("Interrupted sleep waiting for next job poll.",e); - } - // System.out.println("I'm alive "+alTitle); - } - } - if (jobComplete) { - parseResult(); // tidy up and make results available to user - } + void pollJob(WSJob job) throws Exception { + ((MsaWSJob) job).result = server.getResult(((MsaWSJob) job).jobId); } - - void StartJob(MsaWSJob j) { - if (j.submitted) { - if (Cache.log.isDebugEnabled()) { - Cache.log.debug("Tried to submit an already submitted job "+j.jobId); + void StartJob(WSJob job) + { + if (!(job instanceof MsaWSJob)) { + throw new Error("StartJob(MsaWSJob) called on a WSJobInstance "+job.getClass()); + } + MsaWSJob j = (MsaWSJob) job; + if (j.submitted) + { + if (Cache.log.isDebugEnabled()) + { + Cache.log.debug("Tried to submit an already submitted job " + j.jobId); } return; } - if (j.seqs.getSeqs()==null) { + if (j.seqs.getSeqs() == null) + { // special case - selection consisted entirely of empty sequences... - j.submitted=true; - j.result=new MsaResult(); + j.submitted = true; + j.result = new MsaResult(); j.result.setFinished(true); j.result.setStatus("Empty Alignment Job"); - j.result.setMsa(null); + ((MsaResult) j.result).setMsa(null); } - try { + try + { vamsas.objects.simple.WsJobId jobsubmit = server.align(j.seqs); - - if ((jobsubmit != null) && (jobsubmit.getStatus() == 1)) { + + if ( (jobsubmit != null) && (jobsubmit.getStatus() == 1)) + { j.jobId = jobsubmit.getJobId(); - j.submitted=true; + j.submitted = true; j.subjobComplete = false; // System.out.println(WsURL + " Job Id '" + jobId + "'"); - } else { - if (jobsubmit == null) { + } + else + { + if (jobsubmit == null) + { throw new Exception( "Server at " + WsUrl - + " returned null object, it probably cannot be contacted. Try again later ?"); + + + " returned null object, it probably cannot be contacted. Try again later ?"); } - + throw new Exception(jobsubmit.getJobId()); } - } catch (Exception e) { + } + catch (Exception e) + { // TODO: JBPNote catch timeout or other fault types explicitly // For unexpected errors System.err - .println(WebServiceName - + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n" - + "When contacting Server:" + WsUrl + "\n" - + e.toString() + "\n"); + .println(WebServiceName + + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n" + + "When contacting Server:" + WsUrl + "\n" + + e.toString() + "\n"); j.allowedServerExceptions = 0; wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR); wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR); wsInfo - .appendProgressText( - j.jobnum, - "Failed to submit sequences for alignment.\n" - + "It is most likely that there is a problem with the server.\n" - + "Just close the window\n"); - + .appendProgressText( + j.jobnum, + "Failed to submit sequences for alignment.\n" + + "It is most likely that there is a problem with the server.\n" + + "Just close the window\n"); + // e.printStackTrace(); // TODO: JBPNote DEBUG } } - + private jalview.datamodel.Sequence[] getVamsasAlignment( - vamsas.objects.simple.Alignment valign) { + vamsas.objects.simple.Alignment valign) + { vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs(); - jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.length]; - - for (int i = 0, j = seqs.length; i < j; i++) { + jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs. + length]; + + for (int i = 0, j = seqs.length; i < j; i++) + { msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(), seqs[i] - .getSeq()); + .getSeq()); } - + return msa; } - - void parseResult() { - try { - for (int j=0; j