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 jalview.analysis.AlignSeq;
24 import jalview.bin.Cache;
25 import jalview.datamodel.Alignment;
26 import jalview.datamodel.AlignmentI;
27 import jalview.datamodel.AlignmentOrder;
28 import jalview.datamodel.AlignmentView;
29 import jalview.datamodel.HiddenColumns;
30 import jalview.datamodel.Sequence;
31 import jalview.datamodel.SequenceI;
32 import jalview.gui.AlignFrame;
33 import jalview.gui.Desktop;
34 import jalview.gui.SplitFrame;
35 import jalview.gui.WebserviceInfo;
36 import jalview.util.MessageManager;
37 import jalview.ws.AWsJob;
38 import jalview.ws.JobStateSummary;
39 import jalview.ws.WSClientI;
40 import jalview.ws.jws2.dm.JabaWsParamSet;
41 import jalview.ws.params.ArgumentI;
42 import jalview.ws.params.WsParamSetI;
44 import java.util.ArrayList;
45 import java.util.List;
46 import java.util.Vector;
48 import javax.swing.JInternalFrame;
50 import compbio.data.msa.MsaWS;
51 import compbio.metadata.Argument;
52 import compbio.metadata.ChunkHolder;
53 import compbio.metadata.JobStatus;
54 import compbio.metadata.Preset;
56 class MsaWSThread extends AWS2Thread implements WSClientI
58 boolean submitGaps = false; // pass sequences including gaps to alignment
62 boolean preserveOrder = true; // and always store and recover sequence
66 class MsaWSJob extends JWs2Job
73 ArrayList<compbio.data.sequence.FastaSequence> seqs = new ArrayList<>();
78 compbio.data.sequence.Alignment alignment;
80 // set if the job didn't get run - then the input is simply returned to the
82 private boolean returnInput = false;
92 public MsaWSJob(int jobNum, SequenceI[] inSeqs)
95 if (!prepareInput(inSeqs, 2))
98 subjobComplete = true;
104 Vector<String[]> emptySeqs = new Vector();
107 * prepare input sequences for MsaWS service
110 * jalview sequences to be prepared
112 * minimum number of residues required for this MsaWS service
113 * @return true if seqs contains sequences to be submitted to service.
115 // TODO: return compbio.seqs list or nothing to indicate validity.
116 private boolean prepareInput(SequenceI[] seqs, int minlen)
121 throw new Error(MessageManager.getString(
122 "error.implementation_error_minlen_must_be_greater_zero"));
124 for (int i = 0; i < seqs.length; i++)
126 if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
131 boolean valid = nseqs > 1; // need at least two seqs
132 compbio.data.sequence.FastaSequence seq;
133 for (int i = 0, n = 0; i < seqs.length; i++)
135 String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
139 SeqNames.put(newname,
140 jalview.analysis.SeqsetUtils.SeqCharacterHash(seqs[i]));
141 if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
143 // make new input sequence with or without gaps
144 seq = new compbio.data.sequence.FastaSequence(newname,
145 (submitGaps) ? seqs[i].getSequenceAsString()
146 : AlignSeq.extractGaps(
147 jalview.util.Comparison.GapChars,
148 seqs[i].getSequenceAsString()));
154 if (seqs[i].getEnd() >= seqs[i].getStart())
156 empty = (submitGaps) ? seqs[i].getSequenceAsString()
157 : AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
158 seqs[i].getSequenceAsString());
160 emptySeqs.add(new String[] { newname, empty });
168 * @return true if getAlignment will return a valid alignment result.
171 public boolean hasResults()
173 if (subjobComplete && isFinished() && (alignment != null
174 || (emptySeqs != null && emptySeqs.size() > 0)))
183 * get the alignment including any empty sequences in the original order
184 * with original ids. Caller must access the alignment.getMetadata() object
185 * to annotate the final result passsed to the user.
187 * @return { SequenceI[], AlignmentOrder }
189 public Object[] getAlignment()
191 // is this a generic subjob or a Jws2 specific Object[] return signature
194 SequenceI[] alseqs = null;
195 char alseq_gapchar = '-';
197 if (alignment.getSequences().size() > 0)
199 alseqs = new SequenceI[alignment.getSequences().size()];
200 for (compbio.data.sequence.FastaSequence seq : alignment
203 alseqs[alseq_l++] = new Sequence(seq.getId(),
206 alseq_gapchar = alignment.getMetadata().getGapchar();
209 // add in the empty seqs.
210 if (emptySeqs.size() > 0)
212 SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
217 for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
219 if (w < alseqs[i].getLength())
221 w = alseqs[i].getLength();
223 t_alseqs[i] = alseqs[i];
227 // check that aligned width is at least as wide as emptySeqs width.
229 for (i = 0, w = emptySeqs.size(); i < w; i++)
231 String[] es = emptySeqs.get(i);
232 if (es != null && es[1] != null)
234 int sw = es[1].length();
241 // make a gapped string.
242 StringBuffer insbuff = new StringBuffer(w);
243 for (i = 0; i < nw; i++)
245 insbuff.append(alseq_gapchar);
249 for (i = 0; i < alseq_l; i++)
251 int sw = t_alseqs[i].getLength();
255 alseqs[i].setSequence(t_alseqs[i].getSequenceAsString()
256 + insbuff.substring(0, sw - nw));
260 for (i = 0, w = emptySeqs.size(); i < w; i++)
262 String[] es = emptySeqs.get(i);
265 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(es[0],
266 insbuff.toString(), 1, 0);
270 if (es[1].length() < nw)
272 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
274 es[1] + insbuff.substring(0, nw - es[1].length()),
275 1, 1 + es[1].length());
279 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
286 AlignmentOrder msaorder = new AlignmentOrder(alseqs);
287 // always recover the order - makes parseResult()'s life easier.
288 jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
289 // account for any missing sequences
290 jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
291 return new Object[] { alseqs, msaorder };
297 * mark subjob as cancelled and set result object appropriatly
302 subjobComplete = true;
308 * @return boolean true if job can be submitted.
311 public boolean hasValidInput()
313 // TODO: get attributes for this MsaWS instance to check if it can do two
314 // sequence alignment.
315 if (seqs != null && seqs.size() >= 2) // two or more sequences is valid ?
322 StringBuffer jobProgress = new StringBuffer();
324 public void setStatus(String string)
326 jobProgress.setLength(0);
327 jobProgress.append(string);
331 public String getStatus()
333 return jobProgress.toString();
337 public boolean hasStatus()
339 return jobProgress != null;
343 * @return the lastChunk
345 public long getLastChunk()
352 * the lastChunk to set
354 public void setLastChunk(long lastChunk)
356 this.lastChunk = lastChunk;
359 String alignmentProgram = null;
361 public String getAlignmentProgram()
363 return alignmentProgram;
366 public boolean hasArguments()
368 return (arguments != null && arguments.size() > 0)
369 || (preset != null && preset instanceof JabaWsParamSet);
372 public List<Argument> getJabaArguments()
374 List<Argument> newargs = new ArrayList<>();
375 if (preset != null && preset instanceof JabaWsParamSet)
377 newargs.addAll(((JabaWsParamSet) preset).getjabaArguments());
379 if (arguments != null && arguments.size() > 0)
381 newargs.addAll(JabaParamStore.getJabafromJwsArgs(arguments));
387 * add a progess header to status string containing presets/args used
389 public void addInitialStatus()
393 jobProgress.append("Using "
394 + (preset instanceof JabaPreset ? "Server" : "User")
395 + "Preset: " + preset.getName());
396 if (preset instanceof JabaWsParamSet)
398 for (Argument opt : getJabaArguments())
401 opt.getName() + " " + opt.getDefaultValue() + "\n");
405 if (arguments != null && arguments.size() > 0)
407 jobProgress.append("With custom parameters : \n");
408 // merge arguments with preset's own arguments.
409 for (Argument opt : getJabaArguments())
412 opt.getName() + " " + opt.getDefaultValue() + "\n");
415 jobProgress.append("\nJob Output:\n");
418 public boolean isPresetJob()
420 return preset != null && preset instanceof JabaPreset;
423 public Preset getServerPreset()
425 return (isPresetJob()) ? ((JabaPreset) preset).p : null;
429 String alTitle; // name which will be used to form new alignment window.
431 AlignmentI dataset; // dataset to which the new alignment will be
435 @SuppressWarnings("unchecked")
439 * set basic options for this (group) of Msa jobs
446 private MsaWSThread(MsaWS server, String wsUrl, WebserviceInfo wsinfo,
447 jalview.gui.AlignFrame alFrame, AlignmentView alview,
448 String wsname, boolean subgaps, boolean presorder)
450 super(alFrame, wsinfo, alview, wsname, wsUrl);
451 this.server = server;
452 this.submitGaps = subgaps;
453 this.preserveOrder = presorder;
457 * create one or more Msa jobs to align visible seuqences in _msa
470 MsaWSThread(MsaWS server2, WsParamSetI preset, List<ArgumentI> paramset,
471 String wsUrl, WebserviceInfo wsinfo,
472 jalview.gui.AlignFrame alFrame, String wsname, String title,
473 AlignmentView _msa, boolean subgaps, boolean presorder,
476 this(server2, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
477 OutputHeader = wsInfo.getProgressText();
481 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
484 int nvalid = 0, njobs = conmsa.length;
485 jobs = new AWsJob[njobs];
486 for (int j = 0; j < njobs; j++)
490 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), conmsa[j]);
494 jobs[j] = new MsaWSJob(0, conmsa[j]);
496 if (jobs[j].hasValidInput())
500 jobs[j].setPreset(preset);
501 jobs[j].setArguments(paramset);
502 ((MsaWSJob) jobs[j]).alignmentProgram = wsname;
505 wsinfo.setProgressName("region " + jobs[j].getJobnum(),
506 jobs[j].getJobnum());
508 wsinfo.setProgressText(jobs[j].getJobnum(), OutputHeader);
510 validInput = nvalid > 0;
514 boolean validInput = false;
518 * @return true if the thread will perform a calculation
520 public boolean hasValidInput()
526 public boolean isCancellable()
532 public void cancelJob()
534 if (!jobComplete && jobs != null)
536 boolean cancelled = true;
537 for (int job = 0; job < jobs.length; job++)
539 if (jobs[job].isSubmitted() && !jobs[job].isSubjobComplete())
541 String cancelledMessage = "";
544 boolean cancelledJob = server.cancelJob(jobs[job].getJobId());
545 if (true) // cancelledJob || true)
548 // if the Jaba server indicates the job can't be cancelled, its
549 // because its running on the server's local execution engine
550 // so we just close the window anyway.
551 cancelledMessage = "Job cancelled.";
552 ((MsaWSJob) jobs[job]).cancel(); // TODO: refactor to avoid this
554 wsInfo.setStatus(jobs[job].getJobnum(),
555 WebserviceInfo.STATE_CANCELLED_OK);
559 // VALID UNSTOPPABLE JOB
560 cancelledMessage += "Server cannot cancel this job. just close the window.\n";
562 // wsInfo.setStatus(jobs[job].jobnum,
563 // WebserviceInfo.STATE_RUNNING);
565 } catch (Exception exc)
567 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
570 "Exception whilst cancelling " + jobs[job].getJobId(),
573 wsInfo.setProgressText(jobs[job].getJobnum(),
574 OutputHeader + cancelledMessage + "\n");
578 // if we hadn't submitted then just mark the job as cancelled.
579 jobs[job].setSubjobComplete(true);
580 wsInfo.setStatus(jobs[job].getJobnum(),
581 WebserviceInfo.STATE_CANCELLED_OK);
587 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
590 this.interrupt(); // kick thread to update job states.
596 wsInfo.setProgressText(OutputHeader
597 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
603 public void pollJob(AWsJob job) throws Exception
605 // TODO: investigate if we still need to cast here in J1.6
606 MsaWSJob j = ((MsaWSJob) job);
607 // this is standard code, but since the interface doesn't comprise of a
608 // basic one that implements (getJobStatus, pullExecStatistics) we have to
609 // repeat the code for all jw2s services.
610 j.setjobStatus(server.getJobStatus(job.getJobId()));
611 updateJobProgress(j);
617 * @return true if more job progress data was available
620 protected boolean updateJobProgress(MsaWSJob j) throws Exception
622 StringBuffer response = j.jobProgress;
623 long lastchunk = j.getLastChunk();
624 boolean changed = false;
627 j.setLastChunk(lastchunk);
628 ChunkHolder chunk = server.pullExecStatistics(j.getJobId(),
632 changed |= chunk.getChunk().length() > 0;
633 response.append(chunk.getChunk());
634 lastchunk = chunk.getNextPosition();
638 } catch (InterruptedException x)
644 } while (lastchunk >= 0 && j.getLastChunk() != lastchunk);
649 public void StartJob(AWsJob job)
651 Exception lex = null;
652 // boiler plate template
653 if (!(job instanceof MsaWSJob))
655 throw new Error(MessageManager.formatMessage(
656 "error.implementation_error_msawbjob_called", new String[]
657 { job.getClass().toString() }));
659 MsaWSJob j = (MsaWSJob) job;
662 if (Cache.log.isDebugEnabled())
665 "Tried to submit an already submitted job " + j.getJobId());
671 if (j.seqs == null || j.seqs.size() == 0)
673 // special case - selection consisted entirely of empty sequences...
674 j.setjobStatus(JobStatus.FINISHED);
675 j.setStatus(MessageManager.getString("label.empty_alignment_job"));
679 j.addInitialStatus(); // list the presets/parameters used for the job in
683 j.setJobId(server.presetAlign(j.seqs, j.getServerPreset()));
685 else if (j.hasArguments())
687 j.setJobId(server.customAlign(j.seqs, j.getJabaArguments()));
691 j.setJobId(server.align(j.seqs));
694 if (j.getJobId() != null)
696 j.setSubmitted(true);
697 j.setSubjobComplete(false);
698 // System.out.println(WsURL + " Job Id '" + jobId + "'");
703 throw new Exception(MessageManager.formatMessage(
704 "exception.web_service_returned_null_try_later",
708 } catch (compbio.metadata.UnsupportedRuntimeException _lex)
711 wsInfo.appendProgressText(MessageManager.formatMessage(
712 "info.job_couldnt_be_run_server_doesnt_support_program",
714 { _lex.getMessage() }));
715 wsInfo.warnUser(_lex.getMessage(),
716 MessageManager.getString("warn.service_not_supported"));
717 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
718 wsInfo.setStatus(j.getJobnum(),
719 WebserviceInfo.STATE_STOPPED_SERVERERROR);
720 } catch (compbio.metadata.LimitExceededException _lex)
723 wsInfo.appendProgressText(MessageManager.formatMessage(
724 "info.job_couldnt_be_run_exceeded_hard_limit", new String[]
725 { _lex.getMessage() }));
726 wsInfo.warnUser(_lex.getMessage(),
727 MessageManager.getString("warn.input_is_too_big"));
728 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
729 wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_ERROR);
730 } catch (compbio.metadata.WrongParameterException _lex)
733 wsInfo.warnUser(_lex.getMessage(),
734 MessageManager.getString("warn.invalid_job_param_set"));
735 wsInfo.appendProgressText(MessageManager.formatMessage(
736 "info.job_couldnt_be_run_incorrect_param_setting",
738 { _lex.getMessage() }));
739 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
740 wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_ERROR);
743 // For unexpected errors
744 System.err.println(WebServiceName
745 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
746 + "When contacting Server:" + WsUrl + "\n");
747 e.printStackTrace(System.err);
748 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
749 wsInfo.setStatus(j.getJobnum(),
750 WebserviceInfo.STATE_STOPPED_SERVERERROR);
751 } catch (Exception e)
753 // For unexpected errors
754 System.err.println(WebServiceName
755 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
756 + "When contacting Server:" + WsUrl + "\n");
757 e.printStackTrace(System.err);
758 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
759 wsInfo.setStatus(j.getJobnum(),
760 WebserviceInfo.STATE_STOPPED_SERVERERROR);
763 if (!j.isSubmitted())
765 // Boilerplate code here
766 // TODO: JBPNote catch timeout or other fault types explicitly
768 j.setAllowedServerExceptions(0);
769 wsInfo.appendProgressText(j.getJobnum(), MessageManager.getString(
770 "info.failed_to_submit_sequences_for_alignment"));
776 public void parseResult()
778 long progbar = System.currentTimeMillis();
779 wsInfo.setProgressBar(
780 MessageManager.getString("status.collecting_job_results"),
782 int results = 0; // number of result sets received
783 JobStateSummary finalState = new JobStateSummary();
786 for (int j = 0; j < jobs.length; j++)
788 MsaWSJob msjob = ((MsaWSJob) jobs[j]);
789 if (jobs[j].isFinished() && msjob.alignment == null)
791 int nunchanged = 3, nexcept = 3;
792 boolean jpchanged = false, jpex = false;
797 jpchanged = updateJobProgress(msjob);
803 } catch (Exception e)
807 "Exception when retrieving remaining Job progress data for job "
808 + msjob.getJobId() + " on server " + WsUrl);
812 // set flag remember that we've had an exception.
820 Thread.sleep(jpex ? 2400 : 1200); // wait a bit longer if we
821 // experienced an exception.
822 } catch (Exception ex)
828 } while (nunchanged > 0 && nexcept > 0);
830 if (Cache.log.isDebugEnabled())
832 System.out.println("Job Execution file for job: "
833 + msjob.getJobId() + " on server " + WsUrl);
834 System.out.println(msjob.getStatus());
835 System.out.println("*** End of status");
840 msjob.alignment = server.getResult(msjob.getJobId());
841 } catch (compbio.metadata.ResultNotAvailableException e)
843 // job has failed for some reason - probably due to invalid
846 "Results not available for finished job - marking as broken job.",
848 msjob.jobProgress.append(
849 "\nResult not available. Probably due to invalid input or parameter settings. Server error message below:\n\n"
850 + e.getLocalizedMessage());
851 msjob.setjobStatus(JobStatus.FAILED);
852 } catch (Exception e)
854 Cache.log.error("Couldn't get Alignment for job.", e);
855 // TODO: Increment count and retry ?
856 msjob.setjobStatus(JobStatus.UNDEFINED);
859 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
860 if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete()
861 && jobs[j].hasResults())
864 compbio.data.sequence.Alignment alignment = ((MsaWSJob) jobs[j]).alignment;
865 if (alignment != null)
867 // server.close(jobs[j].getJobnum());
868 // wsInfo.appendProgressText(jobs[j].getJobnum(),
869 // "\nAlignment Object Method Notes\n");
870 // wsInfo.appendProgressText(jobs[j].getJobnum(),
872 // "+alignment.getMetadata().getProgram().toString());
873 // JBPNote The returned files from a webservice could be
874 // hidden behind icons in the monitor window that,
875 // when clicked, pop up their corresponding data
879 } catch (Exception ex)
883 "Unexpected exception when processing results for " + alTitle,
885 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
889 wsInfo.showResultsNewFrame
890 .addActionListener(new java.awt.event.ActionListener()
893 public void actionPerformed(java.awt.event.ActionEvent evt)
895 displayResults(true);
899 .addActionListener(new java.awt.event.ActionListener()
902 public void actionPerformed(java.awt.event.ActionEvent evt)
904 displayResults(false);
907 wsInfo.setResultsReady();
911 wsInfo.setFinishedNoResults();
913 updateGlobalStatus(finalState);
914 wsInfo.setProgressBar(null, progbar);
918 * Display alignment results in a new frame (or - not currently supported -
919 * added to an existing alignment).
923 void displayResults(boolean newFrame)
925 // view input or result data for each block
926 List<AlignmentOrder> alorders = new ArrayList<>();
927 SequenceI[][] results = new SequenceI[jobs.length][];
928 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
929 String lastProgram = null;
931 for (int j = 0; j < jobs.length; j++)
933 if (jobs[j].hasResults())
935 msjob = (MsaWSJob) jobs[j];
936 Object[] res = msjob.getAlignment();
937 lastProgram = msjob.getAlignmentProgram();
938 alorders.add((AlignmentOrder) res[1]);
939 results[j] = (SequenceI[]) res[0];
940 orders[j] = (AlignmentOrder) res[1];
942 // SequenceI[] alignment = input.getUpdated
949 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
950 // trash references to original result data
951 for (int j = 0; j < jobs.length; j++)
956 SequenceI[] alignment = (SequenceI[]) newview[0];
957 HiddenColumns hidden = (HiddenColumns) newview[1];
958 Alignment al = new Alignment(alignment);
959 // TODO: add 'provenance' property to alignment from the method notes
960 if (lastProgram != null)
962 al.setProperty("Alignment Program", lastProgram);
964 // accompanying each subjob
967 al.setDataset(dataset);
970 propagateDatasetMappings(al);
971 // JBNote- TODO: warn user if a block is input rather than aligned data ?
975 displayInNewFrame(al, alorders, hidden);
979 // TODO 2.9.x feature
980 System.out.println("MERGE WITH OLD FRAME");
981 // TODO: modify alignment in original frame, replacing old for new
982 // alignment using the commands.EditCommand model to ensure the update can
988 * Display the alignment result in a new frame.
992 * @param columnselection
994 protected void displayInNewFrame(AlignmentI al,
995 List<AlignmentOrder> alorders, HiddenColumns hidden)
997 AlignFrame af = new AlignFrame(al, hidden, AlignFrame.DEFAULT_WIDTH,
998 AlignFrame.DEFAULT_HEIGHT);
1000 // initialise with same renderer settings as in parent alignframe.
1001 af.getFeatureRenderer().transferSettings(this.featureSettings);
1003 if (alorders.size() > 0)
1005 addSortByMenuItems(af, alorders);
1008 // TODO: refactor retrieve and show as new splitFrame as Desktop method
1011 * If alignment was requested from one half of a SplitFrame, show in a
1012 * SplitFrame with the other pane similarly aligned.
1014 AlignFrame requestedBy = getRequestingAlignFrame();
1015 if (requestedBy != null && requestedBy.getSplitViewContainer() != null
1016 && requestedBy.getSplitViewContainer()
1017 .getComplement(requestedBy) != null)
1019 AlignmentI complement = requestedBy.getSplitViewContainer()
1020 .getComplement(requestedBy);
1021 String complementTitle = requestedBy.getSplitViewContainer()
1022 .getComplementTitle(requestedBy);
1023 // becomes null if the alignment window was closed before the alignment
1025 AlignmentI copyComplement = new Alignment(complement);
1026 // todo should this be done by copy constructor?
1027 copyComplement.setGapCharacter(complement.getGapCharacter());
1028 // share the same dataset (and the mappings it holds)
1029 copyComplement.setDataset(complement.getDataset());
1030 copyComplement.alignAs(al);
1031 if (copyComplement.getHeight() > 0)
1033 af.setTitle(alTitle);
1034 AlignFrame af2 = new AlignFrame(copyComplement,
1035 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
1036 af2.setTitle(complementTitle);
1037 String linkedTitle = MessageManager
1038 .getString("label.linked_view_title");
1039 JInternalFrame splitFrame = new SplitFrame(
1040 al.isNucleotide() ? af : af2, al.isNucleotide() ? af2 : af);
1041 Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
1047 * Not from SplitFrame, or failed to created a complementary alignment
1049 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
1050 AlignFrame.DEFAULT_HEIGHT);
1054 * Add sort order options to the AlignFrame menus.
1059 protected void addSortByMenuItems(AlignFrame af,
1060 List<AlignmentOrder> alorders)
1063 if (alorders.size() == 1)
1065 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
1070 // construct a non-redundant ordering set
1071 List<String> names = new ArrayList<>();
1072 for (int i = 0, l = alorders.size(); i < l; i++)
1074 String orderName = " Region " + i;
1079 if (alorders.get(i).equals(alorders.get(j)))
1083 orderName += "," + j;
1091 if (i == 0 && j == 1)
1097 names.add(orderName);
1100 for (int i = 0, l = alorders.size(); i < l; i++)
1102 af.addSortByOrderMenuItem(
1103 WebServiceName + (names.get(i)) + " Ordering",
1110 public boolean canMergeResults()