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.WsParamSetI;
43 import java.util.ArrayList;
44 import java.util.Hashtable;
45 import java.util.List;
47 import java.util.Vector;
49 import javax.swing.JInternalFrame;
51 import compbio.data.msa.MsaWS;
52 import compbio.metadata.Argument;
53 import compbio.metadata.ChunkHolder;
54 import compbio.metadata.JobStatus;
55 import compbio.metadata.Preset;
57 class MsaWSThread extends AWS2Thread implements WSClientI
59 boolean submitGaps = false; // pass sequences including gaps to alignment
63 boolean preserveOrder = true; // and always store and recover sequence
67 class MsaWSJob extends JWs2Job
71 WsParamSetI preset = null;
73 List<Argument> arguments = null;
78 ArrayList<compbio.data.sequence.FastaSequence> seqs = new ArrayList<>();
83 compbio.data.sequence.Alignment alignment;
85 // set if the job didn't get run - then the input is simply returned to the
87 private boolean returnInput = false;
97 public MsaWSJob(int jobNum, SequenceI[] inSeqs)
100 if (!prepareInput(inSeqs, 2))
103 subjobComplete = true;
109 Hashtable<String, Map> SeqNames = new Hashtable();
111 Vector<String[]> emptySeqs = new Vector();
114 * prepare input sequences for MsaWS service
117 * jalview sequences to be prepared
119 * minimum number of residues required for this MsaWS service
120 * @return true if seqs contains sequences to be submitted to service.
122 // TODO: return compbio.seqs list or nothing to indicate validity.
123 private boolean prepareInput(SequenceI[] seqs, int minlen)
128 throw new Error(MessageManager.getString(
129 "error.implementation_error_minlen_must_be_greater_zero"));
131 for (int i = 0; i < seqs.length; i++)
133 if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
138 boolean valid = nseqs > 1; // need at least two seqs
139 compbio.data.sequence.FastaSequence seq;
140 for (int i = 0, n = 0; i < seqs.length; i++)
142 String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
146 SeqNames.put(newname,
147 jalview.analysis.SeqsetUtils.SeqCharacterHash(seqs[i]));
148 if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
150 // make new input sequence with or without gaps
151 seq = new compbio.data.sequence.FastaSequence(newname,
152 (submitGaps) ? seqs[i].getSequenceAsString()
153 : AlignSeq.extractGaps(
154 jalview.util.Comparison.GapChars,
155 seqs[i].getSequenceAsString()));
161 if (seqs[i].getEnd() >= seqs[i].getStart())
163 empty = (submitGaps) ? seqs[i].getSequenceAsString()
164 : AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
165 seqs[i].getSequenceAsString());
167 emptySeqs.add(new String[] { newname, empty });
175 * @return true if getAlignment will return a valid alignment result.
178 public boolean hasResults()
180 if (subjobComplete && isFinished() && (alignment != null
181 || (emptySeqs != null && emptySeqs.size() > 0)))
190 * get the alignment including any empty sequences in the original order
191 * with original ids. Caller must access the alignment.getMetadata() object
192 * to annotate the final result passsed to the user.
194 * @return { SequenceI[], AlignmentOrder }
196 public Object[] getAlignment()
198 // is this a generic subjob or a Jws2 specific Object[] return signature
201 SequenceI[] alseqs = null;
202 char alseq_gapchar = '-';
204 if (alignment.getSequences().size() > 0)
206 alseqs = new SequenceI[alignment.getSequences().size()];
207 for (compbio.data.sequence.FastaSequence seq : alignment
210 alseqs[alseq_l++] = new Sequence(seq.getId(),
213 alseq_gapchar = alignment.getMetadata().getGapchar();
216 // add in the empty seqs.
217 if (emptySeqs.size() > 0)
219 SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
224 for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
226 if (w < alseqs[i].getLength())
228 w = alseqs[i].getLength();
230 t_alseqs[i] = alseqs[i];
234 // check that aligned width is at least as wide as emptySeqs width.
236 for (i = 0, w = emptySeqs.size(); i < w; i++)
238 String[] es = emptySeqs.get(i);
239 if (es != null && es[1] != null)
241 int sw = es[1].length();
248 // make a gapped string.
249 StringBuffer insbuff = new StringBuffer(w);
250 for (i = 0; i < nw; i++)
252 insbuff.append(alseq_gapchar);
256 for (i = 0; i < alseq_l; i++)
258 int sw = t_alseqs[i].getLength();
262 alseqs[i].setSequence(t_alseqs[i].getSequenceAsString()
263 + insbuff.substring(0, sw - nw));
267 for (i = 0, w = emptySeqs.size(); i < w; i++)
269 String[] es = emptySeqs.get(i);
272 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(es[0],
273 insbuff.toString(), 1, 0);
277 if (es[1].length() < nw)
279 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
281 es[1] + insbuff.substring(0, nw - es[1].length()),
282 1, 1 + es[1].length());
286 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
293 AlignmentOrder msaorder = new AlignmentOrder(alseqs);
294 // always recover the order - makes parseResult()'s life easier.
295 jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
296 // account for any missing sequences
297 jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
298 return new Object[] { alseqs, msaorder };
304 * mark subjob as cancelled and set result object appropriatly
309 subjobComplete = true;
315 * @return boolean true if job can be submitted.
318 public boolean hasValidInput()
320 // TODO: get attributes for this MsaWS instance to check if it can do two
321 // sequence alignment.
322 if (seqs != null && seqs.size() >= 2) // two or more sequences is valid ?
329 StringBuffer jobProgress = new StringBuffer();
331 public void setStatus(String string)
333 jobProgress.setLength(0);
334 jobProgress.append(string);
338 public String getStatus()
340 return jobProgress.toString();
344 public boolean hasStatus()
346 return jobProgress != null;
350 * @return the lastChunk
352 public long getLastChunk()
359 * the lastChunk to set
361 public void setLastChunk(long lastChunk)
363 this.lastChunk = lastChunk;
366 String alignmentProgram = null;
368 public String getAlignmentProgram()
370 return alignmentProgram;
373 public boolean hasArguments()
375 return (arguments != null && arguments.size() > 0)
376 || (preset != null && preset instanceof JabaWsParamSet);
379 public List<Argument> getJabaArguments()
381 List<Argument> newargs = new ArrayList<>();
382 if (preset != null && preset instanceof JabaWsParamSet)
384 newargs.addAll(((JabaWsParamSet) preset).getjabaArguments());
386 if (arguments != null && arguments.size() > 0)
388 newargs.addAll(arguments);
394 * add a progess header to status string containing presets/args used
396 public void addInitialStatus()
400 jobProgress.append("Using "
401 + (preset instanceof JabaPreset ? "Server" : "User")
402 + "Preset: " + preset.getName());
403 if (preset instanceof JabaWsParamSet)
405 for (Argument opt : ((JabaWsParamSet) preset).getjabaArguments())
408 opt.getName() + " " + opt.getDefaultValue() + "\n");
412 if (arguments != null && arguments.size() > 0)
414 jobProgress.append("With custom parameters : \n");
415 // merge arguments with preset's own arguments.
416 for (Argument opt : arguments)
419 opt.getName() + " " + opt.getDefaultValue() + "\n");
422 jobProgress.append("\nJob Output:\n");
425 public boolean isPresetJob()
427 return preset != null && preset instanceof JabaPreset;
430 public Preset getServerPreset()
432 return (isPresetJob()) ? ((JabaPreset) preset).p : null;
436 String alTitle; // name which will be used to form new alignment window.
438 AlignmentI dataset; // dataset to which the new alignment will be
442 @SuppressWarnings("unchecked")
446 * set basic options for this (group) of Msa jobs
453 private MsaWSThread(MsaWS server, String wsUrl, WebserviceInfo wsinfo,
454 jalview.gui.AlignFrame alFrame, AlignmentView alview,
455 String wsname, boolean subgaps, boolean presorder)
457 super(alFrame, wsinfo, alview, wsname, wsUrl);
458 this.server = server;
459 this.submitGaps = subgaps;
460 this.preserveOrder = presorder;
464 * create one or more Msa jobs to align visible seuqences in _msa
477 MsaWSThread(MsaWS server2, WsParamSetI preset, List<Argument> paramset,
478 String wsUrl, WebserviceInfo wsinfo,
479 jalview.gui.AlignFrame alFrame, String wsname, String title,
480 AlignmentView _msa, boolean subgaps, boolean presorder,
483 this(server2, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
484 OutputHeader = wsInfo.getProgressText();
488 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
491 int nvalid = 0, njobs = conmsa.length;
492 jobs = new MsaWSJob[njobs];
493 for (int j = 0; j < njobs; j++)
497 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), conmsa[j]);
501 jobs[j] = new MsaWSJob(0, conmsa[j]);
503 if (((MsaWSJob) jobs[j]).hasValidInput())
507 ((MsaWSJob) jobs[j]).preset = preset;
508 ((MsaWSJob) jobs[j]).arguments = paramset;
509 ((MsaWSJob) jobs[j]).alignmentProgram = wsname;
512 wsinfo.setProgressName("region " + jobs[j].getJobnum(),
513 jobs[j].getJobnum());
515 wsinfo.setProgressText(jobs[j].getJobnum(), OutputHeader);
517 validInput = nvalid > 0;
521 boolean validInput = false;
525 * @return true if the thread will perform a calculation
527 public boolean hasValidInput()
533 public boolean isCancellable()
539 public void cancelJob()
541 if (!jobComplete && jobs != null)
543 boolean cancelled = true;
544 for (int job = 0; job < jobs.length; job++)
546 if (jobs[job].isSubmitted() && !jobs[job].isSubjobComplete())
548 String cancelledMessage = "";
551 boolean cancelledJob = server.cancelJob(jobs[job].getJobId());
552 if (true) // cancelledJob || true)
555 // if the Jaba server indicates the job can't be cancelled, its
556 // because its running on the server's local execution engine
557 // so we just close the window anyway.
558 cancelledMessage = "Job cancelled.";
559 ((MsaWSJob) jobs[job]).cancel(); // TODO: refactor to avoid this
561 wsInfo.setStatus(jobs[job].getJobnum(),
562 WebserviceInfo.STATE_CANCELLED_OK);
566 // VALID UNSTOPPABLE JOB
567 cancelledMessage += "Server cannot cancel this job. just close the window.\n";
569 // wsInfo.setStatus(jobs[job].jobnum,
570 // WebserviceInfo.STATE_RUNNING);
572 } catch (Exception exc)
574 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
577 "Exception whilst cancelling " + jobs[job].getJobId(),
580 wsInfo.setProgressText(jobs[job].getJobnum(),
581 OutputHeader + cancelledMessage + "\n");
585 // if we hadn't submitted then just mark the job as cancelled.
586 jobs[job].setSubjobComplete(true);
587 wsInfo.setStatus(jobs[job].getJobnum(),
588 WebserviceInfo.STATE_CANCELLED_OK);
594 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
597 this.interrupt(); // kick thread to update job states.
603 wsInfo.setProgressText(OutputHeader
604 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
610 public void pollJob(AWsJob job) throws Exception
612 // TODO: investigate if we still need to cast here in J1.6
613 MsaWSJob j = ((MsaWSJob) job);
614 // this is standard code, but since the interface doesn't comprise of a
615 // basic one that implements (getJobStatus, pullExecStatistics) we have to
616 // repeat the code for all jw2s services.
617 j.setjobStatus(server.getJobStatus(job.getJobId()));
618 updateJobProgress(j);
624 * @return true if more job progress data was available
627 protected boolean updateJobProgress(MsaWSJob j) throws Exception
629 StringBuffer response = j.jobProgress;
630 long lastchunk = j.getLastChunk();
631 boolean changed = false;
634 j.setLastChunk(lastchunk);
635 ChunkHolder chunk = server.pullExecStatistics(j.getJobId(),
639 changed |= chunk.getChunk().length() > 0;
640 response.append(chunk.getChunk());
641 lastchunk = chunk.getNextPosition();
645 } catch (InterruptedException x)
651 } while (lastchunk >= 0 && j.getLastChunk() != lastchunk);
656 public void StartJob(AWsJob job)
658 Exception lex = null;
659 // boiler plate template
660 if (!(job instanceof MsaWSJob))
662 throw new Error(MessageManager.formatMessage(
663 "error.implementation_error_msawbjob_called", new String[]
664 { job.getClass().toString() }));
666 MsaWSJob j = (MsaWSJob) job;
669 if (Cache.log.isDebugEnabled())
672 "Tried to submit an already submitted job " + j.getJobId());
678 if (j.seqs == null || j.seqs.size() == 0)
680 // special case - selection consisted entirely of empty sequences...
681 j.setjobStatus(JobStatus.FINISHED);
682 j.setStatus(MessageManager.getString("label.empty_alignment_job"));
686 j.addInitialStatus(); // list the presets/parameters used for the job in
690 j.setJobId(server.presetAlign(j.seqs, j.getServerPreset()));
692 else if (j.hasArguments())
694 j.setJobId(server.customAlign(j.seqs, j.getJabaArguments()));
698 j.setJobId(server.align(j.seqs));
701 if (j.getJobId() != null)
703 j.setSubmitted(true);
704 j.setSubjobComplete(false);
705 // System.out.println(WsURL + " Job Id '" + jobId + "'");
710 throw new Exception(MessageManager.formatMessage(
711 "exception.web_service_returned_null_try_later",
715 } catch (compbio.metadata.UnsupportedRuntimeException _lex)
718 wsInfo.appendProgressText(MessageManager.formatMessage(
719 "info.job_couldnt_be_run_server_doesnt_support_program",
721 { _lex.getMessage() }));
722 wsInfo.warnUser(_lex.getMessage(),
723 MessageManager.getString("warn.service_not_supported"));
724 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
725 wsInfo.setStatus(j.getJobnum(),
726 WebserviceInfo.STATE_STOPPED_SERVERERROR);
727 } catch (compbio.metadata.LimitExceededException _lex)
730 wsInfo.appendProgressText(MessageManager.formatMessage(
731 "info.job_couldnt_be_run_exceeded_hard_limit", new String[]
732 { _lex.getMessage() }));
733 wsInfo.warnUser(_lex.getMessage(),
734 MessageManager.getString("warn.input_is_too_big"));
735 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
736 wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_ERROR);
737 } catch (compbio.metadata.WrongParameterException _lex)
740 wsInfo.warnUser(_lex.getMessage(),
741 MessageManager.getString("warn.invalid_job_param_set"));
742 wsInfo.appendProgressText(MessageManager.formatMessage(
743 "info.job_couldnt_be_run_incorrect_param_setting",
745 { _lex.getMessage() }));
746 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
747 wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_ERROR);
750 // For unexpected errors
751 System.err.println(WebServiceName
752 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
753 + "When contacting Server:" + WsUrl + "\n");
754 e.printStackTrace(System.err);
755 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
756 wsInfo.setStatus(j.getJobnum(),
757 WebserviceInfo.STATE_STOPPED_SERVERERROR);
758 } catch (Exception e)
760 // For unexpected errors
761 System.err.println(WebServiceName
762 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
763 + "When contacting Server:" + WsUrl + "\n");
764 e.printStackTrace(System.err);
765 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
766 wsInfo.setStatus(j.getJobnum(),
767 WebserviceInfo.STATE_STOPPED_SERVERERROR);
770 if (!j.isSubmitted())
772 // Boilerplate code here
773 // TODO: JBPNote catch timeout or other fault types explicitly
775 j.setAllowedServerExceptions(0);
776 wsInfo.appendProgressText(j.getJobnum(), MessageManager.getString(
777 "info.failed_to_submit_sequences_for_alignment"));
783 public void parseResult()
785 long progbar = System.currentTimeMillis();
786 wsInfo.setProgressBar(
787 MessageManager.getString("status.collecting_job_results"),
789 int results = 0; // number of result sets received
790 JobStateSummary finalState = new JobStateSummary();
793 for (int j = 0; j < jobs.length; j++)
795 MsaWSJob msjob = ((MsaWSJob) jobs[j]);
796 if (jobs[j].isFinished() && msjob.alignment == null)
798 int nunchanged = 3, nexcept = 3;
799 boolean jpchanged = false, jpex = false;
804 jpchanged = updateJobProgress(msjob);
810 } catch (Exception e)
814 "Exception when retrieving remaining Job progress data for job "
815 + msjob.getJobId() + " on server " + WsUrl);
819 // set flag remember that we've had an exception.
827 Thread.sleep(jpex ? 2400 : 1200); // wait a bit longer if we
828 // experienced an exception.
829 } catch (Exception ex)
835 } while (nunchanged > 0 && nexcept > 0);
837 if (Cache.log.isDebugEnabled())
839 System.out.println("Job Execution file for job: "
840 + msjob.getJobId() + " on server " + WsUrl);
841 System.out.println(msjob.getStatus());
842 System.out.println("*** End of status");
847 msjob.alignment = server.getResult(msjob.getJobId());
848 } catch (compbio.metadata.ResultNotAvailableException e)
850 // job has failed for some reason - probably due to invalid
853 "Results not available for finished job - marking as broken job.",
855 msjob.jobProgress.append(
856 "\nResult not available. Probably due to invalid input or parameter settings. Server error message below:\n\n"
857 + e.getLocalizedMessage());
858 msjob.setjobStatus(JobStatus.FAILED);
859 } catch (Exception e)
861 Cache.log.error("Couldn't get Alignment for job.", e);
862 // TODO: Increment count and retry ?
863 msjob.setjobStatus(JobStatus.UNDEFINED);
866 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
867 if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete()
868 && jobs[j].hasResults())
871 compbio.data.sequence.Alignment alignment = ((MsaWSJob) jobs[j]).alignment;
872 if (alignment != null)
874 // server.close(jobs[j].getJobnum());
875 // wsInfo.appendProgressText(jobs[j].getJobnum(),
876 // "\nAlignment Object Method Notes\n");
877 // wsInfo.appendProgressText(jobs[j].getJobnum(),
879 // "+alignment.getMetadata().getProgram().toString());
880 // JBPNote The returned files from a webservice could be
881 // hidden behind icons in the monitor window that,
882 // when clicked, pop up their corresponding data
886 } catch (Exception ex)
890 "Unexpected exception when processing results for " + alTitle,
892 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
896 wsInfo.showResultsNewFrame
897 .addActionListener(new java.awt.event.ActionListener()
900 public void actionPerformed(java.awt.event.ActionEvent evt)
902 displayResults(true);
906 .addActionListener(new java.awt.event.ActionListener()
909 public void actionPerformed(java.awt.event.ActionEvent evt)
911 displayResults(false);
914 wsInfo.setResultsReady();
918 wsInfo.setFinishedNoResults();
920 updateGlobalStatus(finalState);
921 wsInfo.setProgressBar(null, progbar);
925 * Display alignment results in a new frame (or - not currently supported -
926 * added to an existing alignment).
930 void displayResults(boolean newFrame)
932 // view input or result data for each block
933 List<AlignmentOrder> alorders = new ArrayList<>();
934 SequenceI[][] results = new SequenceI[jobs.length][];
935 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
936 String lastProgram = null;
938 for (int j = 0; j < jobs.length; j++)
940 if (jobs[j].hasResults())
942 msjob = (MsaWSJob) jobs[j];
943 Object[] res = msjob.getAlignment();
944 lastProgram = msjob.getAlignmentProgram();
945 alorders.add((AlignmentOrder) res[1]);
946 results[j] = (SequenceI[]) res[0];
947 orders[j] = (AlignmentOrder) res[1];
949 // SequenceI[] alignment = input.getUpdated
956 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
957 // trash references to original result data
958 for (int j = 0; j < jobs.length; j++)
963 SequenceI[] alignment = (SequenceI[]) newview[0];
964 HiddenColumns hidden = (HiddenColumns) newview[1];
965 Alignment al = new Alignment(alignment);
966 // TODO: add 'provenance' property to alignment from the method notes
967 if (lastProgram != null)
969 al.setProperty("Alignment Program", lastProgram);
971 // accompanying each subjob
974 al.setDataset(dataset);
977 propagateDatasetMappings(al);
978 // JBNote- TODO: warn user if a block is input rather than aligned data ?
982 displayInNewFrame(al, alorders, hidden);
986 // TODO 2.9.x feature
987 System.out.println("MERGE WITH OLD FRAME");
988 // TODO: modify alignment in original frame, replacing old for new
989 // alignment using the commands.EditCommand model to ensure the update can
995 * Display the alignment result in a new frame.
999 * @param columnselection
1001 protected void displayInNewFrame(AlignmentI al,
1002 List<AlignmentOrder> alorders, HiddenColumns hidden)
1004 AlignFrame af = new AlignFrame(al, hidden, AlignFrame.DEFAULT_WIDTH,
1005 AlignFrame.DEFAULT_HEIGHT);
1007 // initialise with same renderer settings as in parent alignframe.
1008 af.getFeatureRenderer().transferSettings(this.featureSettings);
1010 if (alorders.size() > 0)
1012 addSortByMenuItems(af, alorders);
1015 // TODO: refactor retrieve and show as new splitFrame as Desktop method
1018 * If alignment was requested from one half of a SplitFrame, show in a
1019 * SplitFrame with the other pane similarly aligned.
1021 AlignFrame requestedBy = getRequestingAlignFrame();
1022 if (requestedBy != null && requestedBy.getSplitViewContainer() != null
1023 && requestedBy.getSplitViewContainer()
1024 .getComplement(requestedBy) != null)
1026 AlignmentI complement = requestedBy.getSplitViewContainer()
1027 .getComplement(requestedBy);
1028 String complementTitle = requestedBy.getSplitViewContainer()
1029 .getComplementTitle(requestedBy);
1030 // becomes null if the alignment window was closed before the alignment
1032 AlignmentI copyComplement = new Alignment(complement);
1033 // todo should this be done by copy constructor?
1034 copyComplement.setGapCharacter(complement.getGapCharacter());
1035 // share the same dataset (and the mappings it holds)
1036 copyComplement.setDataset(complement.getDataset());
1037 copyComplement.alignAs(al);
1038 if (copyComplement.getHeight() > 0)
1040 af.setTitle(alTitle);
1041 AlignFrame af2 = new AlignFrame(copyComplement,
1042 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
1043 af2.setTitle(complementTitle);
1044 String linkedTitle = MessageManager
1045 .getString("label.linked_view_title");
1046 JInternalFrame splitFrame = new SplitFrame(
1047 al.isNucleotide() ? af : af2, al.isNucleotide() ? af2 : af);
1048 Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
1054 * Not from SplitFrame, or failed to created a complementary alignment
1056 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
1057 AlignFrame.DEFAULT_HEIGHT);
1061 * Add sort order options to the AlignFrame menus.
1066 protected void addSortByMenuItems(AlignFrame af,
1067 List<AlignmentOrder> alorders)
1070 if (alorders.size() == 1)
1072 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
1077 // construct a non-redundant ordering set
1078 List<String> names = new ArrayList<>();
1079 for (int i = 0, l = alorders.size(); i < l; i++)
1081 String orderName = " Region " + i;
1086 if (alorders.get(i).equals(alorders.get(j)))
1090 orderName += "," + j;
1098 if (i == 0 && j == 1)
1104 names.add(orderName);
1107 for (int i = 0, l = alorders.size(); i < l; i++)
1109 af.addSortByOrderMenuItem(
1110 WebServiceName + (names.get(i)) + " Ordering",
1117 public boolean canMergeResults()