From 14193747f3831242bc7dfac12394eb20eb0ba480 Mon Sep 17 00:00:00 2001 From: gmungoc Date: Fri, 27 Oct 2017 10:54:03 +0100 Subject: [PATCH] JAL-1793 spike branch updated to latest --- RELEASE | 4 +- build.xml | 10 +- help/html/calculations/pairwise.html | 20 +- help/html/releases.html | 47 +++- src/MCview/AppletPDBCanvas.java | 2 +- src/MCview/PDBCanvas.java | 3 +- src/jalview/analysis/AlignSeq.java | 94 ++++--- src/jalview/analysis/AlignmentUtils.java | 33 +-- src/jalview/api/AlignViewportI.java | 5 + src/jalview/appletgui/AlignFrame.java | 8 +- src/jalview/appletgui/AlignViewport.java | 22 -- src/jalview/appletgui/AlignmentPanel.java | 17 -- src/jalview/appletgui/AppletJmol.java | 6 +- src/jalview/appletgui/AppletJmolBinding.java | 8 + src/jalview/appletgui/ExtJmol.java | 13 +- src/jalview/appletgui/OverviewPanel.java | 4 + src/jalview/appletgui/PaintRefresher.java | 19 +- src/jalview/controller/AlignViewController.java | 8 - src/jalview/datamodel/AlignmentAnnotation.java | 13 - src/jalview/datamodel/Mapping.java | 13 - src/jalview/datamodel/Sequence.java | 2 +- src/jalview/datamodel/SequenceFeature.java | 129 ++++++++-- .../datamodel/features/FeatureAttributeType.java | 12 + src/jalview/datamodel/features/FeatureSource.java | 78 ++++++ src/jalview/datamodel/features/FeatureSourceI.java | 45 ++++ src/jalview/datamodel/features/FeatureSources.java | 51 ++++ src/jalview/ext/ensembl/EnsemblGene.java | 77 +++--- src/jalview/ext/ensembl/EnsemblLookup.java | 102 +++++++- src/jalview/ext/ensembl/EnsemblSymbol.java | 1 - src/jalview/ext/jmol/JalviewJmolBinding.java | 18 +- src/jalview/gui/AlignFrame.java | 28 +-- src/jalview/gui/AlignViewport.java | 24 -- src/jalview/gui/AlignmentPanel.java | 88 ++++--- src/jalview/gui/AppJmol.java | 11 +- src/jalview/gui/AppJmolBinding.java | 6 + src/jalview/gui/AquaInternalFrameManager.java | 256 ++++++++++++++++++++ src/jalview/gui/CalculationChooser.java | 12 +- src/jalview/gui/ChimeraViewFrame.java | 21 +- src/jalview/gui/CrossRefAction.java | 2 +- src/jalview/gui/CutAndPasteHtmlTransfer.java | 1 + src/jalview/gui/Desktop.java | 47 +--- src/jalview/gui/FeatureSettings.java | 7 +- src/jalview/gui/FontChooser.java | 2 +- src/jalview/gui/IProgressIndicator.java | 3 +- src/jalview/gui/IdPanel.java | 4 +- src/jalview/gui/Jalview2XML.java | 88 ++----- src/jalview/gui/OverviewPanel.java | 18 +- src/jalview/gui/PCAPanel.java | 15 ++ src/jalview/gui/PaintRefresher.java | 25 +- src/jalview/gui/PairwiseAlignPanel.java | 112 ++++++--- src/jalview/gui/PopupMenu.java | 8 +- src/jalview/gui/ProgressBar.java | 141 ++++++----- src/jalview/gui/PromptUserConfig.java | 10 +- src/jalview/gui/SeqPanel.java | 21 +- src/jalview/gui/StructureChooser.java | 60 ++--- src/jalview/gui/StructureViewerBase.java | 4 +- src/jalview/gui/TreePanel.java | 41 +++- src/jalview/gui/ViewSelectionMenu.java | 9 - src/jalview/io/FileLoader.java | 14 -- src/jalview/io/InputStreamParser.java | 7 - src/jalview/io/SequenceAnnotationReport.java | 48 +--- src/jalview/io/vcf/VCFLoader.java | 142 +++++++++-- src/jalview/javascript/JSFunctionExec.java | 13 - .../javascript/MouseOverStructureListener.java | 7 - src/jalview/jbgui/GCutAndPasteHtmlTransfer.java | 19 ++ .../structure/StructureSelectionManager.java | 104 ++++---- src/jalview/util/MappingUtils.java | 51 ++++ src/jalview/util/StringUtils.java | 41 ++++ src/jalview/viewmodel/AlignmentViewport.java | 32 ++- src/jalview/viewmodel/ViewportRanges.java | 32 ++- src/jalview/ws/jws2/AbstractJabaCalcWorker.java | 20 ++ src/jalview/ws/jws2/jabaws2/Jws2Instance.java | 8 +- test/jalview/analysis/AlignmentGenerator.java | 95 +++++--- test/jalview/analysis/AlignmentUtilsTests.java | 72 +++++- test/jalview/analysis/TestAlignSeq.java | 10 +- test/jalview/datamodel/SequenceFeatureTest.java | 43 ++++ test/jalview/gui/FreeUpMemoryTest.java | 216 +++++++++++++++++ test/jalview/gui/PairwiseAlignmentPanelTest.java | 73 ++++++ test/jalview/gui/ProgressBarTest.java | 18 +- test/jalview/io/vcf/VCFLoaderTest.java | 23 +- test/jalview/io/vcf/testVcf.dat | 2 +- .../models/AAStructureBindingModelTest.java | 6 +- test/jalview/util/MappingUtilsTest.java | 45 ++++ test/jalview/util/StringUtilsTest.java | 22 ++ test/jalview/viewmodel/ViewportRangesTest.java | 60 +++++ 85 files changed, 2298 insertions(+), 853 deletions(-) create mode 100644 src/jalview/datamodel/features/FeatureAttributeType.java create mode 100644 src/jalview/datamodel/features/FeatureSource.java create mode 100644 src/jalview/datamodel/features/FeatureSourceI.java create mode 100644 src/jalview/datamodel/features/FeatureSources.java create mode 100644 src/jalview/gui/AquaInternalFrameManager.java create mode 100644 test/jalview/gui/FreeUpMemoryTest.java create mode 100644 test/jalview/gui/PairwiseAlignmentPanelTest.java diff --git a/RELEASE b/RELEASE index cecefec..f1faf34 100644 --- a/RELEASE +++ b/RELEASE @@ -1,2 +1,2 @@ -jalview.release=releases/Release_2_10_2b1_Branch -jalview.version=2.10.2b2 +jalview.release=releases/Release_2_10_3_Branch +jalview.version=2.10.3 diff --git a/build.xml b/build.xml index f39fdf3..bb146dd 100755 --- a/build.xml +++ b/build.xml @@ -106,8 +106,8 @@ - - + + @@ -425,7 +425,7 @@ - + @@ -451,8 +451,8 @@ - - j2se version="1.7+" + + j2se version="1.8+" diff --git a/help/html/calculations/pairwise.html b/help/html/calculations/pairwise.html index bb80b84..1090253 100755 --- a/help/html/calculations/pairwise.html +++ b/help/html/calculations/pairwise.html @@ -38,11 +38,21 @@

Gap open : 12
Gap extend : 2

-

When you select the pairwise alignment option a new window will - come up which will display the alignments in a text format as they - are calculated. Also displayed is information about the alignment - such as alignment score, length and percentage identity between the +

When you select the pairwise alignment option, a new window + will come up which displays the alignments in a text format, for + example:

+

+

+    FER1_SPIOL/5-13 TTMMGMAT
+ |. .. ||
+ FER1_MESCR/5-15 TAALSGAT +
+ shows the aligned sequences, where '|' links identical residues, and + (for peptide) '.' links residues that have a positive PAM250 score. +

The window also shows information about the alignment such as + alignment score, length and percentage identity between the sequences.

-

 

+

A button is also provided to allow you to view the sequences as + an alignment.

diff --git a/help/html/releases.html b/help/html/releases.html index cb2b278..92af377 100755 --- a/help/html/releases.html +++ b/help/html/releases.html @@ -89,11 +89,27 @@ li:before { Structure views don't get updated unless their colours have changed +
  • All linked sequences are highlighted for a structure mousover (Jmol) or selection (Chimera)
  • +
  • 'Cancel' button in progress bar for JABAWS AACon, RNAAliFold and Disorder prediction jobs +
  • + +
  • Stop codons are excluded in CDS/Protein view from Ensembl locus cross-references
  • +
  • Start/End limits are shown in Pairwise Alignment report
    • Example groovy script for generating a matrix of percent identity scores for current alignment.
    + Testing and Deployment +
    • Test to catch memory leaks in Jalview UI
    +
    - + General +
      +
    • Pressing tab after updating the colour threshold text field doesn't trigger an update to the alignment view
    • +
    • Race condition when parsing sequence ID strings in parallel
    • +
    • Overview windows are also closed when alignment window is closed
    • +
    • Export of features doesn't always respect group visibility
    • +
    + Desktop
    • Structures with whitespace chainCode cannot be viewed in Chimera
    • Protein annotation panel too high in CDS/Protein view @@ -101,10 +117,33 @@ li:before {
    • Can't edit the query after the server error warning icon is shown in Uniprot and PDB Free Text Search Dialogs
    • Slow EnsemblGenome ID lookup
    • -
    • Race condition when parsing sequence ID strings in parallel
    • +
    • Revised Ensembl REST API CDNA query
    • Hidden column marker in last column not rendered when switching back from Wrapped to normal view
    • Annotation display corrupted when scrolling right in unwapped alignment view
    • +
    • Existing features on subsequence incorrectly relocated when full sequence retrieved from database
    • +
    • Last reported memory still shown when Desktop->Show Memory is unticked (OSX only)
    • +
    • Amend Features dialog doesn't allow features of same type and group to be selected for amending
    • +
    • Jalview becomes sluggish in wide alignments when hidden columns are present
    • +
    • Jalview freezes when loading and displaying several structures
    • +
    • Black outlines left after resizing or moving a window
    • +
    • Unable to minimise windows within the Jalview desktop on OSX
    • +
    • Mouse wheel doesn't scroll vertically when in wrapped alignment mode
    • +
    • Scale mark not shown when close to right hand end of alignment
    • +
    • Pairwise alignment only aligns selected regions of each selected sequence
    • +
    • Alignment ruler height set incorrectly after canceling the Alignment Window's Font dialog
    • +
    • Show cross-references not enabled after restoring project until a new view is created
    • +
    + Applet
    +
      +
    • Concurrent modification exception when closing alignment panel
    + BioJSON
    +
      +
    • + BioJSON export does not preserve non-positional features +
    • +
    +
    @@ -1462,6 +1501,10 @@ li:before { after clicking on it to create new annotation for a column. +
  • + Null Pointer Exception raised when + pressing Add on an orphaned cut'n'paste window. +
  • diff --git a/src/MCview/AppletPDBCanvas.java b/src/MCview/AppletPDBCanvas.java index f94faba..b15c3cc 100644 --- a/src/MCview/AppletPDBCanvas.java +++ b/src/MCview/AppletPDBCanvas.java @@ -159,7 +159,7 @@ public class AppletPDBCanvas extends Panel try { - pdb = ssm.setMapping(seq, chains, pdbentry.getFile(), protocol); + pdb = ssm.setMapping(seq, chains, pdbentry.getFile(), protocol, null); if (protocol == DataSourceType.PASTE) { diff --git a/src/MCview/PDBCanvas.java b/src/MCview/PDBCanvas.java index b2f2503..ab172f2 100644 --- a/src/MCview/PDBCanvas.java +++ b/src/MCview/PDBCanvas.java @@ -153,7 +153,8 @@ public class PDBCanvas extends JPanel try { - pdb = ssm.setMapping(seq, chains, pdbentry.getFile(), protocol); + pdb = ssm.setMapping(seq, chains, pdbentry.getFile(), protocol, + ap.alignFrame); if (protocol.equals(jalview.io.DataSourceType.PASTE)) { diff --git a/src/jalview/analysis/AlignSeq.java b/src/jalview/analysis/AlignSeq.java index 34a21e6..1b2578e 100755 --- a/src/jalview/analysis/AlignSeq.java +++ b/src/jalview/analysis/AlignSeq.java @@ -36,6 +36,7 @@ import jalview.util.MessageManager; import java.awt.Color; import java.awt.Graphics; +import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -49,6 +50,14 @@ import java.util.StringTokenizer; */ public class AlignSeq { + private static final int MAX_NAME_LENGTH = 30; + + private static final int GAP_OPEN_COST = 120; + + private static final int GAP_EXTEND_COST = 20; + + private static final int GAP_INDEX = -1; + public static final String PEP = "pep"; public static final String DNA = "dna"; @@ -61,7 +70,7 @@ public class AlignSeq float[][] F; - int[][] traceback; + int[][] traceback; // todo is this actually used? int[] seq1; @@ -96,30 +105,20 @@ public class AlignSeq /** DOCUMENT ME!! */ public int seq2start; - /** DOCUMENT ME!! */ public int seq2end; int count; - /** DOCUMENT ME!! */ public float maxscore; - float pid; - int prev = 0; - int gapOpen = 120; - - int gapExtend = 20; - StringBuffer output = new StringBuffer(); String type; // AlignSeq.PEP or AlignSeq.DNA private ScoreMatrix scoreMatrix; - private static final int GAP_INDEX = -1; - /** * Creates a new AlignSeq object. * @@ -378,11 +377,10 @@ public class AlignSeq } } - // System.out.println(maxi + " " + maxj + " " + score[maxi][maxj]); int i = maxi; int j = maxj; int trace; - maxscore = score[i][j] / 10; + maxscore = score[i][j] / 10f; seq1end = maxi + 1; seq2end = maxj + 1; @@ -451,49 +449,48 @@ public class AlignSeq /** * DOCUMENT ME! */ - public void printAlignment(java.io.PrintStream os) + public void printAlignment(PrintStream os) { // TODO: Use original sequence characters rather than re-translated // characters in output // Find the biggest id length for formatting purposes - String s1id = s1.getName(), s2id = s2.getName(); - int maxid = s1.getName().length(); - if (s2.getName().length() > maxid) - { - maxid = s2.getName().length(); - } - if (maxid > 30) + String s1id = getAlignedSeq1().getDisplayId(true); + String s2id = getAlignedSeq2().getDisplayId(true); + int nameLength = Math.max(s1id.length(), s2id.length()); + if (nameLength > MAX_NAME_LENGTH) { - maxid = 30; + int truncateBy = nameLength - MAX_NAME_LENGTH; + nameLength = MAX_NAME_LENGTH; // JAL-527 - truncate the sequence ids - if (s1.getName().length() > maxid) + if (s1id.length() > nameLength) { - s1id = s1.getName().substring(0, 30); + int slashPos = s1id.lastIndexOf('/'); + s1id = s1id.substring(0, slashPos - truncateBy) + + s1id.substring(slashPos); } - if (s2.getName().length() > maxid) + if (s2id.length() > nameLength) { - s2id = s2.getName().substring(0, 30); + int slashPos = s2id.lastIndexOf('/'); + s2id = s2id.substring(0, slashPos - truncateBy) + + s2id.substring(slashPos); } } - int len = 72 - maxid - 1; + int len = 72 - nameLength - 1; int nochunks = ((aseq1.length - count) / len) + ((aseq1.length - count) % len > 0 ? 1 : 0); - pid = 0; + float pid = 0f; output.append("Score = ").append(score[maxi][maxj]).append(NEWLINE); output.append("Length of alignment = ") .append(String.valueOf(aseq1.length - count)).append(NEWLINE); output.append("Sequence "); - output.append(new Format("%" + maxid + "s").form(s1.getName())); - output.append(" : ").append(String.valueOf(s1.getStart())) - .append(" - ").append(String.valueOf(s1.getEnd())); + Format nameFormat = new Format("%" + nameLength + "s"); + output.append(nameFormat.form(s1id)); output.append(" (Sequence length = ") .append(String.valueOf(s1str.length())).append(")") .append(NEWLINE); output.append("Sequence "); - output.append(new Format("%" + maxid + "s").form(s2.getName())); - output.append(" : ").append(String.valueOf(s2.getStart())) - .append(" - ").append(String.valueOf(s2.getEnd())); + output.append(nameFormat.form(s2id)); output.append(" (Sequence length = ") .append(String.valueOf(s2str.length())).append(")") .append(NEWLINE).append(NEWLINE); @@ -503,7 +500,7 @@ public class AlignSeq for (int j = 0; j < nochunks; j++) { // Print the first aligned sequence - output.append(new Format("%" + (maxid) + "s").form(s1id)).append(" "); + output.append(nameFormat.form(s1id)).append(" "); for (int i = 0; i < len; i++) { @@ -514,7 +511,7 @@ public class AlignSeq } output.append(NEWLINE); - output.append(new Format("%" + (maxid) + "s").form(" ")).append(" "); + output.append(nameFormat.form(" ")).append(" "); /* * Print out the match symbols: @@ -534,7 +531,7 @@ public class AlignSeq pid++; output.append("|"); } - else if (type.equals("pep")) + else if (PEP.equals(type)) { if (pam250.getPairwiseScore(c1, c2) > 0) { @@ -554,8 +551,7 @@ public class AlignSeq // Now print the second aligned sequence output = output.append(NEWLINE); - output = output.append(new Format("%" + (maxid) + "s").form(s2id)) - .append(" "); + output = output.append(nameFormat.form(s2id)).append(" "); for (int i = 0; i < len; i++) { @@ -569,7 +565,8 @@ public class AlignSeq } pid = pid / (aseq1.length - count) * 100; - output = output.append(new Format("Percentage ID = %2.2f\n").form(pid)); + output.append(new Format("Percentage ID = %3.2f\n").form(pid)); + output.append(NEWLINE); try { os.print(output.toString()); @@ -591,7 +588,6 @@ public class AlignSeq public int findTrace(int i, int j) { int t = 0; - // float pairwiseScore = lookup[seq1[i]][seq2[j]]; float pairwiseScore = scoreMatrix.getPairwiseScore(s1str.charAt(i), s2str.charAt(j)); float max = score[i - 1][j - 1] + (pairwiseScore * 10); @@ -640,19 +636,19 @@ public class AlignSeq // top left hand element score[0][0] = scoreMatrix.getPairwiseScore(s1str.charAt(0), s2str.charAt(0)) * 10; - E[0][0] = -gapExtend; + E[0][0] = -GAP_EXTEND_COST; F[0][0] = 0; // Calculate the top row first for (int j = 1; j < m; j++) { // What should these values be? 0 maybe - E[0][j] = max(score[0][j - 1] - gapOpen, E[0][j - 1] - gapExtend); - F[0][j] = -gapExtend; + E[0][j] = max(score[0][j - 1] - GAP_OPEN_COST, E[0][j - 1] - GAP_EXTEND_COST); + F[0][j] = -GAP_EXTEND_COST; float pairwiseScore = scoreMatrix.getPairwiseScore(s1str.charAt(0), s2str.charAt(j)); - score[0][j] = max(pairwiseScore * 10, -gapOpen, -gapExtend); + score[0][j] = max(pairwiseScore * 10, -GAP_OPEN_COST, -GAP_EXTEND_COST); traceback[0][j] = 1; } @@ -660,8 +656,8 @@ public class AlignSeq // Now do the left hand column for (int i = 1; i < n; i++) { - E[i][0] = -gapOpen; - F[i][0] = max(score[i - 1][0] - gapOpen, F[i - 1][0] - gapExtend); + E[i][0] = -GAP_OPEN_COST; + F[i][0] = max(score[i - 1][0] - GAP_OPEN_COST, F[i - 1][0] - GAP_EXTEND_COST); float pairwiseScore = scoreMatrix.getPairwiseScore(s1str.charAt(i), s2str.charAt(0)); @@ -674,8 +670,8 @@ public class AlignSeq { for (int j = 1; j < m; j++) { - E[i][j] = max(score[i][j - 1] - gapOpen, E[i][j - 1] - gapExtend); - F[i][j] = max(score[i - 1][j] - gapOpen, F[i - 1][j] - gapExtend); + E[i][j] = max(score[i][j - 1] - GAP_OPEN_COST, E[i][j - 1] - GAP_EXTEND_COST); + F[i][j] = max(score[i - 1][j] - GAP_OPEN_COST, F[i - 1][j] - GAP_EXTEND_COST); float pairwiseScore = scoreMatrix.getPairwiseScore(s1str.charAt(i), s2str.charAt(j)); diff --git a/src/jalview/analysis/AlignmentUtils.java b/src/jalview/analysis/AlignmentUtils.java index 228446b..bef667d 100644 --- a/src/jalview/analysis/AlignmentUtils.java +++ b/src/jalview/analysis/AlignmentUtils.java @@ -2217,7 +2217,10 @@ public class AlignmentUtils /** * Returns a mapping from dna to protein by inspecting sequence features of - * type "CDS" on the dna. + * type "CDS" on the dna. A mapping is constructed if the total CDS feature + * length is 3 times the peptide length (optionally after dropping a trailing + * stop codon). This method does not check whether the CDS nucleotide sequence + * translates to the peptide sequence. * * @param dnaSeq * @param proteinSeq @@ -2229,6 +2232,15 @@ public class AlignmentUtils List ranges = findCdsPositions(dnaSeq); int mappedDnaLength = MappingUtils.getLength(ranges); + /* + * if not a whole number of codons, something is wrong, + * abort mapping + */ + if (mappedDnaLength % CODON_LENGTH > 0) + { + return null; + } + int proteinLength = proteinSeq.getLength(); int proteinStart = proteinSeq.getStart(); int proteinEnd = proteinSeq.getEnd(); @@ -2252,8 +2264,12 @@ public class AlignmentUtils if (codesForResidues == (proteinLength + 1)) { // assuming extra codon is for STOP and not in peptide + // todo: check trailing codon is indeed a STOP codon codesForResidues--; + mappedDnaLength -= CODON_LENGTH; + MappingUtils.removeEndPositions(CODON_LENGTH, ranges); } + if (codesForResidues == proteinLength) { proteinRange.add(new int[] { proteinStart, proteinEnd }); @@ -2264,7 +2280,7 @@ public class AlignmentUtils /** * Returns a list of CDS ranges found (as sequence positions base 1), i.e. of - * start/end positions of sequence features of type "CDS" (or a sub-type of + * [start, end] positions of sequence features of type "CDS" (or a sub-type of * CDS in the Sequence Ontology). The ranges are sorted into ascending start * position order, so this method is only valid for linear CDS in the same * sense as the protein product. @@ -2283,7 +2299,6 @@ public class AlignmentUtils return result; } SequenceFeatures.sortFeatures(sfs, true); - int startPhase = 0; for (SequenceFeature sf : sfs) { @@ -2301,7 +2316,7 @@ public class AlignmentUtils */ int begin = sf.getBegin(); int end = sf.getEnd(); - if (result.isEmpty()) + if (result.isEmpty() && phase > 0) { begin += phase; if (begin > end) @@ -2316,16 +2331,6 @@ public class AlignmentUtils } /* - * remove 'startPhase' positions (usually 0) from the first range - * so we begin at the start of a complete codon - */ - if (!result.isEmpty()) - { - // TODO JAL-2022 correctly model start phase > 0 - result.get(0)[0] += startPhase; - } - - /* * Finally sort ranges by start position. This avoids a dependency on * keeping features in order on the sequence (if they are in order anyway, * the sort will have almost no work to do). The implicit assumption is CDS diff --git a/src/jalview/api/AlignViewportI.java b/src/jalview/api/AlignViewportI.java index 3cb06c1..931eba6 100644 --- a/src/jalview/api/AlignViewportI.java +++ b/src/jalview/api/AlignViewportI.java @@ -21,6 +21,7 @@ package jalview.api; import jalview.analysis.Conservation; +import jalview.analysis.TreeModel; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentView; @@ -485,4 +486,8 @@ public interface AlignViewportI extends ViewStyleI */ @Override void setProteinFontAsCdna(boolean b); + + public abstract TreeModel getCurrentTree(); + + public abstract void setCurrentTree(TreeModel tree); } diff --git a/src/jalview/appletgui/AlignFrame.java b/src/jalview/appletgui/AlignFrame.java index 676d3cf..ef87671 100644 --- a/src/jalview/appletgui/AlignFrame.java +++ b/src/jalview/appletgui/AlignFrame.java @@ -1603,10 +1603,12 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, { System.exit(0); } - else + + viewport = null; + if (alignPanel != null && alignPanel.overviewPanel != null) { + alignPanel.overviewPanel.dispose(); } - viewport = null; alignPanel = null; this.dispose(); } @@ -4147,7 +4149,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, { // register the association(s) and quit, don't create any windows. if (StructureSelectionManager.getStructureSelectionManager(applet) - .setMapping(seqs, chains, pdb.getFile(), protocol) == null) + .setMapping(seqs, chains, pdb.getFile(), protocol, null) == null) { System.err.println("Failed to map " + pdb.getFile() + " (" + protocol + ") to any sequences"); diff --git a/src/jalview/appletgui/AlignViewport.java b/src/jalview/appletgui/AlignViewport.java index b07666e..262948d 100644 --- a/src/jalview/appletgui/AlignViewport.java +++ b/src/jalview/appletgui/AlignViewport.java @@ -20,7 +20,6 @@ */ package jalview.appletgui; -import jalview.analysis.TreeModel; import jalview.api.AlignViewportI; import jalview.api.FeatureSettingsModelI; import jalview.bin.JalviewLite; @@ -54,23 +53,12 @@ public class AlignViewport extends AlignmentViewport boolean validCharWidth = true; - TreeModel currentTree = null; - public jalview.bin.JalviewLite applet; boolean MAC = false; private AnnotationColumnChooser annotationColumnSelectionState; - @Override - public void finalize() - { - applet = null; - quality = null; - alignment = null; - colSel = null; - } - public AlignViewport(AlignmentI al, JalviewLite applet) { super(al); @@ -274,16 +262,6 @@ public class AlignViewport extends AlignmentViewport ranges.setEndSeq(height / getCharHeight()); } - public void setCurrentTree(TreeModel tree) - { - currentTree = tree; - } - - public TreeModel getCurrentTree() - { - return currentTree; - } - boolean centreColumnLabels; public boolean getCentreColumnLabels() diff --git a/src/jalview/appletgui/AlignmentPanel.java b/src/jalview/appletgui/AlignmentPanel.java index 906acc1..270b2f7 100644 --- a/src/jalview/appletgui/AlignmentPanel.java +++ b/src/jalview/appletgui/AlignmentPanel.java @@ -73,23 +73,6 @@ public class AlignmentPanel extends Panel // this value is set false when selection area being dragged boolean fastPaint = true; - @Override - public void finalize() throws Throwable - { - alignFrame = null; - av = null; - vpRanges = null; - seqPanel = null; - seqPanelHolder = null; - sequenceHolderPanel = null; - scalePanel = null; - scalePanelHolder = null; - annotationPanel = null; - annotationPanelHolder = null; - annotationSpaceFillerHolder = null; - super.finalize(); - } - public AlignmentPanel(AlignFrame af, final AlignViewport av) { try diff --git a/src/jalview/appletgui/AppletJmol.java b/src/jalview/appletgui/AppletJmol.java index 49219b9..3d1442d 100644 --- a/src/jalview/appletgui/AppletJmol.java +++ b/src/jalview/appletgui/AppletJmol.java @@ -134,7 +134,7 @@ public class AppletJmol extends EmbmenuFrame implements AlignmentPanel ap; - List _aps = new ArrayList(); // remove? never + List _aps = new ArrayList<>(); // remove? never // added to String fileLoadingError; @@ -213,7 +213,7 @@ public class AppletJmol extends EmbmenuFrame implements { reader = StructureSelectionManager .getStructureSelectionManager(ap.av.applet) - .setMapping(seq, chains, pdbentry.getFile(), protocol); + .setMapping(seq, chains, pdbentry.getFile(), protocol, null); // PROMPT USER HERE TO ADD TO NEW OR EXISTING VIEW? // FOR NOW, LETS JUST OPEN A NEW WINDOW } @@ -394,7 +394,7 @@ public class AppletJmol extends EmbmenuFrame implements void centerViewer() { - Vector toshow = new Vector(); + Vector toshow = new Vector<>(); for (int i = 0; i < chainMenu.getItemCount(); i++) { if (chainMenu.getItem(i) instanceof CheckboxMenuItem) diff --git a/src/jalview/appletgui/AppletJmolBinding.java b/src/jalview/appletgui/AppletJmolBinding.java index d5d53fb..2f61b24 100644 --- a/src/jalview/appletgui/AppletJmolBinding.java +++ b/src/jalview/appletgui/AppletJmolBinding.java @@ -24,6 +24,7 @@ import jalview.api.AlignmentViewPanel; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; import jalview.ext.jmol.JalviewJmolBinding; +import jalview.gui.IProgressIndicator; import jalview.io.DataSourceType; import jalview.structure.StructureSelectionManager; @@ -183,4 +184,11 @@ class AppletJmolBinding extends JalviewJmolBinding // TODO Auto-generated method stub return null; } + + @Override + protected IProgressIndicator getIProgressIndicator() + { + // no progress indicators on the applet + return null; + } } diff --git a/src/jalview/appletgui/ExtJmol.java b/src/jalview/appletgui/ExtJmol.java index 3966536..89228d5 100644 --- a/src/jalview/appletgui/ExtJmol.java +++ b/src/jalview/appletgui/ExtJmol.java @@ -26,6 +26,7 @@ import jalview.api.SequenceRenderer; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; import jalview.ext.jmol.JalviewJmolBinding; +import jalview.gui.IProgressIndicator; import jalview.io.DataSourceType; import java.awt.Container; @@ -65,6 +66,13 @@ public class ExtJmol extends JalviewJmolBinding } @Override + protected IProgressIndicator getIProgressIndicator() + { + // no progress indicators on applet (could access javascript for this) + return null; + } + + @Override public void updateColours(Object source) { @@ -92,6 +100,7 @@ public class ExtJmol extends JalviewJmolBinding } } + @Override public SequenceRenderer getSequenceRenderer(AlignmentViewPanel alignment) { @@ -137,8 +146,8 @@ public class ExtJmol extends JalviewJmolBinding @Override public void refreshPdbEntries() { - List pdbe = new ArrayList(); - List fileids = new ArrayList(); + List pdbe = new ArrayList<>(); + List fileids = new ArrayList<>(); SequenceI[] sq = ap.av.getAlignment().getSequencesArray(); for (int s = 0; s < sq.length; s++) { diff --git a/src/jalview/appletgui/OverviewPanel.java b/src/jalview/appletgui/OverviewPanel.java index 0256055..8ce597d 100755 --- a/src/jalview/appletgui/OverviewPanel.java +++ b/src/jalview/appletgui/OverviewPanel.java @@ -31,6 +31,7 @@ import java.awt.BorderLayout; import java.awt.CheckboxMenuItem; import java.awt.Cursor; import java.awt.Dimension; +import java.awt.Frame; import java.awt.Panel; import java.awt.PopupMenu; import java.awt.event.ComponentAdapter; @@ -322,6 +323,9 @@ public class OverviewPanel extends Panel implements Runnable, try { av.getRanges().removePropertyChangeListener(this); + Frame parent = (Frame) getParent(); + parent.dispose(); + parent.setVisible(false); } finally { av = null; diff --git a/src/jalview/appletgui/PaintRefresher.java b/src/jalview/appletgui/PaintRefresher.java index 32507fe..fe99187 100755 --- a/src/jalview/appletgui/PaintRefresher.java +++ b/src/jalview/appletgui/PaintRefresher.java @@ -24,8 +24,8 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.SequenceI; import java.awt.Component; -import java.util.Enumeration; import java.util.Hashtable; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Vector; @@ -78,13 +78,14 @@ public class PaintRefresher return; } - for (String id : components.keySet()) + Iterator it = components.keySet().iterator(); + while (it.hasNext()) { - Vector comps = components.get(id); + Vector comps = components.get(it.next()); comps.removeElement(comp); - if (comps.size() == 0) + if (comps.isEmpty()) { - components.remove(id); + it.remove(); } } } @@ -110,10 +111,10 @@ public class PaintRefresher return; } - Enumeration e = comps.elements(); - while (e.hasMoreElements()) + Iterator it = comps.iterator(); + while (it.hasNext()) { - comp = e.nextElement(); + comp = it.next(); if (comp == source) { @@ -122,7 +123,7 @@ public class PaintRefresher if (!comp.isValid()) { - comps.removeElement(comp); + it.remove(); } else if (validateSequences && comp instanceof AlignmentPanel && source instanceof AlignmentPanel) diff --git a/src/jalview/controller/AlignViewController.java b/src/jalview/controller/AlignViewController.java index 69e31cf..460c2b3 100644 --- a/src/jalview/controller/AlignViewController.java +++ b/src/jalview/controller/AlignViewController.java @@ -52,14 +52,6 @@ public class AlignViewController implements AlignViewControllerI */ private AlignViewControllerGuiI avcg; - @Override - protected void finalize() throws Throwable - { - viewport = null; - alignPanel = null; - avcg = null; - }; - public AlignViewController(AlignViewControllerGuiI alignFrame, AlignViewportI viewport, AlignmentViewPanel alignPanel) { diff --git a/src/jalview/datamodel/AlignmentAnnotation.java b/src/jalview/datamodel/AlignmentAnnotation.java index c464af2..09facbf 100755 --- a/src/jalview/datamodel/AlignmentAnnotation.java +++ b/src/jalview/datamodel/AlignmentAnnotation.java @@ -241,19 +241,6 @@ public class AlignmentAnnotation private boolean isrna; - /* - * (non-Javadoc) - * - * @see java.lang.Object#finalize() - */ - @Override - protected void finalize() throws Throwable - { - sequenceRef = null; - groupRef = null; - super.finalize(); - } - public static int getGraphValueFromString(String string) { if (string.equalsIgnoreCase("BAR_GRAPH")) diff --git a/src/jalview/datamodel/Mapping.java b/src/jalview/datamodel/Mapping.java index 328b96a..b5184fb 100644 --- a/src/jalview/datamodel/Mapping.java +++ b/src/jalview/datamodel/Mapping.java @@ -693,19 +693,6 @@ public class Mapping to = tto; } - /* - * (non-Javadoc) - * - * @see java.lang.Object#finalize() - */ - @Override - protected void finalize() throws Throwable - { - map = null; - to = null; - super.finalize(); - } - /** * Returns an iterator which can serve up the aligned codon column positions * and their corresponding peptide products diff --git a/src/jalview/datamodel/Sequence.java b/src/jalview/datamodel/Sequence.java index 9680766..1905f42 100755 --- a/src/jalview/datamodel/Sequence.java +++ b/src/jalview/datamodel/Sequence.java @@ -713,7 +713,7 @@ public class Sequence extends ASequence implements SequenceI @Override public String getChromosomeId() { - // strip of "chromosome:" prefix to chrId + // strip off "chromosome:" prefix to chrId return ref.getAccessionId().substring( DBRefEntry.CHROMOSOME.length() + 1); } diff --git a/src/jalview/datamodel/SequenceFeature.java b/src/jalview/datamodel/SequenceFeature.java index 4208ce1..8f82a1a 100755 --- a/src/jalview/datamodel/SequenceFeature.java +++ b/src/jalview/datamodel/SequenceFeature.java @@ -20,7 +20,11 @@ */ package jalview.datamodel; +import jalview.datamodel.features.FeatureAttributeType; import jalview.datamodel.features.FeatureLocationI; +import jalview.datamodel.features.FeatureSourceI; +import jalview.datamodel.features.FeatureSources; +import jalview.util.StringUtils; import java.util.HashMap; import java.util.Map; @@ -29,10 +33,9 @@ import java.util.TreeMap; import java.util.Vector; /** - * DOCUMENT ME! - * - * @author $author$ - * @version $Revision$ + * A class that models a single contiguous feature on a sequence. If flag + * 'contactFeature' is true, the start and end positions are interpreted instead + * as two contact points. */ public class SequenceFeature implements FeatureLocationI { @@ -52,6 +55,8 @@ public class SequenceFeature implements FeatureLocationI // private key for ENA location designed not to conflict with real GFF data private static final String LOCATION = "!Location"; + private static final String ROW_DATA = "%s%s%s"; + /* * map of otherDetails special keys, and their value fields' delimiter */ @@ -60,6 +65,8 @@ public class SequenceFeature implements FeatureLocationI static { INFO_KEYS.put("CSQ", ","); + // todo capture second level metadata (CSQ FORMAT) + // and delimiter "|" so as to report in a table within a table? } /* @@ -95,6 +102,12 @@ public class SequenceFeature implements FeatureLocationI public Vector links; + /* + * the identifier (if known) for the FeatureSource held in FeatureSources, + * as a provider of metadata about feature attributes + */ + private String source; + /** * Constructs a duplicate feature. Note: Uses makes a shallow copy of the * otherDetails map, so the new and original SequenceFeature may reference the @@ -166,6 +179,8 @@ public class SequenceFeature implements FeatureLocationI this(newType, sf.getDescription(), newBegin, newEnd, newScore, newGroup); + this.source = sf.source; + if (sf.otherDetails != null) { otherDetails = new HashMap(); @@ -548,30 +563,31 @@ public class SequenceFeature implements FeatureLocationI } /** - * Answers a formatted text report of feature details + * Answers an html-formatted report of feature details * * @return */ public String getDetailsReport() { + FeatureSourceI metadata = FeatureSources.getInstance() + .getSource(source); + StringBuilder sb = new StringBuilder(128); - if (begin == end) - { - sb.append(String.format("%s %d %s", type, begin, description)); - } - else - { - sb.append(String.format("%s %d-%d %s", type, begin, end, description)); - } + sb.append("
    "); + sb.append(""); + sb.append(String.format(ROW_DATA, "Type", type, "")); + sb.append(String.format(ROW_DATA, "Start/end", begin == end ? begin + : begin + (isContactFeature() ? ":" : "-") + end, "")); + String desc = StringUtils.stripHtmlTags(description); + sb.append(String.format(ROW_DATA, "Description", desc, "")); if (!Float.isNaN(score) && score != 0f) { - sb.append(" score=").append(score); + sb.append(String.format(ROW_DATA, "Score", score, "")); } if (featureGroup != null) { - sb.append(" (").append(featureGroup).append(")"); + sb.append(String.format(ROW_DATA, "Group", featureGroup, "")); } - sb.append("\n\n"); if (otherDetails != null) { @@ -591,18 +607,89 @@ public class SequenceFeature implements FeatureLocationI /* * split selected INFO data by delimiter over multiple lines */ - sb.append(key).append("=\n "); String delimiter = INFO_KEYS.get(key); - String value = entry.getValue().toString(); - sb.append(value.replace(delimiter, "\n ")); + String[] values = entry.getValue().toString().split(delimiter); + for (String value : values) + { + sb.append(String.format(ROW_DATA, key, "", value)); + } } else - { - sb.append(key + "=" + entry.getValue().toString() + "\n"); + { // tried
    but it failed to provide a tooltip :-( + String attDesc = null; + if (metadata != null) + { + attDesc = metadata.getAttributeName(key); + } + String value = entry.getValue().toString(); + if (isValueInteresting(key, value, metadata)) + { + sb.append(String.format(ROW_DATA, key, attDesc == null ? "" + : attDesc, value)); + } } } } + sb.append("
    "); + String text = sb.toString(); return text; } + + /** + * Answers true if we judge the value is worth displaying, by some heuristic + * rules, else false + * + * @param key + * @param value + * @param metadata + * @return + */ + boolean isValueInteresting(String key, String value, + FeatureSourceI metadata) + { + /* + * currently suppressing zero values as well as null or empty + */ + if (value == null || "".equals(value) || ".".equals(value) + || "0".equals(value)) + { + return false; + } + + if (metadata == null) + { + return true; + } + + FeatureAttributeType attType = metadata.getAttributeType(key); + if (attType != null + && (attType == FeatureAttributeType.Float || attType + .equals(FeatureAttributeType.Integer))) + { + try + { + float fval = Float.valueOf(value); + if (fval == 0f) + { + return false; + } + } catch (NumberFormatException e) + { + // ignore + } + } + + return true; // default to interesting + } + + /** + * Sets the feature source identifier + * + * @param theSource + */ + public void setSource(String theSource) + { + source = theSource; + } } diff --git a/src/jalview/datamodel/features/FeatureAttributeType.java b/src/jalview/datamodel/features/FeatureAttributeType.java new file mode 100644 index 0000000..fd3069d --- /dev/null +++ b/src/jalview/datamodel/features/FeatureAttributeType.java @@ -0,0 +1,12 @@ +package jalview.datamodel.features; + +/** + * A class to model the datatype of feature attributes. + * + * @author gmcarstairs + * + */ +public enum FeatureAttributeType +{ + String, Integer, Float, Character, Flag; +} diff --git a/src/jalview/datamodel/features/FeatureSource.java b/src/jalview/datamodel/features/FeatureSource.java new file mode 100644 index 0000000..a1be1dc --- /dev/null +++ b/src/jalview/datamodel/features/FeatureSource.java @@ -0,0 +1,78 @@ +package jalview.datamodel.features; + +import java.util.HashMap; +import java.util.Map; + +/** + * A class to model one source of feature data, including metadata about + * attributes of features + * + * @author gmcarstairs + * + */ +public class FeatureSource implements FeatureSourceI +{ + private String name; + + private Map attributeNames; + + private Map attributeTypes; + + /** + * Constructor + * + * @param theName + */ + public FeatureSource(String theName) + { + this.name = theName; + attributeNames = new HashMap<>(); + attributeTypes = new HashMap<>(); + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() + { + return name; + } + + /** + * {@inheritDoc} + */ + @Override + public String getAttributeName(String attributeId) + { + return attributeNames.get(attributeId); + } + + /** + * {@inheritDoc} + */ + @Override + public FeatureAttributeType getAttributeType(String attributeId) + { + return attributeTypes.get(attributeId); + } + + /** + * {@inheritDoc} + */ + @Override + public void setAttributeName(String id, String attName) + { + attributeNames.put(id, attName); + } + + /** + * {@inheritDoc} + */ + @Override + public void setAttributeType(String id, FeatureAttributeType type) + { + attributeTypes.put(id, type); + } + +} diff --git a/src/jalview/datamodel/features/FeatureSourceI.java b/src/jalview/datamodel/features/FeatureSourceI.java new file mode 100644 index 0000000..c873593 --- /dev/null +++ b/src/jalview/datamodel/features/FeatureSourceI.java @@ -0,0 +1,45 @@ +package jalview.datamodel.features; + +public interface FeatureSourceI +{ + /** + * Answers a name for the feature source (not necessarily unique) + * + * @return + */ + String getName(); + + /** + * Answers the 'long name' of an attribute given its id (short name or + * abbreviation), or null if not known + * + * @param attributeId + * @return + */ + String getAttributeName(String attributeId); + + /** + * Sets the 'long name' of an attribute given its id (short name or + * abbreviation). + * + * @param id + * @param name + */ + void setAttributeName(String id, String name); + + /** + * Answers the datatype of the attribute with given id, or null if not known + * + * @param attributeId + * @return + */ + FeatureAttributeType getAttributeType(String attributeId); + + /** + * Sets the datatype of the attribute with given id + * + * @param id + * @param type + */ + void setAttributeType(String id, FeatureAttributeType type); +} diff --git a/src/jalview/datamodel/features/FeatureSources.java b/src/jalview/datamodel/features/FeatureSources.java new file mode 100644 index 0000000..96efb41 --- /dev/null +++ b/src/jalview/datamodel/features/FeatureSources.java @@ -0,0 +1,51 @@ +package jalview.datamodel.features; + +import java.util.HashMap; +import java.util.Map; + +public class FeatureSources +{ + private static FeatureSources instance = new FeatureSources(); + + private Map sources; + + /** + * Answers the singelton instance of this class + * + * @return + */ + public static FeatureSources getInstance() + { + return instance; + } + + private FeatureSources() + { + sources = new HashMap<>(); + } + + /** + * Answers the FeatureSource with the given unique identifier, or null if not + * known + * + * @param sourceId + * @return + */ + public FeatureSourceI getSource(String sourceId) + { + return sources.get(sourceId); + } + + /** + * Adds the given source under the given key. This will replace any existing + * source with the same id, it is the caller's responsibility to ensure keys + * are unique if necessary. + * + * @param sourceId + * @param source + */ + public void addSource(String sourceId, FeatureSource source) + { + sources.put(sourceId, source); + } +} diff --git a/src/jalview/ext/ensembl/EnsemblGene.java b/src/jalview/ext/ensembl/EnsemblGene.java index afff4c2..cdcfa96 100644 --- a/src/jalview/ext/ensembl/EnsemblGene.java +++ b/src/jalview/ext/ensembl/EnsemblGene.java @@ -147,10 +147,9 @@ public class EnsemblGene extends EnsemblSeqProxy continue; } - parseChromosomeLocations(geneAlignment); - if (geneAlignment.getHeight() == 1) { + findGeneLoci(geneAlignment.getSequenceAt(0), geneId); getTranscripts(geneAlignment, geneId); } if (al == null) @@ -166,42 +165,64 @@ public class EnsemblGene extends EnsemblSeqProxy } /** + * Calls the /lookup/id REST service, parses the response for gene + * coordinates, and if successful, adds these to the sequence. If this fails, + * fall back on trying to parse the sequence description in case it is in + * Ensembl-gene format e.g. chromosome:GRCh38:17:45051610:45109016:1. + * + * @param seq + * @param geneId + */ + void findGeneLoci(SequenceI seq, String geneId) + { + GeneLociI geneLoci = new EnsemblLookup(getDomain()).getGeneLoci(geneId); + if (geneLoci != null) + { + seq.setGeneLoci(geneLoci.getSpeciesId(), geneLoci.getAssemblyId(), + geneLoci.getChromosomeId(), geneLoci.getMap()); + } + else + { + parseChromosomeLocations(seq); + } + } + + /** * Parses and saves fields of an Ensembl-style description e.g. * chromosome:GRCh38:17:45051610:45109016:1 * - * @param alignment + * @param seq */ - private void parseChromosomeLocations(AlignmentI alignment) + boolean parseChromosomeLocations(SequenceI seq) { - for (SequenceI seq : alignment.getSequences()) + String description = seq.getDescription(); + if (description == null) { - String description = seq.getDescription(); - if (description == null) + return false; + } + String[] tokens = description.split(":"); + if (tokens.length == 6 && tokens[0].startsWith(DBRefEntry.CHROMOSOME)) + { + String ref = tokens[1]; + String chrom = tokens[2]; + try { - continue; - } - String[] tokens = description.split(":"); - if (tokens.length == 6 && tokens[0].startsWith(DBRefEntry.CHROMOSOME)) + int chStart = Integer.parseInt(tokens[3]); + int chEnd = Integer.parseInt(tokens[4]); + boolean forwardStrand = "1".equals(tokens[5]); + String species = ""; // not known here + int[] from = new int[] { seq.getStart(), seq.getEnd() }; + int[] to = new int[] { forwardStrand ? chStart : chEnd, + forwardStrand ? chEnd : chStart }; + MapList map = new MapList(from, to, 1, 1); + seq.setGeneLoci(species, ref, chrom, map); + return true; + } catch (NumberFormatException e) { - String ref = tokens[1]; - String chrom = tokens[2]; - try - { - int chStart = Integer.parseInt(tokens[3]); - int chEnd = Integer.parseInt(tokens[4]); - boolean forwardStrand = "1".equals(tokens[5]); - String species = ""; // dunno yet! - int[] from = new int[] { seq.getStart(), seq.getEnd() }; - int[] to = new int[] { forwardStrand ? chStart : chEnd, - forwardStrand ? chEnd : chStart }; - MapList map = new MapList(from, to, 1, 1); - seq.setGeneLoci(species, ref, chrom, map); - } catch (NumberFormatException e) - { - System.err.println("Bad integers in description " + description); - } + System.err.println("Bad integers in description " + description); } } + return false; } /** diff --git a/src/jalview/ext/ensembl/EnsemblLookup.java b/src/jalview/ext/ensembl/EnsemblLookup.java index f314b0a..0d1b554 100644 --- a/src/jalview/ext/ensembl/EnsemblLookup.java +++ b/src/jalview/ext/ensembl/EnsemblLookup.java @@ -20,13 +20,17 @@ */ package jalview.ext.ensembl; +import jalview.bin.Cache; import jalview.datamodel.AlignmentI; +import jalview.datamodel.GeneLociI; +import jalview.util.MapList; import java.io.BufferedReader; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.function.Function; @@ -124,15 +128,20 @@ public class EnsemblLookup extends EnsemblRestClient } /** - * Calls the Ensembl lookup REST endpoint and retrieves the 'Parent' for the - * given identifier, or null if not found + * Calls the Ensembl lookup REST endpoint and returns + *
      + *
    • the 'id' for the identifier if its type is "Gene"
    • + *
    • the 'Parent' if its type is 'Transcript'
    • + *
        + * If the type is 'Translation', does a recursive call to this method, passing + * in the 'Parent' (transcript id). * * @param identifier * @return */ public String getGeneId(String identifier) { - return getResult(identifier, br -> parseGeneId(br)); + return (String) getResult(identifier, br -> parseGeneId(br)); } /** @@ -144,16 +153,19 @@ public class EnsemblLookup extends EnsemblRestClient */ public String getSpecies(String identifier) { - return getResult(identifier, br -> getAttribute(br, SPECIES)); + return (String) getResult(identifier, br -> getAttribute(br, SPECIES)); } /** + * Calls the /lookup/id rest service and delegates parsing of the JSON + * response to the supplied parser + * * @param identifier - * @param attribute + * @param parser * @return */ - protected String getResult(String identifier, - Function parser) + protected Object getResult(String identifier, + Function parser) { List ids = Arrays.asList(new String[] { identifier }); @@ -257,4 +269,80 @@ public class EnsemblLookup extends EnsemblRestClient return geneId; } + /** + * Calls the /lookup/id rest service for the given id, and if successful, + * parses and returns the gene's chromosomal coordinates + * + * @param geneId + * @return + */ + public GeneLociI getGeneLoci(String geneId) + { + return (GeneLociI) getResult(geneId, br -> parseGeneLoci(br)); + } + + /** + * Parses the /lookup/id response for species, asssembly_name, + * seq_region_name, start, end and returns an object that wraps them, or null + * if unsuccessful + * + * @param br + * @return + */ + GeneLociI parseGeneLoci(BufferedReader br) + { + JSONParser jp = new JSONParser(); + try + { + JSONObject val = (JSONObject) jp.parse(br); + final String species = val.get("species").toString(); + final String assembly = val.get("assembly_name").toString(); + final String chromosome = val.get("seq_region_name").toString(); + String strand = val.get("strand").toString(); + int start = Integer.parseInt(val.get("start").toString()); + int end = Integer.parseInt(val.get("end").toString()); + int fromEnd = end - start + 1; + boolean reverseStrand = "-1".equals(strand); + int toStart = reverseStrand ? end : start; + int toEnd = reverseStrand ? start : end; + List fromRange = Collections.singletonList(new int[] { 1, + fromEnd }); + List toRange = Collections.singletonList(new int[] { toStart, + toEnd }); + final MapList map = new MapList(fromRange, toRange, 1, 1); + return new GeneLociI() + { + + @Override + public String getSpeciesId() + { + return species == null ? "" : species; + } + + @Override + public String getAssemblyId() + { + return assembly; + } + + @Override + public String getChromosomeId() + { + return chromosome; + } + + @Override + public MapList getMap() + { + return map; + } + }; + } catch (ParseException | NullPointerException | IOException + | NumberFormatException | ClassCastException e) + { + Cache.log.error("Error looking up gene loci: " + e.getMessage()); + } + return null; + } + } diff --git a/src/jalview/ext/ensembl/EnsemblSymbol.java b/src/jalview/ext/ensembl/EnsemblSymbol.java index 75598a0..65be906 100644 --- a/src/jalview/ext/ensembl/EnsemblSymbol.java +++ b/src/jalview/ext/ensembl/EnsemblSymbol.java @@ -152,7 +152,6 @@ public class EnsemblSymbol extends EnsemblXref if (br != null) { String geneId = parseSymbolResponse(br); - System.out.println(url + " returned " + geneId); if (geneId != null && !result.contains(geneId)) { result.add(geneId); diff --git a/src/jalview/ext/jmol/JalviewJmolBinding.java b/src/jalview/ext/jmol/JalviewJmolBinding.java index 50aba62..41bc116 100644 --- a/src/jalview/ext/jmol/JalviewJmolBinding.java +++ b/src/jalview/ext/jmol/JalviewJmolBinding.java @@ -27,6 +27,7 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.HiddenColumns; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; +import jalview.gui.IProgressIndicator; import jalview.io.DataSourceType; import jalview.io.StructureFile; import jalview.schemes.ColourSchemeI; @@ -72,7 +73,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel */ private boolean associateNewStructs = false; - Vector atomsPicked = new Vector(); + Vector atomsPicked = new Vector<>(); private List chainNames; @@ -610,7 +611,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel } if (modelFileNames == null) { - List mset = new ArrayList(); + List mset = new ArrayList<>(); _modelFileNameMap = new int[viewer.ms.mc]; String m = viewer.ms.getModelFileName(0); if (m != null) @@ -670,7 +671,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel @Override public synchronized String[] getStructureFiles() { - List mset = new ArrayList(); + List mset = new ArrayList<>(); if (viewer == null) { return new String[0]; @@ -1060,8 +1061,8 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel fileLoadingError = null; String[] oldmodels = modelFileNames; modelFileNames = null; - chainNames = new ArrayList(); - chainFile = new Hashtable(); + chainNames = new ArrayList<>(); + chainFile = new Hashtable<>(); boolean notifyLoaded = false; String[] modelfilenames = getStructureFiles(); // first check if we've lost any structures @@ -1127,7 +1128,8 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel // see JAL-623 - need method of matching pasted data up { pdb = getSsm().setMapping(getSequence()[pe], getChains()[pe], - pdbfile, DataSourceType.PASTE); + pdbfile, DataSourceType.PASTE, + getIProgressIndicator()); getPdbEntry(modelnum).setFile("INLINE" + pdb.getId()); matches = true; foundEntry = true; @@ -1159,7 +1161,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel } // Explicitly map to the filename used by Jmol ; pdb = getSsm().setMapping(getSequence()[pe], getChains()[pe], - fileName, protocol); + fileName, protocol, getIProgressIndicator()); // pdbentry[pe].getFile(), protocol); } @@ -1227,6 +1229,8 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel return chainNames; } + protected abstract IProgressIndicator getIProgressIndicator(); + public void notifyNewPickingModeMeasurement(int iatom, String strMeasure) { notifyAtomPicked(iatom, strMeasure, null); diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index bf94ec0..5b812c2 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -164,8 +164,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, AlignViewport viewport; - ViewportRanges vpRanges; - public AlignViewControllerI avc; List alignPanels = new ArrayList<>(); @@ -337,7 +335,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, progressBar = new ProgressBar(this.statusPanel, this.statusBar); } - vpRanges = viewport.getRanges(); avc = new jalview.controller.AlignViewController(this, viewport, alignPanel); if (viewport.getAlignmentConservationAnnotation() == null) @@ -655,9 +652,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { (viewport.cursorMode ? "on" : "off") })); if (viewport.cursorMode) { - alignPanel.getSeqPanel().seqCanvas.cursorX = vpRanges + ViewportRanges ranges = viewport.getRanges(); + alignPanel.getSeqPanel().seqCanvas.cursorX = ranges .getStartRes(); - alignPanel.getSeqPanel().seqCanvas.cursorY = vpRanges + alignPanel.getSeqPanel().seqCanvas.cursorY = ranges .getStartSeq(); } alignPanel.getSeqPanel().seqCanvas.repaint(); @@ -690,10 +688,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, break; } case KeyEvent.VK_PAGE_UP: - vpRanges.pageUp(); + viewport.getRanges().pageUp(); break; case KeyEvent.VK_PAGE_DOWN: - vpRanges.pageDown(); + viewport.getRanges().pageDown(); break; } } @@ -2149,7 +2147,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { // propagate alignment changed. - vpRanges.setEndSeq(alignment.getHeight()); + viewport.getRanges().setEndSeq(alignment.getHeight()); if (annotationAdded) { // Duplicate sequence annotation in all views. @@ -2550,7 +2548,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { trimRegion = new TrimRegionCommand("Remove Left", true, seqs, column, viewport.getAlignment()); - vpRanges.setStartRes(0); + viewport.getRanges().setStartRes(0); } else { @@ -2615,13 +2613,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // This is to maintain viewport position on first residue // of first sequence SequenceI seq = viewport.getAlignment().getSequenceAt(0); - int startRes = seq.findPosition(vpRanges.getStartRes()); + ViewportRanges ranges = viewport.getRanges(); + int startRes = seq.findPosition(ranges.getStartRes()); // ShiftList shifts; // viewport.getAlignment().removeGaps(shifts=new ShiftList()); // edit.alColumnChanges=shifts.getInverse(); // if (viewport.hasHiddenColumns) // viewport.getColumnSelection().compensateForEdits(shifts); - vpRanges.setStartRes(seq.findIndex(startRes) - 1); + ranges.setStartRes(seq.findIndex(startRes) - 1); viewport.firePropertyChange("alignment", null, viewport.getAlignment().getSequences()); @@ -2654,12 +2653,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // This is to maintain viewport position on first residue // of first sequence SequenceI seq = viewport.getAlignment().getSequenceAt(0); - int startRes = seq.findPosition(vpRanges.getStartRes()); + int startRes = seq.findPosition(viewport.getRanges().getStartRes()); addHistoryItem(new RemoveGapsCommand("Remove Gaps", seqs, start, end, viewport.getAlignment())); - vpRanges.setStartRes(seq.findIndex(startRes) - 1); + viewport.getRanges().setStartRes(seq.findIndex(startRes) - 1); viewport.firePropertyChange("alignment", null, viewport.getAlignment().getSequences()); @@ -2715,8 +2714,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, /* * Create a new AlignmentPanel (with its own, new Viewport) */ - AlignmentPanel newap = new Jalview2XML().copyAlignPanel(alignPanel, - true); + AlignmentPanel newap = new Jalview2XML().copyAlignPanel(alignPanel); if (!copyAnnotation) { /* diff --git a/src/jalview/gui/AlignViewport.java b/src/jalview/gui/AlignViewport.java index c22a37d..90271c8 100644 --- a/src/jalview/gui/AlignViewport.java +++ b/src/jalview/gui/AlignViewport.java @@ -76,8 +76,6 @@ public class AlignViewport extends AlignmentViewport { Font font; - TreeModel currentTree = null; - boolean cursorMode = false; boolean antiAlias = false; @@ -448,27 +446,6 @@ public class AlignViewport extends AlignmentViewport } /** - * DOCUMENT ME! - * - * @param tree - * DOCUMENT ME! - */ - public void setCurrentTree(TreeModel tree) - { - currentTree = tree; - } - - /** - * DOCUMENT ME! - * - * @return DOCUMENT ME! - */ - public TreeModel getCurrentTree() - { - return currentTree; - } - - /** * returns the visible column regions of the alignment * * @param selectedRegionOnly @@ -1110,5 +1087,4 @@ public class AlignViewport extends AlignmentViewport } fr.setTransparency(featureSettings.getTransparency()); } - } diff --git a/src/jalview/gui/AlignmentPanel.java b/src/jalview/gui/AlignmentPanel.java index b2c4809..3a1dbe8 100644 --- a/src/jalview/gui/AlignmentPanel.java +++ b/src/jalview/gui/AlignmentPanel.java @@ -76,8 +76,6 @@ public class AlignmentPanel extends GAlignmentPanel implements { public AlignViewport av; - ViewportRanges vpRanges; - OverviewPanel overviewPanel; private SeqPanel seqPanel; @@ -121,7 +119,6 @@ public class AlignmentPanel extends GAlignmentPanel implements { alignFrame = af; this.av = av; - vpRanges = av.getRanges(); setSeqPanel(new SeqPanel(av, this)); setIdPanel(new IdPanel(av, this)); @@ -153,11 +150,12 @@ public class AlignmentPanel extends GAlignmentPanel implements // reset the viewport ranges when the alignment panel is resized // in particular, this initialises the end residue value when Jalview // is initialised + ViewportRanges ranges = av.getRanges(); if (av.getWrapAlignment()) { int widthInRes = getSeqPanel().seqCanvas.getWrappedCanvasWidth( getSeqPanel().seqCanvas.getWidth()); - vpRanges.setViewportWidth(widthInRes); + ranges.setViewportWidth(widthInRes); } else { @@ -166,8 +164,8 @@ public class AlignmentPanel extends GAlignmentPanel implements int heightInSeq = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight(); - vpRanges.setViewportWidth(widthInRes); - vpRanges.setViewportHeight(heightInSeq); + ranges.setViewportWidth(widthInRes); + ranges.setViewportHeight(heightInSeq); } } @@ -377,6 +375,7 @@ public class AlignmentPanel extends GAlignmentPanel implements int verticalOffset, boolean redrawOverview, boolean centre) { int startv, endv, starts, ends; + ViewportRanges ranges = av.getRanges(); if (results == null || results.isEmpty() || av == null || av.getAlignment() == null) @@ -404,7 +403,7 @@ public class AlignmentPanel extends GAlignmentPanel implements */ if (centre) { - int offset = (vpRanges.getEndRes() - vpRanges.getStartRes() + 1) / 2 - 1; + int offset = (ranges.getEndRes() - ranges.getStartRes() + 1) / 2 - 1; start = Math.max(start - offset, 0); end = end + offset - 1; } @@ -440,33 +439,33 @@ public class AlignmentPanel extends GAlignmentPanel implements if (!av.getWrapAlignment()) { - if ((startv = vpRanges.getStartRes()) >= start) + if ((startv = ranges.getStartRes()) >= start) { /* * Scroll left to make start of search results visible */ setScrollValues(start, seqIndex); } - else if ((endv = vpRanges.getEndRes()) <= end) + else if ((endv = ranges.getEndRes()) <= end) { /* * Scroll right to make end of search results visible */ setScrollValues(startv + end - endv, seqIndex); } - else if ((starts = vpRanges.getStartSeq()) > seqIndex) + else if ((starts = ranges.getStartSeq()) > seqIndex) { /* * Scroll up to make start of search results visible */ - setScrollValues(vpRanges.getStartRes(), seqIndex); + setScrollValues(ranges.getStartRes(), seqIndex); } - else if ((ends = vpRanges.getEndSeq()) <= seqIndex) + else if ((ends = ranges.getEndSeq()) <= seqIndex) { /* * Scroll down to make end of search results visible */ - setScrollValues(vpRanges.getStartRes(), starts + seqIndex - ends + setScrollValues(ranges.getStartRes(), starts + seqIndex - ends + 1); } /* @@ -476,7 +475,7 @@ public class AlignmentPanel extends GAlignmentPanel implements } else { - scrollNeeded = vpRanges.scrollToWrappedVisible(start); + scrollNeeded = ranges.scrollToWrappedVisible(start); } paintAlignment(redrawOverview, false); @@ -605,7 +604,8 @@ public class AlignmentPanel extends GAlignmentPanel implements fontChanged(); setAnnotationVisible(av.isShowAnnotation()); boolean wrap = av.getWrapAlignment(); - vpRanges.setStartSeq(0); + ViewportRanges ranges = av.getRanges(); + ranges.setStartSeq(0); scalePanelHolder.setVisible(!wrap); hscroll.setVisible(!wrap); idwidthAdjuster.setVisible(!wrap); @@ -628,7 +628,7 @@ public class AlignmentPanel extends GAlignmentPanel implements { int widthInRes = getSeqPanel().seqCanvas .getWrappedCanvasWidth(canvasWidth); - vpRanges.setViewportWidth(widthInRes); + ranges.setViewportWidth(widthInRes); } else { @@ -636,8 +636,8 @@ public class AlignmentPanel extends GAlignmentPanel implements int heightInSeq = (getSeqPanel().seqCanvas.getHeight() / av.getCharHeight()); - vpRanges.setViewportWidth(widthInRes); - vpRanges.setViewportHeight(heightInSeq); + ranges.setViewportWidth(widthInRes); + ranges.setViewportHeight(heightInSeq); } } @@ -736,10 +736,12 @@ public class AlignmentPanel extends GAlignmentPanel implements return; } + ViewportRanges ranges = av.getRanges(); + if (evt.getSource() == hscroll) { - int oldX = vpRanges.getStartRes(); - int oldwidth = vpRanges.getViewportWidth(); + int oldX = ranges.getStartRes(); + int oldwidth = ranges.getViewportWidth(); int x = hscroll.getValue(); int width = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth(); @@ -750,12 +752,12 @@ public class AlignmentPanel extends GAlignmentPanel implements { return; } - vpRanges.setViewportStartAndWidth(x, width); + ranges.setViewportStartAndWidth(x, width); } else if (evt.getSource() == vscroll) { - int oldY = vpRanges.getStartSeq(); - int oldheight = vpRanges.getViewportHeight(); + int oldY = ranges.getStartSeq(); + int oldheight = ranges.getViewportHeight(); int y = vscroll.getValue(); int height = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight(); @@ -766,7 +768,7 @@ public class AlignmentPanel extends GAlignmentPanel implements { return; } - vpRanges.setViewportStartAndHeight(y, height); + ranges.setViewportStartAndHeight(y, height); } repaint(); } @@ -783,6 +785,8 @@ public class AlignmentPanel extends GAlignmentPanel implements { return; // no horizontal scroll when wrapped } + final ViewportRanges ranges = av.getRanges(); + if (evt.getSource() == vscroll) { int newY = vscroll.getValue(); @@ -792,8 +796,8 @@ public class AlignmentPanel extends GAlignmentPanel implements * this prevents infinite recursion of events when the scroll/viewport * ranges values are the same */ - int oldX = vpRanges.getStartRes(); - int oldY = vpRanges.getWrappedScrollPosition(oldX); + int oldX = ranges.getStartRes(); + int oldY = ranges.getWrappedScrollPosition(oldX); if (oldY == newY) { return; @@ -803,9 +807,9 @@ public class AlignmentPanel extends GAlignmentPanel implements /* * limit page up/down to one width's worth of positions */ - int rowSize = vpRanges.getViewportWidth(); + int rowSize = ranges.getViewportWidth(); int newX = newY > oldY ? oldX + rowSize : oldX - rowSize; - vpRanges.setViewportStartAndWidth(Math.max(0, newX), rowSize); + ranges.setViewportStartAndWidth(Math.max(0, newX), rowSize); } } else @@ -826,8 +830,8 @@ public class AlignmentPanel extends GAlignmentPanel implements "Unexpected path through code: Wrapped jar file opened with wrap alignment set in preferences"); // scroll to start of panel - vpRanges.setStartRes(0); - vpRanges.setStartSeq(0); + ranges.setStartRes(0); + ranges.setStartSeq(0); } }); } @@ -880,7 +884,8 @@ public class AlignmentPanel extends GAlignmentPanel implements /* * set scroll bar positions */ - setScrollValues(vpRanges.getStartRes(), vpRanges.getStartSeq()); + ViewportRanges ranges = av.getRanges(); + setScrollValues(ranges.getStartRes(), ranges.getStartSeq()); } /** @@ -892,8 +897,9 @@ public class AlignmentPanel extends GAlignmentPanel implements */ private void setScrollingForWrappedPanel(int topLeftColumn) { - int scrollPosition = vpRanges.getWrappedScrollPosition(topLeftColumn); - int maxScroll = vpRanges.getWrappedMaxScroll(topLeftColumn); + ViewportRanges ranges = av.getRanges(); + int scrollPosition = ranges.getWrappedScrollPosition(topLeftColumn); + int maxScroll = ranges.getWrappedMaxScroll(topLeftColumn); /* * a scrollbar's value can be set to at most (maximum-extent) @@ -1604,13 +1610,14 @@ public class AlignmentPanel extends GAlignmentPanel implements if (annotationPanel != null) { annotationPanel.dispose(); + annotationPanel = null; } if (av != null) { av.removePropertyChangeListener(propertyChangeListener); - jalview.structure.StructureSelectionManager ssm = av - .getStructureSelectionManager(); + propertyChangeListener = null; + StructureSelectionManager ssm = av.getStructureSelectionManager(); ssm.removeStructureViewerListener(getSeqPanel(), null); ssm.removeSelectionListener(getSeqPanel()); ssm.removeCommandListener(av); @@ -1633,9 +1640,15 @@ public class AlignmentPanel extends GAlignmentPanel implements */ protected void closeChildFrames() { + if (overviewPanel != null) + { + overviewPanel.dispose(); + overviewPanel = null; + } if (calculationDialog != null) { calculationDialog.closeFrame(); + calculationDialog = null; } } @@ -1882,8 +1895,9 @@ public class AlignmentPanel extends GAlignmentPanel implements public void propertyChange(PropertyChangeEvent evt) { // update this panel's scroll values based on the new viewport ranges values - int x = vpRanges.getStartRes(); - int y = vpRanges.getStartSeq(); + ViewportRanges ranges = av.getRanges(); + int x = ranges.getStartRes(); + int y = ranges.getStartSeq(); setScrollValues(x, y); // now update any complementary alignment (its viewport ranges object diff --git a/src/jalview/gui/AppJmol.java b/src/jalview/gui/AppJmol.java index a4597d3..fef7451 100644 --- a/src/jalview/gui/AppJmol.java +++ b/src/jalview/gui/AppJmol.java @@ -157,6 +157,11 @@ public class AppJmol extends StructureViewerBase IProgressIndicator progressBar = null; + @Override + protected IProgressIndicator getIProgressIndicator() + { + return progressBar; + } /** * add a single PDB structure to a new or existing Jmol view * @@ -248,7 +253,7 @@ public class AppJmol extends StructureViewerBase @Override protected List getViewersFor(AlignmentPanel apanel) { - List result = new ArrayList(); + List result = new ArrayList<>(); JInternalFrame[] frames = Desktop.instance.getAllFrames(); for (JInternalFrame frame : frames) @@ -300,7 +305,7 @@ public class AppJmol extends StructureViewerBase @Override void showSelectedChains() { - Vector toshow = new Vector(); + Vector toshow = new Vector<>(); for (int i = 0; i < chainMenu.getItemCount(); i++) { if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem) @@ -489,7 +494,7 @@ public class AppJmol extends StructureViewerBase // todo - record which pdbids were successfully imported. StringBuilder errormsgs = new StringBuilder(); - List files = new ArrayList(); + List files = new ArrayList<>(); String pdbid = ""; try { diff --git a/src/jalview/gui/AppJmolBinding.java b/src/jalview/gui/AppJmolBinding.java index 9325172..724cec1 100644 --- a/src/jalview/gui/AppJmolBinding.java +++ b/src/jalview/gui/AppJmolBinding.java @@ -49,6 +49,12 @@ public class AppJmolBinding extends JalviewJmolBinding } @Override + protected IProgressIndicator getIProgressIndicator() + { + return appJmolWindow.progressBar; + } + + @Override public SequenceRenderer getSequenceRenderer(AlignmentViewPanel alignment) { return new SequenceRenderer(((AlignmentPanel) alignment).av); diff --git a/src/jalview/gui/AquaInternalFrameManager.java b/src/jalview/gui/AquaInternalFrameManager.java new file mode 100644 index 0000000..829135b --- /dev/null +++ b/src/jalview/gui/AquaInternalFrameManager.java @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jalview.gui; + +import java.awt.Container; +import java.beans.PropertyVetoException; +import java.util.Vector; + +import javax.swing.DefaultDesktopManager; +import javax.swing.DesktopManager; +import javax.swing.JInternalFrame; + +/** + * Based on AquaInternalFrameManager + * + * DesktopManager implementation for Aqua + * + * Mac is more like Windows than it's like Motif/Basic + * + * From WindowsDesktopManager: + * + * This class implements a DesktopManager which more closely follows the MDI + * model than the DefaultDesktopManager. Unlike the DefaultDesktopManager + * policy, MDI requires that the selected and activated child frames are the + * same, and that that frame always be the top-most window. + *

        + * The maximized state is managed by the DesktopManager with MDI, instead of + * just being a property of the individual child frame. This means that if the + * currently selected window is maximized and another window is selected, that + * new window will be maximized. + * + * Downloaded from + * https://raw.githubusercontent.com/frohoff/jdk8u-jdk/master/src/macosx/classes/com/apple/laf/AquaInternalFrameManager.java + * + * Patch from Jim Procter - when the most recently opened frame is closed, + * correct behaviour is to go to the next most recent frame, rather than wrap + * around to the bottom of the window stack (as the original implementation + * does) + * + */ +public class AquaInternalFrameManager extends DefaultDesktopManager +{ + // Variables + + /* The frame which is currently selected/activated. + * We store this value to enforce Mac's single-selection model. + */ + JInternalFrame fCurrentFrame; + + JInternalFrame fInitialFrame; + + /* The list of frames, sorted by order of creation. + * This list is necessary because by default the order of + * child frames in the JDesktopPane changes during frame + * activation (the activated frame is moved to index 0). + * We preserve the creation order so that "next" and "previous" + * frame actions make sense. + */ + Vector fChildFrames = new Vector<>(1); + + /** + * keep a reference to the original LAF manager so we can iconise/de-iconise + * correctly + */ + private DesktopManager ourManager; + + public AquaInternalFrameManager(DesktopManager desktopManager) + { + ourManager = desktopManager; + } + + @Override + public void closeFrame(final JInternalFrame f) + { + if (f == fCurrentFrame) + { + boolean mostRecentFrame = fChildFrames + .indexOf(f) == fChildFrames.size() - 1; + if (!mostRecentFrame) + { + activateNextFrame(); + } + else + { + activatePreviousFrame(); + } + } + fChildFrames.removeElement(f); + super.closeFrame(f); + } + + @Override + public void deiconifyFrame(final JInternalFrame f) + { + JInternalFrame.JDesktopIcon desktopIcon; + + desktopIcon = f.getDesktopIcon(); + // If the icon moved, move the frame to that spot before expanding it + // reshape does delta checks for us + f.reshape(desktopIcon.getX(), desktopIcon.getY(), f.getWidth(), + f.getHeight()); + ourManager.deiconifyFrame(f); + } + + void addIcon(final Container c, + final JInternalFrame.JDesktopIcon desktopIcon) + { + c.add(desktopIcon); + } + + /** + * Removes the frame from its parent and adds its desktopIcon to the parent. + */ + @Override + public void iconifyFrame(final JInternalFrame f) + { + ourManager.iconifyFrame(f); + } + + // WindowsDesktopManager code + @Override + public void activateFrame(final JInternalFrame f) + { + try + { + if (f != null) + { + super.activateFrame(f); + } + + // If this is the first activation, add to child list. + if (fChildFrames.indexOf(f) == -1) + { + fChildFrames.addElement(f); + } + + if (fCurrentFrame != null && f != fCurrentFrame) + { + if (fCurrentFrame.isSelected()) + { + fCurrentFrame.setSelected(false); + } + } + + if (f != null && !f.isSelected()) + { + f.setSelected(true); + } + + fCurrentFrame = f; + } catch (final PropertyVetoException e) + { + } + } + + private void switchFrame(final boolean next) + { + if (fCurrentFrame == null) + { + // initialize first frame we find + if (fInitialFrame != null) + { + activateFrame(fInitialFrame); + } + return; + } + + final int count = fChildFrames.size(); + if (count <= 1) + { + // No other child frames. + return; + } + + final int currentIndex = fChildFrames.indexOf(fCurrentFrame); + if (currentIndex == -1) + { + // the "current frame" is no longer in the list + fCurrentFrame = null; + return; + } + + int nextIndex; + if (next) + { + nextIndex = currentIndex + 1; + if (nextIndex == count) + { + nextIndex = 0; + } + } + else + { + nextIndex = currentIndex - 1; + if (nextIndex == -1) + { + nextIndex = count - 1; + } + } + final JInternalFrame f = fChildFrames.elementAt(nextIndex); + activateFrame(f); + fCurrentFrame = f; + } + + /** + * Activate the next child JInternalFrame, as determined by the frames' + * Z-order. If there is only one child frame, it remains activated. If there + * are no child frames, nothing happens. + */ + public void activateNextFrame() + { + switchFrame(true); + } + + /** + * same as above but will activate a frame if none have been selected + */ + public void activateNextFrame(final JInternalFrame f) + { + fInitialFrame = f; + switchFrame(true); + } + + /** + * Activate the previous child JInternalFrame, as determined by the frames' + * Z-order. If there is only one child frame, it remains activated. If there + * are no child frames, nothing happens. + */ + public void activatePreviousFrame() + { + switchFrame(false); + } +} \ No newline at end of file diff --git a/src/jalview/gui/CalculationChooser.java b/src/jalview/gui/CalculationChooser.java index a9f3966..e403dba 100644 --- a/src/jalview/gui/CalculationChooser.java +++ b/src/jalview/gui/CalculationChooser.java @@ -105,6 +105,11 @@ public class CalculationChooser extends JPanel List tips = new ArrayList(); + /* + * the most recently opened PCA results panel + */ + private PCAPanel pcaPanel; + /** * Constructor * @@ -534,7 +539,7 @@ public class CalculationChooser extends JPanel JvOptionPane.WARNING_MESSAGE); return; } - new PCAPanel(af.alignPanel, modelName, params); + pcaPanel = new PCAPanel(af.alignPanel, modelName, params); } /** @@ -592,4 +597,9 @@ public class CalculationChooser extends JPanel { } } + + public PCAPanel getPcaPanel() + { + return pcaPanel; + } } diff --git a/src/jalview/gui/ChimeraViewFrame.java b/src/jalview/gui/ChimeraViewFrame.java index ba360af..89de2e8 100644 --- a/src/jalview/gui/ChimeraViewFrame.java +++ b/src/jalview/gui/ChimeraViewFrame.java @@ -358,7 +358,7 @@ public class ChimeraViewFrame extends StructureViewerBase @Override protected List getViewersFor(AlignmentPanel ap) { - List result = new ArrayList(); + List result = new ArrayList<>(); JInternalFrame[] frames = Desktop.instance.getAllFrames(); for (JInternalFrame frame : frames) @@ -414,7 +414,7 @@ public class ChimeraViewFrame extends StructureViewerBase @Override void showSelectedChains() { - List toshow = new ArrayList(); + List toshow = new ArrayList<>(); for (int i = 0; i < chainMenu.getItemCount(); i++) { if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem) @@ -484,8 +484,8 @@ public class ChimeraViewFrame extends StructureViewerBase // todo - record which pdbids were successfully imported. StringBuilder errormsgs = new StringBuilder(128); StringBuilder files = new StringBuilder(128); - List filePDB = new ArrayList(); - List filePDBpos = new ArrayList(); + List filePDB = new ArrayList<>(); + List filePDBpos = new ArrayList<>(); PDBEntry thePdbEntry = null; StructureFile pdb = null; try @@ -598,9 +598,12 @@ public class ChimeraViewFrame extends StructureViewerBase stopProgressBar("", startTime); } // Explicitly map to the filename used by Chimera ; + pdb = jmb.getSsm().setMapping(jmb.getSequence()[pos], - jmb.getChains()[pos], pe.getFile(), protocol); + jmb.getChains()[pos], pe.getFile(), protocol, + progressBar); stashFoundChains(pdb, pe.getFile()); + } catch (OutOfMemoryError oomerror) { new OOMWarning( @@ -658,7 +661,7 @@ public class ChimeraViewFrame extends StructureViewerBase /** * Fetch PDB data and save to a local file. Returns the full path to the file, - * or null if fetch fails. + * or null if fetch fails. TODO: refactor to common with Jmol ? duplication * * @param processingEntry * @return @@ -891,4 +894,10 @@ public class ChimeraViewFrame extends StructureViewerBase } return reply; } + + @Override + protected IProgressIndicator getIProgressIndicator() + { + return progressBar; + } } diff --git a/src/jalview/gui/CrossRefAction.java b/src/jalview/gui/CrossRefAction.java index 21a0a84..285e574 100644 --- a/src/jalview/gui/CrossRefAction.java +++ b/src/jalview/gui/CrossRefAction.java @@ -336,7 +336,7 @@ public class CrossRefAction implements Runnable /* * hack: ignore cross-references to Ensembl protein ids - * (can't fetch chromosomal mapping for these) + * (or use map/translation perhaps?) * todo: is there an equivalent in EnsemblGenomes? */ if (accession.startsWith("ENSP")) diff --git a/src/jalview/gui/CutAndPasteHtmlTransfer.java b/src/jalview/gui/CutAndPasteHtmlTransfer.java index 71a1520..2e51bce 100644 --- a/src/jalview/gui/CutAndPasteHtmlTransfer.java +++ b/src/jalview/gui/CutAndPasteHtmlTransfer.java @@ -141,6 +141,7 @@ public class CutAndPasteHtmlTransfer extends GCutAndPasteHtmlTransfer */ public void setText(String text) { + textarea.setDocument(textarea.getEditorKit().createDefaultDocument()); textarea.setText(text); } diff --git a/src/jalview/gui/Desktop.java b/src/jalview/gui/Desktop.java index 1f8983f..2d1ba12 100644 --- a/src/jalview/gui/Desktop.java +++ b/src/jalview/gui/Desktop.java @@ -68,8 +68,6 @@ import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DropTargetListener; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; @@ -361,7 +359,10 @@ public class Desktop extends jalview.jbgui.GDesktop desktop.setDesktopManager( new MyDesktopManager( (Platform.isWindows() ? new DefaultDesktopManager() - : desktop.getDesktopManager()))); + : Platform.isAMac() + ? new AquaInternalFrameManager( + desktop.getDesktopManager()) + : desktop.getDesktopManager()))); Rectangle dims = getLastKnownDimensions(""); if (dims != null) @@ -431,24 +432,6 @@ public class Desktop extends jalview.jbgui.GDesktop }); desktop.addMouseListener(ma); - this.addFocusListener(new FocusListener() - { - - @Override - public void focusLost(FocusEvent e) - { - // TODO Auto-generated method stub - - } - - @Override - public void focusGained(FocusEvent e) - { - Cache.log.debug("Relaying windows after focus gain"); - // make sure that we sort windows properly after we gain focus - instance.relayerWindows(); - } - }); this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this)); // Spawn a thread that shows the splashscreen SwingUtilities.invokeLater(new Runnable() @@ -886,6 +869,10 @@ public class Desktop extends jalview.jbgui.GDesktop JInternalFrame itf = desktop.getSelectedFrame(); if (itf != null) { + if (itf instanceof AlignFrame) + { + Jalview.setCurrentAlignFrame((AlignFrame) itf); + } itf.requestFocus(); } } @@ -912,15 +899,7 @@ public class Desktop extends jalview.jbgui.GDesktop menuItem.removeActionListener(menuItem.getActionListeners()[0]); } windowMenu.remove(menuItem); - JInternalFrame itf = desktop.getSelectedFrame(); - if (itf != null) - { - itf.requestFocus(); - if (itf instanceof AlignFrame) - { - Jalview.setCurrentAlignFrame((AlignFrame) itf); - } - } + System.gc(); }; }); @@ -2512,14 +2491,6 @@ public class Desktop extends jalview.jbgui.GDesktop } } - /** - * fixes stacking order after a modal dialog to ensure windows that should be - * on top actually are - */ - public void relayerWindows() - { - - } /** * Accessor method to quickly get all the AlignmentFrames loaded. diff --git a/src/jalview/gui/FeatureSettings.java b/src/jalview/gui/FeatureSettings.java index b768339..3f1d9c7 100644 --- a/src/jalview/gui/FeatureSettings.java +++ b/src/jalview/gui/FeatureSettings.java @@ -541,13 +541,8 @@ public class FeatureSettings extends JPanel public void itemStateChanged(ItemEvent evt) { fr.setGroupVisibility(check.getText(), check.isSelected()); - af.alignPanel.getSeqPanel().seqCanvas.repaint(); - if (af.alignPanel.overviewPanel != null) - { - af.alignPanel.overviewPanel.updateOverviewImage(); - } - resetTable(new String[] { grp }); + af.alignPanel.paintAlignment(true, true); } }); groupPanel.add(check); diff --git a/src/jalview/gui/FontChooser.java b/src/jalview/gui/FontChooser.java index 80ac189..f3c8e8f 100755 --- a/src/jalview/gui/FontChooser.java +++ b/src/jalview/gui/FontChooser.java @@ -235,7 +235,7 @@ public class FontChooser extends GFontChooser ap.av.setScaleProteinAsCdna(oldProteinScale); ap.av.setProteinFontAsCdna(oldMirrorFont); ap.av.antiAlias = oldSmoothFont; - ap.paintAlignment(true, false); + ap.fontChanged(); if (scaleAsCdna.isVisible() && scaleAsCdna.isEnabled()) { diff --git a/src/jalview/gui/IProgressIndicator.java b/src/jalview/gui/IProgressIndicator.java index 981e94c..35bd871 100644 --- a/src/jalview/gui/IProgressIndicator.java +++ b/src/jalview/gui/IProgressIndicator.java @@ -34,7 +34,8 @@ public interface IProgressIndicator * is removed with a second call with same ID. * * @param message - * - displayed message for operation + * - displayed message for operation. Please ensure message is + * internationalised. * @param id * - unique handle for this indicator */ diff --git a/src/jalview/gui/IdPanel.java b/src/jalview/gui/IdPanel.java index 35fd1b4..a4f79c2 100755 --- a/src/jalview/gui/IdPanel.java +++ b/src/jalview/gui/IdPanel.java @@ -154,7 +154,7 @@ public class IdPanel extends JPanel { av.getRanges().scrollRight(true); } - else if (!av.getWrapAlignment()) + else { av.getRanges().scrollUp(false); } @@ -165,7 +165,7 @@ public class IdPanel extends JPanel { av.getRanges().scrollRight(false); } - else if (!av.getWrapAlignment()) + else { av.getRanges().scrollUp(true); } diff --git a/src/jalview/gui/Jalview2XML.java b/src/jalview/gui/Jalview2XML.java index b357234..4a15024 100644 --- a/src/jalview/gui/Jalview2XML.java +++ b/src/jalview/gui/Jalview2XML.java @@ -216,34 +216,6 @@ public class Jalview2XML } } - void clearSeqRefs() - { - if (_cleartables) - { - if (seqRefIds != null) - { - seqRefIds.clear(); - } - if (seqsToIds != null) - { - seqsToIds.clear(); - } - if (incompleteSeqs != null) - { - incompleteSeqs.clear(); - } - // seqRefIds = null; - // seqsToIds = null; - } - else - { - // do nothing - warn("clearSeqRefs called when _cleartables was not set. Doing nothing."); - // seqRefIds = new Hashtable(); - // seqsToIds = new IdentityHashMap(); - } - } - void initSeqRefs() { if (seqsToIds == null) @@ -1084,7 +1056,7 @@ public class Jalview2XML // SAVE TREES // ///////////////////////////////// - if (!storeDS && av.currentTree != null) + if (!storeDS && av.getCurrentTree() != null) { // FIND ANY ASSOCIATED TREES // NOT IMPLEMENTED FOR HEADLESS STATE AT PRESENT @@ -1102,7 +1074,7 @@ public class Jalview2XML { Tree tree = new Tree(); tree.setTitle(tp.getTitle()); - tree.setCurrentTree((av.currentTree == tp.getTree())); + tree.setCurrentTree((av.getCurrentTree() == tp.getTree())); tree.setNewick(tp.getTree().print()); tree.setThreshold(tp.treeCanvas.threshold); @@ -4250,7 +4222,8 @@ public class Jalview2XML StructureData filedat = oldFiles.get(id); String pdbFile = filedat.getFilePath(); SequenceI[] seq = filedat.getSeqList().toArray(new SequenceI[0]); - binding.getSsm().setMapping(seq, null, pdbFile, DataSourceType.FILE); + binding.getSsm().setMapping(seq, null, pdbFile, DataSourceType.FILE, + null); binding.addSequenceForStructFile(pdbFile, seq); } // and add the AlignmentPanel's reference to the view panel @@ -5342,28 +5315,25 @@ public class Jalview2XML } - public jalview.gui.AlignmentPanel copyAlignPanel(AlignmentPanel ap, - boolean keepSeqRefs) + /** + * Provides a 'copy' of an alignment view (on action New View) by 'saving' the + * view as XML (but not to file), and then reloading it + * + * @param ap + * @return + */ + public AlignmentPanel copyAlignPanel(AlignmentPanel ap) { initSeqRefs(); JalviewModel jm = saveState(ap, null, null, null); - if (!keepSeqRefs) - { - clearSeqRefs(); - jm.getJalviewModelSequence().getViewport(0).setSequenceSetId(null); - } - else - { - uniqueSetSuffix = ""; - jm.getJalviewModelSequence().getViewport(0).setId(null); // we don't - // overwrite the - // view we just - // copied - } + uniqueSetSuffix = ""; + jm.getJalviewModelSequence().getViewport(0).setId(null); + // we don't overwrite the view we just copied + if (this.frefedSequence == null) { - frefedSequence = new Vector(); + frefedSequence = new Vector(); } viewportsAdded.clear(); @@ -5383,32 +5353,8 @@ public class Jalview2XML return af.alignPanel; } - /** - * flag indicating if hashtables should be cleared on finalization TODO this - * flag may not be necessary - */ - private final boolean _cleartables = true; - private Hashtable jvids2vobj; - /* - * (non-Javadoc) - * - * @see java.lang.Object#finalize() - */ - @Override - protected void finalize() throws Throwable - { - // really make sure we have no buried refs left. - if (_cleartables) - { - clearSeqRefs(); - } - this.seqRefIds = null; - this.seqsToIds = null; - super.finalize(); - } - private void warn(String msg) { warn(msg, null); diff --git a/src/jalview/gui/OverviewPanel.java b/src/jalview/gui/OverviewPanel.java index 51d7a84..9ddb751 100755 --- a/src/jalview/gui/OverviewPanel.java +++ b/src/jalview/gui/OverviewPanel.java @@ -40,8 +40,10 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.beans.PropertyChangeEvent; +import java.beans.PropertyVetoException; import javax.swing.JCheckBoxMenuItem; +import javax.swing.JInternalFrame; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.SwingUtilities; @@ -350,8 +352,22 @@ public class OverviewPanel extends JPanel { try { - av.getRanges().removePropertyChangeListener(this); + if (av != null) + { + av.getRanges().removePropertyChangeListener(this); + } + oviewCanvas.dispose(); + + /* + * close the parent frame (which also removes it from the + * Desktop Windows menu) + */ + ((JInternalFrame) SwingUtilities.getAncestorOfClass( + JInternalFrame.class, (this))).setClosed(true); + } catch (PropertyVetoException e) + { + // ignore } finally { progressPanel = null; diff --git a/src/jalview/gui/PCAPanel.java b/src/jalview/gui/PCAPanel.java index f861a7c..9f52d26 100644 --- a/src/jalview/gui/PCAPanel.java +++ b/src/jalview/gui/PCAPanel.java @@ -79,6 +79,8 @@ public class PCAPanel extends GPCAPanel int top = 0; + private boolean working; + /** * Creates a new PCAPanel object using default score model and parameters * @@ -234,6 +236,7 @@ public class PCAPanel extends GPCAPanel message = MessageManager.getString("label.pca_calculating"); } progress.setProgressBar(message, progId); + working = true; try { calcSettings.setEnabled(false); @@ -252,6 +255,7 @@ public class PCAPanel extends GPCAPanel } catch (OutOfMemoryError er) { new OOMWarning("calculating PCA", er); + working = false; return; } finally { @@ -266,6 +270,7 @@ public class PCAPanel extends GPCAPanel .getString("label.principal_component_analysis"), 475, 450); this.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT)); } + working = false; } @Override @@ -788,4 +793,14 @@ public class PCAPanel extends GPCAPanel top = t; zCombobox.setSelectedIndex(2); } + + /** + * Answers true if PCA calculation is in progress, else false + * + * @return + */ + public boolean isWorking() + { + return working; + } } diff --git a/src/jalview/gui/PaintRefresher.java b/src/jalview/gui/PaintRefresher.java index d731e70..ced5544 100755 --- a/src/jalview/gui/PaintRefresher.java +++ b/src/jalview/gui/PaintRefresher.java @@ -26,9 +26,9 @@ import jalview.datamodel.SequenceI; import java.awt.Component; import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Map.Entry; /** * Route datamodel/view update events for a sequence set to any display @@ -74,26 +74,21 @@ public class PaintRefresher */ public static void RemoveComponent(Component comp) { - List emptied = new ArrayList(); - for (Entry> registered : components.entrySet()) + if (components == null) { - String id = registered.getKey(); - List comps = components.get(id); + return; + } + + Iterator it = components.keySet().iterator(); + while (it.hasNext()) + { + List comps = components.get(it.next()); comps.remove(comp); if (comps.isEmpty()) { - emptied.add(id); + it.remove(); } } - - /* - * Remove now empty ids after the above (to avoid - * ConcurrentModificationException). - */ - for (String id : emptied) - { - components.remove(id); - } } public static void Refresh(Component source, String id) diff --git a/src/jalview/gui/PairwiseAlignPanel.java b/src/jalview/gui/PairwiseAlignPanel.java index f75407c..d081794 100755 --- a/src/jalview/gui/PairwiseAlignPanel.java +++ b/src/jalview/gui/PairwiseAlignPanel.java @@ -22,7 +22,8 @@ package jalview.gui; import jalview.analysis.AlignSeq; import jalview.datamodel.Alignment; -import jalview.datamodel.Sequence; +import jalview.datamodel.AlignmentView; +import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; import jalview.jbgui.GPairwiseAlignPanel; import jalview.util.MessageManager; @@ -40,49 +41,56 @@ import java.util.Vector; public class PairwiseAlignPanel extends GPairwiseAlignPanel { + private static final String DASHES = "---------------------\n"; + AlignmentViewport av; - Vector sequences; + Vector sequences; /** * Creates a new PairwiseAlignPanel object. * - * @param av + * @param viewport * DOCUMENT ME! */ - public PairwiseAlignPanel(AlignmentViewport av) + public PairwiseAlignPanel(AlignmentViewport viewport) { super(); - this.av = av; + this.av = viewport; - sequences = new Vector(); + sequences = new Vector(); - SequenceI[] seqs; - String[] seqStrings = av.getViewAsString(true); + SequenceGroup selectionGroup = viewport.getSelectionGroup(); + boolean isSelection = selectionGroup != null + && selectionGroup.getSize() > 0; + AlignmentView view = viewport.getAlignmentView(isSelection); + // String[] seqStrings = viewport.getViewAsString(true); + String[] seqStrings = view.getSequenceStrings(viewport + .getGapCharacter()); - if (av.getSelectionGroup() == null) + SequenceI[] seqs; + if (isSelection) { - seqs = av.getAlignment().getSequencesArray(); + seqs = (SequenceI[]) view.getAlignmentAndHiddenColumns(viewport + .getGapCharacter())[0]; } else { - seqs = av.getSelectionGroup().getSequencesInOrder(av.getAlignment()); + seqs = av.getAlignment().getSequencesArray(); } - String type = (av.getAlignment().isNucleotide()) ? AlignSeq.DNA + String type = (viewport.getAlignment().isNucleotide()) ? AlignSeq.DNA : AlignSeq.PEP; float[][] scores = new float[seqs.length][seqs.length]; - double totscore = 0; + double totscore = 0D; int count = seqs.length; - - Sequence seq; + boolean first = true; for (int i = 1; i < count; i++) { for (int j = 0; j < i; j++) { - AlignSeq as = new AlignSeq(seqs[i], seqStrings[i], seqs[j], seqStrings[j], type); @@ -94,9 +102,15 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel as.calcScoreMatrix(); as.traceAlignment(); + if (!first) + { + System.out.println(DASHES); + textarea.append(DASHES); + } + first = false; as.printAlignment(System.out); - scores[i][j] = (float) as.getMaxScore() - / (float) as.getASeq1().length; + scores[i][j] = as.getMaxScore() + / as.getASeq1().length; totscore = totscore + scores[i][j]; textarea.append(as.getOutput()); @@ -107,28 +121,53 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel if (count > 2) { - System.out.println( - "Pairwise alignment scaled similarity score matrix\n"); + printScoreMatrix(seqs, scores, totscore); + } + } - for (int i = 0; i < count; i++) - { - jalview.util.Format.print(System.out, "%s \n", - ("" + i) + " " + seqs[i].getName()); - } + /** + * Prints a matrix of seqi-seqj pairwise alignment scores to sysout + * + * @param seqs + * @param scores + * @param totscore + */ + protected void printScoreMatrix(SequenceI[] seqs, float[][] scores, + double totscore) + { + System.out + .println("Pairwise alignment scaled similarity score matrix\n"); - System.out.println("\n"); + for (int i = 0; i < seqs.length; i++) + { + System.out.println(String.format("%3d %s", i + 1, + seqs[i].getDisplayId(true))); + } + + /* + * table heading columns for sequences 1, 2, 3... + */ + System.out.print("\n "); + for (int i = 0; i < seqs.length; i++) + { + System.out.print(String.format("%7d", i + 1)); + } + System.out.println(); - for (int i = 0; i < count; i++) + for (int i = 0; i < seqs.length; i++) + { + System.out.print(String.format("%3d", i + 1)); + for (int j = 0; j < i; j++) { - for (int j = 0; j < i; j++) - { - jalview.util.Format.print(System.out, "%7.3f", - scores[i][j] / totscore); - } + /* + * as a fraction of tot score, outputs are 0 <= score <= 1 + */ + System.out.print(String.format("%7.3f", scores[i][j] / totscore)); } - - System.out.println("\n"); + System.out.println(); } + + System.out.println("\n"); } /** @@ -137,13 +176,14 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel * @param e * DOCUMENT ME! */ + @Override protected void viewInEditorButton_actionPerformed(ActionEvent e) { - Sequence[] seq = new Sequence[sequences.size()]; + SequenceI[] seq = new SequenceI[sequences.size()]; for (int i = 0; i < sequences.size(); i++) { - seq[i] = (Sequence) sequences.elementAt(i); + seq[i] = sequences.elementAt(i); } AlignFrame af = new AlignFrame(new Alignment(seq), diff --git a/src/jalview/gui/PopupMenu.java b/src/jalview/gui/PopupMenu.java index 40f5764..6da7d4f 100644 --- a/src/jalview/gui/PopupMenu.java +++ b/src/jalview/gui/PopupMenu.java @@ -49,6 +49,7 @@ import jalview.schemes.PIDColourScheme; import jalview.util.GroupUrlLink; import jalview.util.GroupUrlLink.UrlStringTooLongException; import jalview.util.MessageManager; +import jalview.util.StringUtils; import jalview.util.UrlLink; import java.awt.Color; @@ -552,6 +553,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener String description = sf.getDescription(); if (description != null) { + description = StringUtils.stripHtmlTags(description); if (description.length() <= 6) { desc = desc + " " + description; @@ -585,8 +587,12 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener */ protected void showFeatureDetails(SequenceFeature sf) { - CutAndPasteTransfer cap = new CutAndPasteTransfer(); + CutAndPasteHtmlTransfer cap = new CutAndPasteHtmlTransfer(); + // it appears Java's CSS does not support border-collaps :-( + cap.addStylesheetRule("table { border-collapse: collapse;}"); + cap.addStylesheetRule("table, td, th {border: 1px solid black;}"); cap.setText(sf.getDetailsReport()); + Desktop.addInternalFrame(cap, MessageManager.getString("label.feature_details"), 500, 500); } diff --git a/src/jalview/gui/ProgressBar.java b/src/jalview/gui/ProgressBar.java index ea341e3..011d810 100644 --- a/src/jalview/gui/ProgressBar.java +++ b/src/jalview/gui/ProgressBar.java @@ -89,8 +89,8 @@ public class ProgressBar implements IProgressIndicator } this.statusPanel = container; this.statusBar = statusBar; - this.progressBars = new Hashtable(); - this.progressBarHandlers = new Hashtable(); + this.progressBars = new Hashtable<>(); + this.progressBarHandlers = new Hashtable<>(); } @@ -119,46 +119,52 @@ public class ProgressBar implements IProgressIndicator * execution. */ @Override - public void setProgressBar(String message, long id) + public void setProgressBar(final String message, final long id) { - Long longId = Long.valueOf(id); - - JPanel progressPanel = progressBars.get(longId); - if (progressPanel != null) + SwingUtilities.invokeLater(new Runnable() { - /* - * Progress bar is displayed for this id - remove it now, and any handler - */ - progressBars.remove(id); - if (message != null && statusBar != null) - { - statusBar.setText(message); - } - if (progressBarHandlers.containsKey(longId)) + @Override + public void run() { - progressBarHandlers.remove(longId); - } - removeRow(progressPanel); - } - else - { - /* - * No progress bar for this id - add one now - */ - progressPanel = new JPanel(new BorderLayout(10, 5)); + JPanel progressPanel = progressBars.get(id); + if (progressPanel != null) + { + /* + * Progress bar is displayed for this id - remove it now, and any handler + */ + progressBars.remove(id); + if (message != null && statusBar != null) + { + statusBar.setText(message); + } + if (progressBarHandlers.containsKey(id)) + { + progressBarHandlers.remove(id); + } + removeRow(progressPanel); + } + else + { + /* + * No progress bar for this id - add one now + */ + progressPanel = new JPanel(new BorderLayout(10, 5)); - JProgressBar progressBar = new JProgressBar(); - progressBar.setIndeterminate(true); + JProgressBar progressBar = new JProgressBar(); + progressBar.setIndeterminate(true); - progressPanel.add(new JLabel(message), BorderLayout.WEST); - progressPanel.add(progressBar, BorderLayout.CENTER); + progressPanel.add(new JLabel(message), BorderLayout.WEST); + progressPanel.add(progressBar, BorderLayout.CENTER); - addRow(progressPanel); + addRow(progressPanel); - progressBars.put(longId, progressPanel); - } + progressBars.put(id, progressPanel); + } + + refreshLayout(); + } + }); - refreshLayout(); } /** @@ -215,41 +221,50 @@ public class ProgressBar implements IProgressIndicator public void registerHandler(final long id, final IProgressIndicatorHandler handler) { - Long longId = Long.valueOf(id); - final JPanel progressPanel = progressBars.get(longId); - if (progressPanel == null) - { - System.err.println( - "call setProgressBar before registering the progress bar's handler."); - return; - } - - /* - * Nothing useful to do if not a Cancel handler - */ - if (!handler.canCancel()) - { - return; - } - - progressBarHandlers.put(longId, handler); - JButton cancel = new JButton(MessageManager.getString("action.cancel")); final IProgressIndicator us = this; - cancel.addActionListener(new ActionListener() - { + SwingUtilities.invokeLater(new Runnable() + { @Override - public void actionPerformed(ActionEvent e) + public void run() { - handler.cancelActivity(id); - us.setProgressBar(MessageManager - .formatMessage("label.cancelled_params", new Object[] - { ((JLabel) progressPanel.getComponent(0)).getText() }), - id); + final JPanel progressPanel = progressBars.get(id); + if (progressPanel == null) + { + System.err.println( + "call setProgressBar before registering the progress bar's handler."); + return; + } + + /* + * Nothing useful to do if not a Cancel handler + */ + if (!handler.canCancel()) + { + return; + } + + progressBarHandlers.put(id, handler); + JButton cancel = new JButton( + MessageManager.getString("action.cancel")); + cancel.addActionListener(new ActionListener() + { + + @Override + public void actionPerformed(ActionEvent e) + { + handler.cancelActivity(id); + us.setProgressBar(MessageManager + .formatMessage("label.cancelled_params", new Object[] + { ((JLabel) progressPanel.getComponent(0)).getText() }), + id); + } + }); + progressPanel.add(cancel, BorderLayout.EAST); + refreshLayout(); + } }); - progressPanel.add(cancel, BorderLayout.EAST); - refreshLayout(); } } diff --git a/src/jalview/gui/PromptUserConfig.java b/src/jalview/gui/PromptUserConfig.java index 6261015..cb59452 100644 --- a/src/jalview/gui/PromptUserConfig.java +++ b/src/jalview/gui/PromptUserConfig.java @@ -24,8 +24,6 @@ import jalview.bin.Cache; import java.awt.Component; -import javax.swing.JOptionPane; - public class PromptUserConfig implements Runnable { /** @@ -120,6 +118,7 @@ public class PromptUserConfig implements Runnable this.allowCancel = allowCancel; } + @Override public void run() { if (property == null) @@ -206,12 +205,7 @@ public class PromptUserConfig implements Runnable (allowCancel) ? JvOptionPane.YES_NO_CANCEL_OPTION : JvOptionPane.YES_NO_OPTION, JvOptionPane.QUESTION_MESSAGE); - // now, ask the desktop to relayer any external windows that might have - // been obsured - if (Desktop.instance != null) - { - Desktop.instance.relayerWindows(); - } + // and finish parsing the result jalview.bin.Cache.log.debug("Got response : " + reply); if (reply == JvOptionPane.YES_OPTION) diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java index 282c810..6148a2e 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -718,10 +718,12 @@ public class SeqPanel extends JPanel } /** - * DOCUMENT ME! + * Action on mouse movement is to update the status bar to show the current + * sequence position, and (if features are shown) to show any features at the + * position in a tooltip. Does nothing if the mouse move does not change + * residue position. * * @param evt - * DOCUMENT ME! */ @Override public void mouseMoved(MouseEvent evt) @@ -734,7 +736,8 @@ public class SeqPanel extends JPanel } final int column = findColumn(evt); - int seq = findSeq(evt); + final int seq = findSeq(evt); + if (column < 0 || seq < 0 || seq >= av.getAlignment().getHeight()) { lastMouseSeq = -1; @@ -1626,7 +1629,7 @@ public class SeqPanel extends JPanel av.getRanges().scrollRight(true); } - else if (!av.getWrapAlignment()) + else { av.getRanges().scrollUp(false); } @@ -1637,12 +1640,18 @@ public class SeqPanel extends JPanel { av.getRanges().scrollRight(false); } - else if (!av.getWrapAlignment()) + else { av.getRanges().scrollUp(true); } } - // TODO Update tooltip for new position. + + /* + * update status bar and tooltip for new position + * (need to synthesize a mouse movement to refresh tooltip) + */ + mouseMoved(e); + ToolTipManager.sharedInstance().mouseMoved(e); } /** diff --git a/src/jalview/gui/StructureChooser.java b/src/jalview/gui/StructureChooser.java index da10e3f..20f4a49 100644 --- a/src/jalview/gui/StructureChooser.java +++ b/src/jalview/gui/StructureChooser.java @@ -157,8 +157,8 @@ public class StructureChooser extends GStructureChooser Collection wantedFields = pdbDocFieldPrefs .getStructureSummaryFields(); - discoveredStructuresSet = new LinkedHashSet(); - HashSet errors = new HashSet(); + discoveredStructuresSet = new LinkedHashSet<>(); + HashSet errors = new HashSet<>(); for (SequenceI seq : selectedSequences) { FTSRestRequest pdbRequest = new FTSRestRequest(); @@ -223,7 +223,7 @@ public class StructureChooser extends GStructureChooser public void loadLocalCachedPDBEntries() { - ArrayList entries = new ArrayList(); + ArrayList entries = new ArrayList<>(); for (SequenceI seq : selectedSequences) { if (seq.getDatasetSequence() != null @@ -257,7 +257,7 @@ public class StructureChooser extends GStructureChooser boolean isPDBRefsFound = false; boolean isUniProtRefsFound = false; StringBuilder queryBuilder = new StringBuilder(); - Set seqRefs = new LinkedHashSet(); + Set seqRefs = new LinkedHashSet<>(); if (seq.getAllPDBEntries() != null && queryBuilder.length() < MAX_QLENGTH) @@ -401,8 +401,8 @@ public class StructureChooser extends GStructureChooser lbl_loading.setVisible(true); Collection wantedFields = pdbDocFieldPrefs .getStructureSummaryFields(); - Collection filteredResponse = new HashSet(); - HashSet errors = new HashSet(); + Collection filteredResponse = new HashSet<>(); + HashSet errors = new HashSet<>(); for (SequenceI seq : selectedSequences) { @@ -453,7 +453,7 @@ public class StructureChooser extends GStructureChooser if (!filteredResponse.isEmpty()) { final int filterResponseCount = filteredResponse.size(); - Collection reorderedStructuresSet = new LinkedHashSet(); + Collection reorderedStructuresSet = new LinkedHashSet<>(); reorderedStructuresSet.addAll(filteredResponse); reorderedStructuresSet.addAll(discoveredStructuresSet); getResultTable().setModel(FTSRestResponse @@ -725,11 +725,10 @@ public class StructureChooser extends GStructureChooser @Override public void ok_ActionPerformed() { - final long progressSessionId = System.currentTimeMillis(); final StructureSelectionManager ssm = ap.getStructureSelectionManager(); + final int preferredHeight = pnl_filter.getHeight(); - ssm.setProgressIndicator(this); - ssm.setProgressSessionId(progressSessionId); + new Thread(new Runnable() { @Override @@ -747,7 +746,7 @@ public class StructureChooser extends GStructureChooser int[] selectedRows = getResultTable().getSelectedRows(); PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length]; int count = 0; - List selectedSeqsToView = new ArrayList(); + List selectedSeqsToView = new ArrayList<>(); for (int row : selectedRows) { String pdbIdStr = getResultTable() @@ -761,6 +760,7 @@ public class StructureChooser extends GStructureChooser pdbEntry = getFindEntry(pdbIdStr, selectedSeq.getAllPDBEntries()); } + if (pdbEntry == null) { pdbEntry = new PDBEntry(); @@ -783,7 +783,7 @@ public class StructureChooser extends GStructureChooser .getModelIndex(); int refSeqColIndex = tbl_local_pdb.getColumn("Ref Sequence") .getModelIndex(); - List selectedSeqsToView = new ArrayList(); + List selectedSeqsToView = new ArrayList<>(); for (int row : selectedRows) { PDBEntry pdbEntry = (PDBEntry) tbl_local_pdb.getValueAt(row, @@ -805,7 +805,6 @@ public class StructureChooser extends GStructureChooser { selectedSequence = userSelectedSeq; } - String pdbIdStr = txt_search.getText(); PDBEntry pdbEntry = selectedSequence.getPDBEntry(pdbIdStr); if (pdbEntry == null) @@ -847,6 +846,7 @@ public class StructureChooser extends GStructureChooser { selectedSequence }); } closeAction(preferredHeight); + mainFrame.dispose(); } }).start(); } @@ -870,13 +870,15 @@ public class StructureChooser extends GStructureChooser final PDBEntry[] pdbEntriesToView, final AlignmentPanel alignPanel, SequenceI[] sequences) { - ssm.setProgressBar(MessageManager - .getString("status.launching_3d_structure_viewer")); + long progressId = sequences.hashCode(); + setProgressBar(MessageManager + .getString("status.launching_3d_structure_viewer"), progressId); final StructureViewer sViewer = new StructureViewer(ssm); + setProgressBar(null, progressId); if (SiftsSettings.isMapWithSifts()) { - List seqsWithoutSourceDBRef = new ArrayList(); + List seqsWithoutSourceDBRef = new ArrayList<>(); int p = 0; // TODO: skip PDBEntry:Sequence pairs where PDBEntry doesn't look like a // real PDB ID. For moment, we can also safely do this if there is already @@ -907,41 +909,43 @@ public class StructureChooser extends GStructureChooser if (!seqsWithoutSourceDBRef.isEmpty()) { int y = seqsWithoutSourceDBRef.size(); - ssm.setProgressBar(null); - ssm.setProgressBar(MessageManager.formatMessage( + setProgressBar(MessageManager.formatMessage( "status.fetching_dbrefs_for_sequences_without_valid_refs", - y)); + y), progressId); SequenceI[] seqWithoutSrcDBRef = new SequenceI[y]; int x = 0; for (SequenceI fSeq : seqsWithoutSourceDBRef) { seqWithoutSrcDBRef[x++] = fSeq; } + DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef); dbRefFetcher.fetchDBRefs(true); + + setProgressBar("Fetch complete.", progressId); // todo i18n } } if (pdbEntriesToView.length > 1) { - ArrayList seqsMap = new ArrayList(); + ArrayList seqsMap = new ArrayList<>(); for (SequenceI seq : sequences) { seqsMap.add(new SequenceI[] { seq }); } SequenceI[][] collatedSeqs = seqsMap.toArray(new SequenceI[0][0]); - ssm.setProgressBar(null); - ssm.setProgressBar(MessageManager.getString( - "status.fetching_3d_structures_for_selected_entries")); + + setProgressBar(MessageManager + .getString("status.fetching_3d_structures_for_selected_entries"), progressId); sViewer.viewStructures(pdbEntriesToView, collatedSeqs, alignPanel); } else { - ssm.setProgressBar(null); - ssm.setProgressBar(MessageManager.formatMessage( + setProgressBar(MessageManager.formatMessage( "status.fetching_3d_structures_for", - pdbEntriesToView[0].getId())); + pdbEntriesToView[0].getId()),progressId); sViewer.viewStructures(pdbEntriesToView[0], sequences, alignPanel); } + setProgressBar(null, progressId); } /** @@ -1000,7 +1004,7 @@ public class StructureChooser extends GStructureChooser String searchTerm = txt_search.getText().toLowerCase(); searchTerm = searchTerm.split(":")[0]; // System.out.println(">>>>> search term : " + searchTerm); - List wantedFields = new ArrayList(); + List wantedFields = new ArrayList<>(); FTSRestRequest pdbRequest = new FTSRestRequest(); pdbRequest.setAllowEmptySeq(false); pdbRequest.setResponseSize(1); @@ -1062,7 +1066,7 @@ public class StructureChooser extends GStructureChooser public PDBEntryTableModel(List pdbEntries) { - this.pdbEntries = new ArrayList(pdbEntries); + this.pdbEntries = new ArrayList<>(pdbEntries); } @Override diff --git a/src/jalview/gui/StructureViewerBase.java b/src/jalview/gui/StructureViewerBase.java index c8854a7..31c20ed 100644 --- a/src/jalview/gui/StructureViewerBase.java +++ b/src/jalview/gui/StructureViewerBase.java @@ -310,6 +310,8 @@ public abstract class StructureViewerBase extends GStructureViewer public abstract ViewerType getViewerType(); + protected abstract IProgressIndicator getIProgressIndicator(); + /** * add a new structure (with associated sequences and chains) to this viewer, * retrieving it if necessary first. @@ -460,7 +462,7 @@ public abstract class StructureViewerBase extends GStructureViewer * create the mappings */ apanel.getStructureSelectionManager().setMapping(seq, chains, - pdbFilename, DataSourceType.FILE); + pdbFilename, DataSourceType.FILE, getIProgressIndicator()); /* * alert the FeatureRenderer to show new (PDB RESNUM) features diff --git a/src/jalview/gui/TreePanel.java b/src/jalview/gui/TreePanel.java index 9403057..2727db1 100755 --- a/src/jalview/gui/TreePanel.java +++ b/src/jalview/gui/TreePanel.java @@ -55,6 +55,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.List; @@ -63,6 +64,8 @@ import javax.imageio.ImageIO; import javax.swing.ButtonGroup; import javax.swing.JMenuItem; import javax.swing.JRadioButtonMenuItem; +import javax.swing.event.InternalFrameAdapter; +import javax.swing.event.InternalFrameEvent; import org.jibble.epsgraphics.EpsGraphics2D; @@ -141,7 +144,35 @@ public class TreePanel extends GTreePanel buildAssociatedViewMenu(); - av.addPropertyChangeListener(new java.beans.PropertyChangeListener() + final PropertyChangeListener listener = addAlignmentListener(); + + /* + * remove listener when window is closed, so that this + * panel can be garbage collected + */ + addInternalFrameListener(new InternalFrameAdapter() + { + @Override + public void internalFrameClosed(InternalFrameEvent evt) + { + if (av != null) + { + av.removePropertyChangeListener(listener); + } + } + }); + + TreeLoader tl = new TreeLoader(newTree, inputData); + tl.start(); + + } + + /** + * @return + */ + protected PropertyChangeListener addAlignmentListener() + { + final PropertyChangeListener listener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) @@ -168,11 +199,9 @@ public class TreePanel extends GTreePanel repaint(); } } - }); - - TreeLoader tl = new TreeLoader(newTree, inputData); - tl.start(); - + }; + av.addPropertyChangeListener(listener); + return listener; } @Override diff --git a/src/jalview/gui/ViewSelectionMenu.java b/src/jalview/gui/ViewSelectionMenu.java index cdbb4fa..2a7743a 100644 --- a/src/jalview/gui/ViewSelectionMenu.java +++ b/src/jalview/gui/ViewSelectionMenu.java @@ -60,15 +60,6 @@ public class ViewSelectionMenu extends JMenu private ItemListener _handler; - @Override - protected void finalize() throws Throwable - { - _selectedviews = null; - _handler = null; - _allviews = null; - super.finalize(); - } - /** * create a new view selection menu. This menu has some standard entries * (select all, invert selection), and a checkbox for every view. Mousing over diff --git a/src/jalview/io/FileLoader.java b/src/jalview/io/FileLoader.java index 26641b1..f26d6da 100755 --- a/src/jalview/io/FileLoader.java +++ b/src/jalview/io/FileLoader.java @@ -606,18 +606,4 @@ public class FileLoader implements Runnable return tempStructFile.toString(); } - /* - * (non-Javadoc) - * - * @see java.lang.Object#finalize() - */ - @Override - protected void finalize() throws Throwable - { - source = null; - alignFrame = null; - viewport = null; - super.finalize(); - } - } diff --git a/src/jalview/io/InputStreamParser.java b/src/jalview/io/InputStreamParser.java index d269e97..65ba74a 100644 --- a/src/jalview/io/InputStreamParser.java +++ b/src/jalview/io/InputStreamParser.java @@ -47,11 +47,4 @@ public class InputStreamParser extends FileParse error = false; } - @Override - protected void finalize() throws Throwable - { - dataIn = null; - super.finalize(); - } - } diff --git a/src/jalview/io/SequenceAnnotationReport.java b/src/jalview/io/SequenceAnnotationReport.java index 13f41d4..6d819d3 100644 --- a/src/jalview/io/SequenceAnnotationReport.java +++ b/src/jalview/io/SequenceAnnotationReport.java @@ -26,6 +26,7 @@ import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; import jalview.io.gff.GffConstants; import jalview.util.MessageManager; +import jalview.util.StringUtils; import jalview.util.UrlLink; import java.util.Arrays; @@ -183,50 +184,11 @@ public class SequenceAnnotationReport sb.append(" ").append(feature.end); } - if (feature.getDescription() != null - && !feature.description.equals(feature.getType())) + String description = feature.getDescription(); + if (description != null && !description.equals(feature.getType())) { - String tmpString = feature.getDescription(); - String tmp2up = tmpString.toUpperCase(); - int startTag = tmp2up.indexOf(""); - if (startTag > -1) - { - tmpString = tmpString.substring(startTag + 6); - tmp2up = tmp2up.substring(startTag + 6); - } - int endTag = tmp2up.indexOf(""); - if (endTag > -1) - { - tmpString = tmpString.substring(0, endTag); - tmp2up = tmp2up.substring(0, endTag); - } - endTag = tmp2up.indexOf(""); - if (endTag > -1) - { - tmpString = tmpString.substring(0, endTag); - } - - if (startTag > -1) - { - sb.append("; ").append(tmpString); - } - else - { - if (tmpString.indexOf("<") > -1 || tmpString.indexOf(">") > -1) - { - // The description does not specify html is to - // be used, so we must remove < > symbols - tmpString = tmpString.replaceAll("<", "<"); - tmpString = tmpString.replaceAll(">", ">"); - - sb.append("; "); - sb.append(tmpString); - } - else - { - sb.append("; ").append(tmpString); - } - } + description = StringUtils.stripHtmlTags(description); + sb.append("; ").append(description); } // check score should be shown if (!Float.isNaN(feature.getScore())) diff --git a/src/jalview/io/vcf/VCFLoader.java b/src/jalview/io/vcf/VCFLoader.java index e381b26..5adc55c 100644 --- a/src/jalview/io/vcf/VCFLoader.java +++ b/src/jalview/io/vcf/VCFLoader.java @@ -6,6 +6,7 @@ import htsjdk.variant.variantcontext.VariantContext; import htsjdk.variant.vcf.VCFHeader; import htsjdk.variant.vcf.VCFHeaderLine; import htsjdk.variant.vcf.VCFHeaderLineCount; +import htsjdk.variant.vcf.VCFHeaderLineType; import htsjdk.variant.vcf.VCFInfoHeaderLine; import jalview.analysis.AlignmentUtils; @@ -17,6 +18,9 @@ import jalview.datamodel.GeneLociI; import jalview.datamodel.Mapping; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; +import jalview.datamodel.features.FeatureAttributeType; +import jalview.datamodel.features.FeatureSource; +import jalview.datamodel.features.FeatureSources; import jalview.ext.ensembl.EnsemblMap; import jalview.ext.htsjdk.VCFReader; import jalview.io.gff.Gff3Helper; @@ -116,6 +120,12 @@ public class VCFLoader private int csqAlleleNumberFieldIndex = -1; private int csqFeatureFieldIndex = -1; + /* + * a unique identifier under which to save metadata about feature + * attributes (selected INFO field data) + */ + private String sourceId; + /** * Constructor given an alignment context * @@ -175,18 +185,19 @@ public class VCFLoader reader = new VCFReader(filePath); header = reader.getFileHeader(); - VCFHeaderLine ref = header - .getOtherHeaderLine(VCFHeader.REFERENCE_KEY); + + sourceId = filePath; + + saveMetadata(sourceId); /* * get offset of CSQ ALLELE_NUM and Feature if declared */ locateCsqFields(); - // check if reference is wrt assembly19 (GRCh37) - // todo may need to allow user to specify reference assembly? - boolean isRefGrch37 = (ref != null && ref.getValue().contains( - "assembly19")); + VCFHeaderLine ref = header + .getOtherHeaderLine(VCFHeader.REFERENCE_KEY); + String vcfAssembly = ref.getValue(); int varCount = 0; int seqCount = 0; @@ -196,7 +207,7 @@ public class VCFLoader */ for (SequenceI seq : al.getSequences()) { - int added = loadSequenceVCF(seq, reader, isRefGrch37); + int added = loadSequenceVCF(seq, reader, vcfAssembly); if (added > 0) { seqCount++; @@ -239,6 +250,47 @@ public class VCFLoader } /** + * Reads metadata (such as INFO field descriptions and datatypes) and saves + * them for future reference + * + * @param sourceId + */ + void saveMetadata(String sourceId) + { + FeatureSource metadata = new FeatureSource(sourceId); + + for (VCFInfoHeaderLine info : header.getInfoHeaderLines()) + { + String attributeId = info.getID(); + String desc = info.getDescription(); + VCFHeaderLineType type = info.getType(); + FeatureAttributeType attType = null; + switch (type) + { + case Character: + attType = FeatureAttributeType.Character; + break; + case Flag: + attType = FeatureAttributeType.Flag; + break; + case Float: + attType = FeatureAttributeType.Float; + break; + case Integer: + attType = FeatureAttributeType.Integer; + break; + case String: + attType = FeatureAttributeType.String; + break; + } + metadata.setAttributeName(attributeId, desc); + metadata.setAttributeType(attributeId, attType); + } + + FeatureSources.getInstance().addSource(sourceId, metadata); + } + + /** * Records the position of selected fields defined in the CSQ INFO header (if * there is one). CSQ fields are declared in the CSQ INFO Description e.g. *

        @@ -336,16 +388,17 @@ public class VCFLoader /** * Tries to add overlapping variants read from a VCF file to the given * sequence, and returns the number of variant features added. Note that this - * requires the sequence to hold information as to its chromosomal positions - * and reference, in order to be able to map the VCF variants to the sequence. + * requires the sequence to hold information as to its species, chromosomal + * positions and reference assembly, in order to be able to map the VCF + * variants to the sequence (or not) * * @param seq * @param reader - * @param isVcfRefGrch37 + * @param vcfAssembly * @return */ protected int loadSequenceVCF(SequenceI seq, VCFReader reader, - boolean isVcfRefGrch37) + String vcfAssembly) { int count = 0; GeneLociI seqCoords = seq.getGeneLoci(); @@ -357,16 +410,55 @@ public class VCFLoader return 0; } + if (!vcfSpeciesMatchesSequence(vcfAssembly, seqCoords.getSpeciesId())) + { + return 0; + } + List seqChromosomalContigs = seqCoords.getMap().getToRanges(); for (int[] range : seqChromosomalContigs) { - count += addVcfVariants(seq, reader, range, isVcfRefGrch37); + count += addVcfVariants(seq, reader, range, vcfAssembly); } return count; } /** + * Answers true if the species inferred from the VCF reference identifier + * matches that for the sequence + * + * @param vcfAssembly + * @param speciesId + * @return + */ + boolean vcfSpeciesMatchesSequence(String vcfAssembly, String speciesId) + { + // PROBLEM 1 + // there are many aliases for species - how to equate one with another? + // PROBLEM 2 + // VCF ##reference header is an unstructured URI - how to extract species? + // perhaps check if ref includes any (Ensembl) alias of speciesId?? + // TODO ask the user to confirm this?? + + if (vcfAssembly.contains("Homo_sapiens") // gnomAD exome data example + && "HOMO_SAPIENS".equals(speciesId)) // Ensembl species id + { + return true; + } + + if (vcfAssembly.contains("c_elegans") // VEP VCF response example + && "CAENORHABDITIS_ELEGANS".equals(speciesId)) // Ensembl + { + return true; + } + + // this is not a sustainable solution... + + return false; + } + + /** * Queries the VCF reader for any variants that overlap the given chromosome * region of the sequence, and adds as variant features. Returns the number of * overlapping variants found. @@ -376,12 +468,12 @@ public class VCFLoader * @param range * start-end range of a sequence region in its chromosomal * coordinates - * @param isVcfRefGrch37 - * true if the VCF is with reference to GRCh37 + * @param vcfAssembly + * the '##reference' identifier for the VCF reference assembly * @return */ protected int addVcfVariants(SequenceI seq, VCFReader reader, - int[] range, boolean isVcfRefGrch37) + int[] range, String vcfAssembly) { GeneLociI seqCoords = seq.getGeneLoci(); @@ -390,22 +482,24 @@ public class VCFLoader String species = seqCoords.getSpeciesId(); /* - * map chromosomal coordinates from GRCh38 (sequence) to - * GRCh37 (VCF) if necessary + * map chromosomal coordinates from sequence to VCF if the VCF + * data has a different reference assembly to the sequence */ - // TODO generalise for other assemblies and species + // TODO generalise for non-human species + // - or get the user to choose in a dialog + int offset = 0; - String fromRef = "GRCh38"; - if (fromRef.equalsIgnoreCase(seqRef) && isVcfRefGrch37) + if ("GRCh38".equalsIgnoreCase(seqRef) // Ensembl + && vcfAssembly.contains("Homo_sapiens_assembly19")) // gnomAD { String toRef = "GRCh37"; int[] newRange = mapReferenceRange(range, chromosome, "human", - fromRef, toRef); + seqRef, toRef); if (newRange == null) { System.err.println(String.format( "Failed to map %s:%s:%s:%d:%d to %s", species, chromosome, - fromRef, range[0], range[1], toRef)); + seqRef, range[0], range[1], toRef)); return 0; } offset = newRange[0] - range[0]; @@ -575,6 +669,7 @@ public class VCFLoader SequenceFeature sf = new SequenceFeature(type, alleles, featureStart, featureEnd, score, FEATURE_GROUP_VCF); + sf.setSource(sourceId); sf.setValue(Gff3Helper.ALLELES, alleles); @@ -856,8 +951,7 @@ public class VCFLoader */ EnsemblMap mapper = new EnsemblMap(); int[] mapping = mapper.getAssemblyMapping(species, chromosome, fromRef, - toRef, - queryRange); + toRef, queryRange); if (mapping == null) { diff --git a/src/jalview/javascript/JSFunctionExec.java b/src/jalview/javascript/JSFunctionExec.java index 083cd26..29f3fa9 100644 --- a/src/jalview/javascript/JSFunctionExec.java +++ b/src/jalview/javascript/JSFunctionExec.java @@ -39,19 +39,6 @@ public class JSFunctionExec implements Runnable jvlite.setExecutor(this); } - @Override - protected void finalize() throws Throwable - { - jvlite = null; - executor = null; - if (jsExecQueue != null) - { - jsExecQueue.clear(); - } - jsExecQueue = null; - super.finalize(); - } - private Vector jsExecQueue; private Thread executor = null; diff --git a/src/jalview/javascript/MouseOverStructureListener.java b/src/jalview/javascript/MouseOverStructureListener.java index 874bfd3..6071933 100644 --- a/src/jalview/javascript/MouseOverStructureListener.java +++ b/src/jalview/javascript/MouseOverStructureListener.java @@ -299,13 +299,6 @@ public class MouseOverStructureListener extends JSFunctionExec } @Override - public void finalize() throws Throwable - { - jvlite = null; - super.finalize(); - } - - @Override public void releaseReferences(Object svl) { diff --git a/src/jalview/jbgui/GCutAndPasteHtmlTransfer.java b/src/jalview/jbgui/GCutAndPasteHtmlTransfer.java index abc0b3d..a6e0ace 100644 --- a/src/jalview/jbgui/GCutAndPasteHtmlTransfer.java +++ b/src/jalview/jbgui/GCutAndPasteHtmlTransfer.java @@ -39,6 +39,8 @@ import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JScrollPane; +import javax.swing.text.EditorKit; +import javax.swing.text.html.HTMLEditorKit; /** * DOCUMENT ME! @@ -85,6 +87,7 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame { try { + textarea.setEditorKit(new HTMLEditorKit()); setJMenuBar(editMenubar); jbInit(); } catch (Exception e) @@ -272,4 +275,20 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame { } + + /** + * Adds the given stylesheet rule to the Html editor. However note that CSS + * support is limited. + * + * @param rule + * @see javax.swing.text.html.CSS + */ + public void addStylesheetRule(String rule) + { + EditorKit editorKit = textarea.getEditorKit(); + if (editorKit != null) + { + ((HTMLEditorKit) editorKit).getStyleSheet().addRule(rule); + } + } } diff --git a/src/jalview/structure/StructureSelectionManager.java b/src/jalview/structure/StructureSelectionManager.java index b973f45..35e2536 100644 --- a/src/jalview/structure/StructureSelectionManager.java +++ b/src/jalview/structure/StructureSelectionManager.java @@ -66,7 +66,7 @@ public class StructureSelectionManager static IdentityHashMap instances; - private List mappings = new ArrayList(); + private List mappings = new ArrayList<>(); private boolean processSecondaryStructure = false; @@ -74,20 +74,16 @@ public class StructureSelectionManager private boolean addTempFacAnnot = false; - private IProgressIndicator progressIndicator; - private SiftsClient siftsClient = null; - private long progressSessionId; - /* * Set of any registered mappings between (dataset) sequences. */ - private List seqmappings = new ArrayList(); + private List seqmappings = new ArrayList<>(); - private List commandListeners = new ArrayList(); + private List commandListeners = new ArrayList<>(); - private List sel_listeners = new ArrayList(); + private List sel_listeners = new ArrayList<>(); /** * @return true if will try to use external services for processing secondary @@ -175,9 +171,9 @@ public class StructureSelectionManager * map between the PDB IDs (or structure identifiers) used by Jalview and the * absolute filenames for PDB data that corresponds to it */ - Map pdbIdFileName = new HashMap(); + Map pdbIdFileName = new HashMap<>(); - Map pdbFileNameId = new HashMap(); + Map pdbFileNameId = new HashMap<>(); public void registerPDBFile(String idForFile, String absoluteFile) { @@ -228,7 +224,7 @@ public class StructureSelectionManager } if (instances == null) { - instances = new java.util.IdentityHashMap(); + instances = new java.util.IdentityHashMap<>(); } StructureSelectionManager instance = instances.get(context); if (instance == null) @@ -324,9 +320,11 @@ public class StructureSelectionManager * @return null or the structure data parsed as a pdb file */ synchronized public StructureFile setMapping(SequenceI[] sequence, - String[] targetChains, String pdbFile, DataSourceType protocol) + String[] targetChains, String pdbFile, DataSourceType protocol, + IProgressIndicator progress) { - return setMapping(true, sequence, targetChains, pdbFile, protocol); + return computeMapping(true, sequence, targetChains, pdbFile, protocol, + progress); } /** @@ -353,6 +351,16 @@ public class StructureSelectionManager SequenceI[] sequenceArray, String[] targetChainIds, String pdbFile, DataSourceType sourceType) { + return computeMapping(forStructureView, sequenceArray, targetChainIds, + pdbFile, sourceType, null); + } + + synchronized public StructureFile computeMapping( + boolean forStructureView, SequenceI[] sequenceArray, + String[] targetChainIds, String pdbFile, DataSourceType sourceType, + IProgressIndicator progress) + { + long progressSessionId = System.currentTimeMillis() * 3; /* * There will be better ways of doing this in the future, for now we'll use * the tried and tested MCview pdb mapping @@ -500,12 +508,14 @@ public class StructureSelectionManager pdbFile = "INLINE" + pdb.getId(); } - List seqToStrucMapping = new ArrayList(); + List seqToStrucMapping = new ArrayList<>(); if (isMapUsingSIFTs && seq.isProtein()) { - setProgressBar(null); - setProgressBar(MessageManager - .getString("status.obtaining_mapping_with_sifts")); + if (progress!=null) { + progress.setProgressBar(MessageManager + .getString("status.obtaining_mapping_with_sifts"), + progressSessionId); + } jalview.datamodel.Mapping sqmpping = maxAlignseq .getMappingFromS1(false); if (targetChainId != null && !targetChainId.trim().isEmpty()) @@ -538,7 +548,7 @@ public class StructureSelectionManager } else { - List foundSiftsMappings = new ArrayList(); + List foundSiftsMappings = new ArrayList<>(); for (PDBChain chain : pdb.getChains()) { try @@ -575,20 +585,25 @@ public class StructureSelectionManager } else { - setProgressBar(null); - setProgressBar(MessageManager - .getString("status.obtaining_mapping_with_nw_alignment")); + if (progress != null) + { + progress.setProgressBar(MessageManager + .getString("status.obtaining_mapping_with_nw_alignment"), + progressSessionId); + } StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId, maxChain, pdb, maxAlignseq); seqToStrucMapping.add(nwMapping); ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0)); - } - if (forStructureView) { mappings.addAll(seqToStrucMapping); } + if (progress != null) + { + progress.setProgressBar(null, progressSessionId); + } } return pdb; } @@ -683,7 +698,7 @@ public class StructureSelectionManager .getMappingFromS1(false); maxChain.transferRESNUMFeatures(seq, null); - HashMap mapping = new HashMap(); + HashMap mapping = new HashMap<>(); int resNum = -10000; int index = 0; char insCode = ' '; @@ -737,7 +752,7 @@ public class StructureSelectionManager * Remove mappings to the closed listener's PDB files, but first check if * another listener is still interested */ - List pdbs = new ArrayList(Arrays.asList(pdbfiles)); + List pdbs = new ArrayList<>(Arrays.asList(pdbfiles)); StructureListener sl; for (int i = 0; i < listeners.size(); i++) @@ -758,7 +773,7 @@ public class StructureSelectionManager */ if (pdbs.size() > 0) { - List tmp = new ArrayList(); + List tmp = new ArrayList<>(); for (StructureMapping sm : mappings) { if (!pdbs.contains(sm.pdbfile)) @@ -844,7 +859,7 @@ public class StructureSelectionManager && sm.pdbchain.equals(atom.getChain())) { int indexpos = sm.getSeqPos(atom.getPdbResNum()); - if (lastipos != indexpos && lastseq != sm.sequence) + if (lastipos != indexpos || lastseq != sm.sequence) { results.addResult(sm.sequence, indexpos, indexpos); lastipos = indexpos; @@ -952,7 +967,7 @@ public class StructureSelectionManager return; } int atomNo; - List atoms = new ArrayList(); + List atoms = new ArrayList<>(); for (StructureMapping sm : mappings) { if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence() @@ -1060,7 +1075,7 @@ public class StructureSelectionManager public StructureMapping[] getMapping(String pdbfile) { - List tmp = new ArrayList(); + List tmp = new ArrayList<>(); for (StructureMapping sm : mappings) { if (sm.pdbfile.equals(pdbfile)) @@ -1220,7 +1235,7 @@ public class StructureSelectionManager } } - Vector view_listeners = new Vector(); + Vector view_listeners = new Vector<>(); public synchronized void sendViewPosition( jalview.api.AlignmentViewPanel source, int startRes, int endRes, @@ -1343,35 +1358,6 @@ public class StructureSelectionManager return null; } - public IProgressIndicator getProgressIndicator() - { - return progressIndicator; - } - - public void setProgressIndicator(IProgressIndicator progressIndicator) - { - this.progressIndicator = progressIndicator; - } - - public long getProgressSessionId() - { - return progressSessionId; - } - - public void setProgressSessionId(long progressSessionId) - { - this.progressSessionId = progressSessionId; - } - - public void setProgressBar(String message) - { - if (progressIndicator == null) - { - return; - } - progressIndicator.setProgressBar(message, progressSessionId); - } - public List getSequenceMappings() { return seqmappings; diff --git a/src/jalview/util/MappingUtils.java b/src/jalview/util/MappingUtils.java index d21eac3..f5dd883 100644 --- a/src/jalview/util/MappingUtils.java +++ b/src/jalview/util/MappingUtils.java @@ -967,4 +967,55 @@ public final class MappingUtils return (min <= queryRange[0] && max >= queryRange[0] && min <= queryRange[1] && max >= queryRange[1]); } + + /** + * Removes the specified number of positions from the given ranges. Provided + * to allow a stop codon to be stripped from a CDS sequence so that it matches + * the peptide translation length. + * + * @param positions + * @param ranges + * a list of (single) [start, end] ranges + * @return + */ + public static void removeEndPositions(int positions, + List ranges) + { + int toRemove = positions; + Iterator it = new ReverseListIterator<>(ranges); + while (toRemove > 0) + { + int[] endRange = it.next(); + if (endRange.length != 2) + { + /* + * not coded for [start1, end1, start2, end2, ...] + */ + System.err + .println("MappingUtils.removeEndPositions doesn't handle multiple ranges"); + return; + } + + int length = endRange[1] - endRange[0] + 1; + if (length <= 0) + { + /* + * not coded for a reverse strand range (end < start) + */ + System.err + .println("MappingUtils.removeEndPositions doesn't handle reverse strand"); + return; + } + if (length > toRemove) + { + endRange[1] -= toRemove; + toRemove = 0; + } + else + { + toRemove -= length; + it.remove(); + } + } + } } diff --git a/src/jalview/util/StringUtils.java b/src/jalview/util/StringUtils.java index b3456aa..2e8ace8 100644 --- a/src/jalview/util/StringUtils.java +++ b/src/jalview/util/StringUtils.java @@ -403,4 +403,45 @@ public class StringUtils } return s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase(); } + + /** + * A helper method that strips off any leading or trailing html and body tags. + * If no html tag is found, then also html-encodes angle bracket characters. + * + * @param text + * @return + */ + public static String stripHtmlTags(String text) + { + if (text == null) + { + return null; + } + String tmp2up = text.toUpperCase(); + int startTag = tmp2up.indexOf(""); + if (startTag > -1) + { + text = text.substring(startTag + 6); + tmp2up = tmp2up.substring(startTag + 6); + } + // is omission of "" intentional here?? + int endTag = tmp2up.indexOf(""); + if (endTag > -1) + { + text = text.substring(0, endTag); + tmp2up = tmp2up.substring(0, endTag); + } + endTag = tmp2up.indexOf(""); + if (endTag > -1) + { + text = text.substring(0, endTag); + } + + if (startTag == -1 && (text.contains("<") || text.contains(">"))) + { + text = text.replaceAll("<", "<"); + text = text.replaceAll(">", ">"); + } + return text; + } } diff --git a/src/jalview/viewmodel/AlignmentViewport.java b/src/jalview/viewmodel/AlignmentViewport.java index b260cab..a0cbff4 100644 --- a/src/jalview/viewmodel/AlignmentViewport.java +++ b/src/jalview/viewmodel/AlignmentViewport.java @@ -22,6 +22,7 @@ package jalview.viewmodel; import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder; import jalview.analysis.Conservation; +import jalview.analysis.TreeModel; import jalview.api.AlignCalcManagerI; import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; @@ -79,7 +80,7 @@ import java.util.Map; public abstract class AlignmentViewport implements AlignViewportI, CommandListener, VamsasSource { - final protected ViewportRanges ranges; + protected ViewportRanges ranges; protected ViewStyleI viewStyle = new ViewStyle(); @@ -947,11 +948,15 @@ public abstract class AlignmentViewport groupConsensus = null; groupConservation = null; hconsensus = null; + hconservation = null; hcomplementConsensus = null; - // colour scheme may hold reference to consensus - residueShading = null; - // TODO remove listeners from changeSupport? + gapcounts = null; + calculator = null; + residueShading = null; // may hold a reference to Consensus changeSupport = null; + ranges = null; + currentTree = null; + selectionGroup = null; setAlignment(null); } @@ -1333,7 +1338,10 @@ public abstract class AlignmentViewport public void removePropertyChangeListener( java.beans.PropertyChangeListener listener) { - changeSupport.removePropertyChangeListener(listener); + if (changeSupport != null) + { + changeSupport.removePropertyChangeListener(listener); + } } /** @@ -2869,6 +2877,8 @@ public abstract class AlignmentViewport */ private SearchResultsI searchResults = null; + protected TreeModel currentTree = null; + @Override public boolean hasSearchResults() { @@ -2927,4 +2937,16 @@ public abstract class AlignmentViewport + ((ignoreGapsInConsensusCalculation) ? " without gaps" : "")); return sq; } + + @Override + public void setCurrentTree(TreeModel tree) + { + currentTree = tree; + } + + @Override + public TreeModel getCurrentTree() + { + return currentTree; + } } diff --git a/src/jalview/viewmodel/ViewportRanges.java b/src/jalview/viewmodel/ViewportRanges.java index 42d490e..24ff57f 100644 --- a/src/jalview/viewmodel/ViewportRanges.java +++ b/src/jalview/viewmodel/ViewportRanges.java @@ -402,23 +402,39 @@ public class ViewportRanges extends ViewportProperties */ public boolean scrollUp(boolean up) { + /* + * if in unwrapped mode, scroll up or down one sequence row; + * if in wrapped mode, scroll by one visible width of columns + */ if (up) { - if (startSeq < 1) + if (wrappedMode) { - return false; + pageUp(); + } + else + { + if (startSeq < 1) + { + return false; + } + setStartSeq(startSeq - 1); } - - setStartSeq(startSeq - 1); } else { - if (endSeq >= getVisibleAlignmentHeight() - 1) + if (wrappedMode) { - return false; + pageDown(); + } + else + { + if (endSeq >= getVisibleAlignmentHeight() - 1) + { + return false; + } + setStartSeq(startSeq + 1); } - - setStartSeq(startSeq + 1); } return true; } diff --git a/src/jalview/ws/jws2/AbstractJabaCalcWorker.java b/src/jalview/ws/jws2/AbstractJabaCalcWorker.java index b972ab8..dd64e77 100644 --- a/src/jalview/ws/jws2/AbstractJabaCalcWorker.java +++ b/src/jalview/ws/jws2/AbstractJabaCalcWorker.java @@ -30,6 +30,7 @@ import jalview.datamodel.AnnotatedCollectionI; import jalview.datamodel.SequenceI; import jalview.gui.AlignFrame; import jalview.gui.IProgressIndicator; +import jalview.gui.IProgressIndicatorHandler; import jalview.schemes.ResidueProperties; import jalview.workers.AlignCalcWorker; import jalview.ws.jws2.dm.AAConSettings; @@ -220,7 +221,26 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker progressId = System.currentTimeMillis()); } rslt = submitToService(seqs); + if (guiProgress != null) + { + guiProgress.registerHandler(progressId, + new IProgressIndicatorHandler() + { + @Override + public boolean cancelActivity(long id) + { + cancelCurrentJob(); + return true; + } + + @Override + public boolean canCancel() + { + return true; + } + }); + } boolean finished = false; long rpos = 0; do diff --git a/src/jalview/ws/jws2/jabaws2/Jws2Instance.java b/src/jalview/ws/jws2/jabaws2/Jws2Instance.java index cb8f75a..2f3c298 100644 --- a/src/jalview/ws/jws2/jabaws2/Jws2Instance.java +++ b/src/jalview/ws/jws2/jabaws2/Jws2Instance.java @@ -170,13 +170,11 @@ public class Jws2Instance { try { - Closeable svc = (Closeable) service; - service = null; - svc.close(); - } catch (Exception e) + ((Closeable) service).close(); + } catch (Throwable t) { + // ignore } - ; } super.finalize(); } diff --git a/test/jalview/analysis/AlignmentGenerator.java b/test/jalview/analysis/AlignmentGenerator.java index 3187fd9..9d3877c 100644 --- a/test/jalview/analysis/AlignmentGenerator.java +++ b/test/jalview/analysis/AlignmentGenerator.java @@ -27,39 +27,25 @@ import jalview.datamodel.SequenceI; import jalview.gui.JvOptionPane; import jalview.io.FastaFile; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintStream; import java.util.Arrays; import java.util.Random; import org.testng.annotations.BeforeClass; /** - * Generates, and outputs in Fasta format, a random DNA alignment for given + * Generates, and outputs in Fasta format, a random peptide or nucleotide alignment for given * sequence length and count. Will regenerate the same alignment each time if * the same random seed is used (so may be used for reproducible unit tests). * Not guaranteed to reproduce the same results between versions, as the rules * may get tweaked to produce more 'realistic' results. * - * Arguments: - *

          - *
        • length (number of bases in each sequence)
        • - *
        • height (number of sequences)
        • - *
        • a whole number random seed
        • - *
        • percentage of gaps to include (0-100)
        • - *
        • percentage chance of variation of each position (0-100)
        • - *
        - * * @author gmcarstairs - * */ public class AlignmentGenerator { - @BeforeClass(alwaysRun = true) - public void setUpJvOptionPane() - { - JvOptionPane.setInteractiveMode(false); - JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION); - } - private static final char GAP = '-'; private static final char ZERO = '0'; @@ -72,51 +58,76 @@ public class AlignmentGenerator private Random random; + private PrintStream ps; /** - * Outputs a DNA 'alignment' where each position is a random choice from - * 'GTCA-'. + * Outputs a pseudo-randomly generated nucleotide or peptide alignment + * Arguments: + *
          + *
        • n (for nucleotide) or p (for peptide)
        • + *
        • length (number of bases in each sequence)
        • + *
        • height (number of sequences)
        • + *
        • a whole number random seed
        • + *
        • percentage of gaps to include (0-100)
        • + *
        • percentage chance of variation of each position (0-100)
        • + *
        • (optional) path to a file to write the alignment to
        • + *
        + * * * @param args + * @throws FileNotFoundException */ - public static void main(String[] args) + public static void main(String[] args) throws FileNotFoundException { - if (args.length != 6) + if (args.length != 6 && args.length != 7) { usage(); return; } + + PrintStream ps = System.out; + if (args.length == 7) + { + ps = new PrintStream(new File(args[6])); + } + boolean nucleotide = args[0].toLowerCase().startsWith("n"); int width = Integer.parseInt(args[1]); int height = Integer.parseInt(args[2]); long randomSeed = Long.valueOf(args[3]); int gapPercentage = Integer.valueOf(args[4]); int changePercentage = Integer.valueOf(args[5]); - AlignmentI al = new AlignmentGenerator(nucleotide).generate(width, - height, - randomSeed, gapPercentage, changePercentage); - System.out.println("; " + height + " sequences of " + width + ps.println("; " + height + " sequences of " + width + " bases with " + gapPercentage + "% gaps and " + changePercentage + "% mutations (random seed = " + randomSeed + ")"); - System.out.println(new FastaFile().print(al.getSequencesArray(), true)); + + new AlignmentGenerator(nucleotide, ps).generate(width, height, + randomSeed, gapPercentage, changePercentage); + + if (ps != System.out) + { + ps.close(); + } } /** - * Print parameter help. + * Prints parameter help */ private static void usage() { System.out.println("Usage:"); System.out.println("arg0: n (for nucleotide) or p (for peptide)"); System.out.println("arg1: number of (non-gap) bases per sequence"); - System.out.println("arg2: number sequences"); + System.out.println("arg2: number of sequences"); System.out .println("arg3: an integer as random seed (same seed = same results)"); System.out.println("arg4: percentage of gaps to (randomly) generate"); System.out .println("arg5: percentage of 'mutations' to (randomly) generate"); + System.out + .println("arg6: (optional) path to output file (default is sysout)"); System.out.println("Example: AlignmentGenerator n 12 15 387 10 5"); System.out .println("- 15 nucleotide sequences of 12 bases each, approx 10% gaps and 5% mutations, random seed = 387"); @@ -124,16 +135,28 @@ public class AlignmentGenerator } /** - * Constructor that sets nucleotide or peptide symbol set + * Constructor that sets nucleotide or peptide symbol set, and also writes the + * generated alignment to sysout */ public AlignmentGenerator(boolean nuc) { - BASES = nuc ? NUCS : PEPS; + this(nuc, System.out); + } + + /** + * Constructor that sets nucleotide or peptide symbol set, and also writes the + * generated alignment to the specified output stream (if not null). This can + * be used to write the alignment to a file or sysout. + */ + public AlignmentGenerator(boolean nucleotide, PrintStream printStream) + { + BASES = nucleotide ? NUCS : PEPS; + ps = printStream; } /** - * Outputs a DNA 'alignment' of given width and height, where each position is - * a random choice from 'GTCA-'. + * Outputs an 'alignment' of given width and height, where each position is a + * random choice from the symbol alphabet, or - for gap * * @param width * @param height @@ -153,6 +176,12 @@ public class AlignmentGenerator seqno + 1, width, changePercentage); } AlignmentI al = new Alignment(seqs); + + if (ps != null) + { + ps.println(new FastaFile().print(al.getSequencesArray(), true)); + } + return al; } diff --git a/test/jalview/analysis/AlignmentUtilsTests.java b/test/jalview/analysis/AlignmentUtilsTests.java index d229a39..1bff8bf 100644 --- a/test/jalview/analysis/AlignmentUtilsTests.java +++ b/test/jalview/analysis/AlignmentUtilsTests.java @@ -64,6 +64,8 @@ import org.testng.annotations.Test; public class AlignmentUtilsTests { + private static Sequence ts = new Sequence("short", + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklm"); @BeforeClass(alwaysRun = true) public void setUpJvOptionPane() @@ -72,9 +74,6 @@ public class AlignmentUtilsTests JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION); } - private static Sequence ts = new Sequence("short", - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklm"); - @Test(groups = { "Functional" }) public void testExpandContext() { @@ -2633,4 +2632,71 @@ public class AlignmentUtilsTests assertEquals("[ [1, 12] ] 1:1 to [ [158, 164, 210, 214] ]", toMap.toString()); } + + /** + * Tests for the method that maps nucleotide to protein based on CDS features + */ + @Test(groups = "Functional") + public void testMapCdsToProtein() + { + SequenceI peptide = new Sequence("pep", "KLQ"); + + /* + * Case 1: CDS 3 times length of peptide + * NB method only checks lengths match, not translation + */ + SequenceI dna = new Sequence("dna", "AACGacgtCTCCT"); + dna.createDatasetSequence(); + dna.addSequenceFeature(new SequenceFeature("CDS", "", 1, 4, null)); + dna.addSequenceFeature(new SequenceFeature("CDS", "", 9, 13, null)); + MapList ml = AlignmentUtils.mapCdsToProtein(dna, peptide); + assertEquals(3, ml.getFromRatio()); + assertEquals(1, ml.getToRatio()); + assertEquals("[[1, 3]]", + Arrays.deepToString(ml.getToRanges().toArray())); + assertEquals("[[1, 4], [9, 13]]", + Arrays.deepToString(ml.getFromRanges().toArray())); + + /* + * Case 2: CDS 3 times length of peptide + stop codon + * (note code does not currently check trailing codon is a stop codon) + */ + dna = new Sequence("dna", "AACGacgtCTCCTTGA"); + dna.createDatasetSequence(); + dna.addSequenceFeature(new SequenceFeature("CDS", "", 1, 4, null)); + dna.addSequenceFeature(new SequenceFeature("CDS", "", 9, 16, null)); + ml = AlignmentUtils.mapCdsToProtein(dna, peptide); + assertEquals(3, ml.getFromRatio()); + assertEquals(1, ml.getToRatio()); + assertEquals("[[1, 3]]", + Arrays.deepToString(ml.getToRanges().toArray())); + assertEquals("[[1, 4], [9, 13]]", + Arrays.deepToString(ml.getFromRanges().toArray())); + + /* + * Case 3: CDS not 3 times length of peptide - no mapping is made + */ + dna = new Sequence("dna", "AACGacgtCTCCTTG"); + dna.createDatasetSequence(); + dna.addSequenceFeature(new SequenceFeature("CDS", "", 1, 4, null)); + dna.addSequenceFeature(new SequenceFeature("CDS", "", 9, 15, null)); + ml = AlignmentUtils.mapCdsToProtein(dna, peptide); + assertNull(ml); + + /* + * Case 4: incomplete start codon corresponding to X in peptide + */ + dna = new Sequence("dna", "ACGacgtCTCCTTGG"); + dna.createDatasetSequence(); + SequenceFeature sf = new SequenceFeature("CDS", "", 1, 3, null); + sf.setPhase("2"); // skip 2 positions (AC) to start of next codon (GCT) + dna.addSequenceFeature(sf); + dna.addSequenceFeature(new SequenceFeature("CDS", "", 8, 15, null)); + peptide = new Sequence("pep", "XLQ"); + ml = AlignmentUtils.mapCdsToProtein(dna, peptide); + assertEquals("[[2, 3]]", + Arrays.deepToString(ml.getToRanges().toArray())); + assertEquals("[[3, 3], [8, 12]]", + Arrays.deepToString(ml.getFromRanges().toArray())); + } } diff --git a/test/jalview/analysis/TestAlignSeq.java b/test/jalview/analysis/TestAlignSeq.java index 70e59c5..e2e5594 100644 --- a/test/jalview/analysis/TestAlignSeq.java +++ b/test/jalview/analysis/TestAlignSeq.java @@ -64,7 +64,7 @@ public class TestAlignSeq s2 = new Sequence("Seq2", "ASDFA"); s2.setStart(5); s2.setEnd(9); - s3 = new Sequence("Seq1", "SDFAQQQSSS"); + s3 = new Sequence("Seq3", "SDFAQQQSSS"); } @@ -125,10 +125,10 @@ public class TestAlignSeq }; as.printAlignment(ps); - String expected = "Score = 320.0\nLength of alignment = 10\nSequence Seq1 : 3 - 18 (Sequence length = 14)\nSequence Seq1 : 1 - 10 (Sequence length = 10)\n\n" - + "Seq1 SDFAQQQRRR\n" - + " ||||||| \n" - + "Seq1 SDFAQQQSSS\n\n" + "Percentage ID = 70.00\n"; + String expected = "Score = 320.0\nLength of alignment = 10\nSequence Seq1/4-13 (Sequence length = 14)\nSequence Seq3/1-10 (Sequence length = 10)\n\n" + + "Seq1/4-13 SDFAQQQRRR\n" + + " ||||||| \n" + + "Seq3/1-10 SDFAQQQSSS\n\n" + "Percentage ID = 70.00\n\n"; assertEquals(expected, baos.toString()); } } diff --git a/test/jalview/datamodel/SequenceFeatureTest.java b/test/jalview/datamodel/SequenceFeatureTest.java index fbeb365..8c9cbc9 100644 --- a/test/jalview/datamodel/SequenceFeatureTest.java +++ b/test/jalview/datamodel/SequenceFeatureTest.java @@ -273,4 +273,47 @@ public class SequenceFeatureTest "group"); assertTrue(sf.isContactFeature()); } + + @Test(groups = { "Functional" }) + public void testGetDetailsReport() + { + // single locus, no group, no score + SequenceFeature sf = new SequenceFeature("variant", "G,C", 22, 22, null); + String expected = "
        " + + "" + + "
        Typevariant
        Start/end22
        DescriptionG,C
        "; + assertEquals(expected, sf.getDetailsReport()); + + // contact feature + sf = new SequenceFeature("Disulphide Bond", "a description", 28, 31, + null); + expected = "
        " + + "" + + "
        TypeDisulphide Bond
        Start/end28:31
        Descriptiona description
        "; + assertEquals(expected, sf.getDetailsReport()); + + sf = new SequenceFeature("variant", "G,C", 22, 33, + 12.5f, "group"); + sf.setValue("Parent", "ENSG001"); + sf.setValue("Child", "ENSP002"); + expected = "
        " + + "" + + "" + + "" + + "" + + "" + + "
        Typevariant
        Start/end22-33
        DescriptionG,C
        Score12.5
        Groupgroup
        ChildENSP002
        ParentENSG001
        "; + assertEquals(expected, sf.getDetailsReport()); + + /* + * feature with embedded html link in description + */ + String desc = "Fer2 Status: True Positive Pfam 8_8"; + sf = new SequenceFeature("Pfam", desc, 8, 83, "Uniprot"); + expected = "
        " + + "" + + "" + + "
        TypePfam
        Start/end8-83
        DescriptionFer2 Status: True Positive Pfam 8_8
        GroupUniprot
        "; + assertEquals(expected, sf.getDetailsReport()); + } } diff --git a/test/jalview/gui/FreeUpMemoryTest.java b/test/jalview/gui/FreeUpMemoryTest.java new file mode 100644 index 0000000..e93bfac --- /dev/null +++ b/test/jalview/gui/FreeUpMemoryTest.java @@ -0,0 +1,216 @@ +package jalview.gui; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import jalview.analysis.AlignmentGenerator; +import jalview.bin.Cache; +import jalview.bin.Jalview; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.SequenceGroup; +import jalview.io.DataSourceType; +import jalview.io.FileLoader; + +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class FreeUpMemoryTest +{ + private static final int ONE_MB = 1000 * 1000; + + /** + * Configure (read-only) Jalview property settings for test + */ + @BeforeClass(alwaysRun = true) + public void setUp() + { + Jalview.main(new String[] { "-nonews", "-props", + "test/jalview/testProps.jvprops" }); + Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS", + Boolean.TRUE.toString()); + Cache.applicationProperties.setProperty("SHOW_QUALITY", + Boolean.TRUE.toString()); + Cache.applicationProperties.setProperty("SHOW_CONSERVATION", + Boolean.TRUE.toString()); + Cache.applicationProperties.setProperty("SHOW_OCCUPANCY", + Boolean.TRUE.toString()); + Cache.applicationProperties.setProperty("SHOW_IDENTITY", + Boolean.TRUE.toString()); + } + + /** + * A simple test that memory is released when all windows are closed. + *
          + *
        • generates a reasonably large alignment and loads it
        • + *
        • performs various operations on the alignment
        • + *
        • closes all windows
        • + *
        • requests garbage collection
        • + *
        • asserts that the remaining memory footprint (heap usage) is 'not large' + *
        • + *
        + * If the test fails, this suggests that a reference to some large object + * (perhaps the alignment data, or some annotation / Tree / PCA data) has + * failed to be garbage collected. If this is the case, the heap will need to + * be inspected manually (suggest using jvisualvm) in order to track down + * where large objects are still referenced. The code (for example + * AlignmentViewport.dispose()) should then be updated to ensure references to + * large objects are set to null when they are no longer required. + * + * @throws IOException + */ + @Test(groups = "Memory") + public void testFreeMemoryOnClose() throws IOException + { + File f = generateAlignment(); + f.deleteOnExit(); + + doStuffInJalview(f); + + Desktop.instance.closeAll_actionPerformed(null); + + checkUsedMemory(35L); + } + + /** + * Requests garbage collection and then checks whether remaining memory in use + * is less than the expected value (in Megabytes) + * + * @param expectedMax + */ + protected void checkUsedMemory(long expectedMax) + { + /* + * request garbage collection and wait briefly for it to run; + * NB there is no guarantee when, or whether, it will do so + */ + System.gc(); + waitFor(100); + + /* + * a second gc() call should not be necessary - but it is! + * the test passes with it, and fails without it + */ + System.gc(); + waitFor(100); + + /* + * check used memory is 'reasonably low' + */ + long availableMemory = Runtime.getRuntime().totalMemory() / ONE_MB; + long freeMemory = Runtime.getRuntime().freeMemory() / ONE_MB; + long usedMemory = availableMemory - freeMemory; + + /* + * sanity check - fails if any frame was added after + * closeAll_actionPerformed + */ + assertEquals(Desktop.instance.getAllFrames().length, 0); + + /* + * if this assertion fails + * - set a breakpoint here + * - run jvisualvm to inspect a heap dump of Jalview + * - identify large objects in the heap and their referers + * - fix code as necessary to null the references on close + */ + System.out.println("Used memory after gc = " + usedMemory + "MB"); + assertTrue(usedMemory < expectedMax, String.format( + "Used memory %d should be less than %d (Recommend running test manually to verify)", + usedMemory, + expectedMax)); + } + + /** + * Loads an alignment from file and exercises various operations in Jalview + * + * @param f + */ + protected void doStuffInJalview(File f) + { + /* + * load alignment, wait for consensus and other threads to complete + */ + AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(f.getPath(), + DataSourceType.FILE); + while (af.getViewport().isCalcInProgress()) + { + waitFor(200); + } + + /* + * set a selection group - potential memory leak if it retains + * a reference to the alignment + */ + SequenceGroup sg = new SequenceGroup(); + sg.setStartRes(0); + sg.setEndRes(100); + AlignmentI al = af.viewport.getAlignment(); + for (int i = 0; i < al.getHeight(); i++) + { + sg.addSequence(al.getSequenceAt(i), false); + } + af.viewport.setSelectionGroup(sg); + + /* + * compute Tree and PCA (on all sequences, 100 columns) + */ + af.openTreePcaDialog(); + CalculationChooser dialog = af.alignPanel.getCalculationDialog(); + dialog.openPcaPanel("BLOSUM62", dialog.getSimilarityParameters(true)); + dialog.openTreePanel("BLOSUM62", dialog.getSimilarityParameters(false)); + + /* + * wait until Tree and PCA have been computed + */ + while (af.viewport.getCurrentTree() == null + && dialog.getPcaPanel().isWorking()) + { + waitFor(10); + } + + /* + * give Swing time to add the PCA panel (?!?) + */ + waitFor(100); + } + + /** + * Wait for waitMs miliseconds + * + * @param waitMs + */ + protected void waitFor(int waitMs) + { + try + { + Thread.sleep(waitMs); + } catch (InterruptedException e) + { + } + } + + /** + * Generates an alignment and saves it in a temporary file, to be loaded by + * Jalview. We use a peptide alignment (so Conservation and Quality are + * calculated), which is wide enough to ensure Consensus, Conservation and + * Occupancy have a significant memory footprint (if not removed from the + * heap). + * + * @return + * @throws IOException + */ + private File generateAlignment() throws IOException + { + File f = File.createTempFile("MemoryTest", "fa"); + PrintStream ps = new PrintStream(f); + AlignmentGenerator ag = new AlignmentGenerator(false, ps); + int width = 100000; + int height = 100; + ag.generate(width, height, 0, 10, 15); + return f; + } +} diff --git a/test/jalview/gui/PairwiseAlignmentPanelTest.java b/test/jalview/gui/PairwiseAlignmentPanelTest.java new file mode 100644 index 0000000..3322ee8 --- /dev/null +++ b/test/jalview/gui/PairwiseAlignmentPanelTest.java @@ -0,0 +1,73 @@ +package jalview.gui; + +import static org.testng.Assert.assertEquals; + +import jalview.datamodel.AlignmentI; +import jalview.datamodel.SequenceGroup; +import jalview.io.DataSourceType; +import jalview.io.FileLoader; + +import javax.swing.JTextArea; + +import junit.extensions.PA; + +import org.testng.annotations.Test; + +public class PairwiseAlignmentPanelTest +{ + @Test(groups = "Functional") + public void testConstructor_withSelectionGroup() + { + AlignFrame af = new FileLoader().LoadFileWaitTillLoaded( + "examples/uniref50.fa", DataSourceType.FILE); + AlignViewport viewport = af.getViewport(); + AlignmentI al = viewport.getAlignment(); + + /* + * select columns 29-36 of sequences 4 and 5 for alignment + * Q93XJ9_SOLTU/23-29 L-KAISNV + * FER1_PEA/26-32 V-TTTKAF + */ + SequenceGroup sg = new SequenceGroup(); + sg.addSequence(al.getSequenceAt(3), false); + sg.addSequence(al.getSequenceAt(4), false); + sg.setStartRes(28); + sg.setEndRes(35); + viewport.setSelectionGroup(sg); + + PairwiseAlignPanel testee = new PairwiseAlignPanel(viewport); + + String text = ((JTextArea) PA.getValue(testee, "textarea")).getText(); + String expected = "Score = 80.0\n" + "Length of alignment = 4\n" + + "Sequence FER1_PEA/29-32 (Sequence length = 7)\n" + + "Sequence Q93XJ9_SOLTU/23-26 (Sequence length = 7)\n\n" + + " FER1_PEA/29-32 TKAF\n" + " ||.\n" + + "Q93XJ9_SOLTU/23-26 LKAI\n\n" + "Percentage ID = 50.00\n\n"; + assertEquals(text, expected); + } + + /** + * This test aligns the same sequences as testConstructor_withSelectionGroup + * but as a complete alignment (no selection). Note that in fact the user is + * currently required to make a selection in order to calculate pairwise + * alignments, so this case does not arise. + */ + @Test(groups = "Functional") + public void testConstructor_noSelectionGroup() + { + String seqs = ">Q93XJ9_SOLTU/23-29\nL-KAISNV\n>FER1_PEA/26-32\nV-TTTKAF\n"; + AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqs, + DataSourceType.PASTE); + AlignViewport viewport = af.getViewport(); + + PairwiseAlignPanel testee = new PairwiseAlignPanel(viewport); + + String text = ((JTextArea) PA.getValue(testee, "textarea")).getText(); + String expected = "Score = 80.0\n" + "Length of alignment = 4\n" + + "Sequence FER1_PEA/29-32 (Sequence length = 7)\n" + + "Sequence Q93XJ9_SOLTU/23-26 (Sequence length = 7)\n\n" + + " FER1_PEA/29-32 TKAF\n" + " ||.\n" + + "Q93XJ9_SOLTU/23-26 LKAI\n\n" + "Percentage ID = 50.00\n\n"; + assertEquals(text, expected); + } +} diff --git a/test/jalview/gui/ProgressBarTest.java b/test/jalview/gui/ProgressBarTest.java index a1715e9..72a288b 100644 --- a/test/jalview/gui/ProgressBarTest.java +++ b/test/jalview/gui/ProgressBarTest.java @@ -29,6 +29,7 @@ import java.awt.GridLayout; import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.SwingUtilities; import org.testng.Assert; import org.testng.annotations.BeforeClass; @@ -119,8 +120,15 @@ public class ProgressBarTest * @param layout * @param msgs */ - private void verifyProgress(GridLayout layout, String[] msgs) + private void verifyProgress(final GridLayout layout, final String[] msgs) { + try + { + SwingUtilities.invokeAndWait(new Runnable() + { + @Override + public void run() + { int msgCount = msgs.length; assertEquals(1 + msgCount, layout.getRows()); assertEquals(msgCount, statusPanel.getComponentCount()); @@ -132,5 +140,13 @@ public class ProgressBarTest assertEquals(msgs[i++], ((JLabel) ((JPanel) c).getComponent(0)).getText()); } + } + }); + } catch (Exception e) + { + throw new AssertionError( + "Unexpected exception waiting for progress bar validation", + e); + } } } diff --git a/test/jalview/io/vcf/VCFLoaderTest.java b/test/jalview/io/vcf/VCFLoaderTest.java index 4a254d2..5607b4b 100644 --- a/test/jalview/io/vcf/VCFLoaderTest.java +++ b/test/jalview/io/vcf/VCFLoaderTest.java @@ -55,7 +55,7 @@ public class VCFLoaderTest private static final String[] VCF = { "##fileformat=VCFv4.2", "##INFO=", - "##reference=GRCh38", + "##reference=Homo_sapiens/GRCh38", "#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO", // A/T,C variants in position 2 of gene sequence (precedes transcript) // should create 2 variant features with respective scores @@ -191,7 +191,8 @@ public class VCFLoaderTest SequenceI gene1 = alignment.findName("gene1"); int[] to = new int[] { 45051610, 45051634 }; int[] from = new int[] { gene1.getStart(), gene1.getEnd() }; - gene1.setGeneLoci("human", "GRCh38", "17", new MapList(from, to, 1, 1)); + gene1.setGeneLoci("homo_sapiens", "GRCh38", "17", new MapList(from, to, + 1, 1)); /* * map 'transcript1' to chromosome via 'gene1' @@ -201,7 +202,8 @@ public class VCFLoaderTest to = new int[] { 45051612, 45051619, 45051624, 45051633 }; SequenceI transcript1 = alignment.findName("transcript1"); from = new int[] { transcript1.getStart(), transcript1.getEnd() }; - transcript1.setGeneLoci("human", "GRCh38", "17", new MapList(from, to, + transcript1.setGeneLoci("homo_sapiens", "GRCh38", "17", new MapList( + from, to, 1, 1)); /* @@ -210,7 +212,8 @@ public class VCFLoaderTest SequenceI gene2 = alignment.findName("gene2"); to = new int[] { 45051634, 45051610 }; from = new int[] { gene2.getStart(), gene2.getEnd() }; - gene2.setGeneLoci("human", "GRCh38", "17", new MapList(from, to, 1, 1)); + gene2.setGeneLoci("homo_sapiens", "GRCh38", "17", new MapList(from, to, + 1, 1)); /* * map 'transcript2' to chromosome via 'gene2' @@ -220,7 +223,8 @@ public class VCFLoaderTest to = new int[] { 45051633, 45051624, 45051619, 45051612 }; SequenceI transcript2 = alignment.findName("transcript2"); from = new int[] { transcript2.getStart(), transcript2.getEnd() }; - transcript2.setGeneLoci("human", "GRCh38", "17", new MapList(from, to, + transcript2.setGeneLoci("homo_sapiens", "GRCh38", "17", new MapList( + from, to, 1, 1)); /* @@ -248,7 +252,8 @@ public class VCFLoaderTest SequenceI gene3 = alignment.findName("gene3"); to = new int[] { 45051610, 45051634 }; from = new int[] { gene3.getStart(), gene3.getEnd() }; - gene3.setGeneLoci("human", "GRCh38", "5", new MapList(from, to, 1, 1)); + gene3.setGeneLoci("homo_sapiens", "GRCh38", "5", new MapList(from, to, + 1, 1)); /* * map 'transcript3' to chromosome @@ -256,7 +261,8 @@ public class VCFLoaderTest SequenceI transcript3 = alignment.findName("transcript3"); to = new int[] { 45051612, 45051619, 45051624, 45051633 }; from = new int[] { transcript3.getStart(), transcript3.getEnd() }; - transcript3.setGeneLoci("human", "GRCh38", "5", new MapList(from, to, + transcript3.setGeneLoci("homo_sapiens", "GRCh38", "5", new MapList( + from, to, 1, 1)); /* @@ -266,7 +272,8 @@ public class VCFLoaderTest to = new int[] { 45051615, 45051617, 45051619, 45051632, 45051634, 45051634 }; from = new int[] { transcript4.getStart(), transcript4.getEnd() }; - transcript4.setGeneLoci("human", "GRCh38", "5", new MapList(from, to, + transcript4.setGeneLoci("homo_sapiens", "GRCh38", "5", new MapList( + from, to, 1, 1)); /* diff --git a/test/jalview/io/vcf/testVcf.dat b/test/jalview/io/vcf/testVcf.dat index e9e6c22..77e070c 100644 --- a/test/jalview/io/vcf/testVcf.dat +++ b/test/jalview/io/vcf/testVcf.dat @@ -4,7 +4,7 @@ ##INFO= ##INFO= ##INFO= -##reference=GRCh38 +##reference=/Homo_sapiens/GRCh38 #CHROM POS ID REF ALT QUAL FILTER INFO 5 45051610 . C A 81.96 RF;AC0 AC=1;AF=0.1;AN=0;AF_Female=2;AB_MEDIAN=6.00000e-01;CSQ=A|missense_variant|MODIFIER|WASH7P|gene3|Transcript|transcript3|rna|Benign,A|downstream_gene_variant|MODIFIER|WASH7P|gene3|Transcript|transcript4|mrna|Bad 5 45051614 . C T 1666.64 RF AC=1;AF=0.2;AN=0;AF_Female=2;AB_MEDIAN=6.00000e-01;CSQ=T|missense_variant|MODIFIER|WASH7P|gene3|Transcript|transcript3|rna|Benign,T|downstream_gene_variant|MODIFIER|WASH7P|gene3|Transcript|transcript4|mrna|Bad diff --git a/test/jalview/structures/models/AAStructureBindingModelTest.java b/test/jalview/structures/models/AAStructureBindingModelTest.java index aea3687..af02d5e 100644 --- a/test/jalview/structures/models/AAStructureBindingModelTest.java +++ b/test/jalview/structures/models/AAStructureBindingModelTest.java @@ -275,11 +275,11 @@ public class AAStructureBindingModelTest StructureSelectionManager ssm = new StructureSelectionManager(); ssm.setMapping(new SequenceI[] { seq1a, seq1b }, null, PDB_1, - DataSourceType.PASTE); + DataSourceType.PASTE, null); ssm.setMapping(new SequenceI[] { seq2 }, null, PDB_2, - DataSourceType.PASTE); + DataSourceType.PASTE, null); ssm.setMapping(new SequenceI[] { seq3 }, null, PDB_3, - DataSourceType.PASTE); + DataSourceType.PASTE, null); testee = new AAStructureBindingModel(ssm, pdbFiles, seqs, null) { diff --git a/test/jalview/util/MappingUtilsTest.java b/test/jalview/util/MappingUtilsTest.java index 87070d7..d4cf98a 100644 --- a/test/jalview/util/MappingUtilsTest.java +++ b/test/jalview/util/MappingUtilsTest.java @@ -1238,4 +1238,49 @@ public class MappingUtilsTest assertFalse(MappingUtils.rangeContains(null, new int[] { 1, 10 })); } + @Test(groups = "Functional") + public void testRemoveEndPositions() + { + List ranges = new ArrayList<>(); + + /* + * case 1: truncate last range + */ + ranges.add(new int[] { 1, 10 }); + ranges.add(new int[] { 20, 30 }); + MappingUtils.removeEndPositions(5, ranges); + assertEquals(2, ranges.size()); + assertEquals(25, ranges.get(1)[1]); + + /* + * case 2: remove last range + */ + ranges.clear(); + ranges.add(new int[] { 1, 10 }); + ranges.add(new int[] { 20, 22 }); + MappingUtils.removeEndPositions(3, ranges); + assertEquals(1, ranges.size()); + assertEquals(10, ranges.get(0)[1]); + + /* + * case 3: truncate penultimate range + */ + ranges.clear(); + ranges.add(new int[] { 1, 10 }); + ranges.add(new int[] { 20, 21 }); + MappingUtils.removeEndPositions(3, ranges); + assertEquals(1, ranges.size()); + assertEquals(9, ranges.get(0)[1]); + + /* + * case 4: remove last two ranges + */ + ranges.clear(); + ranges.add(new int[] { 1, 10 }); + ranges.add(new int[] { 20, 20 }); + ranges.add(new int[] { 30, 30 }); + MappingUtils.removeEndPositions(3, ranges); + assertEquals(1, ranges.size()); + assertEquals(9, ranges.get(0)[1]); + } } diff --git a/test/jalview/util/StringUtilsTest.java b/test/jalview/util/StringUtilsTest.java index b6f8a25..084219a 100644 --- a/test/jalview/util/StringUtilsTest.java +++ b/test/jalview/util/StringUtilsTest.java @@ -228,4 +228,26 @@ public class StringUtilsTest assertEquals("", StringUtils.toSentenceCase("")); assertNull(StringUtils.toSentenceCase(null)); } + + @Test(groups = { "Functional" }) + public void testStripHtmlTags() + { + assertNull(StringUtils.stripHtmlTags(null)); + assertEquals("", StringUtils.stripHtmlTags("")); + assertEquals( + "label", + StringUtils + .stripHtmlTags("label")); + + // if no "" tag, < and > get html-encoded (not sure why) + assertEquals("<a href=\"something\">label</href>", + StringUtils.stripHtmlTags("label")); + + // gets removed but not (is this intentional?) + assertEquals("

        hello", + StringUtils.stripHtmlTags("

        hello")); + + assertEquals("kdHydro < 12.53", + StringUtils.stripHtmlTags("kdHydro < 12.53")); + } } diff --git a/test/jalview/viewmodel/ViewportRangesTest.java b/test/jalview/viewmodel/ViewportRangesTest.java index 851b1b7..c0cb4ba 100644 --- a/test/jalview/viewmodel/ViewportRangesTest.java +++ b/test/jalview/viewmodel/ViewportRangesTest.java @@ -763,6 +763,66 @@ public class ViewportRangesTest { } } } + + @Test(groups = { "Functional" }) + public void testScrollUp_wrapped() + { + /* + * alignment 30 tall and 45 wide + */ + AlignmentI al2 = gen.generate(45, 30, 1, 0, 5); + + /* + * wrapped view, 5 sequences high, start at sequence offset 1 + */ + ViewportRanges vr = new ViewportRanges(al2); + vr.setWrappedMode(true); + vr.setViewportStartAndHeight(1, 5); + + /* + * offset wrapped view to column 3 + */ + vr.setStartEndRes(3, 22); + + int startRes = vr.getStartRes(); + int width = vr.getViewportWidth(); + assertEquals(startRes, 3); + assertEquals(width, 20); + + // in wrapped mode, we change startRes but not startSeq + // scroll down: + vr.scrollUp(false); + assertEquals(vr.getStartSeq(), 1); + assertEquals(vr.getStartRes(), 23); + + // scroll up returns to original position + vr.scrollUp(true); + assertEquals(vr.getStartSeq(), 1); + assertEquals(vr.getStartRes(), 3); + + // scroll up again returns to 'origin' + vr.scrollUp(true); + assertEquals(vr.getStartSeq(), 1); + assertEquals(vr.getStartRes(), 0); + + /* + * offset 3 columns once more and do some scroll downs + */ + vr.setStartEndRes(3, 22); + vr.scrollUp(false); + assertEquals(vr.getStartSeq(), 1); + assertEquals(vr.getStartRes(), 23); + vr.scrollUp(false); + assertEquals(vr.getStartSeq(), 1); + assertEquals(vr.getStartRes(), 43); + + /* + * scroll down beyond end of alignment does nothing + */ + vr.scrollUp(false); + assertEquals(vr.getStartSeq(), 1); + assertEquals(vr.getStartRes(), 43); + } } // mock listener for property change events -- 1.7.10.2