Merge branch 'develop' into features/JAL-2393customMatrices
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 21 Feb 2017 11:56:39 +0000 (11:56 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 21 Feb 2017 11:56:39 +0000 (11:56 +0000)
Conflicts:
src/jalview/analysis/PCA.java
src/jalview/schemes/ResidueProperties.java
src/jalview/schemes/ScoreMatrix.java

53 files changed:
resources/lang/Messages.properties
resources/lang/Messages_es.properties
resources/scoreModel/blosum62.scm [new file with mode: 0644]
resources/scoreModel/blosum80.scm [new file with mode: 0644]
resources/scoreModel/dna.scm [new file with mode: 0644]
resources/scoreModel/pam250.scm [new file with mode: 0644]
resources/scoreModel/seqspace.scm [new file with mode: 0644]
src/MCview/AppletPDBCanvas.java
src/MCview/PDBCanvas.java
src/jalview/analysis/AlignSeq.java
src/jalview/analysis/Conservation.java
src/jalview/analysis/NJTree.java
src/jalview/analysis/PCA.java
src/jalview/analysis/scoremodels/FeatureDistanceModel.java [moved from src/jalview/analysis/scoremodels/FeatureScoreModel.java with 54% similarity]
src/jalview/analysis/scoremodels/PIDDistanceModel.java [moved from src/jalview/analysis/scoremodels/PIDScoreModel.java with 87% similarity]
src/jalview/analysis/scoremodels/PairwiseDistanceModel.java [moved from src/jalview/analysis/scoremodels/PairwiseSeqScoreModel.java with 55% similarity]
src/jalview/analysis/scoremodels/PairwiseScoreModelI.java [new file with mode: 0644]
src/jalview/analysis/scoremodels/SWDistanceModel.java [moved from src/jalview/analysis/scoremodels/SWScoreModel.java with 91% similarity]
src/jalview/analysis/scoremodels/ScoreMatrix.java [new file with mode: 0644]
src/jalview/analysis/scoremodels/ScoreModels.java [new file with mode: 0644]
src/jalview/api/SiftsClientI.java
src/jalview/api/analysis/DistanceModelI.java [moved from src/jalview/api/analysis/ScoreModelI.java with 70% similarity]
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/TreePanel.java
src/jalview/datamodel/BinarySequence.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/PCAPanel.java
src/jalview/gui/TreeChooser.java [new file with mode: 0644]
src/jalview/gui/TreePanel.java
src/jalview/io/FileFormat.java
src/jalview/io/IdentifyFile.java
src/jalview/io/ScoreMatrixFile.java [new file with mode: 0644]
src/jalview/io/vamsas/Tree.java
src/jalview/jbgui/GAlignFrame.java
src/jalview/jbgui/GPCAPanel.java
src/jalview/schemes/Blosum62ColourScheme.java
src/jalview/schemes/ResidueProperties.java
src/jalview/schemes/ScoreMatrix.java [deleted file]
src/jalview/structure/StructureSelectionManager.java
src/jalview/util/Comparison.java
src/jalview/util/SetUtils.java [new file with mode: 0644]
src/jalview/viewmodel/PCAModel.java
src/jalview/ws/sifts/SiftsClient.java
test/jalview/analysis/AlignSeqTest.java
test/jalview/analysis/TestAlignSeq.java
test/jalview/analysis/scoremodels/FeatureDistanceModelTest.java [moved from test/jalview/analysis/scoremodels/FeatureScoreModelTest.java with 74% similarity]
test/jalview/analysis/scoremodels/ScoreMatrixTest.java [new file with mode: 0644]
test/jalview/analysis/scoremodels/ScoreModelsTest.java [new file with mode: 0644]
test/jalview/io/IdentifyFileTest.java
test/jalview/io/ScoreMatrixFileTest.java [new file with mode: 0644]
test/jalview/schemes/ScoreMatrixPrinter.java [deleted file]
test/jalview/schemes/ScoreMatrixTest.java [deleted file]
test/jalview/util/SetUtilsTest.java [new file with mode: 0644]

index f720f39..bfd48ed 100644 (file)
@@ -78,7 +78,7 @@ action.scale_left = Scale Left
 action.scale_right = Scale Right
 action.by_tree_order = By Tree Order
 action.sort = Sort
-action.calculate_tree = Calculate Tree
+action.calculate_tree = Calculate Tree...
 action.help = Help
 action.by_annotation = By Annotation...
 action.invert_sequence_selection = Invert Sequence Selection
@@ -168,6 +168,7 @@ label.redo_command = Redo {0}
 label.principal_component_analysis = Principal Component Analysis
 label.average_distance_identity = Average Distance Using % Identity
 label.neighbour_joining_identity = Neighbour Joining Using % Identity
+label.choose_tree = Choose Tree Calculation
 label.treecalc_title = {0} Using {1}
 label.tree_calc_av = Average Distance
 label.tree_calc_nj = Neighbour Joining
@@ -330,6 +331,7 @@ label.colour_residues_above_occurrence = Colour residues above % occurrence
 label.set_this_label_text = set this label text
 label.sequences_from = Sequences from {0}
 label.successfully_loaded_file  = Successfully loaded file {0}
+label.successfully_loaded_matrix  = Successfully loaded score matrix {0}
 label.successfully_saved_to_file_in_format = Successfully saved to file: {0} in {1} format.
 label.copied_sequences_to_clipboard = Copied {0} sequences to clipboard.
 label.check_file_matches_sequence_ids_alignment = Check that the file matches sequence IDs in the alignment.
index d408fee..d771dd5 100644 (file)
@@ -165,6 +165,7 @@ label.redo_command = Rehacer {0}
 label.principal_component_analysis = Análisis del Componente Principal
 label.average_distance_identity = Distancia Media Usando % de Identidad
 label.neighbour_joining_identity = Unir vecinos utilizando % de Identidad
+label.choose_tree = Elegir el cálculo del árbol
 label.treecalc_title = {0} utilizando {1}
 label.tree_calc_av = Distancia media
 label.tree_calc_nj = Unir vecinos
diff --git a/resources/scoreModel/blosum62.scm b/resources/scoreModel/blosum62.scm
new file mode 100644 (file)
index 0000000..c7af6b0
--- /dev/null
@@ -0,0 +1,43 @@
+ScoreMatrix BLOSUM62
+ARNDCQEGHILKMFPSTWYVBZX *
+#
+# The BLOSUM62 substitution matrix, as at https://www.ncbi.nlm.nih.gov/Class/FieldGuide/BLOSUM62.txt
+# The first line declares a ScoreMatrix with the name BLOSUM62 (shown in menus)
+# The second line gives the symbols for which scores are held in the matrix
+# These may include a space (but not as the first or last character)
+#
+# Scores are not symbol case sensitive, unless column(s) are provided for lower case characters
+# The 'guide symbol' at the start of each row of score values is optional
+#
+# Comment header line with symbols is provided as a guide
+# Values may be integer or floating point, delimited by tab, space, comma or combinations
+#
+#      A       R       N       D       C       Q       E       G       H       I       L       K       M       F       P       S       T       W       Y       V       B       Z       X           *
+#
+A      4       -1      -2      -2      0       -1      -1      0       -2      -1      -1      -1      -1      -2      -1      1       0       -3      -2      0       -2      -1      0       -4 -4
+R      -1      5       0       -2      -3      1       0       -2      0       -3      -2      2       -1      -3      -2      -1      -1      -3      -2      -3      -1      0       -1      -4 -4
+N      -2      0       6       1       -3      0       0       0       1       -3      -3      0       -2      -3      -2      1       0       -4      -2      -3      3       0       -1      -4 -4
+D      -2      -2      1       6       -3      0       2       -1      -1      -3      -4      -1      -3      -3      -1      0       -1      -4      -3      -3      4       1       -1      -4 -4
+C      0       3       -3      -3      9       -3      -4      -3      -3      -1      -1      -3      -1      -2      -3      -1      -1      -2      -2      -1      -3      -3      -2      -4 -4
+Q      -1      1       0       0       -3      5       2       -2      0       -3      -2      1       0       -3      -1      0       -1      -2      -1      -2      0       3       -1      -4 -4
+E      -1      0       0       2       -4      2       5       -2      0       -3      -3      1       -2      -3      -1      0       -1      -3      -2      -2      1       4       -1      -4 -4
+G      0       -2      0       -1      -3      -2      -2      6       -2      -4      -4      -2      -3      -3      -2      0       -2      -2      -3      -3      -1      -2      -1      -4 -4
+H      -2      0       1       -1      -3      0       0       -2      8       -3      -3      -1      -2      -1      -2      -1      -2      -2      2       -3      0       0       -1      -4 -4
+I      -1      -3      -3      -3      -1      -3      -3      -4      -3      4       2       -3      1       0       -3      -2      -1      -3      -1      3       -3      -3      -1      -4 -4
+L      -1      -2      -3      -4      -1      -2      -3      -4      -3      2       4       -2      2       0       -3      -2      -1      -2      -1      1       -4      -3      -1      -4 -4
+K      -1      2       0       -1      -3      1       1       -2      -1      -3      -2      5       -1      -3      -1      0       -1      -3      -2      -2      0       1       -1      -4 -4
+M      -1      -1      -2      -3      -1      0       -2      -3      -2      1       2       -1      5       0       -2      -1      -1      -1      -1      1       -3      -1      -1      -4 -4
+F      -2      -3      -3      -3      -2      -3      -3      -3      -1      0       0       -3      0       6       -4      -2      -2      1       3       -1      -3      -3      -1      -4 -4
+P      -1      -2      -2      -1      -3      -1      -1      -2      -2      -3      -3      -1      -2      -4      7       -1      -1      -4      -3      -2      -2      -1      -2      -4 -4
+S      1       -1      1       0       -1      0       0       0       -1      -2      -2      0       -1      -2      -1      4       1       -3      -2      -2      0       0       0       -4 -4
+T      0       -1      0       -1      -1      -1      -1      -2      -2      -1      -1      -1      -1      -2      -1      1       5       -2      -2      0       -1      -1      0       -4 -4
+W      -3      -3      -4      -4      -2      -2      -3      -2      -2      -3      -2      -3      -1      1       -4      -3      -2      11      2       -3      -4      -3      -2      -4 -4
+Y      -2      -2      -2      -3      -2      -1      -2      -3      2       -1      -1      -2      -1      3       -3      -2      -2      2       7       -1      -3      -2      -1      -4 -4
+V      0       -3      -3      -3      -1      -2      -2      -3      -3      3       1       -2      1       -1      -2      -2      0       -3      -1      4       -3      -2      -1      -4 -4
+B      -2      -1      3       4       -3      0       1       -1      0       -3      -4      0       -3      -3      -2      0       -1      -4      -3      -3      4       1       -1      -4 -4
+Z      -1      0       0       1       -3      3       4       -2      0       -3      -3      1       -1      -3      -1      0       -1      -3      -2      -2      1       4       -1      -4 -4
+X      0       -1      -1      -1      -2      -1      -1      -1      -1      -1      -1      -1      -1      -1      -2      0       0       -2      -1      -1      -1      -1      -1      -4 -4
+       -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      1  1
+*      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      -4      1  1
+#
+#      A       R       N       D       C       Q       E       G       H       I       L       K       M       F       P       S       T       W       Y       V       B       Z       X       *
diff --git a/resources/scoreModel/blosum80.scm b/resources/scoreModel/blosum80.scm
new file mode 100644 (file)
index 0000000..8de04b7
--- /dev/null
@@ -0,0 +1,46 @@
+ScoreMatrix BLOSUM80
+ARNDCQEGHILKMFPSTWYVBJZX*
+#
+# Source: https://www.ncbi.nlm.nih.gov/IEB/ToolBox/C_DOC/lxr/source/data/BLOSUM80
+#
+# ** THIS IS A SUGGESTED FILE, NOT VALIDATED **
+# Please read https://www.biostars.org/p/190004/ for possible alternatives!
+#
+# The first line declares a ScoreMatrix with the name BLOSUM80
+# The second line gives the symbols for which scores are held in the matrix
+# These may include a space (but not as the first or last character)
+#
+# Scores are not symbol case sensitive, unless column(s) are provided for lower case characters
+# The 'guide symbol' at the start of each row of score values is optional
+#
+# Comment header line with symbols is provided as a guide
+# Values may be integer or floating point, delimited by tab, space, comma or combinations
+#
+#      A       R       N       D       C       Q       E       G       H       I       L       K       M       F       P       S       T       W       Y       V       B       J       Z       X        *
+A      5       -2      -2      -2      -1      -1      -1      0       -2      -2      -2      -1      -1      -3      -1      1       0       -3      -2      0       -2      -2      -1      -1      -6
+R      -2      6       -1      -2      -4      1       -1      -3      0       -3      -3      2       -2      -4      -2      -1      -1      -4      -3      -3      -1      -3      0       -1      -6
+N      -2      -1      6       1       -3      0       -1      -1      0       -4      -4      0       -3      -4      -3      0       0       -4      -3      -4      5       -4      0       -1      -6
+D      -2      -2      1       6       -4      -1      1       -2      -2      -4      -5      -1      -4      -4      -2      -1      -1      -6      -4      -4      5       -5      1       -1      -6
+C      -1      -4      -3      -4      9       -4      -5      -4      -4      -2      -2      -4      -2      -3      -4      -2      -1      -3      -3      -1      -4      -2      -4      -1      -6
+Q      -1      1       0       -1      -4      6       2       -2      1       -3      -3      1       0       -4      -2      0       -1      -3      -2      -3      0       -3      4       -1      -6
+E      -1      -1      -1      1       -5      2       6       -3      0       -4      -4      1       -2      -4      -2      0       -1      -4      -3      -3      1       -4      5       -1      -6
+G      0       -3      -1      -2      -4      -2      -3      6       -3      -5      -4      -2      -4      -4      -3      -1      -2      -4      -4      -4      -1      -5      -3      -1      -6
+H      -2      0       0       -2      -4      1       0       -3      8       -4      -3      -1      -2      -2      -3      -1      -2      -3      2       -4      -1      -4      0       -1      -6
+I      -2      -3      -4      -4      -2      -3      -4      -5      -4      5       1       -3      1       -1      -4      -3      -1      -3      -2      3       -4      3       -4      -1      -6
+L      -2      -3      -4      -5      -2      -3      -4      -4      -3      1       4       -3      2       0       -3      -3      -2      -2      -2      1       -4      3       -3      -1      -6
+K      -1      2       0       -1      -4      1       1       -2      -1      -3      -3      5       -2      -4      -1      -1      -1      -4      -3      -3      -1      -3      1       -1      -6
+M      -1      -2      -3      -4      -2      0       -2      -4      -2      1       2       -2      6       0       -3      -2      -1      -2      -2      1       -3      2       -1      -1      -6
+F      -3      -4      -4      -4      -3      -4      -4      -4      -2      -1      0       -4      0       6       -4      -3      -2      0       3       -1      -4      0       -4      -1      -6
+P      -1      -2      -3      -2      -4      -2      -2      -3      -3      -4      -3      -1      -3      -4      8       -1      -2      -5      -4      -3      -2      -4      -2      -1      -6
+S      1       -1      0       -1      -2      0       0       -1      -1      -3      -3      -1      -2      -3      -1      5       1       -4      -2      -2      0       -3      0       -1      -6
+T      0       -1      0       -1      -1      -1      -1      -2      -2      -1      -2      -1      -1      -2      -2      1       5       -4      -2      0       -1      -1      -1      -1      -6
+W      -3      -4      -4      -6      -3      -3      -4      -4      -3      -3      -2      -4      -2      0       -5      -4      -4      11      2       -3      -5      -3      -3      -1      -6
+Y      -2      -3      -3      -4      -3      -2      -3      -4      2       -2      -2      -3      -2      3       -4      -2      -2      2       7       -2      -3      -2      -3      -1      -6
+V      0       -3      -4      -4      -1      -3      -3      -4      -4      3       1       -3      1       -1      -3      -2      0       -3      -2      4       -4      2       -3      -1      -6
+B      -2      -1      5       5       -4      0       1       -1      -1      -4      -4      -1      -3      -4      -2      0       -1      -5      -3      -4      5       -4      0       -1      -6
+J      -2      -3      -4      -5      -2      -3      -4      -5      -4      3       3       -3      2       0       -4      -3      -1      -3      -2      2       -4      3       -3      -1      -6
+Z      -1      0       0       1       -4      4       5       -3      0       -4      -3      1       -1      -4      -2      0       -1      -3      -3      -3      0       -3      5       -1      -6
+X      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -1      -6
+*      -6      -6      -6      -6      -6      -6      -6      -6      -6      -6      -6      -6      -6      -6      -6      -6      -6      -6      -6      -6      -6      -6      -6      -6      1
+#
+#      A       R       N       D       C       Q       E       G       H       I       L       K       M       F       P       S       T       W       Y       V       B       J       Z       X       *
diff --git a/resources/scoreModel/dna.scm b/resources/scoreModel/dna.scm
new file mode 100644 (file)
index 0000000..4a196fb
--- /dev/null
@@ -0,0 +1,36 @@
+ScoreMatrix DNA
+ACGTUIXRYN -
+#
+# A DNA substitution matrix.
+# This is an ad-hoc matrix which, in addition to penalising mutations between the common 
+# nucleotides (ACGT), includes T/U equivalence in order to allow both DNA and/or RNA. 
+# In addition, it encodes weak equivalence between R and Y with AG and CTU, respectively, 
+# and N is allowed to match any other base weakly. 
+# This matrix also includes I (Inosine) and X (Xanthine), but encodes them to weakly match
+# any of (ACGTU), and unfavourably match each other.
+#
+# The first line declares a ScoreMatrix with the name DNA (shown in menus)
+# The second line gives the symbols for which scores are held in the matrix
+# These may include a space (but not as the first or last character)
+# Scores are not case sensitive, unless column(s) are provided for lower case characters
+#
+#
+# Comment line with symbols is provided as a guide
+# Values may be integer or floating point, delimited by tab, space, comma or combinations
+#
+#      A       C       G       T       U       I       X       R       Y       N               -
+#
+       10      -8      -8      -8      -8      1       1       1       -8      1       1       1 
+       -8      10      -8      -8      -8      1       1       -8      1       1       1       1
+       -8      -8      10      -8      -8      1       1       1       -8      1       1       1
+       -8      -8      -8      10      10      1       1       -8      1       1       1       1
+       -8      -8      -8      10      10      1       1       -8      1       1       1       1
+       1       1       1       1       1       10      0       0       0       1       1       1
+       1       1       1       1       1       0       10      0       0       1       1       1
+       1       -8      1       -8      -8      0       0       10      -8      1       1       1
+       -8      1       -8      1       1       0       0       -8      10      1       1       1
+       1       1       1       1       1       1       1       1       1       10      1       1
+       1       1       1       1       1       1       1       1       1       1       1       1
+       1       1       1       1       1       1       1       1       1       1       1       1
+#
+#      A       C       G       T       U       I       X       R       Y       N               -
diff --git a/resources/scoreModel/pam250.scm b/resources/scoreModel/pam250.scm
new file mode 100644 (file)
index 0000000..8df39a1
--- /dev/null
@@ -0,0 +1,42 @@
+ScoreMatrix PAM250
+ARNDCQEGHILKMFPSTWYVBZX *
+#
+# The PAM250 substitution matrix
+# The first line declares a ScoreMatrix with the name PAM250 (shown in menus)
+# The second line gives the symbols for which scores are held in the matrix
+# These may include a space (but not as the first or last character)
+# Scores are not case sensitive, unless column(s) are provided for lower case characters
+#
+#
+# Comment line with symbols is provided as a guide
+# Values may be integer or floating point, delimited by tab, space, comma or combinations
+#
+#      A       R       N       D       C       Q       E       G       H       I       L       K       M       F       P       S       T       W       Y       V       B       Z       X               *
+#
+       2       -2      0       0       -2      0       0       1       -1      -1      -2      -1      -1      -3      1       1       1       -6      -3      0       0        0      0       -8      -8      
+       -2      6       0       -1      -4      1       -1      -3      2       -2      -3      3       0       -4      0       0       -1      2       -4      -2      -1       0      -1      -8      -8      
+       0       0       2       2       -4      1       1       0       2       -2      -3      1       -2      -3      0       1       0       -4      -2      -2      2        1      0       -8      -8      
+       0       -1      2       4       -5      2       3       1       1       -2      -4      0       -3      -6      -1      0       0       -7      -4      -2      3        3      -1      -8      -8      
+       -2      -4      -4      -5      12      -5      -5      -3      -3      -2      -6      -5      -5      -4      -3      0       -2      -8      0       -2      -4      -5      -3      -8      -8      
+       0       1       1       2       -5      4       2       -1      3       -2      -2      1       -1      -5      0       -1      -1      -5      -4      -2      1        3      -1      -8      -8      
+       0       -1      1       3       -5      2       4       0       1       -2      -3      0       -2      -5      -1      0       0       -7      -4      -2      3        3      -1      -8      -8      
+       1       -3      0       1       -3      -1      0       5       -2      -3      -4      -2      -3      -5      0       1       0       -7      -5      -1      0        0      -1      -8      -8      
+       -1      2       2       1       -3      3       1       -2      6       -2      -2      0       -2      -2      0       -1      -1      -3      0       -2      1        2      -1      -8      -8      
+       -1      -2      -2      -2      -2      -2      -2      -3      -2      5       2       -2      2       1       -2      -1      0       -5      -1      4       -2      -2      -1      -8      -8      
+       -2      -3      -3      -4      -6      -2      -3      -4      -2      2       6       -3      4       2       -3      -3      -2      -2      -1      2       -3      -3      -1      -8      -8      
+       -1      3       1       0       -5      1       0       -2      0       -2      -3      5       0       -5      -1      0       0       -3      -4      -2      1        0      -1      -8      -8      
+       -1      0       -2      -3      -5      -1      -2      -3      -2      2       4       0       6       0       -2      -2      -1      -4      -2      2       -2      -2      -1      -8      -8      
+       -3      -4      -3      -6      -4      -5      -5      -5      -2      1       2       -5      0       9       -5      -3      -3      0       7       -1      -4      -5      -2      -8      -8      
+       1       0       0       -1      -3      0       -1      0       0       -2      -3      -1      -2      -5      6       1       0       -6      -5      -1      -1       0      -1      -8      -8      
+       1       0       1       0       0       -1      0       1       -1      -1      -3      0       -2      -3      1       2       1       -2      -3      -1      0        0       0      -8      -8      
+       1       -1      0       0       -2      -1      0       0       -1      0       -2      0       -1      -3      0       1       3       -5      -3      0       0       -1       0      -8      -8      
+       -6      2       -4      -7      -8      -5      -7      -7      -3      -5      -2      -3      -4      0       -6      -2      -5      17      0       -6      -5      -6      -4      -8      -8      
+       -3      -4      -2      -4      0       -4      -4      -5      0       -1      -1      -4      -2      7       -5      -3      -3      0       10      -2      -3      -4      -2      -8      -8      
+       0       -2      -2      -2      -2      -2      -2      -1      -2      4       2       -2      2       -1      -1      -1      0       -6      -2      4       -2      -2      -1      -8      -8      
+       0       -1      2       3       -4      1       3       0       1       -2      -3      1       -2      -4      -1      0       0       -5      -3      -2      3        2      -1      -8      -8      
+       0       0       1       3       -5      3       3       0       2       -2      -3      0       -2      -5      0       0       -1      -6      -4      -2      2        3      -1      -8      -8      
+       0       -1      0       -1      -3      -1      -1      -1      -1      -1      -1      -1      -1      -2      -1      0       0       -4      -2      -1      -1      -1      -1      -8      -8      
+       -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8       1   1
+       -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8      -8       1   1
+#
+#      A       R       N       D       C       Q       E       G       H       I       L       K       M       F       P       S       T       W       Y       V       B       Z       X       *
diff --git a/resources/scoreModel/seqspace.scm b/resources/scoreModel/seqspace.scm
new file mode 100644 (file)
index 0000000..a8bdc06
--- /dev/null
@@ -0,0 +1,31 @@
+ScoreMatrix Identity (SeqSpace)
+ARNDCQEGHILKMFPSTWYVBZX
+#
+# The identity substitution matrix, that gives the SeqSpace PCA calculation as in Jalview 2.10.1
+#
+# 
+#      A       R       N       D       C       Q       E       G       H       I       L       K       M       F       P       S       T       W       Y       V       B       Z       X
+#
+A      1       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0
+R      0       1       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       
+N      0       0       1       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0
+D      0       0       0       1       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       
+C      0       0       0       0       1       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0
+Q      0       0       0       0       0       1       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0
+E      0       0       0       0       0       0       1       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0
+G      0       0       0       0       0       0       0       1       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0
+H      0       0       0       0       0       0       0       0       1       0       0       0       0       0       0       0       0       0       0       0       0       0       0
+I      0       0       0       0       0       0       0       0       0       1       0       0       0       0       0       0       0       0       0       0       0       0       0
+L      0       0       0       0       0       0       0       0       0       0       1       0       0       0       0       0       0       0       0       0       0       0       0
+K      0       0       0       0       0       0       0       0       0       0       0       1       0       0       0       0       0       0       0       0       0       0       0
+M      0       0       0       0       0       0       0       0       0       0       0       0       1       0       0       0       0       0       0       0       0       0       0
+F      0       0       0       0       0       0       0       0       0       0       0       0       0       1       0       0       0       0       0       0       0       0       0
+P      0       0       0       0       0       0       0       0       0       0       0       0       0       0       1       0       0       0       0       0       0       0       0
+S      0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       1       0       0       0       0       0       0       0
+T      0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       1       0       0       0       0       0       0
+W      0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       1       0       0       0       0       0
+Y      0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       1       0       0       0       0
+V      0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       1       0       0       0
+B      0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       1       0       0
+Z      0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       1       0
+X      0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       1
index aac796c..1ab11b8 100644 (file)
@@ -177,7 +177,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
 
     colourBySequence();
 
-    int max = -10;
+    float max = -10;
     int maxchain = -1;
     int pdbstart = 0;
     int pdbend = 0;
index 292de91..5509056 100644 (file)
@@ -176,7 +176,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
 
     colourBySequence();
 
-    int max = -10;
+    float max = -10;
     int maxchain = -1;
     int pdbstart = 0;
     int pdbend = 0;
index 86bf721..ceca6d6 100755 (executable)
  */
 package jalview.analysis;
 
+import jalview.analysis.scoremodels.ScoreMatrix;
+import jalview.analysis.scoremodels.ScoreModels;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Mapping;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
-import jalview.schemes.ResidueProperties;
-import jalview.schemes.ScoreMatrix;
 import jalview.util.Comparison;
 import jalview.util.Format;
 import jalview.util.MapList;
@@ -53,17 +53,11 @@ public class AlignSeq
 
   private static final String NEWLINE = System.lineSeparator();
 
-  static String[] dna = { "A", "C", "G", "T", "-" };
+  float[][] score;
 
-  // "C", "T", "A", "G", "-"};
-  static String[] pep = { "A", "R", "N", "D", "C", "Q", "E", "G", "H", "I",
-      "L", "K", "M", "F", "P", "S", "T", "W", "Y", "V", "B", "Z", "X", "-" };
+  float[][] E;
 
-  int[][] score;
-
-  int[][] E;
-
-  int[][] F;
+  float[][] F;
 
   int[][] traceback;
 
@@ -106,7 +100,7 @@ public class AlignSeq
   int count;
 
   /** DOCUMENT ME!! */
-  public int maxscore;
+  public float maxscore;
 
   float pid;
 
@@ -116,31 +110,26 @@ public class AlignSeq
 
   int gapExtend = 20;
 
-  int[][] lookup = ResidueProperties.getBLOSUM62();
+  float[][] lookup;
 
-  String[] intToStr = pep;
-
-  int defInt = 23;
+  int gapIndex = 23;
 
   StringBuffer output = new StringBuffer();
 
-  String type;
+  String type; // AlignSeq.PEP or AlignSeq.DNA
 
-  private int[] charToInt;
+  private ScoreMatrix scoreModel;
 
   /**
    * Creates a new AlignSeq object.
    * 
-   * @param s1
-   *          DOCUMENT ME!
-   * @param s2
-   *          DOCUMENT ME!
-   * @param type
-   *          DOCUMENT ME!
+   * @param s1 first sequence for alignment
+   * @param s2 second sequence for alignment
+   * @param type molecule type, either AlignSeq.PEP or AlignSeq.DNA
    */
   public AlignSeq(SequenceI s1, SequenceI s2, String type)
   {
-    SeqInit(s1, s1.getSequenceAsString(), s2, s2.getSequenceAsString(),
+    seqInit(s1, s1.getSequenceAsString(), s2, s2.getSequenceAsString(),
             type);
   }
 
@@ -157,7 +146,7 @@ public class AlignSeq
   public AlignSeq(SequenceI s1, String string1, SequenceI s2,
           String string2, String type)
   {
-    SeqInit(s1, string1.toUpperCase(), s2, string2.toUpperCase(), type);
+    seqInit(s1, string1.toUpperCase(), s2, string2.toUpperCase(), type);
   }
 
   /**
@@ -165,7 +154,7 @@ public class AlignSeq
    * 
    * @return DOCUMENT ME!
    */
-  public int getMaxScore()
+  public float getMaxScore()
   {
     return maxscore;
   }
@@ -261,26 +250,6 @@ public class AlignSeq
   }
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public SequenceI getS1()
-  {
-    return s1;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public SequenceI getS2()
-  {
-    return s2;
-  }
-
-  /**
    * 
    * @return aligned instance of Seq 1
    */
@@ -322,36 +291,13 @@ public class AlignSeq
    * @param type
    *          DNA or PEPTIDE
    */
-  public void SeqInit(SequenceI s1, String string1, SequenceI s2,
+  public void seqInit(SequenceI s1, String string1, SequenceI s2,
           String string2, String type)
   {
     this.s1 = s1;
     this.s2 = s2;
     setDefaultParams(type);
-    SeqInit(string1, string2);
-  }
-
-  /**
-   * Construct score matrix for sequences with custom substitution matrix
-   * 
-   * @param s1
-   *          - sequence 1
-   * @param string1
-   *          - string to use for s1
-   * @param s2
-   *          - sequence 2
-   * @param string2
-   *          - string to use for s2
-   * @param scoreMatrix
-   *          - substitution matrix to use for alignment
-   */
-  public void SeqInit(SequenceI s1, String string1, SequenceI s2,
-          String string2, ScoreMatrix scoreMatrix)
-  {
-    this.s1 = s1;
-    this.s2 = s2;
-    setType(scoreMatrix.isDNA() ? AlignSeq.DNA : AlignSeq.PEP);
-    lookup = scoreMatrix.getMatrix();
+    seqInit(string1, string2);
   }
 
   /**
@@ -361,7 +307,7 @@ public class AlignSeq
    * @param string1
    * @param string2
    */
-  private void SeqInit(String string1, String string2)
+  private void seqInit(String string1, String string2)
   {
     s1str = extractGaps(jalview.util.Comparison.GapChars, string1);
     s2str = extractGaps(jalview.util.Comparison.GapChars, string2);
@@ -374,84 +320,37 @@ public class AlignSeq
       return;
     }
 
-    // System.out.println("lookuip " + rt.freeMemory() + " "+ rt.totalMemory());
     seq1 = new int[s1str.length()];
 
-    // System.out.println("seq1 " + rt.freeMemory() +" " + rt.totalMemory());
     seq2 = new int[s2str.length()];
 
-    // System.out.println("seq2 " + rt.freeMemory() + " " + rt.totalMemory());
-    score = new int[s1str.length()][s2str.length()];
+    score = new float[s1str.length()][s2str.length()];
 
-    // System.out.println("score " + rt.freeMemory() + " " + rt.totalMemory());
-    E = new int[s1str.length()][s2str.length()];
+    E = new float[s1str.length()][s2str.length()];
 
-    // System.out.println("E " + rt.freeMemory() + " " + rt.totalMemory());
-    F = new int[s1str.length()][s2str.length()];
+    F = new float[s1str.length()][s2str.length()];
     traceback = new int[s1str.length()][s2str.length()];
 
-    // System.out.println("F " + rt.freeMemory() + " " + rt.totalMemory());
-    seq1 = stringToInt(s1str, type);
-
-    // System.out.println("seq1 " + rt.freeMemory() + " " + rt.totalMemory());
-    seq2 = stringToInt(s2str, type);
-
-    // System.out.println("Seq2 " + rt.freeMemory() + " " + rt.totalMemory());
-    // long tstart = System.currentTimeMillis();
-    // calcScoreMatrix();
-    // long tend = System.currentTimeMillis();
-    // System.out.println("Time take to calculate score matrix = " +
-    // (tend-tstart) + " ms");
-    // printScoreMatrix(score);
-    // System.out.println();
-    // printScoreMatrix(traceback);
-    // System.out.println();
-    // printScoreMatrix(E);
-    // System.out.println();
-    // /printScoreMatrix(F);
-    // System.out.println();
-    // tstart = System.currentTimeMillis();
-    // traceAlignment();
-    // tend = System.currentTimeMillis();
-    // System.out.println("Time take to traceback alignment = " + (tend-tstart)
-    // + " ms");
-  }
-
-  private void setDefaultParams(String type)
-  {
-    setType(type);
+    seq1 = indexEncode(s1str);
 
-    if (type.equals(AlignSeq.PEP))
-    {
-      lookup = ResidueProperties.getDefaultPeptideMatrix();
-    }
-    else if (type.equals(AlignSeq.DNA))
-    {
-      lookup = ResidueProperties.getDefaultDnaMatrix();
-    }
+    seq2 = indexEncode(s2str);
   }
 
-  private void setType(String type2)
+  private void setDefaultParams(String moleculeType)
   {
-    this.type = type2;
-    if (type.equals(AlignSeq.PEP))
-    {
-      intToStr = pep;
-      charToInt = ResidueProperties.aaIndex;
-      defInt = ResidueProperties.maxProteinIndex;
-    }
-    else if (type.equals(AlignSeq.DNA))
-    {
-      intToStr = dna;
-      charToInt = ResidueProperties.nucleotideIndex;
-      defInt = ResidueProperties.maxNucleotideIndex;
-    }
-    else
+    if (!PEP.equals(moleculeType) && !DNA.equals(moleculeType))
     {
       output.append("Wrong type = dna or pep only");
       throw new Error(MessageManager.formatMessage(
-              "error.unknown_type_dna_or_pep", new String[] { type2 }));
+              "error.unknown_type_dna_or_pep",
+              new String[] { moleculeType }));
     }
+
+    type = moleculeType;
+    scoreModel = ScoreModels.getInstance().getDefaultModel(
+            PEP.equals(type));
+    lookup = scoreModel.getMatrix();
+    gapIndex = scoreModel.getMatrixIndex(' ');
   }
 
   /**
@@ -460,7 +359,7 @@ public class AlignSeq
   public void traceAlignment()
   {
     // Find the maximum score along the rhs or bottom row
-    int max = -9999;
+    float max = -Float.MAX_VALUE;
 
     for (int i = 0; i < seq1.length; i++)
     {
@@ -494,21 +393,17 @@ public class AlignSeq
     aseq1 = new int[seq1.length + seq2.length];
     aseq2 = new int[seq1.length + seq2.length];
 
+    StringBuilder sb1 = new StringBuilder(aseq1.length);
+    StringBuilder sb2 = new StringBuilder(aseq2.length);
+
     count = (seq1.length + seq2.length) - 1;
 
-    while ((i > 0) && (j > 0))
+    while (i > 0 && j > 0)
     {
-      if ((aseq1[count] != defInt) && (i >= 0))
-      {
-        aseq1[count] = seq1[i];
-        astr1 = s1str.charAt(i) + astr1;
-      }
-
-      if ((aseq2[count] != defInt) && (j > 0))
-      {
-        aseq2[count] = seq2[j];
-        astr2 = s2str.charAt(j) + astr2;
-      }
+      aseq1[count] = seq1[i];
+      sb1.append(s1str.charAt(i));
+      aseq2[count] = seq2[j];
+      sb2.append(s2str.charAt(j));
 
       trace = findTrace(i, j);
 
@@ -520,14 +415,14 @@ public class AlignSeq
       else if (trace == 1)
       {
         j--;
-        aseq1[count] = defInt;
-        astr1 = "-" + astr1.substring(1);
+        aseq1[count] = gapIndex;
+        sb1.replace(sb1.length() - 1, sb1.length(), "-");
       }
       else if (trace == -1)
       {
         i--;
-        aseq2[count] = defInt;
-        astr2 = "-" + astr2.substring(1);
+        aseq2[count] = gapIndex;
+        sb2.replace(sb2.length() - 1, sb2.length(), "-");
       }
 
       count--;
@@ -536,17 +431,24 @@ public class AlignSeq
     seq1start = i + 1;
     seq2start = j + 1;
 
-    if (aseq1[count] != defInt)
+    if (aseq1[count] != gapIndex)
     {
       aseq1[count] = seq1[i];
-      astr1 = s1str.charAt(i) + astr1;
+      sb1.append(s1str.charAt(i));
     }
 
-    if (aseq2[count] != defInt)
+    if (aseq2[count] != gapIndex)
     {
       aseq2[count] = seq2[j];
-      astr2 = s2str.charAt(j) + astr2;
+      sb2.append(s2str.charAt(j));
     }
+
+    /*
+     * we built the character strings backwards, so now
+     * reverse them to convert to sequence strings
+     */
+    astr1 = sb1.reverse().toString();
+    astr2 = sb2.reverse().toString();
   }
 
   /**
@@ -599,6 +501,8 @@ public class AlignSeq
             .append(String.valueOf(s2str.length())).append(")")
             .append(NEWLINE).append(NEWLINE);
 
+    ScoreMatrix pam250 = ScoreModels.getInstance().getPam250();
+
     for (int j = 0; j < nochunks; j++)
     {
       // Print the first aligned sequence
@@ -615,25 +519,27 @@ public class AlignSeq
       output.append(NEWLINE);
       output.append(new Format("%" + (maxid) + "s").form(" ")).append(" ");
 
-      // Print out the matching chars
+      /*
+       * Print out the match symbols:
+       * | for exact match (ignoring case)
+       * . if PAM250 score is positive
+       * else a space
+       */
       for (int i = 0; i < len; i++)
       {
         if ((i + (j * len)) < astr1.length())
         {
-          boolean sameChar = Comparison.isSameResidue(
-                  astr1.charAt(i + (j * len)), astr2.charAt(i + (j * len)),
-                  false);
-          if (sameChar
-                  && !jalview.util.Comparison.isGap(astr1.charAt(i
-                          + (j * len))))
+          char c1 = astr1.charAt(i + (j * len));
+          char c2 = astr2.charAt(i + (j * len));
+          boolean sameChar = Comparison.isSameResidue(c1, c2, false);
+          if (sameChar && !Comparison.isGap(c1))
           {
             pid++;
             output.append("|");
           }
           else if (type.equals("pep"))
           {
-            if (ResidueProperties.getPAM250(astr1.charAt(i + (j * len)),
-                    astr2.charAt(i + (j * len))) > 0)
+            if (pam250.getPairwiseScore(c1, c2) > 0)
             {
               output.append(".");
             }
@@ -678,46 +584,6 @@ public class AlignSeq
   /**
    * DOCUMENT ME!
    * 
-   * @param mat
-   *          DOCUMENT ME!
-   */
-  public void printScoreMatrix(int[][] mat)
-  {
-    int n = seq1.length;
-    int m = seq2.length;
-
-    for (int i = 0; i < n; i++)
-    {
-      // Print the top sequence
-      if (i == 0)
-      {
-        Format.print(System.out, "%8s", s2str.substring(0, 1));
-
-        for (int jj = 1; jj < m; jj++)
-        {
-          Format.print(System.out, "%5s", s2str.substring(jj, jj + 1));
-        }
-
-        System.out.println();
-      }
-
-      for (int j = 0; j < m; j++)
-      {
-        if (j == 0)
-        {
-          Format.print(System.out, "%3s", s1str.substring(i, i + 1));
-        }
-
-        Format.print(System.out, "%3d ", mat[i][j] / 10);
-      }
-
-      System.out.println();
-    }
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
    * @param i
    *          DOCUMENT ME!
    * @param j
@@ -728,7 +594,7 @@ public class AlignSeq
   public int findTrace(int i, int j)
   {
     int t = 0;
-    int max = score[i - 1][j - 1] + (lookup[seq1[i]][seq2[j]] * 10);
+    float max = score[i - 1][j - 1] + (lookup[seq1[i]][seq2[j]] * 10);
 
     if (F[i][j] > max)
     {
@@ -843,27 +709,27 @@ public class AlignSeq
   /**
    * DOCUMENT ME!
    * 
-   * @param i1
+   * @param f1
    *          DOCUMENT ME!
-   * @param i2
+   * @param f2
    *          DOCUMENT ME!
-   * @param i3
+   * @param f3
    *          DOCUMENT ME!
    * 
    * @return DOCUMENT ME!
    */
-  public int max(int i1, int i2, int i3)
+  private static float max(float f1, float f2, float f3)
   {
-    int max = i1;
+    float max = f1;
 
-    if (i2 > i1)
+    if (f2 > f1)
     {
-      max = i2;
+      max = f2;
     }
 
-    if (i3 > max)
+    if (f3 > max)
     {
-      max = i3;
+      max = f3;
     }
 
     return max;
@@ -872,65 +738,44 @@ public class AlignSeq
   /**
    * DOCUMENT ME!
    * 
-   * @param i1
+   * @param f1
    *          DOCUMENT ME!
-   * @param i2
+   * @param f2
    *          DOCUMENT ME!
    * 
    * @return DOCUMENT ME!
    */
-  public int max(int i1, int i2)
+  private static float max(float f1, float f2)
   {
-    int max = i1;
+    float max = f1;
 
-    if (i2 > i1)
+    if (f2 > f1)
     {
-      max = i2;
+      max = f2;
     }
 
     return max;
   }
 
   /**
-   * DOCUMENT ME!
+   * Converts the character string to an array of integers which are the
+   * corresponding indices to the characters in the score matrix
    * 
    * @param s
-   *          DOCUMENT ME!
-   * @param type
-   *          DOCUMENT ME!
    * 
-   * @return DOCUMENT ME!
+   * @return
    */
-  public int[] stringToInt(String s, String type)
+  int[] indexEncode(String s)
   {
-    int[] seq1 = new int[s.length()];
+    int[] encoded = new int[s.length()];
 
     for (int i = 0; i < s.length(); i++)
     {
-      // String ss = s.substring(i, i + 1).toUpperCase();
       char c = s.charAt(i);
-      if ('a' <= c && c <= 'z')
-      {
-        // TO UPPERCASE !!!
-        c -= ('a' - 'A');
-      }
-
-      try
-      {
-        seq1[i] = charToInt[c]; // set accordingly from setType
-        if (seq1[i] < 0 || seq1[i] > defInt) // set from setType: 23 for
-                                             // peptides, or 4 for NA.
-        {
-          seq1[i] = defInt;
-        }
-
-      } catch (Exception e)
-      {
-        seq1[i] = defInt;
-      }
+      encoded[i] = scoreModel.getMatrixIndex(c);
     }
 
-    return seq1;
+    return encoded;
   }
 
   /**
@@ -1113,7 +958,7 @@ public class AlignSeq
       {
         SequenceI bestm = null;
         AlignSeq bestaseq = null;
-        int bestscore = 0;
+        float bestscore = 0;
         for (SequenceI msq : al.getSequences())
         {
           AlignSeq aseq = doGlobalNWAlignment(msq, sq, dnaOrProtein);
index 565924b..d1a3c37 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.analysis;
 
+import jalview.analysis.scoremodels.ScoreMatrix;
+import jalview.analysis.scoremodels.ScoreModels;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.ResidueCount;
@@ -162,27 +164,30 @@ public class Conservation
   }
 
   /**
-   * Translate sequence i into a numerical representation and store it in the
-   * i'th position of the seqNums array.
+   * Translate sequence i into score matrix indices and store it in the i'th
+   * position of the seqNums array.
    * 
    * @param i
+   * @param sm
    */
-  private void calcSeqNum(int i)
+  private void calcSeqNum(int i, ScoreMatrix sm)
   {
-    String sq = null; // for dumb jbuilder not-inited exception warning
-    int[] sqnum = null;
-
+    int gapIndex = sm.getMatrixIndex(' ');
     int sSize = sequences.length;
 
     if ((i > -1) && (i < sSize))
     {
-      sq = sequences[i].getSequenceAsString();
+      String sq = sequences[i].getSequenceAsString();
 
       if (seqNums.size() <= i)
       {
         seqNums.addElement(new int[sq.length() + 1]);
       }
 
+      /*
+       * the first entry in the array is the sequence's hashcode,
+       * following entries are matrix indices of sequence characters
+       */
       if (sq.hashCode() != seqNums.elementAt(i)[0])
       {
         int j;
@@ -195,14 +200,18 @@ public class Conservation
           maxLength = len;
         }
 
-        sqnum = new int[len + 1]; // better to always make a new array -
+        int[] sqnum = new int[len + 1]; // better to always make a new array -
         // sequence can change its length
         sqnum[0] = sq.hashCode();
 
         for (j = 1; j <= len; j++)
         {
-          sqnum[j] = jalview.schemes.ResidueProperties.aaIndex[sq
-                  .charAt(j - 1)];
+          // sqnum[j] = ResidueProperties.aaIndex[sq.charAt(j - 1)];
+          sqnum[j] = sm.getMatrixIndex(sq.charAt(j - 1));
+          if (sqnum[j] == -1)
+          {
+            sqnum[j] = gapIndex;
+          }
         }
 
         seqNums.setElementAt(sqnum, i);
@@ -532,37 +541,32 @@ public class Conservation
 
   /**
    * DOCUMENT ME!
+   * 
+   * @param sm
    */
-  private void percentIdentity2()
+  private void percentIdentity(ScoreMatrix sm)
   {
     seqNums = new Vector<int[]>();
-    // calcSeqNum(s);
     int i = 0, iSize = sequences.length;
     // Do we need to calculate this again?
     for (i = 0; i < iSize; i++)
     {
-      calcSeqNum(i);
+      calcSeqNum(i, sm);
     }
 
+    int gapIndex = sm.getMatrixIndex(' ');
+
     if ((cons2 == null) || seqNumsChanged)
     {
+      // FIXME remove magic number 24 without changing calc
+      // sm.getSize() returns 25 so doesn't quite do it...
       cons2 = new int[maxLength][24];
 
-      // Initialize the array
-      for (int j = 0; j < 24; j++)
-      {
-        for (i = 0; i < maxLength; i++)
-        {
-          cons2[i][j] = 0;
-        }
-      }
-
-      int[] sqnum;
       int j = 0;
 
       while (j < sequences.length)
       {
-        sqnum = seqNums.elementAt(j);
+        int[] sqnum = seqNums.elementAt(j);
 
         for (i = 1; i < sqnum.length; i++)
         {
@@ -571,21 +575,10 @@ public class Conservation
 
         for (i = sqnum.length - 1; i < maxLength; i++)
         {
-          cons2[i][23]++; // gap count
+          cons2[i][gapIndex]++; // gap count
         }
-
         j++;
       }
-
-      // unnecessary ?
-
-      /*
-       * for (int i=start; i <= end; i++) { int max = -1000; int maxi = -1; int
-       * maxj = -1;
-       * 
-       * for (int j=0;j<24;j++) { if (cons2[i][j] > max) { max = cons2[i][j];
-       * maxi = i; maxj = j; } } }
-       */
     }
   }
 
@@ -601,13 +594,15 @@ public class Conservation
   {
     quality = new Vector<Double>();
 
-    double max = -10000;
-    int[][] BLOSUM62 = ResidueProperties.getBLOSUM62();
+    double max = -Double.MAX_VALUE;
+    ScoreMatrix blosum62 = ScoreModels.getInstance().getBlosum62();
+    float[][] blosumScores = blosum62.getMatrix();
+    int gapIndex = blosum62.getMatrixIndex(' ');
 
     // Loop over columns // JBPNote Profiling info
     // long ts = System.currentTimeMillis();
     // long te = System.currentTimeMillis();
-    percentIdentity2();
+    percentIdentity(blosum62);
 
     int size = seqNums.size();
     int[] lengths = new int[size];
@@ -620,20 +615,24 @@ public class Conservation
       lengths[l] = seqNums.elementAt(l).length - 1;
     }
 
+    // todo ? remove '*' (unused?) from score matrix and
+    // use getSize() here instead of getSize() - 1 ??
+    final int symbolCount = blosum62.getSize() - 1; // 24;
+
     for (j = startRes; j <= endRes; j++)
     {
       bigtot = 0;
 
       // First Xr = depends on column only
-      x = new double[24];
+      x = new double[symbolCount];
 
-      for (ii = 0; ii < 24; ii++)
+      for (ii = 0; ii < symbolCount; ii++)
       {
         x[ii] = 0;
 
-        for (i2 = 0; i2 < 24; i2++)
+        for (i2 = 0; i2 < symbolCount; i2++)
         {
-          x[ii] += (((double) cons2[j][i2] * BLOSUM62[ii][i2]) + 4);
+          x[ii] += (((double) cons2[j][i2] * blosumScores[ii][i2]) + 4);
         }
 
         x[ii] /= size;
@@ -643,18 +642,16 @@ public class Conservation
       for (k = 0; k < size; k++)
       {
         tot = 0;
-        xx = new double[24];
-        seqNum = (j < lengths[k]) ? seqNums.elementAt(k)[j + 1] : 23; // Sequence,
-                                                                      // or gap
-                                                                      // at the
-                                                                      // end
+        xx = new double[symbolCount];
+        seqNum = (j < lengths[k]) ? seqNums.elementAt(k)[j + 1] : gapIndex;
+        // Sequence, or gap at the end
 
         // This is a loop over r
-        for (i = 0; i < 23; i++)
+        for (i = 0; i < symbolCount - 1; i++)
         {
           sr = 0;
 
-          sr = (double) BLOSUM62[i][seqNum] + 4;
+          sr = (double) blosumScores[i][seqNum] + 4;
 
           // Calculate X with another loop over residues
           // System.out.println("Xi " + i + " " + x[i] + " " + sr);
@@ -678,12 +675,13 @@ public class Conservation
       // Need to normalize by gaps
     }
 
-    double newmax = -10000;
+    double newmax = -Double.MAX_VALUE;
 
     for (j = startRes; j <= endRes; j++)
     {
       tmp = quality.elementAt(j).doubleValue();
-      tmp = ((max - tmp) * (size - cons2[j][23])) / size;
+      // tmp = ((max - tmp) * (size - cons2[j][23])) / size;
+      tmp = ((max - tmp) * (size - cons2[j][gapIndex])) / size;
 
       // System.out.println(tmp+ " " + j);
       quality.setElementAt(new Double(tmp), j);
index e0e50fb..7140867 100644 (file)
@@ -20,7 +20,8 @@
  */
 package jalview.analysis;
 
-import jalview.api.analysis.ScoreModelI;
+import jalview.analysis.scoremodels.ScoreModels;
+import jalview.api.analysis.DistanceModelI;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.BinaryNode;
 import jalview.datamodel.CigarArray;
@@ -30,7 +31,6 @@ import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
 import jalview.datamodel.SequenceNode;
 import jalview.io.NewickFile;
-import jalview.schemes.ResidueProperties;
 
 import java.util.Enumeration;
 import java.util.List;
@@ -44,6 +44,15 @@ import java.util.Vector;
  */
 public class NJTree
 {
+  /*
+   * 'methods'
+   */
+  public static final String AVERAGE_DISTANCE = "AV";
+
+  public static final String NEIGHBOUR_JOINING = "NJ";
+
+  public static final String FROM_FILE = "FromFile";
+
   Vector<Cluster> cluster;
 
   SequenceI[] sequence;
@@ -215,7 +224,7 @@ public class NJTree
    *          DOCUMENT ME!
    */
   public NJTree(SequenceI[] sequence, AlignmentView seqData, String type,
-          String pwtype, ScoreModelI sm, int start, int end)
+          String pwtype, DistanceModelI sm, int start, int end)
   {
     this.sequence = sequence;
     this.node = new Vector<SequenceNode>();
@@ -237,14 +246,14 @@ public class NJTree
       this.seqData = new AlignmentView(sdata, start);
     }
     // System.err.println("Made seqData");// dbg
-    if (!(type.equals("NJ")))
+    if (!(type.equals(NEIGHBOUR_JOINING)))
     {
-      type = "AV";
+      type = AVERAGE_DISTANCE;
     }
 
     if (sm == null && !(pwtype.equals("PID")))
     {
-      if (ResidueProperties.getScoreMatrix(pwtype) == null)
+      if (ScoreModels.getInstance().forName(pwtype) == null)
       {
         pwtype = "BLOSUM62";
       }
@@ -384,7 +393,7 @@ public class NJTree
   {
     while (noClus > 2)
     {
-      if (type.equals("NJ"))
+      if (type.equals(NEIGHBOUR_JOINING))
       {
         findMinNJDistance();
       }
@@ -466,7 +475,7 @@ public class NJTree
     ri = findr(i, j);
     rj = findr(j, i);
 
-    if (type.equals("NJ"))
+    if (type.equals(NEIGHBOUR_JOINING))
     {
       findClusterNJDistance(i, j);
     }
@@ -483,7 +492,7 @@ public class NJTree
     SequenceNode tmpi = (node.elementAt(i));
     SequenceNode tmpj = (node.elementAt(j));
 
-    if (type.equals("NJ"))
+    if (type.equals(NEIGHBOUR_JOINING))
     {
       findNewNJDistances(tmpi, tmpj, dist);
     }
@@ -727,17 +736,17 @@ public class NJTree
    * 
    * @return similarity matrix used to compute tree
    */
-  public float[][] findDistances(ScoreModelI _pwmatrix)
+  public float[][] findDistances(DistanceModelI _pwmatrix)
   {
 
     float[][] dist = new float[noseqs][noseqs];
     if (_pwmatrix == null)
     {
       // Resolve substitution model
-      _pwmatrix = ResidueProperties.getScoreModel(pwtype);
+      _pwmatrix = ScoreModels.getInstance().forName(pwtype);
       if (_pwmatrix == null)
       {
-        _pwmatrix = ResidueProperties.getScoreMatrix("BLOSUM62");
+        _pwmatrix = ScoreModels.getInstance().forName("BLOSUM62");
       }
     }
     dist = _pwmatrix.findDistances(seqData);
index 9babaee..b6766c6 100755 (executable)
  */
 package jalview.analysis;
 
+import jalview.analysis.scoremodels.PairwiseDistanceModel;
+import jalview.analysis.scoremodels.ScoreMatrix;
+import jalview.analysis.scoremodels.ScoreModels;
 import jalview.math.MatrixI;
-import jalview.schemes.ResidueProperties;
-import jalview.schemes.ScoreMatrix;
 
 import java.io.PrintStream;
 
@@ -80,14 +81,15 @@ public class PCA implements Runnable
     String sm = s_m;
     if (sm != null)
     {
-      scoreMatrix = ResidueProperties.getScoreMatrix(sm);
+      scoreMatrix = (ScoreMatrix) ((PairwiseDistanceModel) ScoreModels
+              .getInstance()
+              .forName(sm)).getScoreModel();
     }
     if (scoreMatrix == null)
     {
       // either we were given a non-existent score matrix or a scoremodel that
       // isn't based on a pairwise symbol score matrix
-      scoreMatrix = ResidueProperties
-              .getScoreMatrix(sm = (nucleotides ? "DNA" : "BLOSUM62"));
+      scoreMatrix = ScoreModels.getInstance().getDefaultModel(!nucleotides);
     }
     details.append("PCA calculation using " + sm
             + " sequence similarity matrix\n========\n\n");
  */
 package jalview.analysis.scoremodels;
 
-import jalview.api.analysis.ScoreModelI;
+import jalview.api.analysis.DistanceModelI;
 import jalview.api.analysis.ViewBasedAnalysisI;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.SeqCigar;
 import jalview.datamodel.SequenceFeature;
+import jalview.util.SetUtils;
 
-import java.util.ArrayList;
-import java.util.Hashtable;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
-public class FeatureScoreModel implements ScoreModelI, ViewBasedAnalysisI
+public class FeatureDistanceModel implements DistanceModelI, ViewBasedAnalysisI
 {
   jalview.api.FeatureRenderer fr;
 
@@ -42,88 +45,65 @@ public class FeatureScoreModel implements ScoreModelI, ViewBasedAnalysisI
     return true;
   }
 
+  /**
+   * Calculates a distance measure [i][j] between each pair of sequences as the
+   * average number of features they have but do not share. That is, find the
+   * features each sequence pair has at each column, ignore feature types they
+   * have in common, and count the rest. The totals are normalised by the number
+   * of columns processed.
+   */
   @Override
   public float[][] findDistances(AlignmentView seqData)
   {
-    int nofeats = 0;
     List<String> dft = fr.getDisplayedFeatureTypes();
-    nofeats = dft.size();
     SeqCigar[] seqs = seqData.getSequences();
     int noseqs = seqs.length;
     int cpwidth = 0;// = seqData.getWidth();
     float[][] distance = new float[noseqs][noseqs];
-    if (nofeats == 0)
+    if (dft.isEmpty())
     {
-      for (float[] d : distance)
-      {
-        for (int i = 0; i < d.length; d[i++] = 0f)
-        {
-          ;
-        }
-      }
       return distance;
     }
+
     // need to get real position for view position
     int[] viscont = seqData.getVisibleContigs();
+
+    /*
+     * scan each column, compute and add to each distance[i, j]
+     * the number of feature types that seqi and seqj do not share
+     */
     for (int vc = 0; vc < viscont.length; vc += 2)
     {
-
       for (int cpos = viscont[vc]; cpos <= viscont[vc + 1]; cpos++)
       {
         cpwidth++;
-        // get visible features at cpos under view's display settings and
-        // compare them
-        List<Hashtable<String, SequenceFeature>> sfap = new ArrayList<Hashtable<String, SequenceFeature>>();
-        for (int i = 0; i < noseqs; i++)
-        {
-          Hashtable<String, SequenceFeature> types = new Hashtable<String, SequenceFeature>();
-          int spos = seqs[i].findPosition(cpos);
-          if (spos != -1)
-          {
-            List<SequenceFeature> sfs = fr.findFeaturesAtRes(
-                    seqs[i].getRefSeq(), spos);
-            for (SequenceFeature sf : sfs)
-            {
-              types.put(sf.getType(), sf);
-            }
-          }
-          sfap.add(types);
-        }
+
+        /*
+         * first pass: record features types in column for each sequence
+         */
+        Map<SeqCigar, Set<String>> sfap = findFeatureTypesAtColumn(
+                seqs, cpos);
+
+        /*
+         * count feature types on either i'th or j'th sequence but not both
+         * and add this 'distance' measure to the total for [i, j] for j > i
+         */
         for (int i = 0; i < (noseqs - 1); i++)
         {
-          if (cpos == 0)
-          {
-            distance[i][i] = 0f;
-          }
           for (int j = i + 1; j < noseqs; j++)
           {
-            int sfcommon = 0;
-            // compare the two lists of features...
-            Hashtable<String, SequenceFeature> fi = sfap.get(i), fk, fj = sfap
-                    .get(j);
-            if (fi.size() > fj.size())
-            {
-              fk = fj;
-            }
-            else
-            {
-              fk = fi;
-              fi = fj;
-            }
-            for (String k : fi.keySet())
-            {
-              SequenceFeature sfj = fk.get(k);
-              if (sfj != null)
-              {
-                sfcommon++;
-              }
-            }
-            distance[i][j] += (fi.size() + fk.size() - 2f * sfcommon);
-            distance[j][i] += distance[i][j];
+            int seqDistance = SetUtils.countDisjunction(sfap.get(seqs[i]),
+                    sfap.get(seqs[j]));
+            distance[i][j] += seqDistance;
           }
         }
       }
     }
+
+    /*
+     * normalise the distance scores (summed over columns) by the
+     * number of visible columns used in the calculation
+     */
     for (int i = 0; i < noseqs; i++)
     {
       for (int j = i + 1; j < noseqs; j++)
@@ -135,6 +115,36 @@ public class FeatureScoreModel implements ScoreModelI, ViewBasedAnalysisI
     return distance;
   }
 
+  /**
+   * Builds and returns a list (one per SeqCigar) of visible feature types at
+   * the given column position
+   * 
+   * @param seqs
+   * @param columnPosition
+   * @return
+   */
+  protected Map<SeqCigar, Set<String>> findFeatureTypesAtColumn(
+          SeqCigar[] seqs, int columnPosition)
+  {
+    Map<SeqCigar, Set<String>> sfap = new HashMap<SeqCigar, Set<String>>();
+    for (SeqCigar seq : seqs)
+    {
+      Set<String> types = new HashSet<String>();
+      int spos = seq.findPosition(columnPosition);
+      if (spos != -1)
+      {
+        List<SequenceFeature> sfs = fr.findFeaturesAtRes(seq.getRefSeq(),
+                spos);
+        for (SequenceFeature sf : sfs)
+        {
+          types.add(sf.getType());
+        }
+      }
+      sfap.put(seq, types);
+    }
+    return sfap;
+  }
+
   @Override
   public String getName()
   {
  */
 package jalview.analysis.scoremodels;
 
-import jalview.api.analysis.ScoreModelI;
+import jalview.api.analysis.DistanceModelI;
 import jalview.datamodel.AlignmentView;
 import jalview.util.Comparison;
 
-public class PIDScoreModel implements ScoreModelI
+public class PIDDistanceModel implements DistanceModelI
 {
 
   @Override
   public float[][] findDistances(AlignmentView seqData)
   {
     String[] sequenceString = seqData
-            .getSequenceStrings(Comparison.GapChars.charAt(0));
+            .getSequenceStrings(Comparison.GAP_SPACE);
     int noseqs = sequenceString.length;
     float[][] distance = new float[noseqs][noseqs];
     for (int i = 0; i < (noseqs - 1); i++)
@@ -72,4 +72,9 @@ public class PIDScoreModel implements ScoreModelI
     return true;
   }
 
+  @Override
+  public String toString()
+  {
+    return "Percentage identity of sequences";
+  }
 }
  */
 package jalview.analysis.scoremodels;
 
-import jalview.api.analysis.ScoreModelI;
+import jalview.api.analysis.DistanceModelI;
 import jalview.datamodel.AlignmentView;
 import jalview.util.Comparison;
 
-public abstract class PairwiseSeqScoreModel implements ScoreModelI
+public class PairwiseDistanceModel implements DistanceModelI
 {
-  abstract public int getPairwiseScore(char c, char d);
+  PairwiseScoreModelI scoreModel;
 
+  /**
+   * Constructor given something to provide pairwise scores for residues
+   * 
+   * @param sm
+   */
+  public PairwiseDistanceModel(PairwiseScoreModelI sm)
+  {
+    scoreModel = sm;
+  }
+
+  /**
+   * Returns a matrix of [i][j] values representing distances between pairs of
+   * sequences
+   */
+  @Override
   public float[][] findDistances(AlignmentView seqData)
   {
     String[] sequenceString = seqData
-            .getSequenceStrings(Comparison.GapChars.charAt(0));
+            .getSequenceStrings(Comparison.GAP_SPACE);
     int noseqs = sequenceString.length;
     float[][] distance = new float[noseqs][noseqs];
 
-    int maxscore = 0;
+    /*
+     * calculate similarity scores for the upper half of the matrix
+     * as [i, j] = the sum of pairwise scores of corresponding
+     * positions of sequence[i] and sequence[j]
+     */
+    float maxscore = 0;
     int end = sequenceString[0].length();
     for (int i = 0; i < (noseqs - 1); i++)
     {
       for (int j = i; j < noseqs; j++)
       {
-        int score = 0;
+        float score = 0;
 
         for (int k = 0; k < end; k++)
         {
           try
           {
-            score += getPairwiseScore(sequenceString[i].charAt(k),
+            score += scoreModel.getPairwiseScore(
+                    sequenceString[i].charAt(k),
                     sequenceString[j].charAt(k));
           } catch (Exception ex)
           {
@@ -56,7 +77,7 @@ public abstract class PairwiseSeqScoreModel implements ScoreModelI
           }
         }
 
-        distance[i][j] = (float) score;
+        distance[i][j] = score;
 
         if (score > maxscore)
         {
@@ -65,16 +86,43 @@ public abstract class PairwiseSeqScoreModel implements ScoreModelI
       }
     }
 
+    /*
+     * subtract similarity scores from the maximum value to
+     * convert to a distance measure; also populate the lower
+     * half of the result matrix with this value 
+     */
+    // FIXME this assumes the score matrix is symmetric - it may not be?
     for (int i = 0; i < (noseqs - 1); i++)
     {
       for (int j = i; j < noseqs; j++)
       {
-        distance[i][j] = (float) maxscore - distance[i][j];
+        distance[i][j] = maxscore - distance[i][j];
         distance[j][i] = distance[i][j];
       }
     }
     return distance;
   }
 
-  abstract public int[][] getMatrix();
+  @Override
+  public String getName()
+  {
+    return scoreModel.getName();
+  }
+
+  @Override
+  public boolean isDNA()
+  {
+    return scoreModel.isDNA();
+  }
+
+  @Override
+  public boolean isProtein()
+  {
+    return scoreModel.isProtein();
+  }
+
+  public PairwiseScoreModelI getScoreModel()
+  {
+    return scoreModel;
+  }
 }
diff --git a/src/jalview/analysis/scoremodels/PairwiseScoreModelI.java b/src/jalview/analysis/scoremodels/PairwiseScoreModelI.java
new file mode 100644 (file)
index 0000000..8acedf0
--- /dev/null
@@ -0,0 +1,42 @@
+package jalview.analysis.scoremodels;
+
+/**
+ * An interface that describes classes that can compute similarity (aka
+ * substitution) scores for pairs of residues
+ */
+public interface PairwiseScoreModelI
+{
+  /**
+   * Answers a similarity score between two sequence characters (for
+   * substitution of the first by the second). Typically the highest scores are
+   * for identity, and the lowest for substitution of a residue by one with very
+   * different properties.
+   * 
+   * @param c
+   * @param d
+   * @return
+   */
+  abstract public float getPairwiseScore(char c, char d);
+
+  /**
+   * Returns a readable name for the model, suitable for display in menus
+   * 
+   * @return
+   */
+  String getName();
+
+  /**
+   * Answers true if the model is applicable to nucleotide data
+   * 
+   * @return
+   */
+  boolean isDNA();
+
+  /**
+   * Answers true if the model is applicable to peptide data
+   * 
+   * @return
+   */
+  boolean isProtein();
+
+}
 package jalview.analysis.scoremodels;
 
 import jalview.analysis.AlignSeq;
-import jalview.api.analysis.ScoreModelI;
+import jalview.api.analysis.DistanceModelI;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.SequenceI;
 import jalview.util.Comparison;
 
-public class SWScoreModel implements ScoreModelI
+public class SWDistanceModel implements DistanceModelI
 {
 
   @Override
   public float[][] findDistances(AlignmentView seqData)
   {
     SequenceI[] sequenceString = seqData.getVisibleAlignment(
-            Comparison.GapChars.charAt(0)).getSequencesArray();
+            Comparison.GAP_SPACE).getSequencesArray();
     int noseqs = sequenceString.length;
     float[][] distance = new float[noseqs][noseqs];
 
@@ -48,7 +48,7 @@ public class SWScoreModel implements ScoreModelI
         as.calcScoreMatrix();
         as.traceAlignment();
         as.printAlignment(System.out);
-        distance[i][j] = (float) as.maxscore;
+        distance[i][j] = as.maxscore;
 
         if (max < distance[i][j])
         {
@@ -87,6 +87,7 @@ public class SWScoreModel implements ScoreModelI
     return true;
   }
 
+  @Override
   public String toString()
   {
     return "Score between two sequences aligned with Smith Waterman with default Peptide/Nucleotide matrix";
diff --git a/src/jalview/analysis/scoremodels/ScoreMatrix.java b/src/jalview/analysis/scoremodels/ScoreMatrix.java
new file mode 100644 (file)
index 0000000..22c81f1
--- /dev/null
@@ -0,0 +1,341 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.analysis.scoremodels;
+
+import jalview.math.Matrix;
+import jalview.math.MatrixI;
+
+import java.util.Arrays;
+
+public class ScoreMatrix implements PairwiseScoreModelI
+{
+  public static final short UNMAPPED = (short) -1;
+
+  private static final String BAD_ASCII_ERROR = "Unexpected character %s in getPairwiseScore";
+
+  private static final int MAX_ASCII = 127;
+
+  /*
+   * the name of the model as shown in menus
+   */
+  private String name;
+
+  /*
+   * the characters that the model provides scores for
+   */
+  private char[] symbols;
+
+  /*
+   * the score matrix; both dimensions must equal the number of symbols
+   * matrix[i][j] is the substitution score for replacing symbols[i] with symbols[j]
+   */
+  private float[][] matrix;
+
+  /*
+   * quick lookup to convert from an ascii character value to the index
+   * of the corresponding symbol in the score matrix 
+   */
+  private short[] symbolIndex;
+
+  /*
+   * true for Protein Score matrix, false for dna score matrix
+   */
+  private boolean peptide;
+
+  /**
+   * Constructor given a name, symbol alphabet, and matrix of scores for pairs
+   * of symbols. The matrix should be square and of the same size as the
+   * alphabet, for example 20x20 for a 20 symbol alphabet.
+   * 
+   * @param name
+   *          Unique, human readable name for the matrix
+   * @param alphabet
+   *          the symbols to which scores apply
+   * @param matrix
+   *          Pairwise scores indexed according to the symbol alphabet
+   */
+  public ScoreMatrix(String name, char[] alphabet, float[][] matrix)
+  {
+    if (alphabet.length != matrix.length)
+    {
+      throw new IllegalArgumentException(
+              "score matrix size must match alphabet size");
+    }
+    for (float[] row : matrix)
+    {
+      if (row.length != alphabet.length)
+      {
+        throw new IllegalArgumentException(
+                "score matrix size must be square");
+      }
+    }
+
+    this.matrix = matrix;
+    this.name = name;
+    this.symbols = alphabet;
+
+    symbolIndex = buildSymbolIndex(alphabet);
+
+    /*
+     * crude heuristic for now...
+     */
+    peptide = alphabet.length >= 20;
+  }
+
+  /**
+   * Returns an array A where A[i] is the position in the alphabet array of the
+   * character whose value is i. For example if the alphabet is { 'A', 'D', 'X'
+   * } then A['D'] = A[68] = 1.
+   * <p>
+   * Unmapped characters (not in the alphabet) get an index of -1.
+   * <p>
+   * Mappings are added automatically for lower case symbols (for non case
+   * sensitive scoring), unless they are explicitly present in the alphabet (are
+   * scored separately in the score matrix).
+   * 
+   * @param alphabet
+   * @return
+   */
+  static short[] buildSymbolIndex(char[] alphabet)
+  {
+    short[] index = new short[MAX_ASCII + 1];
+    Arrays.fill(index, UNMAPPED);
+    short pos = 0;
+    for (char c : alphabet)
+    {
+      if (c <= MAX_ASCII)
+      {
+        index[c] = pos;
+      }
+
+      /*
+       * also map lower-case character (unless separately mapped)
+       */
+      if (c >= 'A' && c <= 'Z')
+      {
+        short lowerCase = (short) (c + ('a' - 'A'));
+        if (index[lowerCase] == UNMAPPED)
+        {
+          index[lowerCase] = pos;
+        }
+      }
+      pos++;
+    }
+    return index;
+  }
+
+  @Override
+  public String getName()
+  {
+    return name;
+  }
+
+  @Override
+  public boolean isDNA()
+  {
+    return !peptide;
+  }
+
+  @Override
+  public boolean isProtein()
+  {
+    return peptide;
+  }
+
+  /**
+   * Returns the score matrix as used in getPairwiseScore. If using this matrix
+   * directly, callers <em>must</em> also call <code>getMatrixIndex</code> in
+   * order to get the matrix index for each character (symbol).
+   * 
+   * @return
+   * @see #getMatrixIndex(char)
+   */
+  public float[][] getMatrix()
+  {
+    return matrix;
+  }
+
+  /**
+   * Answers the matrix index for a given character, or -1 if unmapped in the
+   * matrix. Use this method only if using <code>getMatrix</code> in order to
+   * compute scores directly (without symbol lookup) for efficiency.
+   * 
+   * @param c
+   * @return
+   * @see #getMatrix()
+   */
+  public int getMatrixIndex(char c)
+  {
+    if (c < symbolIndex.length)
+    {
+      return symbolIndex[c];
+    }
+    else
+    {
+      return UNMAPPED;
+    }
+  }
+
+  /**
+   * Returns the pairwise score for substituting c with d, or zero if c or d is
+   * an unscored or unexpected character
+   */
+  @Override
+  public float getPairwiseScore(char c, char d)
+  {
+    if (c >= symbolIndex.length)
+    {
+      System.err.println(String.format(BAD_ASCII_ERROR, c));
+      return 0;
+    }
+    if (d >= symbolIndex.length)
+    {
+      System.err.println(String.format(BAD_ASCII_ERROR, d));
+      return 0;
+    }
+
+    int cIndex = symbolIndex[c];
+    int dIndex = symbolIndex[d];
+    if (cIndex != UNMAPPED && dIndex != UNMAPPED)
+    {
+      return matrix[cIndex][dIndex];
+    }
+    return 0;
+  }
+
+  /**
+   * pretty print the matrix
+   */
+  @Override
+  public String toString()
+  {
+    return outputMatrix(false);
+  }
+
+  /**
+   * Print the score matrix, optionally formatted as html, with the alphabet symbols as column headings and at the start of each row
+   * @param html
+   * @return
+   */
+  public String outputMatrix(boolean html)
+  {
+    StringBuilder sb = new StringBuilder(512);
+
+    /*
+     * heading row with alphabet
+     */
+    if (html)
+    {
+      sb.append("<table border=\"1\">");
+      sb.append(html ? "<tr><th></th>" : "");
+    }
+    for (char sym : symbols)
+    {
+      if (html)
+      {
+        sb.append("<th>&nbsp;").append(sym).append("&nbsp;</th>");
+      }
+      else
+      {
+        sb.append("\t").append(sym);
+      }
+    }
+    sb.append(html ? "</tr>\n" : "\n");
+
+    /*
+     * table of scores
+     */
+    for (char c1 : symbols)
+    {
+      if (html)
+      {
+        sb.append("<tr><td>");
+      }
+      sb.append(c1).append(html ? "</td>" : "");
+      for (char c2 : symbols)
+      {
+        sb.append(html ? "<td>" : "\t")
+                .append(matrix[symbolIndex[c1]][symbolIndex[c2]])
+                .append(html ? "</td>" : "");
+      }
+      sb.append(html ? "</tr>\n" : "\n");
+    }
+    if (html)
+    {
+      sb.append("</table>");
+    }
+    return sb.toString();
+  }
+
+  /**
+   * Answers the number of symbols coded for (also equal to the number of rows
+   * and columns of the score matrix)
+   * 
+   * @return
+   */
+  public int getSize()
+  {
+    return symbols.length;
+  }
+
+  /**
+   * Computes an NxN matrix where N is the number of sequences, and entry [i, j]
+   * is sequence[i] pairwise multiplied with sequence[j], as a sum of scores
+   * computed using the current score matrix. For example
+   * <ul>
+   * <li>Sequences:</li>
+   * <li>FKL</li>
+   * <li>R-D</li>
+   * <li>QIA</li>
+   * <li>GWC</li>
+   * <li>Score matrix is BLOSUM62</li>
+   * <li>Gaps treated same as X (unknown)</li>
+   * <li>product [0, 0] = F.F + K.K + L.L = 6 + 5 + 4 = 15</li>
+   * <li>product [1, 1] = R.R + -.- + D.D = 5 + -1 + 6 = 10</li>
+   * <li>product [2, 2] = Q.Q + I.I + A.A = 5 + 4 + 4 = 13</li>
+   * <li>product [3, 3] = G.G + W.W + C.C = 6 + 11 + 9 = 26</li>
+   * <li>product[0, 1] = F.R + K.- + L.D = -3 + -1 + -3 = -8
+   * <li>and so on</li>
+   * </ul>
+   */
+  public MatrixI computePairwiseScores(String[] seqs)
+  {
+    double[][] values = new double[seqs.length][];
+    for (int row = 0; row < seqs.length; row++)
+    {
+      values[row] = new double[seqs.length];
+      for (int col = 0; col < seqs.length; col++)
+      {
+        int total = 0;
+        int width = Math.min(seqs[row].length(), seqs[col].length());
+        for (int i = 0; i < width; i++)
+        {
+          char c1 = seqs[row].charAt(i);
+          char c2 = seqs[col].charAt(i);
+          float score = getPairwiseScore(c1, c2);
+          total += score;
+        }
+        values[row][col] = total;
+      }
+    }
+    return new Matrix(values);
+  }
+}
diff --git a/src/jalview/analysis/scoremodels/ScoreModels.java b/src/jalview/analysis/scoremodels/ScoreModels.java
new file mode 100644 (file)
index 0000000..043e6fb
--- /dev/null
@@ -0,0 +1,143 @@
+package jalview.analysis.scoremodels;
+
+import jalview.api.analysis.DistanceModelI;
+import jalview.io.DataSourceType;
+import jalview.io.FileParse;
+import jalview.io.ScoreMatrixFile;
+
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * A class that can register and serve instances of ScoreModelI
+ */
+public class ScoreModels
+{
+  private final ScoreMatrix BLOSUM62;
+
+  private final ScoreMatrix PAM250;
+
+  private final ScoreMatrix DNA;
+
+  private static ScoreModels instance = new ScoreModels();
+
+  private Map<String, DistanceModelI> models;
+
+  public static ScoreModels getInstance()
+  {
+    return instance;
+  }
+
+  /**
+   * Private constructor to enforce use of singleton. Registers Jalview's
+   * "built-in" score models:
+   * <ul>
+   * <li>BLOSUM62</li>
+   * <li>PAM250</li>
+   * <li>SeqSpace (identity matrix)</li>
+   * <li>DNA</li>
+   * <li>Sequence Feature Similarity</li>
+   * <li>Percentage Identity</li>
+   * </ul>
+   */
+  private ScoreModels()
+  {
+    /*
+     * using LinkedHashMap keeps models ordered as added
+     */
+    models = new LinkedHashMap<String, DistanceModelI>();
+    BLOSUM62 = loadScoreMatrix("scoreModel/blosum62.scm");
+    PAM250 = loadScoreMatrix("scoreModel/pam250.scm");
+    loadScoreMatrix("scoreModel/seqspace.scm");
+    // drop seqspace.scm for IdentityScoreModel once JAL-2379 merged in?
+    // registerScoreModel(new IdentityScoreModel());
+    DNA = loadScoreMatrix("scoreModel/dna.scm");
+    registerScoreModel(new FeatureDistanceModel());
+    registerScoreModel(new PIDDistanceModel());
+  }
+
+  /**
+   * Tries to load a score matrix from the given resource file, and if
+   * successful, registers it.
+   * 
+   * @param string
+   * @return
+   */
+  ScoreMatrix loadScoreMatrix(String resourcePath)
+  {
+    try
+    {
+      /*
+       * delegate parsing to ScoreMatrixFile
+       */
+      FileParse fp = new FileParse(resourcePath, DataSourceType.CLASSLOADER);
+      ScoreMatrix sm = new ScoreMatrixFile(fp).parseMatrix();
+      registerScoreModel(sm);
+      return sm;
+    } catch (IOException e)
+    {
+      System.err.println("Error reading " + resourcePath + ": "
+              + e.getMessage());
+    }
+    return null;
+  }
+
+  /**
+   * Registers a pairwise score model
+   * 
+   * @param sm
+   */
+  public void registerScoreModel(PairwiseScoreModelI sm)
+  {
+    registerScoreModel(new PairwiseDistanceModel(sm));
+  }
+
+  /**
+   * Answers an iterable set of the registered score models. Currently these are
+   * returned in the order in which they were registered.
+   * 
+   * @return
+   */
+  public Iterable<DistanceModelI> getModels()
+  {
+    return models.values();
+  }
+
+  public DistanceModelI forName(String s)
+  {
+    return models.get(s);
+  }
+
+  public void registerScoreModel(DistanceModelI sm)
+  {
+    DistanceModelI sm2 = models.get(sm.getName());
+    if (sm2 != null)
+    {
+      System.err.println("Warning: replacing score model " + sm2.getName());
+    }
+    models.put(sm.getName(), sm);
+  }
+
+  /**
+   * Returns the default peptide or nucleotide score model, currently BLOSUM62
+   * or DNA
+   * 
+   * @param forPeptide
+   * @return
+   */
+  public ScoreMatrix getDefaultModel(boolean forPeptide)
+  {
+    return forPeptide ? BLOSUM62 : DNA;
+  }
+
+  public ScoreMatrix getBlosum62()
+  {
+    return BLOSUM62;
+  }
+
+  public ScoreMatrix getPam250()
+  {
+    return PAM250;
+  }
+}
index c795f3f..367a0de 100644 (file)
@@ -100,7 +100,7 @@ public interface SiftsClientI
    * @return Sequence<->Structure mapping as int[][]
    * @throws SiftsException
    */
-  public StringBuffer getMappingOutput(MappingOutputPojo mop)
+  public StringBuilder getMappingOutput(MappingOutputPojo mop)
           throws SiftsException;
 
   /**
similarity index 70%
rename from src/jalview/api/analysis/ScoreModelI.java
rename to src/jalview/api/analysis/DistanceModelI.java
index 31a1c32..3ceeed2 100644 (file)
@@ -22,15 +22,31 @@ package jalview.api.analysis;
 
 import jalview.datamodel.AlignmentView;
 
-public interface ScoreModelI
+/**
+ * An interface that describes classes that can compute distances between pairs
+ * of sequences in an alignment
+ */
+public interface DistanceModelI
 {
 
   float[][] findDistances(AlignmentView seqData);
 
   String getName();
 
+  /**
+   * Answers true if this model is applicable for nucleotide data (so should be
+   * shown in menus in that context)
+   * 
+   * @return
+   */
   boolean isDNA();
 
+  /**
+   * Answers true if this model is applicable for peptide data (so should be
+   * shown in menus in that context)
+   * 
+   * @return
+   */
   boolean isProtein();
 
 }
index 6a0b390..221f793 100644 (file)
@@ -22,6 +22,7 @@ package jalview.appletgui;
 
 import jalview.analysis.AlignmentSorter;
 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
+import jalview.analysis.NJTree;
 import jalview.api.AlignViewControllerGuiI;
 import jalview.api.AlignViewControllerI;
 import jalview.api.AlignViewportI;
@@ -2805,22 +2806,26 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
   public void averageDistanceTreeMenuItem_actionPerformed()
   {
-    NewTreePanel("AV", "PID", "Average distance tree using PID");
+    NewTreePanel(NJTree.AVERAGE_DISTANCE, "PID",
+            "Average distance tree using PID");
   }
 
   public void neighbourTreeMenuItem_actionPerformed()
   {
-    NewTreePanel("NJ", "PID", "Neighbour joining tree using PID");
+    NewTreePanel(NJTree.NEIGHBOUR_JOINING, "PID",
+            "Neighbour joining tree using PID");
   }
 
   protected void njTreeBlosumMenuItem_actionPerformed()
   {
-    NewTreePanel("NJ", "BL", "Neighbour joining tree using BLOSUM62");
+    NewTreePanel(NJTree.NEIGHBOUR_JOINING, "BL",
+            "Neighbour joining tree using BLOSUM62");
   }
 
   protected void avTreeBlosumMenuItem_actionPerformed()
   {
-    NewTreePanel("AV", "BL", "Average distance tree using BLOSUM62");
+    NewTreePanel(NJTree.AVERAGE_DISTANCE, "BL",
+            "Average distance tree using BLOSUM62");
   }
 
   void NewTreePanel(String type, String pwType, String title)
index b4b8ec2..9ac6773 100644 (file)
 package jalview.appletgui;
 
 import jalview.analysis.NJTree;
-import jalview.api.analysis.ScoreModelI;
+import jalview.analysis.scoremodels.ScoreModels;
+import jalview.api.analysis.DistanceModelI;
 import jalview.api.analysis.ViewBasedAnalysisI;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.SequenceI;
 import jalview.io.NewickFile;
-import jalview.schemes.ResidueProperties;
 import jalview.util.MessageManager;
 
 import java.awt.BorderLayout;
@@ -69,6 +69,7 @@ public class TreePanel extends EmbmenuFrame implements ActionListener,
     return tree;
   }
 
+  @Override
   public void finalize() throws Throwable
   {
     ap = null;
@@ -200,6 +201,7 @@ public class TreePanel extends EmbmenuFrame implements ActionListener,
       this.newtree = newtree;
     }
 
+    @Override
     public void run()
     {
       if (newtree != null)
@@ -235,7 +237,7 @@ public class TreePanel extends EmbmenuFrame implements ActionListener,
           seqs = av.getSelectionGroup().getSequencesInOrder(
                   av.getAlignment());
         }
-        ScoreModelI sm = ResidueProperties.getScoreModel(pwtype);
+        DistanceModelI sm = ScoreModels.getInstance().forName(pwtype);
         if (sm instanceof ViewBasedAnalysisI)
         {
           try
@@ -286,6 +288,7 @@ public class TreePanel extends EmbmenuFrame implements ActionListener,
     }
   }
 
+  @Override
   public void actionPerformed(ActionEvent evt)
   {
     if (evt.getSource() == newickOutput)
@@ -302,6 +305,7 @@ public class TreePanel extends EmbmenuFrame implements ActionListener,
     }
   }
 
+  @Override
   public void itemStateChanged(ItemEvent evt)
   {
     if (evt.getSource() == fitToWindow)
index 62ee974..b7e15a6 100755 (executable)
@@ -20,8 +20,8 @@
  */
 package jalview.datamodel;
 
+import jalview.analysis.scoremodels.ScoreMatrix;
 import jalview.schemes.ResidueProperties;
-import jalview.schemes.ScoreMatrix;
 
 /**
  * Encode a sequence as a numeric vector using either classic residue binary
@@ -112,23 +112,23 @@ public class BinarySequence extends Sequence
   /**
    * ancode using substitution matrix given in matrix
    * 
-   * @param matrix
+   * @param smtrx
    */
-  public void matrixEncode(final ScoreMatrix matrix)
+  public void matrixEncode(final ScoreMatrix smtrx)
           throws InvalidSequenceTypeException
   {
-    if (isNa != matrix.isDNA())
+    if (isNa != smtrx.isDNA())
     {
       throw new InvalidSequenceTypeException("matrix "
-              + matrix.getClass().getCanonicalName()
+              + smtrx.getClass().getCanonicalName()
               + " is not a valid matrix for "
               + (isNa ? "nucleotide" : "protein") + "sequences");
     }
-    matrixEncode(matrix.isDNA() ? ResidueProperties.nucleotideIndex
-            : ResidueProperties.aaIndex, matrix.getMatrix());
+    matrixEncode(smtrx.isDNA() ? ResidueProperties.nucleotideIndex
+            : ResidueProperties.aaIndex, smtrx.getMatrix());
   }
 
-  private void matrixEncode(final int[] aaIndex, final int[][] matrix)
+  private void matrixEncode(final int[] aaIndex, final float[][] matrix)
   {
     int nores = initMatrixGetNoRes();
 
index b5fc817..5f0e5d3 100644 (file)
@@ -24,6 +24,7 @@ import jalview.analysis.AlignmentSorter;
 import jalview.analysis.AlignmentUtils;
 import jalview.analysis.CrossRef;
 import jalview.analysis.Dna;
+import jalview.analysis.NJTree;
 import jalview.analysis.ParseProperties;
 import jalview.analysis.SequenceIdMatcher;
 import jalview.api.AlignExportSettingI;
@@ -34,7 +35,7 @@ import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureSettingsControllerI;
 import jalview.api.SplitContainerI;
 import jalview.api.ViewStyleI;
-import jalview.api.analysis.ScoreModelI;
+import jalview.api.analysis.DistanceModelI;
 import jalview.bin.Cache;
 import jalview.bin.Jalview;
 import jalview.commands.CommandI;
@@ -69,6 +70,7 @@ import jalview.io.FileFormat;
 import jalview.io.FileFormatI;
 import jalview.io.FileFormats;
 import jalview.io.FileLoader;
+import jalview.io.FileParse;
 import jalview.io.FormatAdapter;
 import jalview.io.HtmlSvgOutput;
 import jalview.io.IdentifyFile;
@@ -77,12 +79,12 @@ import jalview.io.JalviewFileChooser;
 import jalview.io.JalviewFileView;
 import jalview.io.JnetAnnotationMaker;
 import jalview.io.NewickFile;
+import jalview.io.ScoreMatrixFile;
 import jalview.io.TCoffeeScoreFile;
 import jalview.jbgui.GAlignFrame;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemes;
 import jalview.schemes.ResidueColourScheme;
-import jalview.schemes.ResidueProperties;
 import jalview.schemes.TCoffeeColourScheme;
 import jalview.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
@@ -360,7 +362,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     setMenusFromViewport(viewport);
     buildSortByAnnotationScoresMenu();
-    buildTreeMenu();
+    calculateTree.addActionListener(new ActionListener()
+    {
+
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        new TreeChooser(AlignFrame.this);
+      }
+    });
     buildColourMenu();
 
     if (Desktop.desktop != null)
@@ -3580,54 +3590,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   /**
    * DOCUMENT ME!
    * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  @Override
-  public void averageDistanceTreeMenuItem_actionPerformed(ActionEvent e)
-  {
-    newTreePanel("AV", "PID", "Average distance tree using PID");
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  @Override
-  public void neighbourTreeMenuItem_actionPerformed(ActionEvent e)
-  {
-    newTreePanel("NJ", "PID", "Neighbour joining tree using PID");
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  @Override
-  protected void njTreeBlosumMenuItem_actionPerformed(ActionEvent e)
-  {
-    newTreePanel("NJ", "BL", "Neighbour joining tree using BLOSUM62");
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  @Override
-  protected void avTreeBlosumMenuItem_actionPerformed(ActionEvent e)
-  {
-    newTreePanel("AV", "BL", "Average distance tree using BLOSUM62");
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
    * @param type
    *          DOCUMENT ME!
    * @param pwType
@@ -3834,48 +3796,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * call. Listeners are added to remove the menu item when the treePanel is
    * closed, and adjust the tree leaf to sequence mapping when the alignment is
    * modified.
-   * 
-   * @param treePanel
-   *          Displayed tree window.
-   * @param title
-   *          SortBy menu item title.
    */
   @Override
-  public void buildTreeMenu()
+  public void buildTreeSortMenu()
   {
-    calculateTree.removeAll();
-    // build the calculate menu
-
-    for (final String type : new String[] { "NJ", "AV" })
-    {
-      String treecalcnm = MessageManager.getString("label.tree_calc_"
-              + type.toLowerCase());
-      for (final String pwtype : ResidueProperties.scoreMatrices.keySet())
-      {
-        JMenuItem tm = new JMenuItem();
-        ScoreModelI sm = ResidueProperties.scoreMatrices.get(pwtype);
-        if (sm.isDNA() == viewport.getAlignment().isNucleotide()
-                || sm.isProtein() == !viewport.getAlignment()
-                        .isNucleotide())
-        {
-          String smn = MessageManager.getStringOrReturn(
-                  "label.score_model_", sm.getName());
-          final String title = MessageManager.formatMessage(
-                  "label.treecalc_title", treecalcnm, smn);
-          tm.setText(title);//
-          tm.addActionListener(new java.awt.event.ActionListener()
-          {
-            @Override
-            public void actionPerformed(ActionEvent e)
-            {
-              newTreePanel(type, pwtype, title);
-            }
-          });
-          calculateTree.add(tm);
-        }
-
-      }
-    }
     sortByTreeMenu.removeAll();
 
     List<Component> comps = PaintRefresher.components.get(viewport
@@ -4102,7 +4026,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
       if (nf.getTree() != null)
       {
-        tp = new TreePanel(alignPanel, "FromFile", title, nf, input);
+        tp = new TreePanel(alignPanel, NJTree.FROM_FILE, title, nf, input);
 
         tp.setSize(w, h);
 
@@ -4684,10 +4608,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   }
 
   /**
-   * Attempt to load a "dropped" file or URL string: First by testing whether
-   * it's an Annotation file, then a JNet file, and finally a features file. If
-   * all are false then the user may have dropped an alignment file onto this
-   * AlignFrame.
+   * Attempt to load a "dropped" file or URL string, by testing in turn for
+   * <ul>
+   * <li>an Annotation file</li>
+   * <li>a JNet file</li>
+   * <li>a features file</li>
+   * <li>else try to interpret as an alignment file</li>
+   * </ul>
    * 
    * @param file
    *          either a filename or a URL string.
@@ -4761,7 +4688,18 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           {
             format = new IdentifyFile().identify(file, sourceType);
           }
-          if (FileFormat.Jnet.equals(format))
+          if (FileFormat.ScoreMatrix == format)
+          {
+            ScoreMatrixFile sm = new ScoreMatrixFile(new FileParse(file,
+                    sourceType));
+            sm.parse();
+            // todo: i18n this message
+            statusBar
+                    .setText(MessageManager.formatMessage(
+                            "label.successfully_loaded_matrix",
+                            sm.getMatrixName()));
+          }
+          else if (FileFormat.Jnet.equals(format))
           {
             JPredFile predictions = new JPredFile(file, sourceType);
             new JnetAnnotationMaker();
@@ -5699,6 +5637,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     ColourMenuHelper.setColourSelected(colourMenu, schemeName);
   }
+
+  public void newTreePanel(String treeType, DistanceModelI sm)
+  {
+    String scoreModelName = sm.getName();
+    final String ttl = TreePanel.getPanelTitle(treeType, scoreModelName);
+    newTreePanel(treeType, scoreModelName, ttl);
+  }
 }
 
 class PrintThread extends Thread
index 58ed008..d053468 100644 (file)
@@ -20,6 +20,9 @@
  */
 package jalview.gui;
 
+import jalview.analysis.scoremodels.PairwiseDistanceModel;
+import jalview.analysis.scoremodels.ScoreModels;
+import jalview.api.analysis.DistanceModelI;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentView;
@@ -27,7 +30,6 @@ import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.SeqCigar;
 import jalview.datamodel.SequenceI;
 import jalview.jbgui.GPCAPanel;
-import jalview.schemes.ResidueProperties;
 import jalview.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.viewmodel.PCAModel;
@@ -155,30 +157,28 @@ public class PCAPanel extends GPCAPanel implements Runnable,
   protected void scoreMatrix_menuSelected()
   {
     scoreMatrixMenu.removeAll();
-    for (final String sm : ResidueProperties.scoreMatrices.keySet())
+    for (DistanceModelI sm : ScoreModels.getInstance().getModels())
     {
-      if (ResidueProperties.getScoreMatrix(sm) != null)
+      if (sm instanceof PairwiseDistanceModel)
       {
+        final String name = sm.getName();
         // create an entry for this score matrix for use in PCA
         JCheckBoxMenuItem jm = new JCheckBoxMenuItem();
         jm.setText(MessageManager.getStringOrReturn("label.score_model_",
-                sm));
-        jm.setSelected(pcaModel.getScore_matrix().equals(sm));
-        if ((ResidueProperties.scoreMatrices.get(sm).isDNA() && ResidueProperties.scoreMatrices
-                .get(sm).isProtein())
-                || pcaModel.isNucleotide() == ResidueProperties.scoreMatrices
-                        .get(sm).isDNA())
+                name));
+        jm.setSelected(pcaModel.getScore_matrix().equals(name));
+        if ((pcaModel.isNucleotide() && sm.isDNA())
+                || (!pcaModel.isNucleotide() && sm.isProtein()))
         {
-          final PCAPanel us = this;
           jm.addActionListener(new ActionListener()
           {
             @Override
             public void actionPerformed(ActionEvent e)
             {
-              if (!pcaModel.getScore_matrix().equals(sm))
+              if (!pcaModel.getScore_matrix().equals(name))
               {
-                pcaModel.setScore_matrix(sm);
-                Thread worker = new Thread(us);
+                pcaModel.setScore_matrix(name);
+                Thread worker = new Thread(PCAPanel.this);
                 worker.start();
               }
             }
diff --git a/src/jalview/gui/TreeChooser.java b/src/jalview/gui/TreeChooser.java
new file mode 100644 (file)
index 0000000..55e6650
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.gui;
+
+import jalview.analysis.NJTree;
+import jalview.analysis.scoremodels.ScoreModels;
+import jalview.api.analysis.DistanceModelI;
+import jalview.util.MessageManager;
+
+import java.awt.Color;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JInternalFrame;
+import javax.swing.JLayeredPane;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+
+/**
+ * A dialog to allow a user to select and action Tree calculation options
+ */
+public class TreeChooser extends JPanel
+{
+  private static final Font VERDANA_11PT = new Font("Verdana", 0, 11);
+
+  AlignFrame af;
+
+  JRadioButton neighbourJoining;
+
+  JRadioButton averageDistance;
+
+  JComboBox<String> matrixNames;
+
+  private JInternalFrame frame;
+
+  private ButtonGroup treeTypes;
+
+  /**
+   * Constructor
+   * 
+   * @param af
+   */
+  public TreeChooser(AlignFrame alignFrame)
+  {
+    this.af = alignFrame;
+    init();
+  }
+
+  void init()
+  {
+    frame = new JInternalFrame();
+    frame.setContentPane(this);
+    this.setBackground(Color.white);
+
+    neighbourJoining = new JRadioButton(
+            MessageManager.getString("label.tree_calc_nj"));
+    neighbourJoining.setOpaque(false);
+    averageDistance = new JRadioButton(
+            MessageManager.getString("label.tree_calc_av"));
+    treeTypes = new ButtonGroup();
+    treeTypes.add(neighbourJoining);
+    treeTypes.add(averageDistance);
+    neighbourJoining.setSelected(true);
+
+    matrixNames = new JComboBox<String>();
+    ScoreModels scoreModels = ScoreModels.getInstance();
+    for (DistanceModelI sm : scoreModels.getModels())
+    {
+      boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
+      if (sm.isDNA() && nucleotide || sm.isProtein() && !nucleotide)
+      {
+        matrixNames.addItem(sm.getName());
+      }
+    }
+
+    JButton ok = new JButton(MessageManager.getString("action.ok"));
+    ok.setFont(VERDANA_11PT);
+    ok.addActionListener(new java.awt.event.ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        ok_actionPerformed(e);
+      }
+    });
+
+    JButton cancel = new JButton(MessageManager.getString("action.cancel"));
+    cancel.setFont(VERDANA_11PT);
+    cancel.addActionListener(new java.awt.event.ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        cancel_actionPerformed(e);
+      }
+    });
+
+    JPanel p1 = new JPanel();
+    p1.setOpaque(false);
+    p1.add(neighbourJoining);
+    p1.add(averageDistance);
+    this.add(p1);
+    JPanel p2 = new JPanel(new FlowLayout(FlowLayout.LEFT));
+    p2.setOpaque(false);
+    p2.add(matrixNames, FlowLayout.LEFT);
+    this.add(p2);
+    JPanel p3 = new JPanel();
+    p3.setOpaque(false);
+    p3.add(ok);
+    p3.add(cancel);
+    this.add(p3);
+
+    Desktop.addInternalFrame(frame,
+            MessageManager.getString("label.choose_tree"),
+              400, 200, false);
+
+    frame.setLayer(JLayeredPane.PALETTE_LAYER);
+  }
+
+  /**
+   * Open and calculate the selected tree on 'OK'
+   * 
+   * @param e
+   */
+  protected void ok_actionPerformed(ActionEvent e)
+  {
+    try
+    {
+      frame.setClosed(true);
+      String treeType = neighbourJoining.isSelected() ? NJTree.NEIGHBOUR_JOINING
+              : NJTree.AVERAGE_DISTANCE;
+      DistanceModelI sm = ScoreModels.getInstance().forName(
+              matrixNames.getSelectedItem().toString());
+      af.newTreePanel(treeType, sm);
+    } catch (Exception ex)
+    {
+    }
+  }
+
+  /**
+   * Closes dialog on cancel
+   * 
+   * @param e
+   */
+  protected void cancel_actionPerformed(ActionEvent e)
+  {
+    try
+    {
+      frame.setClosed(true);
+    } catch (Exception ex)
+    {
+    }
+  }
+}
index 25f4c1b..13e2360 100755 (executable)
@@ -22,7 +22,8 @@ package jalview.gui;
 
 import jalview.analysis.AlignmentSorter;
 import jalview.analysis.NJTree;
-import jalview.api.analysis.ScoreModelI;
+import jalview.analysis.scoremodels.ScoreModels;
+import jalview.api.analysis.DistanceModelI;
 import jalview.api.analysis.ViewBasedAnalysisI;
 import jalview.bin.Cache;
 import jalview.commands.CommandI;
@@ -41,7 +42,6 @@ import jalview.io.JalviewFileChooser;
 import jalview.io.JalviewFileView;
 import jalview.io.NewickFile;
 import jalview.jbgui.GTreePanel;
-import jalview.schemes.ResidueProperties;
 import jalview.util.ImageMaker;
 import jalview.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
@@ -107,27 +107,6 @@ public class TreePanel extends GTreePanel
     // showDistances(true);
   }
 
-  /**
-   * Creates a new TreePanel object.
-   * 
-   * @param av
-   *          DOCUMENT ME!
-   * @param seqVector
-   *          DOCUMENT ME!
-   * @param newtree
-   *          DOCUMENT ME!
-   * @param type
-   *          DOCUMENT ME!
-   * @param pwtype
-   *          DOCUMENT ME!
-   */
-  public TreePanel(AlignmentPanel ap, String type, String pwtype,
-          NewickFile newtree)
-  {
-    super();
-    initTreePanel(ap, type, pwtype, newtree, null);
-  }
-
   public TreePanel(AlignmentPanel av, String type, String pwtype,
           NewickFile newtree, AlignmentView inputData)
   {
@@ -321,7 +300,7 @@ public class TreePanel extends GTreePanel
           seqs = av.getSelectionGroup().getSequencesInOrder(
                   av.getAlignment());
         }
-        ScoreModelI sm = ResidueProperties.getScoreModel(pwtype);
+        DistanceModelI sm = ScoreModels.getInstance().forName(pwtype);
         if (sm instanceof ViewBasedAnalysisI)
         {
           try
@@ -400,33 +379,14 @@ public class TreePanel extends GTreePanel
   {
     CutAndPasteTransfer cap = new CutAndPasteTransfer();
 
-    StringBuffer buffer = new StringBuffer();
-
-    if (type.equals("AV"))
-    {
-      buffer.append("Average distance tree using ");
-    }
-    else
-    {
-      buffer.append("Neighbour joining tree using ");
-    }
-
-    if (pwtype.equals("BL"))
-    {
-      buffer.append("BLOSUM62");
-    }
-    else
-    {
-      buffer.append("PID");
-    }
+    String newTitle = getPanelTitle(type, pwtype);
 
-    jalview.io.NewickFile fout = new jalview.io.NewickFile(
-            tree.getTopNode());
+    NewickFile fout = new NewickFile(tree.getTopNode());
     try
     {
       cap.setText(fout.print(tree.isHasBootstrap(), tree.isHasDistances(),
               tree.isHasRootDistance()));
-      Desktop.addInternalFrame(cap, buffer.toString(), 500, 100);
+      Desktop.addInternalFrame(cap, newTitle, 500, 100);
     } catch (OutOfMemoryError oom)
     {
       new OOMWarning("generating newick tree file", oom);
@@ -899,4 +859,43 @@ public class TreePanel extends GTreePanel
       }
     });
   }
+
+  /**
+   * Formats a localised title for the tree panel, like
+   * <p>
+   * Neighbour Joining Using BLOSUM62
+   * <p>
+   * For a tree loaded from file, just uses the file name
+   * 
+   * @param treeType
+   *          NJ or AV or FromFile
+   * @param modelOrFileName
+   * @return
+   */
+  public static String getPanelTitle(String treeType, String modelOrFileName)
+  {
+    if (NJTree.FROM_FILE.equals(treeType))
+    {
+      return modelOrFileName;
+    }
+
+    /*
+     * i18n description of Neighbour Joining or Average Distance method
+     */
+    String treecalcnm = MessageManager.getString("label.tree_calc_"
+            + treeType.toLowerCase());
+
+    /*
+     * i18n description (if any) of score model used
+     */
+    String smn = MessageManager.getStringOrReturn("label.score_model_",
+            modelOrFileName);
+
+    /*
+     * put them together as <method> Using <model>
+     */
+    final String ttl = MessageManager.formatMessage("label.treecalc_title",
+            treecalcnm, smn);
+    return ttl;
+  }
 }
index a11147c..3354b88 100644 (file)
@@ -256,6 +256,21 @@ public enum FileFormat implements FileFormatI
       return new FeaturesFile();
     }
   },
+  ScoreMatrix("Substitution matrix", "", false, false)
+  {
+    @Override
+    public AlignmentFileReaderI getReader(FileParse source)
+            throws IOException
+    {
+      return new ScoreMatrixFile(source);
+    }
+
+    @Override
+    public AlignmentFileWriterI getWriter(AlignmentI al)
+    {
+      return null;
+    }
+  },
   PDB("PDB", "pdb,ent", true, false)
   {
     @Override
index 0556e76..4b6f8e4 100755 (executable)
@@ -141,6 +141,11 @@ public class IdentifyFile
         }
         data = data.toUpperCase();
 
+        if (data.startsWith(ScoreMatrixFile.SCOREMATRIX))
+        {
+          reply = FileFormat.ScoreMatrix;
+          break;
+        }
         if (data.startsWith("##GFF-VERSION"))
         {
           // GFF - possibly embedded in a Jalview features file!
diff --git a/src/jalview/io/ScoreMatrixFile.java b/src/jalview/io/ScoreMatrixFile.java
new file mode 100644 (file)
index 0000000..97fe46b
--- /dev/null
@@ -0,0 +1,209 @@
+package jalview.io;
+
+import jalview.analysis.scoremodels.ScoreMatrix;
+import jalview.analysis.scoremodels.ScoreModels;
+import jalview.datamodel.SequenceI;
+
+import java.io.IOException;
+import java.util.StringTokenizer;
+
+/**
+ * A class that can parse a file containing a substitution matrix and register
+ * it for use in Jalview
+ * 
+ * @author gmcarstairs
+ *
+ */
+// TODO modify the AlignFile / IdentifyFile pattern so that non-alignment files
+// like this are handled more naturally
+public class ScoreMatrixFile extends AlignFile implements
+        AlignmentFileReaderI
+{
+  // first non-comment line identifier - also checked in IdentifyFile
+  public static final String SCOREMATRIX = "SCOREMATRIX";
+
+  private static final String DELIMITERS = " ,\t";
+
+  private static final String COMMENT_CHAR = "#";
+
+  private String matrixName;
+
+  /**
+   * Constructor
+   * 
+   * @param source
+   * @throws IOException
+   */
+  public ScoreMatrixFile(FileParse source) throws IOException
+  {
+    super(false, source);
+  }
+
+  @Override
+  public String print(SequenceI[] sqs, boolean jvsuffix)
+  {
+    return null;
+  }
+
+  /**
+   * Parses the score matrix file, and if successful registers the matrix so it
+   * will be shown in Jalview menus.
+   */
+  @Override
+  public void parse() throws IOException
+  {
+    ScoreMatrix sm = parseMatrix();
+
+    ScoreModels.getInstance().registerScoreModel(sm);
+  }
+
+  /**
+   * Parses the score matrix file and constructs a ScoreMatrix object. If an
+   * error is found in parsing, it is thrown as FileFormatException. Any
+   * warnings are written to syserr.
+   * 
+   * @return
+   * @throws IOException
+   */
+  public ScoreMatrix parseMatrix() throws IOException
+  {
+    ScoreMatrix sm = null;
+    int lineNo = 0;
+    String name = null;
+    String alphabet = null;
+    float[][] scores = null;
+    int size = 0;
+    int row = 0;
+    String err = null;
+    String data;
+
+    while ((data = nextLine()) != null)
+    {
+      lineNo++;
+      data = data.trim();
+      if (data.startsWith(COMMENT_CHAR) || data.length() == 0)
+      {
+        continue;
+      }
+      if (data.toUpperCase().startsWith(SCOREMATRIX))
+      {
+        /*
+         * Parse name from ScoreMatrix <name>
+         * we allow any delimiter after ScoreMatrix then take the rest of the line
+         */
+        if (name != null)
+        {
+          System.err
+                  .println("Warning: 'ScoreMatrix' repeated in file at line "
+                          + lineNo);
+        }
+        StringTokenizer nameLine = new StringTokenizer(data, DELIMITERS);
+        if (nameLine.countTokens() < 2)
+        {
+          err = "Format error: expected 'ScoreMatrix <name>', found '"
+                  + data + "' at line " + lineNo;
+          throw new FileFormatException(err);
+        }
+        nameLine.nextToken(); // 'ScoreMatrix'
+        name = nameLine.nextToken(); // next field
+        name = data.substring(1).substring(data.substring(1).indexOf(name));
+        continue;
+      }
+      else if (name == null)
+      {
+        err = "Format error: 'ScoreMatrix <name>' should be the first non-comment line";
+        throw new FileFormatException(err);
+      }
+
+      /*
+       * next line after ScoreMatrix should be the alphabet of scored symbols
+       */
+      if (alphabet == null)
+      {
+        alphabet = data;
+        size = alphabet.length();
+        scores = new float[size][];
+        continue;
+      }
+
+      /*
+       * too much information
+       */
+      if (row >= size)
+      {
+        err = "Unexpected extra input line in score model file: '" + data
+                + "'";
+        throw new FileFormatException(err);
+      }
+
+      /*
+       * subsequent lines should be the symbol scores
+       * optionally with the symbol as the first column for readability
+       */
+      StringTokenizer scoreLine = new StringTokenizer(data, DELIMITERS);
+      if (scoreLine.countTokens() == size + 1)
+      {
+        /*
+         * check 'guide' symbol is the row'th letter of the alphabet
+         */
+        String symbol = scoreLine.nextToken();
+        if (symbol.length() > 1 || symbol.charAt(0) != alphabet.charAt(row))
+        {
+          err = String
+                  .format("Error parsing score matrix at line %d, expected '%s' but found '%s'",
+                          lineNo, alphabet.charAt(row), symbol);
+          throw new FileFormatException(err);
+        }
+      }
+      if (scoreLine.countTokens() != size)
+      {
+        err = String.format("Expected %d scores at line %d but found %d",
+                size, lineNo, scoreLine.countTokens());
+        throw new FileFormatException(err);
+      }
+      scores[row] = new float[size];
+      int col = 0;
+      String value = null;
+      while (scoreLine.hasMoreTokens())
+      {
+        try
+        {
+          value = scoreLine.nextToken();
+          scores[row][col] = Float.valueOf(value);
+          col++;
+        } catch (NumberFormatException e)
+        {
+          err = String.format(
+                  "Invalid score value '%s' at line %d column %d", value,
+                  lineNo, col);
+          throw new FileFormatException(err);
+        }
+      }
+      row++;
+    }
+
+    /*
+     * out of data - check we found enough
+     */
+    if (row < size)
+    {
+      err = String
+              .format("Expected %d rows of score data in score matrix but only found %d",
+                      size, row);
+      throw new FileFormatException(err);
+    }
+
+    /*
+     * If we get here, then name, alphabet and scores have been parsed successfully
+     */
+    sm = new ScoreMatrix(name, alphabet.toCharArray(), scores);
+    matrixName = name;
+
+    return sm;
+  }
+
+  public String getMatrixName()
+  {
+    return matrixName;
+  }
+}
index a3781a7..89877e2 100644 (file)
@@ -239,8 +239,8 @@ public class Tree extends DatastoreItem
       prov.getEntry(0).addParam(new Param());
       prov.getEntry(0).getParam(0).setName("treeType");
       prov.getEntry(0).getParam(0).setType("utf8");
-      prov.getEntry(0).getParam(0).setContent("NJ"); // TODO: type of tree is a
-      // general parameter
+      prov.getEntry(0).getParam(0).setContent(NJTree.NEIGHBOUR_JOINING);
+      // TODO: type of tree is a general parameter
       int ranges[] = tp.getTree().seqData.getVisibleContigs();
       // VisibleContigs are with respect to alignment coordinates. Still need
       // offsets
index b39f4a8..97dcfa0 100755 (executable)
@@ -129,7 +129,7 @@ public class GAlignFrame extends JInternalFrame
 
   protected JMenu sort = new JMenu();
 
-  protected JMenu calculateTree = new JMenu();
+  protected JMenuItem calculateTree = new JMenuItem();
 
   protected JCheckBoxMenuItem padGapsMenuitem = new JCheckBoxMenuItem();
 
@@ -533,26 +533,6 @@ public class GAlignFrame extends JInternalFrame
         PCAMenuItem_actionPerformed(e);
       }
     });
-    JMenuItem averageDistanceTreeMenuItem = new JMenuItem(
-            MessageManager.getString("label.average_distance_identity"));
-    averageDistanceTreeMenuItem.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        averageDistanceTreeMenuItem_actionPerformed(e);
-      }
-    });
-    JMenuItem neighbourTreeMenuItem = new JMenuItem(
-            MessageManager.getString("label.neighbour_joining_identity"));
-    neighbourTreeMenuItem.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        neighbourTreeMenuItem_actionPerformed(e);
-      }
-    });
 
     this.getContentPane().setLayout(new BorderLayout());
     alignFrameMenuBar.setFont(new java.awt.Font("Verdana", 0, 11));
@@ -563,27 +543,6 @@ public class GAlignFrame extends JInternalFrame
     outputTextboxMenu.setText(MessageManager
             .getString("label.out_to_textbox"));
 
-
-    JMenuItem avDistanceTreeBlosumMenuItem = new JMenuItem(
-            MessageManager.getString("label.average_distance_blosum62"));
-    avDistanceTreeBlosumMenuItem.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        avTreeBlosumMenuItem_actionPerformed(e);
-      }
-    });
-    JMenuItem njTreeBlosumMenuItem = new JMenuItem(
-            MessageManager.getString("label.neighbour_blosum62"));
-    njTreeBlosumMenuItem.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        njTreeBlosumMenuItem_actionPerformed(e);
-      }
-    });
     annotationPanelMenuItem.setActionCommand("");
     annotationPanelMenuItem.setText(MessageManager
             .getString("label.show_annotations"));
@@ -1203,7 +1162,7 @@ public class GAlignFrame extends JInternalFrame
       @Override
       public void menuSelected(MenuEvent e)
       {
-        buildTreeMenu();
+        buildTreeSortMenu();
       }
 
       @Override
@@ -2327,22 +2286,10 @@ public class GAlignFrame extends JInternalFrame
   {
   }
 
-  protected void averageDistanceTreeMenuItem_actionPerformed(ActionEvent e)
-  {
-  }
-
   protected void neighbourTreeMenuItem_actionPerformed(ActionEvent e)
   {
   }
 
-  protected void njTreeBlosumMenuItem_actionPerformed(ActionEvent e)
-  {
-  }
-
-  protected void avTreeBlosumMenuItem_actionPerformed(ActionEvent e)
-  {
-  }
-
   protected void conservationMenuItem_actionPerformed(boolean selected)
   {
   }
@@ -2627,7 +2574,7 @@ public class GAlignFrame extends JInternalFrame
 
   }
 
-  public void buildTreeMenu()
+  public void buildTreeSortMenu()
   {
 
   }
index 0bc6cac..774641c 100755 (executable)
@@ -25,6 +25,7 @@ import jalview.util.MessageManager;
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.FlowLayout;
+import java.awt.Font;
 import java.awt.GridLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
@@ -43,54 +44,20 @@ import javax.swing.event.MenuListener;
 
 public class GPCAPanel extends JInternalFrame
 {
-  JPanel jPanel2 = new JPanel();
+  private static final Font VERDANA_12 = new Font("Verdana", 0, 12);
 
-  JLabel jLabel1 = new JLabel();
+  protected JComboBox<String> xCombobox = new JComboBox<String>();
 
-  JLabel jLabel2 = new JLabel();
+  protected JComboBox<String> yCombobox = new JComboBox<String>();
 
-  JLabel jLabel3 = new JLabel();
-
-  protected JComboBox xCombobox = new JComboBox();
-
-  protected JComboBox yCombobox = new JComboBox();
-
-  protected JComboBox zCombobox = new JComboBox();
-
-  protected JButton resetButton = new JButton();
-
-  FlowLayout flowLayout1 = new FlowLayout();
-
-  BorderLayout borderLayout1 = new BorderLayout();
-
-  JMenuBar jMenuBar1 = new JMenuBar();
-
-  JMenu fileMenu = new JMenu();
-
-  JMenu saveMenu = new JMenu();
+  protected JComboBox<String> zCombobox = new JComboBox<String>();
 
   protected JMenu scoreMatrixMenu = new JMenu();
 
-  JMenuItem eps = new JMenuItem();
-
-  JMenuItem png = new JMenuItem();
-
-  JMenuItem print = new JMenuItem();
-
-  JMenuItem outputValues = new JMenuItem();
-
-  JMenuItem outputPoints = new JMenuItem();
-
-  JMenuItem outputProjPoints = new JMenuItem();
-
   protected JMenu viewMenu = new JMenu();
 
   protected JCheckBoxMenuItem showLabels = new JCheckBoxMenuItem();
 
-  JMenuItem bgcolour = new JMenuItem();
-
-  JMenuItem originalSeqData = new JMenuItem();
-
   protected JMenu associateViewsMenu = new JMenu();
 
   protected JMenu calcSettings = new JMenu();
@@ -103,8 +70,6 @@ public class GPCAPanel extends JInternalFrame
 
   protected JLabel statusBar = new JLabel();
 
-  protected GridLayout statusPanelLayout = new GridLayout();
-
   protected JPanel statusPanel = new JPanel();
 
   public GPCAPanel()
@@ -123,49 +88,55 @@ public class GPCAPanel extends JInternalFrame
       yCombobox.addItem("dim " + i);
       zCombobox.addItem("dim " + i);
     }
-
-    setJMenuBar(jMenuBar1);
   }
 
   private void jbInit() throws Exception
   {
-    this.getContentPane().setLayout(borderLayout1);
-    jPanel2.setLayout(flowLayout1);
-    jLabel1.setFont(new java.awt.Font("Verdana", 0, 12));
+    this.getContentPane().setLayout(new BorderLayout());
+    JPanel jPanel2 = new JPanel();
+    jPanel2.setLayout(new FlowLayout());
+    JLabel jLabel1 = new JLabel();
+    jLabel1.setFont(VERDANA_12);
     jLabel1.setText("x=");
-    jLabel2.setFont(new java.awt.Font("Verdana", 0, 12));
+    JLabel jLabel2 = new JLabel();
+    jLabel2.setFont(VERDANA_12);
     jLabel2.setText("y=");
-    jLabel3.setFont(new java.awt.Font("Verdana", 0, 12));
+    JLabel jLabel3 = new JLabel();
+    jLabel3.setFont(VERDANA_12);
     jLabel3.setText("z=");
     jPanel2.setBackground(Color.white);
     jPanel2.setBorder(null);
-    zCombobox.setFont(new java.awt.Font("Verdana", 0, 12));
-    zCombobox.addActionListener(new java.awt.event.ActionListener()
+    zCombobox.setFont(VERDANA_12);
+    zCombobox.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         zCombobox_actionPerformed(e);
       }
     });
-    yCombobox.setFont(new java.awt.Font("Verdana", 0, 12));
-    yCombobox.addActionListener(new java.awt.event.ActionListener()
+    yCombobox.setFont(VERDANA_12);
+    yCombobox.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         yCombobox_actionPerformed(e);
       }
     });
-    xCombobox.setFont(new java.awt.Font("Verdana", 0, 12));
-    xCombobox.addActionListener(new java.awt.event.ActionListener()
+    xCombobox.setFont(VERDANA_12);
+    xCombobox.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         xCombobox_actionPerformed(e);
       }
     });
-    resetButton.setFont(new java.awt.Font("Verdana", 0, 12));
+    JButton resetButton = new JButton();
+    resetButton.setFont(VERDANA_12);
     resetButton.setText(MessageManager.getString("action.reset"));
-    resetButton.addActionListener(new java.awt.event.ActionListener()
+    resetButton.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -173,51 +144,64 @@ public class GPCAPanel extends JInternalFrame
         resetButton_actionPerformed(e);
       }
     });
+    JMenu fileMenu = new JMenu();
     fileMenu.setText(MessageManager.getString("action.file"));
+    JMenu saveMenu = new JMenu();
     saveMenu.setText(MessageManager.getString("action.save_as"));
-    eps.setText("EPS");
+    JMenuItem eps = new JMenuItem("EPS");
     eps.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         eps_actionPerformed(e);
       }
     });
-    png.setText("PNG");
+    JMenuItem png = new JMenuItem("PNG");
     png.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         png_actionPerformed(e);
       }
     });
+    JMenuItem outputValues = new JMenuItem();
     outputValues.setText(MessageManager.getString("label.output_values"));
     outputValues.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         outputValues_actionPerformed(e);
       }
     });
+    JMenuItem outputPoints = new JMenuItem();
     outputPoints.setText(MessageManager.getString("label.output_points"));
     outputPoints.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         outputPoints_actionPerformed(e);
       }
     });
+    JMenuItem outputProjPoints = new JMenuItem();
     outputProjPoints.setText(MessageManager
             .getString("label.output_transformed_points"));
     outputProjPoints.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         outputProjPoints_actionPerformed(e);
       }
     });
+    JMenuItem print = new JMenuItem();
+    print.setText(MessageManager.getString("action.print"));
     print.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         print_actionPerformed(e);
@@ -226,15 +210,18 @@ public class GPCAPanel extends JInternalFrame
     viewMenu.setText(MessageManager.getString("action.view"));
     viewMenu.addMenuListener(new MenuListener()
     {
+      @Override
       public void menuSelected(MenuEvent e)
       {
         viewMenu_menuSelected();
       }
 
+      @Override
       public void menuDeselected(MenuEvent e)
       {
       }
 
+      @Override
       public void menuCanceled(MenuEvent e)
       {
       }
@@ -243,15 +230,18 @@ public class GPCAPanel extends JInternalFrame
             .getString("label.select_score_model"));
     scoreMatrixMenu.addMenuListener(new MenuListener()
     {
+      @Override
       public void menuSelected(MenuEvent e)
       {
         scoreMatrix_menuSelected();
       }
 
+      @Override
       public void menuDeselected(MenuEvent e)
       {
       }
 
+      @Override
       public void menuCanceled(MenuEvent e)
       {
       }
@@ -259,23 +249,27 @@ public class GPCAPanel extends JInternalFrame
     showLabels.setText(MessageManager.getString("label.show_labels"));
     showLabels.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         showLabels_actionPerformed(e);
       }
     });
-    print.setText(MessageManager.getString("action.print"));
+    JMenuItem bgcolour = new JMenuItem();
     bgcolour.setText(MessageManager.getString("action.background_colour"));
     bgcolour.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         bgcolour_actionPerformed(e);
       }
     });
+    JMenuItem originalSeqData = new JMenuItem();
     originalSeqData.setText(MessageManager.getString("label.input_data"));
     originalSeqData.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         originalSeqData_actionPerformed(e);
@@ -315,12 +309,12 @@ public class GPCAPanel extends JInternalFrame
         jvVersionSetting_actionPerfomed(arg0);
       }
     });
-    calcSettings.add(jvVersionSetting);
+    // calcSettings.add(jvVersionSetting); // todo remove? JAL-2416
     calcSettings.add(nuclSetting);
     calcSettings.add(protSetting);
     calcSettings.add(scoreMatrixMenu);
-    statusPanel.setLayout(statusPanelLayout);
-    statusBar.setFont(new java.awt.Font("Verdana", 0, 12));
+    statusPanel.setLayout(new GridLayout());
+    statusBar.setFont(VERDANA_12);
     // statusPanel.setBackground(Color.lightGray);
     // statusBar.setBackground(Color.lightGray);
     // statusPanel.add(statusBar, null);
@@ -335,9 +329,12 @@ public class GPCAPanel extends JInternalFrame
     jPanel2.add(jLabel3, null);
     jPanel2.add(zCombobox, null);
     jPanel2.add(resetButton, null);
+
+    JMenuBar jMenuBar1 = new JMenuBar();
     jMenuBar1.add(fileMenu);
     jMenuBar1.add(viewMenu);
     jMenuBar1.add(calcSettings);
+    setJMenuBar(jMenuBar1);
     fileMenu.add(saveMenu);
     fileMenu.add(outputValues);
     fileMenu.add(print);
index f35b886..8ac8731 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.schemes;
 
+import jalview.analysis.scoremodels.PairwiseScoreModelI;
+import jalview.analysis.scoremodels.ScoreModels;
 import jalview.datamodel.AnnotatedCollectionI;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceI;
@@ -53,6 +55,8 @@ public class Blosum62ColourScheme extends ResidueColourScheme
   public Color findColour(char res, int j, SequenceI seq,
           String consensusResidue, float pid)
   {
+    PairwiseScoreModelI sm = ScoreModels.getInstance().getBlosum62();
+
     /*
      * compare as upper case; note consensusResidue is 
      * always computed as uppercase
@@ -75,14 +79,14 @@ public class Blosum62ColourScheme extends ResidueColourScheme
     }
     else
     {
-      int c = 0;
+      float score = 0;
 
       for (char consensus : consensusResidue.toCharArray())
       {
-        c += ResidueProperties.getBLOSUM62(consensus, res);
+        score += sm.getPairwiseScore(consensus, res);
       }
 
-      if (c > 0)
+      if (score > 0)
       {
         colour = LIGHT_BLUE;
       }
index 1e6142d..751175d 100755 (executable)
  */
 package jalview.schemes;
 
-import jalview.analysis.scoremodels.FeatureScoreModel;
-import jalview.analysis.scoremodels.PIDScoreModel;
-import jalview.api.analysis.ScoreModelI;
-
 import java.awt.Color;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -36,8 +32,6 @@ import java.util.Vector;
 
 public class ResidueProperties
 {
-  public static Hashtable<String, ScoreModelI> scoreMatrices = new Hashtable<String, ScoreModelI>();
-
   // Stores residue codes/names and colours and other things
   public static final int[] aaIndex; // aaHash version 2.1.1 and below
 
@@ -477,105 +471,6 @@ public class ResidueProperties
 
   // public static final double hydmax = 1.38;
   // public static final double hydmin = -2.53;
-  private static final int[][] BLOSUM62 = {
-      { 4, -1, -2, -2, 0, -1, -1, 0, -2, -1, -1, -1, -1, -2, -1, 1, 0, -3,
-          -2, 0, -2, -1, 0, -4 },
-      { -1, 5, 0, -2, -3, 1, 0, -2, 0, -3, -2, 2, -1, -3, -2, -1, -1, -3,
-          -2, -3, -1, 0, -1, -4 },
-      { -2, 0, 6, 1, -3, 0, 0, 0, 1, -3, -3, 0, -2, -3, -2, 1, 0, -4, -2,
-          -3, 3, 0, -1, -4 },
-      { -2, -2, 1, 6, -3, 0, 2, -1, -1, -3, -4, -1, -3, -3, -1, 0, -1, -4,
-          -3, -3, 4, 1, -1, -4 },
-      { 0, -3, -3, -3, 9, -3, -4, -3, -3, -1, -1, -3, -1, -2, -3, -1, -1,
-          -2, -2, -1, -3, -3, -2, -4 },
-      { -1, 1, 0, 0, -3, 5, 2, -2, 0, -3, -2, 1, 0, -3, -1, 0, -1, -2, -1,
-          -2, 0, 3, -1, -4 },
-      { -1, 0, 0, 2, -4, 2, 5, -2, 0, -3, -3, 1, -2, -3, -1, 0, -1, -3, -2,
-          -2, 1, 4, -1, -4 },
-      { 0, -2, 0, -1, -3, -2, -2, 6, -2, -4, -4, -2, -3, -3, -2, 0, -2, -2,
-          -3, -3, -1, -2, -1, -4 },
-      { -2, 0, 1, -1, -3, 0, 0, -2, 8, -3, -3, -1, -2, -1, -2, -1, -2, -2,
-          2, -3, 0, 0, -1, -4 },
-      { -1, -3, -3, -3, -1, -3, -3, -4, -3, 4, 2, -3, 1, 0, -3, -2, -1, -3,
-          -1, 3, -3, -3, -1, -4 },
-      { -1, -2, -3, -4, -1, -2, -3, -4, -3, 2, 4, -2, 2, 0, -3, -2, -1, -2,
-          -1, 1, -4, -3, -1, -4 },
-      { -1, 2, 0, -1, -3, 1, 1, -2, -1, -3, -2, 5, -1, -3, -1, 0, -1, -3,
-          -2, -2, 0, 1, -1, -4 },
-      { -1, -1, -2, -3, -1, 0, -2, -3, -2, 1, 2, -1, 5, 0, -2, -1, -1, -1,
-          -1, 1, -3, -1, -1, -4 },
-      { -2, -3, -3, -3, -2, -3, -3, -3, -1, 0, 0, -3, 0, 6, -4, -2, -2, 1,
-          3, -1, -3, -3, -1, -4 },
-      { -1, -2, -2, -1, -3, -1, -1, -2, -2, -3, -3, -1, -2, -4, 7, -1, -1,
-          -4, -3, -2, -2, -1, -2, -4 },
-      { 1, -1, 1, 0, -1, 0, 0, 0, -1, -2, -2, 0, -1, -2, -1, 4, 1, -3, -2,
-          -2, 0, 0, 0, -4 },
-      { 0, -1, 0, -1, -1, -1, -1, -2, -2, -1, -1, -1, -1, -2, -1, 1, 5, -2,
-          -2, 0, -1, -1, 0, -4 },
-      { -3, -3, -4, -4, -2, -2, -3, -2, -2, -3, -2, -3, -1, 1, -4, -3, -2,
-          11, 2, -3, -4, -3, -2, -4 },
-      { -2, -2, -2, -3, -2, -1, -2, -3, 2, -1, -1, -2, -1, 3, -3, -2, -2,
-          2, 7, -1, -3, -2, -1, -4 },
-      { 0, -3, -3, -3, -1, -2, -2, -3, -3, 3, 1, -2, 1, -1, -2, -2, 0, -3,
-          -1, 4, -3, -2, -1, -4 },
-      { -2, -1, 3, 4, -3, 0, 1, -1, 0, -3, -4, 0, -3, -3, -2, 0, -1, -4,
-          -3, -3, 4, 1, -1, -4 },
-      { -1, 0, 0, 1, -3, 3, 4, -2, 0, -3, -3, 1, -1, -3, -1, 0, -1, -3, -2,
-          -2, 1, 4, -1, -4 },
-      { 0, -1, -1, -1, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, 0, 0,
-          -2, -1, -1, -1, -1, -1, -4 },
-      { -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4,
-          -4, -4, -4, -4, -4, -4, 1 }, };
-
-  static final int[][] PAM250 = {
-      { 2, -2, 0, 0, -2, 0, 0, 1, -1, -1, -2, -1, -1, -3, 1, 1, 1, -6, -3,
-          0, 0, 0, 0, -8 },
-      { -2, 6, 0, -1, -4, 1, -1, -3, 2, -2, -3, 3, 0, -4, 0, 0, -1, 2, -4,
-          -2, -1, 0, -1, -8 },
-      { 0, 0, 2, 2, -4, 1, 1, 0, 2, -2, -3, 1, -2, -3, 0, 1, 0, -4, -2, -2,
-          2, 1, 0, -8 },
-      { 0, -1, 2, 4, -5, 2, 3, 1, 1, -2, -4, 0, -3, -6, -1, 0, 0, -7, -4,
-          -2, 3, 3, -1, -8 },
-      { -2, -4, -4, -5, 12, -5, -5, -3, -3, -2, -6, -5, -5, -4, -3, 0, -2,
-          -8, 0, -2, -4, -5, -3, -8 },
-      { 0, 1, 1, 2, -5, 4, 2, -1, 3, -2, -2, 1, -1, -5, 0, -1, -1, -5, -4,
-          -2, 1, 3, -1, -8 },
-      { 0, -1, 1, 3, -5, 2, 4, 0, 1, -2, -3, 0, -2, -5, -1, 0, 0, -7, -4,
-          -2, 3, 3, -1, -8 },
-      { 1, -3, 0, 1, -3, -1, 0, 5, -2, -3, -4, -2, -3, -5, 0, 1, 0, -7, -5,
-          -1, 0, 0, -1, -8 },
-      { -1, 2, 2, 1, -3, 3, 1, -2, 6, -2, -2, 0, -2, -2, 0, -1, -1, -3, 0,
-          -2, 1, 2, -1, -8 },
-      { -1, -2, -2, -2, -2, -2, -2, -3, -2, 5, 2, -2, 2, 1, -2, -1, 0, -5,
-          -1, 4, -2, -2, -1, -8 },
-      { -2, -3, -3, -4, -6, -2, -3, -4, -2, 2, 6, -3, 4, 2, -3, -3, -2, -2,
-          -1, 2, -3, -3, -1, -8 },
-      { -1, 3, 1, 0, -5, 1, 0, -2, 0, -2, -3, 5, 0, -5, -1, 0, 0, -3, -4,
-          -2, 1, 0, -1, -8 },
-      { -1, 0, -2, -3, -5, -1, -2, -3, -2, 2, 4, 0, 6, 0, -2, -2, -1, -4,
-          -2, 2, -2, -2, -1, -8 },
-      { -3, -4, -3, -6, -4, -5, -5, -5, -2, 1, 2, -5, 0, 9, -5, -3, -3, 0,
-          7, -1, -4, -5, -2, -8 },
-      { 1, 0, 0, -1, -3, 0, -1, 0, 0, -2, -3, -1, -2, -5, 6, 1, 0, -6, -5,
-          -1, -1, 0, -1, -8 },
-      { 1, 0, 1, 0, 0, -1, 0, 1, -1, -1, -3, 0, -2, -3, 1, 2, 1, -2, -3,
-          -1, 0, 0, 0, -8 },
-      { 1, -1, 0, 0, -2, -1, 0, 0, -1, 0, -2, 0, -1, -3, 0, 1, 3, -5, -3,
-          0, 0, -1, 0, -8 },
-      { -6, 2, -4, -7, -8, -5, -7, -7, -3, -5, -2, -3, -4, 0, -6, -2, -5,
-          17, 0, -6, -5, -6, -4, -8 },
-      { -3, -4, -2, -4, 0, -4, -4, -5, 0, -1, -1, -4, -2, 7, -5, -3, -3, 0,
-          10, -2, -3, -4, -2, -8 },
-      { 0, -2, -2, -2, -2, -2, -2, -1, -2, 4, 2, -2, 2, -1, -1, -1, 0, -6,
-          -2, 4, -2, -2, -1, -8 },
-      { 0, -1, 2, 3, -4, 1, 3, 0, 1, -2, -3, 1, -2, -4, -1, 0, 0, -5, -3,
-          -2, 3, 2, -1, -8 },
-      { 0, 0, 1, 3, -5, 3, 3, 0, 2, -2, -3, 0, -2, -5, 0, 0, -1, -6, -4,
-          -2, 2, 3, -1, -8 },
-      { 0, -1, 0, -1, -3, -1, -1, -1, -1, -1, -1, -1, -1, -2, -1, 0, 0, -4,
-          -2, -1, -1, -1, -1, -8 },
-      { -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8,
-          -8, -8, -8, -8, -8, -8, 1 }, };
 
   // not currently used
   // public static final Map<String, Color> ssHash = new Hashtable<String,
@@ -598,33 +493,6 @@ public class ResidueProperties
    * Color.white, // R Color.white, // Y Color.white, // N Color.white, // Gap
    */
 
-  // JBPNote: patch matrix for T/U equivalence when working with DNA or RNA.
-  // Will equate sequences if working with mixed nucleotide sets.
-  // treats T and U identically. R and Y weak equivalence with AG and CTU.
-  // N matches any other base weakly
-  //
-  static final int[][] DNA = { { 10, -8, -8, -8, -8, 1, 1, 1, -8, 1, 1 }, // A
-      { -8, 10, -8, -8, -8, 1, 1, -8, 1, 1, 1 }, // C
-      { -8, -8, 10, -8, -8, 1, 1, 1, -8, 1, 1 }, // G
-      { -8, -8, -8, 10, 10, 1, 1, -8, 1, 1, 1 }, // T
-      { -8, -8, -8, 10, 10, 1, 1, -8, 1, 1, 1 }, // U
-      { 1, 1, 1, 1, 1, 10, 0, 0, 0, 1, 1 }, // I
-      { 1, 1, 1, 1, 1, 0, 10, 0, 0, 1, 1 }, // X
-      { 1, -8, 1, -8, -8, 0, 0, 10, -8, 1, 1 }, // R
-      { -8, 1, -8, 1, 1, 0, 0, -8, 10, 1, 1 }, // Y
-      { 1, 1, 1, 1, 1, 1, 1, 1, 1, 10, 1 }, // N
-      { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, // -
-  };
-  /**
-   * register matrices in list
-   */
-  static
-  {
-    scoreMatrices.put("BLOSUM62", new ScoreMatrix("BLOSUM62", BLOSUM62, 0));
-    scoreMatrices.put("PAM250", new ScoreMatrix("PAM250", PAM250, 0));
-    scoreMatrices.put("DNA", new ScoreMatrix("DNA", DNA, 1));
-  }
-
   public static List<String> STOP = Arrays.asList("TGA", "TAA", "TAG");
 
   public static String START = "ATG";
@@ -1244,15 +1112,6 @@ public class ResidueProperties
       propMatrixPos[i][i] = maxP;
       propMatrixEpos[i][i] = maxEP;
     }
-    // JAL-1512 comment out physicochemical score matrices for 2.8.1 release
-    // scoreMatrices.put("Conservation Pos", new
-    // ScoreMatrix("Conservation Pos",propMatrixPos,0));
-    // scoreMatrices.put("Conservation Both", new
-    // ScoreMatrix("Conservation Both",propMatrixF,0));
-    // scoreMatrices.put("Conservation EnhPos", new
-    // ScoreMatrix("Conservation EnhPos",propMatrixEpos,0));
-    scoreMatrices.put("PID", new PIDScoreModel());
-    scoreMatrices.put("Displayed Features", new FeatureScoreModel());
   }
 
   private ResidueProperties()
@@ -1279,39 +1138,6 @@ public class ResidueProperties
     return aa3Hash;
   }
 
-  public static int[][] getDNA()
-  {
-    return ResidueProperties.DNA;
-  }
-
-  public static int[][] getBLOSUM62()
-  {
-    return ResidueProperties.BLOSUM62;
-  }
-
-  public static int getPAM250(String A1, String A2)
-  {
-    return getPAM250(A1.charAt(0), A2.charAt(0));
-  }
-
-  public static int getBLOSUM62(char c1, char c2)
-  {
-    int pog = 0;
-
-    try
-    {
-      int a = aaIndex[c1];
-      int b = aaIndex[c2];
-
-      pog = ResidueProperties.BLOSUM62[a][b];
-    } catch (Exception e)
-    {
-      // System.out.println("Unknown residue in " + A1 + " " + A2);
-    }
-
-    return pog;
-  }
-
   public static String codonTranslate(String lccodon)
   {
     String cdn = codonHash2.get(lccodon.toUpperCase());
@@ -1322,53 +1148,6 @@ public class ResidueProperties
     return cdn;
   }
 
-  public static int[][] getDefaultPeptideMatrix()
-  {
-    return ResidueProperties.getBLOSUM62();
-  }
-
-  public static int[][] getDefaultDnaMatrix()
-  {
-    return ResidueProperties.getDNA();
-  }
-
-  /**
-   * get a ScoreMatrix based on its string name
-   * 
-   * @param pwtype
-   * @return matrix in scoreMatrices with key pwtype or null
-   */
-  public static ScoreMatrix getScoreMatrix(String pwtype)
-  {
-    Object val = scoreMatrices.get(pwtype);
-    if (val != null && val instanceof ScoreMatrix)
-    {
-      return (ScoreMatrix) val;
-    }
-    return null;
-  }
-
-  /**
-   * get a ScoreModel based on its string name
-   * 
-   * @param pwtype
-   * @return scoremodel of type pwtype or null
-   */
-  public static ScoreModelI getScoreModel(String pwtype)
-  {
-    return scoreMatrices.get(pwtype);
-  }
-
-  public static int getPAM250(char c, char d)
-  {
-    int a = aaIndex[c];
-    int b = aaIndex[d];
-
-    int pog = ResidueProperties.PAM250[a][b];
-
-    return pog;
-  }
-
   public static Hashtable<String, String> toDssp3State;
   static
   {
diff --git a/src/jalview/schemes/ScoreMatrix.java b/src/jalview/schemes/ScoreMatrix.java
deleted file mode 100644 (file)
index d82f54c..0000000
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
- * Copyright (C) $$Year-Rel$$ The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
- * PURPOSE.  See the GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.schemes;
-
-import jalview.analysis.scoremodels.PairwiseSeqScoreModel;
-import jalview.math.Matrix;
-import jalview.math.MatrixI;
-
-public class ScoreMatrix extends PairwiseSeqScoreModel
-{
-  String name;
-
-  @Override
-  public String getName()
-  {
-    return name;
-  }
-
-  /**
-   * reference to integer score matrix
-   */
-  int[][] matrix;
-
-  /**
-   * 0 for Protein Score matrix. 1 for dna score matrix
-   */
-  int type;
-
-  /**
-   * 
-   * @param name
-   *          Unique, human readable name for the matrix
-   * @param matrix
-   *          Pairwise scores indexed according to appropriate symbol alphabet
-   * @param type
-   *          0 for Protein, 1 for NA
-   */
-  ScoreMatrix(String name, int[][] matrix, int type)
-  {
-    this.matrix = matrix;
-    this.type = type;
-    this.name = name;
-  }
-
-  @Override
-  public boolean isDNA()
-  {
-    return type == 1;
-  }
-
-  @Override
-  public boolean isProtein()
-  {
-    return type == 0;
-  }
-
-  @Override
-  public int[][] getMatrix()
-  {
-    return matrix;
-  }
-
-  /**
-   * Answers the score for substituting first char in A1 with first char in A2
-   * 
-   * @param A1
-   * @param A2
-   * @return
-   */
-  public int getPairwiseScore(String A1, String A2)
-  {
-    return getPairwiseScore(A1.charAt(0), A2.charAt(0));
-  }
-
-  @Override
-  public int getPairwiseScore(char c, char d)
-  {
-    int score = 0;
-
-    try
-    {
-      int a = (type == 0) ? ResidueProperties.aaIndex[c]
-              : ResidueProperties.nucleotideIndex[c];
-      int b = (type == 0) ? ResidueProperties.aaIndex[d]
-              : ResidueProperties.nucleotideIndex[d];
-      score = matrix[a][b];
-    } catch (Exception e)
-    {
-      // System.out.println("Unknown residue in " + A1 + " " + A2);
-    }
-
-    return score;
-  }
-
-  /**
-   * pretty print the matrix
-   */
-  @Override
-  public String toString()
-  {
-    return outputMatrix(false);
-  }
-
-  public String outputMatrix(boolean html)
-  {
-    StringBuffer sb = new StringBuffer();
-    int[] symbols = (type == 0) ? ResidueProperties.aaIndex
-            : ResidueProperties.nucleotideIndex;
-    int symMax = (type == 0) ? ResidueProperties.maxProteinIndex
-            : ResidueProperties.maxNucleotideIndex;
-    boolean header = true;
-    if (html)
-    {
-      sb.append("<table border=\"1\">");
-    }
-    for (char sym = 'A'; sym <= 'Z'; sym++)
-    {
-      if (symbols[sym] >= 0 && symbols[sym] < symMax)
-      {
-        if (header)
-        {
-          sb.append(html ? "<tr><td></td>" : "");
-          for (char sym2 = 'A'; sym2 <= 'Z'; sym2++)
-          {
-            if (symbols[sym2] >= 0 && symbols[sym2] < symMax)
-            {
-              sb.append((html ? "<td>&nbsp;" : "\t") + sym2
-                      + (html ? "&nbsp;</td>" : ""));
-            }
-          }
-          header = false;
-          sb.append(html ? "</tr>\n" : "\n");
-        }
-        if (html)
-        {
-          sb.append("<tr>");
-        }
-        sb.append((html ? "<td>" : "") + sym + (html ? "</td>" : ""));
-        for (char sym2 = 'A'; sym2 <= 'Z'; sym2++)
-        {
-          if (symbols[sym2] >= 0 && symbols[sym2] < symMax)
-          {
-            sb.append((html ? "<td>" : "\t")
-                    + matrix[symbols[sym]][symbols[sym2]]
-                    + (html ? "</td>" : ""));
-          }
-        }
-        sb.append(html ? "</tr>\n" : "\n");
-      }
-    }
-    if (html)
-    {
-      sb.append("</table>");
-    }
-    return sb.toString();
-  }
-
-  /**
-   * Computes an NxN matrix where N is the number of sequences, and entry [i, j]
-   * is sequence[i] pairwise multiplied with sequence[j], as a sum of scores
-   * computed using the current score matrix. For example
-   * <ul>
-   * <li>Sequences:</li>
-   * <li>FKL</li>
-   * <li>R-D</li>
-   * <li>QIA</li>
-   * <li>GWC</li>
-   * <li>Score matrix is BLOSUM62</li>
-   * <li>Gaps treated same as X (unknown)</li>
-   * <li>product [0, 0] = F.F + K.K + L.L = 6 + 5 + 4 = 15</li>
-   * <li>product [1, 1] = R.R + -.- + D.D = 5 + -1 + 6 = 10</li>
-   * <li>product [2, 2] = Q.Q + I.I + A.A = 5 + 4 + 4 = 13</li>
-   * <li>product [3, 3] = G.G + W.W + C.C = 6 + 11 + 9 = 26</li>
-   * <li>product[0, 1] = F.R + K.- + L.D = -3 + -1 + -3 = -8
-   * <li>and so on</li>
-   * </ul>
-   */
-  public MatrixI computePairwiseScores(String[] seqs)
-  {
-    double[][] values = new double[seqs.length][];
-    for (int row = 0; row < seqs.length; row++)
-    {
-      values[row] = new double[seqs.length];
-      for (int col = 0; col < seqs.length; col++)
-      {
-        int total = 0;
-        int width = Math.min(seqs[row].length(), seqs[col].length());
-        for (int i = 0; i < width; i++)
-        {
-          char c1 = seqs[row].charAt(i);
-          char c2 = seqs[col].charAt(i);
-          int score = getPairwiseScore(c1, c2);
-          total += score;
-        }
-        values[row][col] = total;
-      }
-    }
-    return new Matrix(values);
-  }
-}
index 65fd5e7..1c46ca8 100644 (file)
@@ -454,7 +454,7 @@ public class StructureSelectionManager
        * Attempt pairwise alignment of the sequence with each chain in the PDB,
        * and remember the highest scoring chain
        */
-      int max = -10;
+      float max = -10;
       AlignSeq maxAlignseq = null;
       String maxChainId = " ";
       PDBChain maxChain = null;
index 1326647..6c2fb4b 100644 (file)
@@ -34,11 +34,11 @@ public class Comparison
 
   private static final int TO_UPPER_CASE = 'a' - 'A';
 
-  private static final char GAP_SPACE = ' ';
+  public static final char GAP_SPACE = ' ';
 
-  private static final char GAP_DOT = '.';
+  public static final char GAP_DOT = '.';
 
-  private static final char GAP_DASH = '-';
+  public static final char GAP_DASH = '-';
 
   public static final String GapChars = new String(new char[] { GAP_SPACE,
       GAP_DOT, GAP_DASH });
diff --git a/src/jalview/util/SetUtils.java b/src/jalview/util/SetUtils.java
new file mode 100644 (file)
index 0000000..94248cb
--- /dev/null
@@ -0,0 +1,43 @@
+package jalview.util;
+
+import java.util.Set;
+
+public class SetUtils
+{
+  /**
+   * Returns the count of things that are one or other of two sets but not in
+   * both. The sets are not modified.
+   * 
+   * @param set1
+   * @param set2
+   * @return
+   */
+  public static int countDisjunction(Set<? extends Object> set1,
+          Set<? extends Object> set2)
+  {
+    if (set1 == null)
+    {
+      return set2 == null ? 0 : set2.size();
+    }
+    if (set2 == null)
+    {
+      return set1.size();
+    }
+
+    int size1 = set1.size();
+    int size2 = set2.size();
+    Set<? extends Object> smallerSet = size1 < size2 ? set1 : set2;
+    Set<? extends Object> largerSet = (smallerSet == set1 ? set2 : set1);
+    int inCommon = 0;
+    for (Object k : smallerSet)
+    {
+      if (largerSet.contains(k))
+      {
+        inCommon++;
+      }
+    }
+
+    int notInCommon = (size1 - inCommon) + (size2 - inCommon);
+    return notInCommon;
+  }
+}
index 0623dab..4ad6d48 100644 (file)
@@ -44,7 +44,7 @@ public class PCAModel
     seqstrings = seqstrings2;
     seqs = seqs2;
     nucleotide = nucleotide2;
-    score_matrix = nucleotide2 ? "PID" : "BLOSUM62";
+    score_matrix = nucleotide2 ? "DNA" : "BLOSUM62";
   }
 
   private volatile PCA pca;
index 8bae5ba..df5aee9 100644 (file)
@@ -21,6 +21,8 @@
 package jalview.ws.sifts;
 
 import jalview.analysis.AlignSeq;
+import jalview.analysis.scoremodels.ScoreMatrix;
+import jalview.analysis.scoremodels.ScoreModels;
 import jalview.api.DBRefEntryI;
 import jalview.api.SiftsClientI;
 import jalview.datamodel.DBRefEntry;
@@ -943,7 +945,7 @@ public class SiftsClient implements SiftsClientI
   }
 
   @Override
-  public StringBuffer getMappingOutput(MappingOutputPojo mp)
+  public StringBuilder getMappingOutput(MappingOutputPojo mp)
           throws SiftsException
   {
     String seqRes = mp.getSeqResidue();
@@ -965,7 +967,7 @@ public class SiftsClient implements SiftsClientI
     int nochunks = ((seqRes.length()) / len)
             + ((seqRes.length()) % len > 0 ? 1 : 0);
     // output mappings
-    StringBuffer output = new StringBuffer();
+    StringBuilder output = new StringBuilder(512);
     output.append(NEWLINE);
     output.append("Sequence \u27f7 Structure mapping details").append(
             NEWLINE);
@@ -986,6 +988,7 @@ public class SiftsClient implements SiftsClientI
     output.append(String.valueOf(pdbEnd));
     output.append(NEWLINE).append(NEWLINE);
 
+    ScoreMatrix pam250 = ScoreModels.getInstance().getPam250();
     int matchedSeqCount = 0;
     for (int j = 0; j < nochunks; j++)
     {
@@ -1004,27 +1007,29 @@ public class SiftsClient implements SiftsClientI
       output.append(NEWLINE);
       output.append(new Format("%" + (maxid) + "s").form(" ")).append(" ");
 
-      // Print out the matching chars
+      /*
+       * Print out the match symbols:
+       * | for exact match (ignoring case)
+       * . if PAM250 score is positive
+       * else a space
+       */
       for (int i = 0; i < len; i++)
       {
         try
         {
           if ((i + (j * len)) < seqRes.length())
           {
-            boolean sameChar = Comparison.isSameResidue(
-                    seqRes.charAt(i + (j * len)),
-                    strRes.charAt(i + (j * len)), false);
-            if (sameChar
-                    && !jalview.util.Comparison.isGap(seqRes.charAt(i
-                            + (j * len))))
+            char c1 = seqRes.charAt(i + (j * len));
+            char c2 = strRes.charAt(i + (j * len));
+            boolean sameChar = Comparison.isSameResidue(c1, c2, false);
+            if (sameChar && !Comparison.isGap(c1))
             {
               matchedSeqCount++;
               output.append("|");
             }
             else if (type.equals("pep"))
             {
-              if (ResidueProperties.getPAM250(seqRes.charAt(i + (j * len)),
-                      strRes.charAt(i + (j * len))) > 0)
+              if (pam250.getPairwiseScore(c1, c2) > 0)
               {
                 output.append(".");
               }
index 4cb5329..c12f544 100644 (file)
  */
 package jalview.analysis;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals;
 
+import jalview.datamodel.Sequence;
 import jalview.gui.JvOptionPane;
 
 import org.testng.annotations.BeforeClass;
@@ -45,10 +47,32 @@ public class AlignSeqTest
     assertNull(AlignSeq.extractGaps(null, "ACG"));
     assertNull(AlignSeq.extractGaps("-. ", null));
 
-    assertEquals(" AC-G.T", AlignSeq.extractGaps("", " AC-G.T"));
-    assertEquals("AC-G.T", AlignSeq.extractGaps(" ", " AC-G.T"));
-    assertEquals("ACG.T", AlignSeq.extractGaps(" -", " AC-G.T"));
-    assertEquals("ACGT", AlignSeq.extractGaps(" -.", " AC-G.T ."));
-    assertEquals(" ACG.T", AlignSeq.extractGaps("-", " AC-G.T"));
+    assertEquals(AlignSeq.extractGaps("", " AC-G.T"), " AC-G.T");
+    assertEquals(AlignSeq.extractGaps(" ", " AC-G.T"), "AC-G.T");
+    assertEquals(AlignSeq.extractGaps(" -", " AC-G.T"), "ACG.T");
+    assertEquals(AlignSeq.extractGaps(" -.", " AC-G.T ."), "ACGT");
+    assertEquals(AlignSeq.extractGaps("-", " AC-G.T"), " ACG.T");
+  }
+
+  @Test(groups = { "Functional" })
+  public void testIndexEncode_nucleotide()
+  {
+    AlignSeq as = new AlignSeq(new Sequence("s1", "TTAG"), new Sequence(
+            "s2", "ACGT"), AlignSeq.DNA);
+    int[] expected = new int[] { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+        7, 7, 8, 8, 9, 9, 10, -1, 11, -1 };
+    String s = "aAcCgGtTuUiIxXrRyYnN .-?";
+    assertArrayEquals(expected, as.indexEncode(s));
+  }
+
+  @Test(groups = { "Functional" })
+  public void testIndexEncode_peptide()
+  {
+    AlignSeq as = new AlignSeq(new Sequence("s1", "PFY"), new Sequence(
+            "s2", "RQW"), AlignSeq.PEP);
+    int[] expected = new int[] { 0, 0, 1, 1, 2, 2, 21, 21, 22, 22, 23, 24,
+        -1, -1, -1 };
+    String s = "aArRnNzZxX *.-?";
+    assertArrayEquals(expected, as.indexEncode(s));
   }
 }
index 9fc88ea..70e59c5 100644 (file)
@@ -125,7 +125,7 @@ public class TestAlignSeq
     };
 
     as.printAlignment(ps);
-    String expected = "Score = 320\nLength of alignment = 10\nSequence Seq1 :  3 - 18 (Sequence length = 14)\nSequence Seq1 :  1 - 10 (Sequence length = 10)\n\n"
+    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";
@@ -20,6 +20,8 @@
  */
 package jalview.analysis.scoremodels;
 
+import static org.testng.Assert.assertEquals;
+
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
@@ -34,7 +36,7 @@ import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
-public class FeatureScoreModelTest
+public class FeatureDistanceModelTest
 {
 
   @BeforeClass(alwaysRun = true)
@@ -52,6 +54,17 @@ public class FeatureScoreModelTest
 
   int[] sf3 = new int[] { -1, -1, -1, -1, -1, -1, 76, 77 };
 
+  /**
+   * <pre>
+   * Load test alignment and add features to sequences: 
+   *      FER1_MESCR FER1_SPIOL FER3_RAPSA FER1_MAIZE 
+   *  sf1     X          X          X  
+   *  sf2                X                     X 
+   *  sf3                                      X
+   * </pre>
+   * 
+   * @return
+   */
   public AlignFrame getTestAlignmentFrame()
   {
     AlignFrame alf = new FileLoader(false).LoadFileWaitTillLoaded(
@@ -93,10 +106,11 @@ public class FeatureScoreModelTest
   public void testFeatureScoreModel() throws Exception
   {
     AlignFrame alf = getTestAlignmentFrame();
-    FeatureScoreModel fsm = new FeatureScoreModel();
+    FeatureDistanceModel fsm = new FeatureDistanceModel();
     Assert.assertTrue(fsm.configureFromAlignmentView(alf.getCurrentView()
             .getAlignPanel()));
     alf.selectAllSequenceMenuItem_actionPerformed(null);
+
     float[][] dm = fsm.findDistances(alf.getViewport().getAlignmentView(
             true));
     Assert.assertTrue(dm[0][2] == 0f,
@@ -111,7 +125,7 @@ public class FeatureScoreModelTest
     AlignFrame alf = getTestAlignmentFrame();
     // hiding first two columns shouldn't affect the tree
     alf.getViewport().hideColumns(0, 1);
-    FeatureScoreModel fsm = new FeatureScoreModel();
+    FeatureDistanceModel fsm = new FeatureDistanceModel();
     Assert.assertTrue(fsm.configureFromAlignmentView(alf.getCurrentView()
             .getAlignPanel()));
     alf.selectAllSequenceMenuItem_actionPerformed(null);
@@ -130,7 +144,7 @@ public class FeatureScoreModelTest
     // hide columns and check tree changes
     alf.getViewport().hideColumns(3, 4);
     alf.getViewport().hideColumns(0, 1);
-    FeatureScoreModel fsm = new FeatureScoreModel();
+    FeatureDistanceModel fsm = new FeatureDistanceModel();
     Assert.assertTrue(fsm.configureFromAlignmentView(alf.getCurrentView()
             .getAlignPanel()));
     alf.selectAllSequenceMenuItem_actionPerformed(null);
@@ -190,4 +204,56 @@ public class FeatureScoreModelTest
             .size(), 0);
   }
 
+  @Test(groups = { "Functional" })
+  public void testFindDistances() throws Exception
+  {
+    String seqs = ">s1\nABCDE\n>seq2\nABCDE\n";
+    AlignFrame alf = new FileLoader().LoadFileWaitTillLoaded(seqs,
+            DataSourceType.PASTE);
+    SequenceI s1 = alf.getViewport().getAlignment().getSequenceAt(0);
+    SequenceI s2 = alf.getViewport().getAlignment().getSequenceAt(1);
+
+    /*
+     * set domain and variant features thus:
+     *     ----5
+     *  s1 ddd..
+     *  s1 .vvv.
+     *  s1 ..vvv    
+     *  s2 .ddd. 
+     *  s2 vv..v
+     *  The number of unshared feature types per column is
+     *     20120 (two features of the same type doesn't affect score)
+     *  giving an average (pairwise distance) of 5/5 or 1.0 
+     */
+    s1.addSequenceFeature(new SequenceFeature("domain", null, 1, 3, 0f,
+            null));
+    s1.addSequenceFeature(new SequenceFeature("variant", null, 2, 4, 0f,
+            null));
+    s1.addSequenceFeature(new SequenceFeature("variant", null, 3, 5, 0f,
+            null));
+    s2.addSequenceFeature(new SequenceFeature("domain", null, 2, 4, 0f,
+            null));
+    s2.addSequenceFeature(new SequenceFeature("variant", null, 1, 2, 0f,
+            null));
+    s2.addSequenceFeature(new SequenceFeature("variant", null, 5, 5, 0f,
+            null));
+    alf.setShowSeqFeatures(true);
+    alf.getFeatureRenderer().findAllFeatures(true);
+
+    FeatureDistanceModel fsm = new FeatureDistanceModel();
+    Assert.assertTrue(fsm.configureFromAlignmentView(alf.getCurrentView()
+            .getAlignPanel()));
+    alf.selectAllSequenceMenuItem_actionPerformed(null);
+
+    float[][] distances = fsm.findDistances(alf.getViewport()
+            .getAlignmentView(true));
+    assertEquals(distances.length, 2);
+    assertEquals(distances[0][0], 0f);
+    assertEquals(distances[1][1], 0f);
+    // these left to fail pending resolution of
+    // JAL-2424 (dividing score by 6, not 5)
+    assertEquals(distances[0][1], 1f);
+    assertEquals(distances[1][0], 1f);
+  }
+
 }
diff --git a/test/jalview/analysis/scoremodels/ScoreMatrixTest.java b/test/jalview/analysis/scoremodels/ScoreMatrixTest.java
new file mode 100644 (file)
index 0000000..97cb742
--- /dev/null
@@ -0,0 +1,193 @@
+package jalview.analysis.scoremodels;
+import static org.testng.Assert.assertEquals;
+import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals;
+
+import jalview.math.MatrixI;
+
+import org.testng.annotations.Test;
+
+public class ScoreMatrixTest
+{
+  @Test(groups = "Functional")
+  public void testConstructor()
+  {
+    // note score matrix does not have to be symmetric (though it should be!)
+    float[][] scores = new float[3][];
+    scores[0] = new float[] { 1f, 2f, 3f };
+    scores[1] = new float[] { 4f, 5f, 6f };
+    scores[2] = new float[] { 7f, 8f, 9f };
+    ScoreMatrix sm = new ScoreMatrix("Test", "ABC".toCharArray(), scores);
+    assertEquals(sm.getSize(), 3);
+    assertArrayEquals(scores, sm.getMatrix());
+    assertEquals(sm.getPairwiseScore('A', 'a'), 1f);
+    assertEquals(sm.getPairwiseScore('b', 'c'), 6f);
+    assertEquals(sm.getPairwiseScore('c', 'b'), 8f);
+    assertEquals(sm.getPairwiseScore('A', 'D'), 0f);
+    assertEquals(sm.getMatrixIndex('c'), 2);
+    assertEquals(sm.getMatrixIndex(' '), -1);
+  }
+
+  @Test(
+    groups = "Functional",
+    expectedExceptions = { IllegalArgumentException.class })
+  public void testConstructor_matrixTooSmall()
+  {
+    float[][] scores = new float[2][];
+    scores[0] = new float[] { 1f, 2f };
+    scores[1] = new float[] { 3f, 4f };
+    new ScoreMatrix("Test", "ABC".toCharArray(), scores);
+  }
+
+  @Test(
+    groups = "Functional",
+    expectedExceptions = { IllegalArgumentException.class })
+  public void testConstructor_matrixTooBig()
+  {
+    float[][] scores = new float[2][];
+    scores[0] = new float[] { 1f, 2f };
+    scores[1] = new float[] { 3f, 4f };
+    new ScoreMatrix("Test", "A".toCharArray(), scores);
+  }
+
+  @Test(
+    groups = "Functional",
+    expectedExceptions = { IllegalArgumentException.class })
+  public void testConstructor_matrixNotSquare()
+  {
+    float[][] scores = new float[2][];
+    scores[0] = new float[] { 1f, 2f };
+    scores[1] = new float[] { 3f };
+    new ScoreMatrix("Test", "AB".toCharArray(), scores);
+  }
+
+  @Test(groups = "Functional")
+  public void testBuildSymbolIndex()
+  {
+    short[] index = ScoreMatrix.buildSymbolIndex("AX-. yxYp".toCharArray());
+
+    assertEquals(index.length, 128); // ASCII character set size
+
+    assertEquals(index['A'], 0);
+    assertEquals(index['a'], 0); // lower-case mapping added
+    assertEquals(index['X'], 1);
+    assertEquals(index['-'], 2);
+    assertEquals(index['.'], 3);
+    assertEquals(index[' '], 4);
+    assertEquals(index['y'], 5); // lower-case override
+    assertEquals(index['x'], 6); // lower-case override
+    assertEquals(index['Y'], 7);
+    assertEquals(index['p'], 8);
+    assertEquals(index['P'], -1); // lower-case doesn't map upper-case
+
+    /*
+     * check all unmapped symbols have index for unmapped
+     */
+    for (int c = 0; c < index.length; c++)
+    {
+      if (!"AaXx-. Yyp".contains(String.valueOf((char) c)))
+      {
+        assertEquals(index[c], -1);
+      }
+    }
+  }
+
+  /**
+   * check that characters not in the basic ASCII set are simply ignored
+   */
+  @Test(groups = "Functional")
+  public void testBuildSymbolIndex_nonAscii()
+  {
+    char[] weird = new char[] { 128, 245, 'P' };
+    short[] index = ScoreMatrix.buildSymbolIndex(weird);
+    assertEquals(index.length, 128);
+    assertEquals(index['P'], 2);
+    assertEquals(index['p'], 2);
+    for (int c = 0; c < index.length; c++)
+    {
+      if (c != 'P' && c != 'p')
+      {
+        assertEquals(index[c], -1);
+      }
+    }
+  }
+
+  @Test(groups = "Functional")
+  public void testGetMatrixIndex()
+  {
+    ScoreMatrix sm = ScoreModels.getInstance().getBlosum62();
+    assertEquals(sm.getMatrixIndex('A'), 0);
+    assertEquals(sm.getMatrixIndex('R'), 1);
+    assertEquals(sm.getMatrixIndex('r'), 1);
+    assertEquals(sm.getMatrixIndex('N'), 2);
+    assertEquals(sm.getMatrixIndex('D'), 3);
+    assertEquals(sm.getMatrixIndex('X'), 22);
+    assertEquals(sm.getMatrixIndex('x'), 22);
+    assertEquals(sm.getMatrixIndex(' '), 23);
+    assertEquals(sm.getMatrixIndex('*'), 24);
+    assertEquals(sm.getMatrixIndex('.'), -1);
+    assertEquals(sm.getMatrixIndex('-'), -1);
+    assertEquals(sm.getMatrixIndex('?'), -1);
+    assertEquals(sm.getMatrixIndex((char) 128), -1);
+  }
+
+  @Test(groups = "Functional")
+  public void testGetSize()
+  {
+    ScoreMatrix sm = ScoreModels.getInstance().getBlosum62();
+    assertEquals(sm.getMatrix().length, sm.getSize());
+  }
+
+  @Test(groups = "Functional")
+  public void testComputePairwiseScores()
+  {
+    /*
+     * NB score matrix assumes space for gap - Jalview converts
+     * space to gap before computing PCA or Tree
+     */
+    String[] seqs = new String[] { "FKL", "R D", "QIA", "GWC" };
+    ScoreMatrix sm = ScoreModels.getInstance().getBlosum62();
+
+    MatrixI pairwise = sm.computePairwiseScores(seqs);
+
+    /*
+     * should be NxN where N = number of sequences
+     */
+    assertEquals(pairwise.height(), 4);
+    assertEquals(pairwise.width(), 4);
+
+    /*
+     * should be symmetrical (because BLOSUM62 is)
+     */
+    for (int i = 0; i < pairwise.height(); i++)
+    {
+      for (int j = i + 1; j < pairwise.width(); j++)
+      {
+        assertEquals(pairwise.getValue(i, j), pairwise.getValue(j, i),
+                String.format("Not symmetric at [%d, %d]", i, j));
+      }
+    }
+    /*
+     * verify expected BLOSUM dot product scores
+     */
+    // F.F + K.K + L.L = 6 + 5 + 4 = 15
+    assertEquals(pairwise.getValue(0, 0), 15d);
+    // R.R + -.- + D.D = 5 + 1 + 6 = 12
+    assertEquals(pairwise.getValue(1, 1), 12d);
+    // Q.Q + I.I + A.A = 5 + 4 + 4 = 13
+    assertEquals(pairwise.getValue(2, 2), 13d);
+    // G.G + W.W + C.C = 6 + 11 + 9 = 26
+    assertEquals(pairwise.getValue(3, 3), 26d);
+    // F.R + K.- + L.D = -3 + -4 + -4 = -11
+    assertEquals(pairwise.getValue(0, 1), -11d);
+    // F.Q + K.I + L.A = -3 + -3 + -1 = -7
+    assertEquals(pairwise.getValue(0, 2), -7d);
+    // F.G + K.W + L.C = -3 + -3 + -1 = -7
+    assertEquals(pairwise.getValue(0, 3), -7d);
+    // R.Q + -.I + D.A = 1 + -4 + -2 = -5
+    assertEquals(pairwise.getValue(1, 2), -5d);
+    // R.G + -.W + D.C = -2 + -4 + -3 = -9
+    assertEquals(pairwise.getValue(1, 3), -9d);
+    // Q.G + I.W + A.C = -2 + -3 + 0 = -5
+    assertEquals(pairwise.getValue(2, 3), -5d);
+  }
+}
diff --git a/test/jalview/analysis/scoremodels/ScoreModelsTest.java b/test/jalview/analysis/scoremodels/ScoreModelsTest.java
new file mode 100644 (file)
index 0000000..f63843d
--- /dev/null
@@ -0,0 +1,110 @@
+package jalview.analysis.scoremodels;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import jalview.api.analysis.DistanceModelI;
+
+import java.util.Iterator;
+
+import org.testng.annotations.Test;
+
+public class ScoreModelsTest
+{
+  /**
+   * Verify that the singleton constructor successfully loads Jalview's built-in
+   * score models
+   */
+  @Test
+  public void testConstructor()
+  {
+    Iterator<DistanceModelI> models = ScoreModels.getInstance().getModels()
+            .iterator();
+    assertTrue(models.hasNext());
+
+    /*
+     * models are served in order of addition
+     */
+    DistanceModelI sm = models.next();
+    assertTrue(sm instanceof PairwiseDistanceModel);
+    assertEquals(sm.getName(), "BLOSUM62");
+    assertEquals(((PairwiseDistanceModel) sm).getScoreModel()
+            .getPairwiseScore('I', 'R'), -3f);
+
+    sm = models.next();
+    assertTrue(sm instanceof PairwiseDistanceModel);
+    assertEquals(sm.getName(), "PAM250");
+    assertEquals(((PairwiseDistanceModel) sm).getScoreModel()
+            .getPairwiseScore('R', 'C'), -4f);
+
+    sm = models.next();
+    assertTrue(sm instanceof PairwiseDistanceModel);
+    assertEquals(sm.getName(), "Identity (SeqSpace)");
+    assertEquals(((PairwiseDistanceModel) sm).getScoreModel()
+            .getPairwiseScore('R', 'C'), 0f);
+    assertEquals(((PairwiseDistanceModel) sm).getScoreModel()
+            .getPairwiseScore('R', 'r'), 1f);
+
+    sm = models.next();
+    assertTrue(sm instanceof PairwiseDistanceModel);
+    assertEquals(sm.getName(), "DNA");
+    assertEquals(((PairwiseDistanceModel) sm).getScoreModel()
+            .getPairwiseScore('c', 'x'), 1f);
+
+    sm = models.next();
+    assertFalse(sm instanceof PairwiseDistanceModel);
+    assertEquals(sm.getName(), "Sequence Feature Similarity");
+
+    sm = models.next();
+    assertFalse(sm instanceof PairwiseDistanceModel);
+    assertEquals(sm.getName(), "PID");
+  }
+
+  /**
+   * 'Test' that prints out score matrices in tab-delimited format. This test is
+   * intentionally not assigned to any group so would not be run as part of a
+   * suite. It makes no assertions and is just provided as a utility method for
+   * printing out matrices. Relocated here from ScoreMatrixPrinter.
+   */
+  @Test
+  public void printAllMatrices_tabDelimited()
+  {
+    printAllMatrices(false);
+  }
+
+  /**
+   * 'Test' that prints out score matrices in html format. This test is
+   * intentionally not assigned to any group so would not be run as part of a
+   * suite. It makes no assertions and is just provided as a utility method for
+   * printing out matrices. Relocated here from ScoreMatrixPrinter.
+   */
+  @Test
+  public void printAllMatrices_asHtml()
+  {
+    printAllMatrices(true);
+  }
+
+  /**
+   * Print all registered ScoreMatrix as plain or html tables
+   * 
+   * @param asHtml
+   */
+  protected void printAllMatrices(boolean asHtml)
+  {
+    for (DistanceModelI dm : ScoreModels.getInstance().getModels())
+    {
+      if (dm instanceof PairwiseDistanceModel)
+      {
+        PairwiseScoreModelI psm = ((PairwiseDistanceModel) dm)
+                .getScoreModel();
+        if (psm instanceof ScoreMatrix)
+        {
+          ScoreMatrix sm = (ScoreMatrix) psm;
+          System.out.println("ScoreMatrix " + sm.getName());
+          System.out.println(sm.outputMatrix(asHtml));
+        }
+      }
+    }
+  }
+}
index 3d800d8..2e4b9e0 100644 (file)
@@ -110,7 +110,7 @@ public class IdentifyFileTest
         {
             "examples/testdata/cullpdb_pc25_res3.0_R0.3_d150729_chains9361.fasta.15316",
             FileFormat.Fasta },
-
+        { "resources/scoreModel/pam250.scm", FileFormat.ScoreMatrix }
     // { "examples/testdata/test.amsa", "AMSA" },
     // { "examples/test.jnet", "JnetFile" },
     };
diff --git a/test/jalview/io/ScoreMatrixFileTest.java b/test/jalview/io/ScoreMatrixFileTest.java
new file mode 100644 (file)
index 0000000..123de6b
--- /dev/null
@@ -0,0 +1,260 @@
+package jalview.io;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import jalview.analysis.scoremodels.ScoreMatrix;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+
+import org.testng.annotations.Test;
+
+public class ScoreMatrixFileTest
+{
+
+  /**
+   * Test a successful parse of a (small) score matrix file
+   * 
+   * @throws IOException
+   * @throws MalformedURLException
+   */
+  @Test(groups = "Functional")
+  public void testParse() throws MalformedURLException, IOException
+  {
+    /*
+     * some messy but valid input data, with comma, space
+     * or tab (or combinations) as score value delimiters
+     * this example includes 'guide' symbols on score rows
+     */
+    String data = "ScoreMatrix MyTest (example)\n" + "ATU tx-\n"
+            + "A,1.1,1.2,1.3,1.4, 1.5, 1.6, 1.7\n"
+            + "T,2.1 2.2 2.3 2.4 2.5 2.6 2.7\n"
+            + "U\t3.1\t3.2\t3.3\t3.4\t3.5\t3.6\t3.7\n"
+            + " 4.1 ,4.2,\t,4.3 ,\t4.4\t, \4.5,4.6 4.7\n"
+            + "t, 5.1,5.3,5.3,5.4,5.5, 5.6, 5.7\n"
+            + "x\t6.1, 6.2 6.3 6.4 6.5 6.6 6.7\n"
+            + "-, \t7.1\t7.2 7.3, 7.4, 7.5\t,7.6,7.7\n";
+    FileParse fp = new FileParse(data, DataSourceType.PASTE);
+    ScoreMatrixFile parser = new ScoreMatrixFile(fp);
+    ScoreMatrix sm = parser.parseMatrix();
+
+    assertNotNull(sm);
+    assertEquals(sm.getName(), "MyTest (example)");
+    assertTrue(sm.isDNA());
+    assertFalse(sm.isProtein());
+    assertEquals(sm.getPairwiseScore('A', 'A'), 1.1f);
+    assertEquals(sm.getPairwiseScore('A', 'T'), 1.2f);
+    assertEquals(sm.getPairwiseScore('a', 'T'), 1.2f); // A/a equivalent
+    assertEquals(sm.getPairwiseScore('A', 't'), 1.5f); // T/t not equivalent
+    assertEquals(sm.getPairwiseScore('a', 't'), 1.5f);
+    assertEquals(sm.getPairwiseScore('T', ' '), 2.4f);
+    assertEquals(sm.getPairwiseScore('U', 'x'), 3.6f);
+    assertEquals(sm.getPairwiseScore('u', 'x'), 3.6f);
+    assertEquals(sm.getPairwiseScore('U', 'X'), 0f); // X (upper) unmapped
+    assertEquals(sm.getPairwiseScore('A', '.'), 0f); // . unmapped
+    assertEquals(sm.getPairwiseScore('-', '-'), 7.7f);
+    assertEquals(sm.getPairwiseScore('A', (char) 128), 0f); // out of range
+
+    /*
+     * without guide symbols on score rows
+     */
+    data = "ScoreMatrix MyTest\nXY\n1 2\n3 4\n";
+    fp = new FileParse(data, DataSourceType.PASTE);
+    parser = new ScoreMatrixFile(fp);
+    sm = parser.parseMatrix();
+    assertNotNull(sm);
+    assertEquals(sm.getPairwiseScore('X', 'X'), 1f);
+    assertEquals(sm.getPairwiseScore('X', 'y'), 2f);
+    assertEquals(sm.getPairwiseScore('y', 'x'), 3f);
+    assertEquals(sm.getPairwiseScore('y', 'Y'), 4f);
+    assertEquals(sm.getPairwiseScore('D', 'R'), 0f);
+  }
+
+  @Test(groups = "Functional")
+  public void testParse_headerMissing()
+  {
+    String data;
+
+    data = "XY\n1 2\n3 4\n";
+    try
+    {
+      new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
+              .parseMatrix();
+      fail("expected exception");
+    } catch (IOException e)
+    {
+      assertEquals(e.getMessage(),
+              "Format error: 'ScoreMatrix <name>' should be the first non-comment line");
+    }
+  }
+
+  @Test(groups = "Functional")
+  public void testParse_notEnoughRows()
+  {
+    String data = "ScoreMatrix MyTest\nXY\n1 2\n";
+    try
+    {
+      new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
+              .parseMatrix();
+      fail("expected exception");
+    } catch (IOException e)
+    {
+      assertEquals(e.getMessage(),
+              "Expected 2 rows of score data in score matrix but only found 1");
+    }
+  }
+
+  @Test(groups = "Functional")
+  public void testParse_notEnoughColumns()
+  {
+    String data = "ScoreMatrix MyTest\nXY\n1 2\n3\n";
+    try
+    {
+      new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
+              .parseMatrix();
+      fail("expected exception");
+    } catch (IOException e)
+    {
+      assertEquals(e.getMessage(),
+              "Expected 2 scores at line 4 but found 1");
+    }
+  }
+
+  @Test(groups = "Functional")
+  public void testParse_tooManyColumns()
+  {
+    /*
+     * with two too many columns:
+     */
+    String data = "ScoreMatrix MyTest\nXY\n1 2\n3 4 5 6\n";
+    try
+    {
+      new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
+              .parseMatrix();
+      fail("expected exception");
+    } catch (IOException e)
+    {
+      assertEquals(e.getMessage(),
+              "Expected 2 scores at line 4 but found 4");
+    }
+
+    /*
+     * with guide character and one too many columns:
+     */
+    data = "ScoreMatrix MyTest\nXY\nX 1 2\nY 3 4 5\n";
+    try
+    {
+      new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
+              .parseMatrix();
+      fail("expected exception");
+    } catch (IOException e)
+    {
+      assertEquals(e.getMessage(),
+              "Expected 2 scores at line 4 but found 4");
+    }
+
+    /*
+     * with no guide character and one too many columns:
+     * parser guesses the first column is the guide character
+     */
+    data = "ScoreMatrix MyTest\nXY\n1 2\n3 4 5\n";
+    try
+    {
+      new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
+              .parseMatrix();
+      fail("expected exception");
+    } catch (IOException e)
+    {
+      assertEquals(e.getMessage(),
+              "Error parsing score matrix at line 4, expected 'Y' but found '3'");
+    }
+  }
+
+  @Test(groups = "Functional")
+  public void testParse_tooManyRows()
+  {
+    String data = "ScoreMatrix MyTest\nXY\n1 2\n3 4\n6 7";
+    try
+    {
+      new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
+              .parseMatrix();
+      fail("expected exception");
+    } catch (IOException e)
+    {
+      assertEquals(e.getMessage(),
+              "Unexpected extra input line in score model file: '6 7'");
+    }
+  }
+
+  @Test(groups = "Functional")
+  public void testParse_badDelimiter()
+  {
+    String data = "ScoreMatrix MyTest\nXY\n1|2\n3|4\n";
+    try
+    {
+      new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
+              .parseMatrix();
+      fail("expected exception");
+    } catch (IOException e)
+    {
+      assertEquals(e.getMessage(),
+              "Expected 2 scores at line 3 but found 1");
+    }
+  }
+
+  @Test(groups = "Functional")
+  public void testParse_badFloat()
+  {
+    String data = "ScoreMatrix MyTest\nXY\n1 2\n3 four\n";
+    try
+    {
+      new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
+              .parseMatrix();
+      fail("expected exception");
+    } catch (IOException e)
+    {
+      assertEquals(e.getMessage(),
+              "Invalid score value 'four' at line 4 column 1");
+    }
+  }
+
+  @Test(groups = "Functional")
+  public void testParse_badGuideCharacter()
+  {
+    String data = "ScoreMatrix MyTest\nXY\nX 1 2\ny 3 4\n";
+    try
+    {
+      new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
+              .parseMatrix();
+      fail("expected exception");
+    } catch (IOException e)
+    {
+      assertEquals(e.getMessage(),
+              "Error parsing score matrix at line 4, expected 'Y' but found 'y'");
+    }
+  }
+
+  @Test(groups = "Functional")
+  public void testParse_nameMissing()
+  {
+    /*
+     * Name missing
+     */
+    String data = "ScoreMatrix\nXY\n1 2\n3 4\n";
+    try
+    {
+      new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
+              .parseMatrix();
+      fail("expected exception");
+    } catch (IOException e)
+    {
+      assertEquals(
+              e.getMessage(),
+              "Format error: expected 'ScoreMatrix <name>', found 'ScoreMatrix' at line 1");
+    }
+  }
+}
diff --git a/test/jalview/schemes/ScoreMatrixPrinter.java b/test/jalview/schemes/ScoreMatrixPrinter.java
deleted file mode 100644 (file)
index 80241fb..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
- * Copyright (C) $$Year-Rel$$ The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
- * PURPOSE.  See the GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.schemes;
-
-import jalview.api.analysis.ScoreModelI;
-import jalview.gui.JvOptionPane;
-
-import java.util.Map;
-
-import org.testng.annotations.BeforeClass;
-
-public class ScoreMatrixPrinter
-{
-
-  @BeforeClass(alwaysRun = true)
-  public void setUpJvOptionPane()
-  {
-    JvOptionPane.setInteractiveMode(false);
-    JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
-  }
-
-  public void printAllMatrices()
-  {
-    for (Map.Entry<String, ScoreModelI> sm : ResidueProperties.scoreMatrices
-            .entrySet())
-    {
-      System.out.println("Matrix " + sm.getKey());
-      System.out.println(sm.getValue().toString());
-    }
-  }
-
-  public void printHTMLMatrices()
-  {
-    for (Map.Entry<String, ScoreModelI> _sm : ResidueProperties.scoreMatrices
-            .entrySet())
-    {
-      if (_sm.getValue() instanceof ScoreMatrix)
-      {
-        ScoreMatrix sm = (ScoreMatrix) _sm.getValue();
-        System.out.println("Matrix " + _sm.getKey());
-        System.out.println(sm.outputMatrix(true));
-      }
-    }
-  }
-
-}
diff --git a/test/jalview/schemes/ScoreMatrixTest.java b/test/jalview/schemes/ScoreMatrixTest.java
deleted file mode 100644 (file)
index e15dd41..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-package jalview.schemes;
-
-import static org.testng.Assert.assertEquals;
-
-import jalview.math.MatrixI;
-
-import org.testng.annotations.Test;
-
-public class ScoreMatrixTest
-{
-  @Test(groups = "Functional")
-  public void testSymmetric()
-  {
-    verifySymmetric(ResidueProperties.getScoreMatrix("BLOSUM62"));
-    verifySymmetric(ResidueProperties.getScoreMatrix("PAM250"));
-    verifySymmetric(ResidueProperties.getScoreMatrix("DNA"));
-  }
-
-  private void verifySymmetric(ScoreMatrix sm)
-  {
-    int[][] m = sm.getMatrix();
-    int rows = m.length;
-    for (int row = 0; row < rows; row++)
-    {
-      assertEquals(m[row].length, rows);
-      for (int col = 0; col < rows; col++)
-      {
-        assertEquals(m[row][col], m[col][row], String.format("%s [%s, %s]",
-                sm.getName(), ResidueProperties.aa[row],
-                ResidueProperties.aa[col]));
-      }
-    }
-
-    /*
-     * also check the score matrix is sized for 
-     * the number of symbols scored, plus gap
-     */
-    assertEquals(rows, (sm.isDNA() ? ResidueProperties.maxNucleotideIndex
-            : ResidueProperties.maxProteinIndex) + 1);
-  }
-
-  /**
-   * A test that just asserts the expected values in the Blosum62 score matrix
-   */
-  @Test(groups = "Functional")
-  public void testBlosum62_values()
-  {
-    ScoreMatrix sm = ResidueProperties.getScoreMatrix("BLOSUM62");
-
-    /*
-     * verify expected scores against ARNDCQEGHILKMFPSTWYVBZX
-     * scraped from https://www.ncbi.nlm.nih.gov/Class/FieldGuide/BLOSUM62.txt
-     */
-    verifyValues(sm, 'A', new int[] { 4, -1, -2, -2, 0, -1, -1, 0, -2, -1,
-        -1, -1, -1, -2, -1, 1, 0, -3, -2, 0, -2, -1, 0 });
-    verifyValues(sm, 'R', new int[] { -1, 5, 0, -2, -3, 1, 0, -2, 0, -3,
-        -2, 2, -1, -3, -2, -1, -1, -3, -2, -3, -1, 0, -1 });
-    verifyValues(sm, 'N', new int[] { -2, 0, 6, 1, -3, 0, 0, 0, 1, -3, -3,
-        0, -2, -3, -2, 1, 0, -4, -2, -3, 3, 0, -1 });
-    verifyValues(sm, 'D', new int[] { -2, -2, 1, 6, -3, 0, 2, -1, -1, -3,
-        -4, -1, -3, -3, -1, 0, -1, -4, -3, -3, 4, 1, -1 });
-    verifyValues(sm, 'C', new int[] { 0, -3, -3, -3, 9, -3, -4, -3, -3, -1,
-        -1, -3, -1, -2, -3, -1, -1, -2, -2, -1, -3, -3, -2 });
-    verifyValues(sm, 'Q', new int[] { -1, 1, 0, 0, -3, 5, 2, -2, 0, -3, -2,
-        1, 0, -3, -1, 0, -1, -2, -1, -2, 0, 3, -1 });
-    verifyValues(sm, 'E', new int[] { -1, 0, 0, 2, -4, 2, 5, -2, 0, -3, -3,
-        1, -2, -3, -1, 0, -1, -3, -2, -2, 1, 4, -1 });
-    verifyValues(sm, 'G', new int[] { 0, -2, 0, -1, -3, -2, -2, 6, -2, -4,
-        -4, -2, -3, -3, -2, 0, -2, -2, -3, -3, -1, -2, -1 });
-    verifyValues(sm, 'H', new int[] { -2, 0, 1, -1, -3, 0, 0, -2, 8, -3,
-        -3, -1, -2, -1, -2, -1, -2, -2, 2, -3, 0, 0, -1 });
-    verifyValues(sm, 'I', new int[] { -1, -3, -3, -3, -1, -3, -3, -4, -3,
-        4, 2, -3, 1, 0, -3, -2, -1, -3, -1, 3, -3, -3, -1 });
-    verifyValues(sm, 'L', new int[] { -1, -2, -3, -4, -1, -2, -3, -4, -3,
-        2, 4, -2, 2, 0, -3, -2, -1, -2, -1, 1, -4, -3, -1 });
-    verifyValues(sm, 'K', new int[] { -1, 2, 0, -1, -3, 1, 1, -2, -1, -3,
-        -2, 5, -1, -3, -1, 0, -1, -3, -2, -2, 0, 1, -1 });
-    verifyValues(sm, 'M', new int[] { -1, -1, -2, -3, -1, 0, -2, -3, -2, 1,
-        2, -1, 5, 0, -2, -1, -1, -1, -1, 1, -3, -1, -1 });
-    verifyValues(sm, 'F', new int[] { -2, -3, -3, -3, -2, -3, -3, -3, -1,
-        0, 0, -3, 0, 6, -4, -2, -2, 1, 3, -1, -3, -3, -1 });
-    verifyValues(sm, 'P', new int[] { -1, -2, -2, -1, -3, -1, -1, -2, -2,
-        -3, -3, -1, -2, -4, 7, -1, -1, -4, -3, -2, -2, -1, -2 });
-    verifyValues(sm, 'S', new int[] { 1, -1, 1, 0, -1, 0, 0, 0, -1, -2, -2,
-        0, -1, -2, -1, 4, 1, -3, -2, -2, 0, 0, 0 });
-    verifyValues(sm, 'T', new int[] { 0, -1, 0, -1, -1, -1, -1, -2, -2, -1,
-        -1, -1, -1, -2, -1, 1, 5, -2, -2, 0, -1, -1, 0 });
-    verifyValues(sm, 'W', new int[] { -3, -3, -4, -4, -2, -2, -3, -2, -2,
-        -3, -2, -3, -1, 1, -4, -3, -2, 11, 2, -3, -4, -3, -2 });
-    verifyValues(sm, 'Y', new int[] { -2, -2, -2, -3, -2, -1, -2, -3, 2,
-        -1, -1, -2, -1, 3, -3, -2, -2, 2, 7, -1, -3, -2, -1 });
-    verifyValues(sm, 'V', new int[] { 0, -3, -3, -3, -1, -2, -2, -3, -3, 3,
-        1, -2, 1, -1, -2, -2, 0, -3, -1, 4, -3, -2, -1 });
-    verifyValues(sm, 'B', new int[] { -2, -1, 3, 4, -3, 0, 1, -1, 0, -3,
-        -4, 0, -3, -3, -2, 0, -1, -4, -3, -3, 4, 1, -1 });
-    verifyValues(sm, 'Z', new int[] { -1, 0, 0, 1, -3, 3, 4, -2, 0, -3, -3,
-        1, -1, -3, -1, 0, -1, -3, -2, -2, 1, 4, -1 });
-    verifyValues(sm, 'X', new int[] { 0, -1, -1, -1, -2, -1, -1, -1, -1,
-        -1, -1, -1, -1, -1, -2, 0, 0, -2, -1, -1, -1, -1, -1 });
-  }
-  /**
-   * Helper method to check pairwise scores for one residue
-   * 
-   * @param sm
-   * @param res
-   * @param expected
-   *          score values against 'res', in ResidueProperties.aaIndex order
-   */
-  private void verifyValues(ScoreMatrix sm, char res, int[] expected)
-  {
-    for (int j = 0; j < expected.length; j++)
-    {
-      char c2 = ResidueProperties.aa[j].charAt(0);
-      assertEquals(sm.getPairwiseScore(res, c2), expected[j],
-              String.format("%s->%s", res, c2));
-    }
-  }
-
-  @Test(groups = "Functional")
-  public void testComputePairwiseScores()
-  {
-    String[] seqs = new String[] { "FKL", "R-D", "QIA", "GWC" };
-    ScoreMatrix sm = ResidueProperties.getScoreMatrix("BLOSUM62");
-  
-    MatrixI pairwise = sm.computePairwiseScores(seqs);
-  
-    /*
-     * should be NxN where N = number of sequences
-     */
-    assertEquals(pairwise.height(), 4);
-    assertEquals(pairwise.width(), 4);
-  
-    /*
-     * should be symmetrical (because BLOSUM62 is)
-     */
-    for (int i = 0; i < pairwise.height(); i++)
-    {
-      for (int j = 0; j < pairwise.width(); j++)
-      {
-        assertEquals(pairwise.getValue(i, j), pairwise.getValue(j, i),
-                "Not symmetric");
-      }
-    }
-    /*
-     * verify expected BLOSUM dot product scores
-     */
-    // F.F + K.K + L.L = 6 + 5 + 4 = 15
-    assertEquals(pairwise.getValue(0, 0), 15d);
-    // R.R + -.- + D.D = 5 + 1 + 6 = 12
-    assertEquals(pairwise.getValue(1, 1), 12d);
-    // Q.Q + I.I + A.A = 5 + 4 + 4 = 13
-    assertEquals(pairwise.getValue(2, 2), 13d);
-    // G.G + W.W + C.C = 6 + 11 + 9 = 26
-    assertEquals(pairwise.getValue(3, 3), 26d);
-    // F.R + K.- + L.D = -3 + -4 + -4 = -11
-    assertEquals(pairwise.getValue(0, 1), -11d);
-    // F.Q + K.I + L.A = -3 + -3 + -1 = -7
-    assertEquals(pairwise.getValue(0, 2), -7d);
-    // F.G + K.W + L.C = -3 + -3 + -1 = -7
-    assertEquals(pairwise.getValue(0, 3), -7d);
-    // R.Q + -.I + D.A = 1 + -4 + -2 = -5
-    assertEquals(pairwise.getValue(1, 2), -5d);
-    // R.G + -.W + D.C = -2 + -4 + -3 = -9
-    assertEquals(pairwise.getValue(1, 3), -9d);
-    // Q.G + I.W + A.C = -2 + -3 + 0 = -5
-    assertEquals(pairwise.getValue(2, 3), -5d);
-  }
-}
diff --git a/test/jalview/util/SetUtilsTest.java b/test/jalview/util/SetUtilsTest.java
new file mode 100644 (file)
index 0000000..ad17d4f
--- /dev/null
@@ -0,0 +1,46 @@
+package jalview.util;
+
+import static org.testng.Assert.assertEquals;
+
+import java.awt.Color;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.testng.annotations.Test;
+
+public class SetUtilsTest
+{
+  @Test(groups = "Functional")
+  public void testCountDisjunction()
+  {
+    Set<Color> s1 = new HashSet<Color>();
+    assertEquals(SetUtils.countDisjunction(null, null), 0);
+    assertEquals(SetUtils.countDisjunction(s1, null), 0);
+    assertEquals(SetUtils.countDisjunction(null, s1), 0);
+    s1.add(Color.white);
+    assertEquals(SetUtils.countDisjunction(s1, null), 1);
+    assertEquals(SetUtils.countDisjunction(null, s1), 1);
+    assertEquals(SetUtils.countDisjunction(s1, null), 1);
+    assertEquals(SetUtils.countDisjunction(s1, s1), 0);
+
+    Set<Object> s2 = new HashSet<Object>();
+    assertEquals(SetUtils.countDisjunction(s2, s2), 0);
+    assertEquals(SetUtils.countDisjunction(s1, s2), 1);
+    assertEquals(SetUtils.countDisjunction(s2, s1), 1);
+
+    s1.add(Color.yellow);
+    s1.add(Color.blue);
+    s2.add(new Color(Color.yellow.getRGB()));
+
+    /*
+     * now s1 is {white, yellow, blue}
+     *     s2 is {yellow'}
+     */
+    assertEquals(SetUtils.countDisjunction(s1, s2), 2);
+    s2.add(Color.blue);
+    assertEquals(SetUtils.countDisjunction(s1, s2), 1);
+    s2.add(Color.pink);
+    assertEquals(SetUtils.countDisjunction(s1, s2), 2);
+
+  }
+}