2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.ws.jws2;
23 import java.util.ArrayList;
24 import java.util.Hashtable;
25 import java.util.List;
27 import java.util.Vector;
29 import javax.swing.JInternalFrame;
31 import compbio.data.msa.MsaWS;
32 import compbio.metadata.Argument;
33 import compbio.metadata.ChunkHolder;
34 import compbio.metadata.JobStatus;
35 import compbio.metadata.Preset;
36 import jalview.analysis.AlignSeq;
37 import jalview.bin.Console;
38 import jalview.datamodel.Alignment;
39 import jalview.datamodel.AlignmentI;
40 import jalview.datamodel.AlignmentOrder;
41 import jalview.datamodel.AlignmentView;
42 import jalview.datamodel.HiddenColumns;
43 import jalview.datamodel.Sequence;
44 import jalview.datamodel.SequenceI;
45 import jalview.gui.AlignFrame;
46 import jalview.gui.Desktop;
47 import jalview.gui.SplitFrame;
48 import jalview.gui.WebserviceInfo;
49 import jalview.util.IdUtils;
50 import jalview.util.IdUtils.IdType;
51 import jalview.util.MessageManager;
52 import jalview.ws.AWsJob;
53 import jalview.ws.JobStateSummary;
54 import jalview.ws.WSClientI;
55 import jalview.ws.jws2.dm.JabaWsParamSet;
56 import jalview.ws.params.WsParamSetI;
58 class MsaWSThread extends AWS2Thread implements WSClientI
60 boolean submitGaps = false; // pass sequences including gaps to alignment
64 boolean preserveOrder = true; // and always store and recover sequence
68 class MsaWSJob extends JWs2Job
72 WsParamSetI preset = null;
74 List<Argument> arguments = null;
79 ArrayList<compbio.data.sequence.FastaSequence> seqs = new ArrayList<compbio.data.sequence.FastaSequence>();
84 compbio.data.sequence.Alignment alignment;
86 // set if the job didn't get run - then the input is simply returned to the
88 private boolean returnInput = false;
98 public MsaWSJob(int jobNum, SequenceI[] inSeqs)
100 this.jobnum = jobNum;
101 if (!prepareInput(inSeqs, 2))
104 subjobComplete = true;
110 Hashtable<String, Map> SeqNames = new Hashtable();
112 Vector<String[]> emptySeqs = new Vector();
115 * prepare input sequences for MsaWS service
118 * jalview sequences to be prepared
120 * minimum number of residues required for this MsaWS service
121 * @return true if seqs contains sequences to be submitted to service.
123 // TODO: return compbio.seqs list or nothing to indicate validity.
124 private boolean prepareInput(SequenceI[] seqs, int minlen)
129 throw new Error(MessageManager.getString(
130 "error.implementation_error_minlen_must_be_greater_zero"));
132 for (int i = 0; i < seqs.length; i++)
134 if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
139 boolean valid = nseqs > 1; // need at least two seqs
140 compbio.data.sequence.FastaSequence seq;
141 for (int i = 0, n = 0; i < seqs.length; i++)
144 String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
148 SeqNames.put(newname,
149 jalview.analysis.SeqsetUtils.SeqCharacterHash(seqs[i]));
150 if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
152 // make new input sequence with or without gaps
153 seq = new compbio.data.sequence.FastaSequence(newname,
154 (submitGaps) ? seqs[i].getSequenceAsString()
155 : AlignSeq.extractGaps(
156 jalview.util.Comparison.GapChars,
157 seqs[i].getSequenceAsString()));
163 if (seqs[i].getEnd() >= seqs[i].getStart())
165 empty = (submitGaps) ? seqs[i].getSequenceAsString()
166 : AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
167 seqs[i].getSequenceAsString());
169 emptySeqs.add(new String[] { newname, empty });
177 * @return true if getAlignment will return a valid alignment result.
180 public boolean hasResults()
182 if (subjobComplete && isFinished() && (alignment != null
183 || (emptySeqs != null && emptySeqs.size() > 0)))
192 * get the alignment including any empty sequences in the original order
193 * with original ids. Caller must access the alignment.getMetadata() object
194 * to annotate the final result passsed to the user.
196 * @return { SequenceI[], AlignmentOrder }
198 public Object[] getAlignment()
200 // is this a generic subjob or a Jws2 specific Object[] return signature
203 SequenceI[] alseqs = null;
204 char alseq_gapchar = '-';
206 if (alignment.getSequences().size() > 0)
208 alseqs = new SequenceI[alignment.getSequences().size()];
209 for (compbio.data.sequence.FastaSequence seq : alignment
212 alseqs[alseq_l++] = new Sequence(seq.getId(),
215 alseq_gapchar = alignment.getMetadata().getGapchar();
218 // add in the empty seqs.
219 if (emptySeqs.size() > 0)
221 SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
226 for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
228 if (w < alseqs[i].getLength())
230 w = alseqs[i].getLength();
232 t_alseqs[i] = alseqs[i];
236 // check that aligned width is at least as wide as emptySeqs width.
238 for (i = 0, w = emptySeqs.size(); i < w; i++)
240 String[] es = emptySeqs.get(i);
241 if (es != null && es[1] != null)
243 int sw = es[1].length();
250 // make a gapped string.
251 StringBuffer insbuff = new StringBuffer(w);
252 for (i = 0; i < nw; i++)
254 insbuff.append(alseq_gapchar);
258 for (i = 0; i < alseq_l; i++)
260 int sw = t_alseqs[i].getLength();
264 alseqs[i].setSequence(t_alseqs[i].getSequenceAsString()
265 + insbuff.substring(0, sw - nw));
269 for (i = 0, w = emptySeqs.size(); i < w; i++)
271 String[] es = emptySeqs.get(i);
274 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(es[0],
275 insbuff.toString(), 1, 0);
279 if (es[1].length() < nw)
281 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
283 es[1] + insbuff.substring(0, nw - es[1].length()),
284 1, 1 + es[1].length());
288 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
295 AlignmentOrder msaorder = new AlignmentOrder(alseqs);
296 // always recover the order - makes parseResult()'s life easier.
297 jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
298 // account for any missing sequences
299 jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
300 return new Object[] { alseqs, msaorder };
306 * mark subjob as cancelled and set result object appropriatly
311 subjobComplete = true;
317 * @return boolean true if job can be submitted.
320 public boolean hasValidInput()
322 // TODO: get attributes for this MsaWS instance to check if it can do two
323 // sequence alignment.
324 if (seqs != null && seqs.size() >= 2) // two or more sequences is valid ?
331 StringBuffer jobProgress = new StringBuffer();
333 public void setStatus(String string)
335 jobProgress.setLength(0);
336 jobProgress.append(string);
340 public String getStatus()
342 return jobProgress.toString();
346 public boolean hasStatus()
348 return jobProgress != null;
352 * @return the lastChunk
354 public long getLastChunk()
361 * the lastChunk to set
363 public void setLastChunk(long lastChunk)
365 this.lastChunk = lastChunk;
368 String alignmentProgram = null;
370 public String getAlignmentProgram()
372 return alignmentProgram;
375 public boolean hasArguments()
377 return (arguments != null && arguments.size() > 0)
378 || (preset != null && preset instanceof JabaWsParamSet);
381 public List<Argument> getJabaArguments()
383 List<Argument> newargs = new ArrayList<Argument>();
384 if (preset != null && preset instanceof JabaWsParamSet)
386 newargs.addAll(((JabaWsParamSet) preset).getjabaArguments());
388 if (arguments != null && arguments.size() > 0)
390 newargs.addAll(arguments);
396 * add a progess header to status string containing presets/args used
398 public void addInitialStatus()
402 jobProgress.append("Using "
403 + (preset instanceof JabaPreset ? "Server" : "User")
404 + "Preset: " + preset.getName());
405 if (preset instanceof JabaWsParamSet)
407 for (Argument opt : ((JabaWsParamSet) preset).getjabaArguments())
410 opt.getName() + " " + opt.getDefaultValue() + "\n");
414 if (arguments != null && arguments.size() > 0)
416 jobProgress.append("With custom parameters : \n");
417 // merge arguments with preset's own arguments.
418 for (Argument opt : arguments)
421 opt.getName() + " " + opt.getDefaultValue() + "\n");
424 jobProgress.append("\nJob Output:\n");
427 public boolean isPresetJob()
429 return preset != null && preset instanceof JabaPreset;
432 public Preset getServerPreset()
434 return (isPresetJob()) ? ((JabaPreset) preset).p : null;
438 String alTitle; // name which will be used to form new alignment window.
440 AlignmentI dataset; // dataset to which the new alignment will be
444 @SuppressWarnings("unchecked")
448 * set basic options for this (group) of Msa jobs
455 private MsaWSThread(MsaWS server, String wsUrl, WebserviceInfo wsinfo,
456 jalview.gui.AlignFrame alFrame, AlignmentView alview,
457 String wsname, boolean subgaps, boolean presorder)
459 super(alFrame, wsinfo, alview, wsname, wsUrl);
460 this.server = server;
461 this.submitGaps = subgaps;
462 this.preserveOrder = presorder;
466 * create one or more Msa jobs to align visible seuqences in _msa
479 MsaWSThread(MsaWS server2, WsParamSetI preset, List<Argument> paramset,
480 String wsUrl, WebserviceInfo wsinfo,
481 jalview.gui.AlignFrame alFrame, String wsname, String title,
482 AlignmentView _msa, boolean subgaps, boolean presorder,
485 this(server2, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
486 OutputHeader = wsInfo.getProgressText();
490 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
493 int nvalid = 0, njobs = conmsa.length;
494 jobs = new MsaWSJob[njobs];
495 for (int j = 0; j < njobs; j++)
499 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), conmsa[j]);
503 jobs[j] = new MsaWSJob(0, conmsa[j]);
505 if (((MsaWSJob) jobs[j]).hasValidInput())
509 ((MsaWSJob) jobs[j]).preset = preset;
510 ((MsaWSJob) jobs[j]).arguments = paramset;
511 ((MsaWSJob) jobs[j]).alignmentProgram = wsname;
514 wsinfo.setProgressName("region " + jobs[j].getJobnum(),
515 jobs[j].getJobnum());
517 wsinfo.setProgressText(jobs[j].getJobnum(), OutputHeader);
519 validInput = nvalid > 0;
523 boolean validInput = false;
527 * @return true if the thread will perform a calculation
529 public boolean hasValidInput()
535 public boolean isCancellable()
541 public void cancelJob()
543 if (!jobComplete && jobs != null)
545 boolean cancelled = true;
546 for (int job = 0; job < jobs.length; job++)
548 if (jobs[job].isSubmitted() && !jobs[job].isSubjobComplete())
550 String cancelledMessage = "";
553 boolean cancelledJob = server.cancelJob(jobs[job].getJobId());
554 if (true) // cancelledJob || true)
557 // if the Jaba server indicates the job can't be cancelled, its
558 // because its running on the server's local execution engine
559 // so we just close the window anyway.
560 cancelledMessage = "Job cancelled.";
561 ((MsaWSJob) jobs[job]).cancel(); // TODO: refactor to avoid this
563 wsInfo.setStatus(jobs[job].getJobnum(),
564 WebserviceInfo.STATE_CANCELLED_OK);
568 // VALID UNSTOPPABLE JOB
569 cancelledMessage += "Server cannot cancel this job. just close the window.\n";
571 // wsInfo.setStatus(jobs[job].jobnum,
572 // WebserviceInfo.STATE_RUNNING);
574 } catch (Exception exc)
576 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
579 "Exception whilst cancelling " + jobs[job].getJobId(),
582 wsInfo.setProgressText(jobs[job].getJobnum(),
583 OutputHeader + cancelledMessage + "\n");
587 // if we hadn't submitted then just mark the job as cancelled.
588 jobs[job].setSubjobComplete(true);
589 wsInfo.setStatus(jobs[job].getJobnum(),
590 WebserviceInfo.STATE_CANCELLED_OK);
596 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
599 this.interrupt(); // kick thread to update job states.
605 wsInfo.setProgressText(OutputHeader
606 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
612 public void pollJob(AWsJob job) throws Exception
614 // TODO: investigate if we still need to cast here in J1.6
615 MsaWSJob j = ((MsaWSJob) job);
616 // this is standard code, but since the interface doesn't comprise of a
617 // basic one that implements (getJobStatus, pullExecStatistics) we have to
618 // repeat the code for all jw2s services.
619 j.setjobStatus(server.getJobStatus(job.getJobId()));
620 updateJobProgress(j);
626 * @return true if more job progress data was available
629 protected boolean updateJobProgress(MsaWSJob j) throws Exception
631 StringBuffer response = j.jobProgress;
632 long lastchunk = j.getLastChunk();
633 boolean changed = false;
636 j.setLastChunk(lastchunk);
637 ChunkHolder chunk = server.pullExecStatistics(j.getJobId(),
641 changed |= chunk.getChunk().length() > 0;
642 response.append(chunk.getChunk());
643 lastchunk = chunk.getNextPosition();
647 } catch (InterruptedException x)
653 } while (lastchunk >= 0 && j.getLastChunk() != lastchunk);
658 public void StartJob(AWsJob job)
660 Exception lex = null;
661 // boiler plate template
662 if (!(job instanceof MsaWSJob))
664 throw new Error(MessageManager.formatMessage(
665 "error.implementation_error_msawbjob_called", new String[]
666 { job.getClass().toString() }));
668 MsaWSJob j = (MsaWSJob) job;
671 if (Console.isDebugEnabled())
674 "Tried to submit an already submitted job " + j.getJobId());
680 if (j.seqs == null || j.seqs.size() == 0)
682 // special case - selection consisted entirely of empty sequences...
683 j.setjobStatus(JobStatus.FINISHED);
684 j.setStatus(MessageManager.getString("label.empty_alignment_job"));
688 j.addInitialStatus(); // list the presets/parameters used for the job in
692 j.setJobId(server.presetAlign(j.seqs, j.getServerPreset()));
694 else if (j.hasArguments())
696 j.setJobId(server.customAlign(j.seqs, j.getJabaArguments()));
700 j.setJobId(server.align(j.seqs));
703 if (j.getJobId() != null)
705 j.setSubmitted(true);
706 j.setSubjobComplete(false);
707 // jalview.bin.Console.outPrintln(WsURL + " Job Id '" + jobId + "'");
712 throw new Exception(MessageManager.formatMessage(
713 "exception.web_service_returned_null_try_later",
717 } catch (compbio.metadata.UnsupportedRuntimeException _lex)
720 wsInfo.appendProgressText(MessageManager.formatMessage(
721 "info.job_couldnt_be_run_server_doesnt_support_program",
723 { _lex.getMessage() }));
724 wsInfo.warnUser(_lex.getMessage(),
725 MessageManager.getString("warn.service_not_supported"));
726 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
727 wsInfo.setStatus(j.getJobnum(),
728 WebserviceInfo.STATE_STOPPED_SERVERERROR);
729 } catch (compbio.metadata.LimitExceededException _lex)
732 wsInfo.appendProgressText(MessageManager.formatMessage(
733 "info.job_couldnt_be_run_exceeded_hard_limit", new String[]
734 { _lex.getMessage() }));
735 wsInfo.warnUser(_lex.getMessage(),
736 MessageManager.getString("warn.input_is_too_big"));
737 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
738 wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_ERROR);
739 } catch (compbio.metadata.WrongParameterException _lex)
742 wsInfo.warnUser(_lex.getMessage(),
743 MessageManager.getString("warn.invalid_job_param_set"));
744 wsInfo.appendProgressText(MessageManager.formatMessage(
745 "info.job_couldnt_be_run_incorrect_param_setting",
747 { _lex.getMessage() }));
748 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
749 wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_ERROR);
752 // For unexpected errors
753 jalview.bin.Console.errPrintln(WebServiceName
754 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
755 + "When contacting Server:" + WsUrl + "\n");
756 e.printStackTrace(System.err);
757 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
758 wsInfo.setStatus(j.getJobnum(),
759 WebserviceInfo.STATE_STOPPED_SERVERERROR);
760 } catch (Exception e)
762 // For unexpected errors
763 jalview.bin.Console.errPrintln(WebServiceName
764 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
765 + "When contacting Server:" + WsUrl + "\n");
766 e.printStackTrace(System.err);
767 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
768 wsInfo.setStatus(j.getJobnum(),
769 WebserviceInfo.STATE_STOPPED_SERVERERROR);
772 if (!j.isSubmitted())
774 // Boilerplate code here
775 // TODO: JBPNote catch timeout or other fault types explicitly
777 j.setAllowedServerExceptions(0);
778 wsInfo.appendProgressText(j.getJobnum(), MessageManager.getString(
779 "info.failed_to_submit_sequences_for_alignment"));
785 public void parseResult()
787 long progbar = IdUtils.newId(IdType.PROGRESS);
788 wsInfo.setProgressBar(
789 MessageManager.getString("status.collecting_job_results"),
791 int results = 0; // number of result sets received
792 JobStateSummary finalState = new JobStateSummary();
795 for (int j = 0; j < jobs.length; j++)
797 MsaWSJob msjob = ((MsaWSJob) jobs[j]);
798 if (jobs[j].isFinished() && msjob.alignment == null)
800 int nunchanged = 3, nexcept = 3;
801 boolean jpchanged = false, jpex = false;
806 jpchanged = updateJobProgress(msjob);
812 } catch (Exception e)
816 "Exception when retrieving remaining Job progress data for job "
817 + msjob.getJobId() + " on server " + WsUrl);
821 // set flag remember that we've had an exception.
829 Thread.sleep(jpex ? 2400 : 1200); // wait a bit longer if we
830 // experienced an exception.
831 } catch (Exception ex)
837 } while (nunchanged > 0 && nexcept > 0);
839 if (Console.isDebugEnabled())
841 jalview.bin.Console.outPrintln("Job Execution file for job: "
842 + msjob.getJobId() + " on server " + WsUrl);
843 jalview.bin.Console.outPrintln(msjob.getStatus());
844 jalview.bin.Console.outPrintln("*** End of status");
849 msjob.alignment = server.getResult(msjob.getJobId());
850 } catch (compbio.metadata.ResultNotAvailableException e)
852 // job has failed for some reason - probably due to invalid
855 "Results not available for finished job - marking as broken job.",
857 msjob.jobProgress.append(
858 "\nResult not available. Probably due to invalid input or parameter settings. Server error message below:\n\n"
859 + e.getLocalizedMessage());
860 msjob.setjobStatus(JobStatus.FAILED);
861 } catch (Exception e)
863 Console.error("Couldn't get Alignment for job.", e);
864 // TODO: Increment count and retry ?
865 msjob.setjobStatus(JobStatus.UNDEFINED);
868 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
869 if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete()
870 && jobs[j].hasResults())
873 compbio.data.sequence.Alignment alignment = ((MsaWSJob) jobs[j]).alignment;
874 if (alignment != null)
876 // server.close(jobs[j].getJobnum());
877 // wsInfo.appendProgressText(jobs[j].getJobnum(),
878 // "\nAlignment Object Method Notes\n");
879 // wsInfo.appendProgressText(jobs[j].getJobnum(),
881 // "+alignment.getMetadata().getProgram().toString());
882 // JBPNote The returned files from a webservice could be
883 // hidden behind icons in the monitor window that,
884 // when clicked, pop up their corresponding data
888 } catch (Exception ex)
892 "Unexpected exception when processing results for " + alTitle,
894 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
898 wsInfo.showResultsNewFrame
899 .addActionListener(new java.awt.event.ActionListener()
902 public void actionPerformed(java.awt.event.ActionEvent evt)
904 displayResults(true);
908 .addActionListener(new java.awt.event.ActionListener()
911 public void actionPerformed(java.awt.event.ActionEvent evt)
913 displayResults(false);
916 wsInfo.setResultsReady();
920 wsInfo.setFinishedNoResults();
922 updateGlobalStatus(finalState);
923 wsInfo.setProgressBar(null, progbar);
927 * Display alignment results in a new frame (or - not currently supported -
928 * added to an existing alignment).
932 void displayResults(boolean newFrame)
934 // view input or result data for each block
935 List<AlignmentOrder> alorders = new ArrayList<AlignmentOrder>();
936 SequenceI[][] results = new SequenceI[jobs.length][];
937 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
938 String lastProgram = null;
940 for (int j = 0; j < jobs.length; j++)
942 if (jobs[j].hasResults())
944 msjob = (MsaWSJob) jobs[j];
945 Object[] res = msjob.getAlignment();
946 lastProgram = msjob.getAlignmentProgram();
947 alorders.add((AlignmentOrder) res[1]);
948 results[j] = (SequenceI[]) res[0];
949 orders[j] = (AlignmentOrder) res[1];
951 // SequenceI[] alignment = input.getUpdated
958 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
959 // trash references to original result data
960 for (int j = 0; j < jobs.length; j++)
965 SequenceI[] alignment = (SequenceI[]) newview[0];
966 HiddenColumns hidden = (HiddenColumns) newview[1];
967 Alignment al = new Alignment(alignment);
968 // TODO: add 'provenance' property to alignment from the method notes
969 if (lastProgram != null)
971 al.setProperty("Alignment Program", lastProgram);
973 // accompanying each subjob
976 al.setDataset(dataset);
979 propagateDatasetMappings(al);
980 // JBNote- TODO: warn user if a block is input rather than aligned data ?
984 displayInNewFrame(al, alorders, hidden);
989 // TODO 2.9.x feature
990 jalview.bin.Console.outPrintln("MERGE WITH OLD FRAME");
991 // TODO: modify alignment in original frame, replacing old for new
992 // alignment using the commands.EditCommand model to ensure the update can
998 * Display the alignment result in a new frame.
1002 * @param columnselection
1004 protected void displayInNewFrame(AlignmentI al,
1005 List<AlignmentOrder> alorders, HiddenColumns hidden)
1007 AlignFrame af = new AlignFrame(al, hidden, AlignFrame.DEFAULT_WIDTH,
1008 AlignFrame.DEFAULT_HEIGHT);
1010 // initialise with same renderer settings as in parent alignframe.
1011 af.getFeatureRenderer().transferSettings(this.featureSettings);
1013 if (alorders.size() > 0)
1015 addSortByMenuItems(af, alorders);
1018 // TODO: refactor retrieve and show as new splitFrame as Desktop method
1021 * If alignment was requested from one half of a SplitFrame, show in a
1022 * SplitFrame with the other pane similarly aligned.
1024 AlignFrame requestedBy = getRequestingAlignFrame();
1025 if (requestedBy != null && requestedBy.getSplitViewContainer() != null
1026 && requestedBy.getSplitViewContainer()
1027 .getComplement(requestedBy) != null)
1029 AlignmentI complement = requestedBy.getSplitViewContainer()
1030 .getComplement(requestedBy);
1031 String complementTitle = requestedBy.getSplitViewContainer()
1032 .getComplementTitle(requestedBy);
1033 // becomes null if the alignment window was closed before the alignment
1035 AlignmentI copyComplement = new Alignment(complement);
1036 // todo should this be done by copy constructor?
1037 copyComplement.setGapCharacter(complement.getGapCharacter());
1038 // share the same dataset (and the mappings it holds)
1039 copyComplement.setDataset(complement.getDataset());
1040 copyComplement.alignAs(al);
1041 if (copyComplement.getHeight() > 0)
1043 af.setTitle(alTitle);
1044 AlignFrame af2 = new AlignFrame(copyComplement,
1045 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
1046 af2.setTitle(complementTitle);
1047 String linkedTitle = MessageManager
1048 .getString("label.linked_view_title");
1049 JInternalFrame splitFrame = new SplitFrame(
1050 al.isNucleotide() ? af : af2, al.isNucleotide() ? af2 : af);
1051 Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
1057 * Not from SplitFrame, or failed to created a complementary alignment
1059 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
1060 AlignFrame.DEFAULT_HEIGHT);
1064 * Add sort order options to the AlignFrame menus.
1069 protected void addSortByMenuItems(AlignFrame af,
1070 List<AlignmentOrder> alorders)
1073 if (alorders.size() == 1)
1075 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
1080 // construct a non-redundant ordering set
1081 List<String> names = new ArrayList<String>();
1082 for (int i = 0, l = alorders.size(); i < l; i++)
1084 String orderName = " Region " + i;
1089 if (alorders.get(i).equals(alorders.get(j)))
1093 orderName += "," + j;
1101 if (i == 0 && j == 1)
1107 names.add(orderName);
1110 for (int i = 0, l = alorders.size(); i < l; i++)
1112 af.addSortByOrderMenuItem(
1113 WebServiceName + (names.get(i)) + " Ordering",
1120 public boolean canMergeResults()