diff --git a/help/html/releases.html b/help/html/releases.html
index af417a7..0fba08a 100755
--- a/help/html/releases.html
+++ b/help/html/releases.html
@@ -71,25 +71,64 @@ li:before {
diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties
index ae5b0e7..43e055d 100644
--- a/resources/lang/Messages.properties
+++ b/resources/lang/Messages.properties
@@ -172,10 +172,9 @@ 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_calculation = Choose Calculation
-label.treecalc_title = {0} Using {1}
+label.calc_title = {0} Using {1}
label.tree_calc_av = Average Distance
label.tree_calc_nj = Neighbour Joining
-label.select_score_model = Select score model
label.score_model_pid = % Identity
label.score_model_blosum62 = BLOSUM62
label.score_model_pam250 = PAM 250
@@ -416,7 +415,7 @@ label.input_alignment_from_url = Input Alignment From URL
label.input_alignment = Input Alignment
label.couldnt_import_as_vamsas_session = Couldn't import {0} as a new vamsas session.
label.vamsas_document_import_failed = Vamsas Document Import Failed
-label.couldnt_locate = Couldn't locate {0}
+label.couldnt_locate = Could not locate {0}
label.url_not_found = URL not found
label.new_sequence_url_link = New sequence URL link
label.cannot_edit_annotations_in_wrapped_view = Cannot edit annotations in wrapped view
@@ -879,7 +878,6 @@ label.error_unsupported_owwner_user_colour_scheme = Unsupported owner for User C
label.save_alignment_to_file = Save Alignment to file
label.save_features_to_file = Save Features to File
label.save_annotation_to_file = Save Annotation to File
-label.no_features_on_alignment = No features found on alignment
label.save_pdb_file = Save PDB File
label.save_text_to_file = Save Text to File
label.save_state = Save State
@@ -1284,7 +1282,6 @@ label.SEQUENCE_ID_for_DB_ACCESSION1 = Please review your URL links in the 'Conne
label.SEQUENCE_ID_for_DB_ACCESSION2 = URL links using '$SEQUENCE_ID$' for DB accessions now use '$DB_ACCESSION$'.
label.do_not_display_again = Do not display this message again
exception.url_cannot_have_duplicate_id = {0} cannot be used as a label for more than one line
-label.filter = Filter text:
action.customfilter = Custom only
action.showall = Show All
label.insert = Insert:
@@ -1352,7 +1349,6 @@ label.colour_by_text = Colour by text
label.graduated_colour = Graduated Colour
label.by_text_of = By text of
label.by_range_of = By range of
-label.filters_tooltip = Click to set or amend filters
label.or = Or
label.and = And
label.sequence_feature_colours = Sequence Feature Colours
@@ -1363,3 +1359,5 @@ label.most_bound_molecules = Most Bound Molecules
label.most_polymer_residues = Most Polymer Residues
label.cached_structures = Cached Structures
label.free_text_search = Free Text Search
+label.configuration = Configuration
+label.configure_feature_tooltip = Click to configure variable colour or filters
diff --git a/resources/lang/Messages_es.properties b/resources/lang/Messages_es.properties
index 555977d..5aed0cb 100644
--- a/resources/lang/Messages_es.properties
+++ b/resources/lang/Messages_es.properties
@@ -169,10 +169,9 @@ label.principal_component_analysis = An
label.average_distance_identity = Distancia Media Usando % de Identidad
label.neighbour_joining_identity = Unir vecinos utilizando % de Identidad
label.choose_calculation = Elegir el cálculo
-label.treecalc_title = {0} utilizando {1}
+label.calc_title = {0} utilizando {1}
label.tree_calc_av = Distancia media
label.tree_calc_nj = Unir vecinos
-label.select_score_model = Selecciones modelo de puntuación
label.score_model_pid = % Identidad
label.score_model_blosum62 = BLOSUM62
label.score_model_pam250 = PAM 250
@@ -804,7 +803,6 @@ label.error_unsupported_owwner_user_colour_scheme = Propietario no soportado par
label.save_alignment_to_file = Guardar Alineamiento en fichero
label.save_features_to_file = Guardar Características en un fichero
label.save_annotation_to_file = Guardar Anotación en un fichero
-label.no_features_on_alignment = No se han encontrado características en el alineamiento
label.save_pdb_file = Guardar fichero PDB
label.save_text_to_file = Guardar Texto en un fichero
label.save_state = Guardar estado
@@ -1285,7 +1283,6 @@ label.SEQUENCE_ID_for_DB_ACCESSION1 = Por favor, revise sus URLs en la pesta
label.SEQUENCE_ID_for_DB_ACCESSION2 = URL enlaza usando '$SEQUENCE_ID$' para accesiones DB ahora usar '$DB_ACCESSION$'.
label.do_not_display_again = No mostrar este mensaje de nuevo
exception.url_cannot_have_duplicate_id = {0} no puede ser usada como etiqueta en más de un enlace
-label.filter = Filtrar texto:
action.customfilter = Sólo personalizado
action.showall = Mostrar todo
label.insert = Insertar:
@@ -1353,7 +1350,6 @@ label.colour_by_text = Colorear por texto
label.graduated_colour = Color graduado
label.by_text_of = Por texto de
label.by_range_of = Por rango de
-label.filters_tooltip = Haga clic para configurar o modificar los filtros
label.or = O
label.and = Y
label.sequence_feature_colours = Colores de características de las secuencias
@@ -1364,3 +1360,5 @@ label.most_bound_molecules = M
label.most_polymer_residues = Más Residuos de Polímeros
label.cached_structures = Estructuras en Caché
label.free_text_search = Búsqueda de texto libre
+label.configuration = Configuración
+label.configure_feature_tooltip = Haga clic para configurar el color o los filtros
diff --git a/resources/uniprot_mapping.xml b/resources/uniprot_mapping.xml
deleted file mode 100755
index 68868c4..0000000
--- a/resources/uniprot_mapping.xml
+++ /dev/null
@@ -1,104 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/schemas/JalviewUserColours.xsd b/schemas/JalviewUserColours.xsd
index 3934d66..50f0479 100755
--- a/schemas/JalviewUserColours.xsd
+++ b/schemas/JalviewUserColours.xsd
@@ -16,7 +16,13 @@
You should have received a copy of the GNU General Public License along with Jalview. If not, see .
-->
+
+
@@ -43,15 +49,7 @@
-
-
-
-
-
-
-
-
-
+
@@ -102,16 +100,32 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ Whether filter has a threshold, and if so is it
+ above or below
+
+
+
+
+
+
+
+
+
+
+
+ Filter may apply to label, score or some feature attribute
+
+
+
+
+
+
+
diff --git a/schemas/JalviewWsParamSet.xsd b/schemas/JalviewWsParamSet.xsd
index 443d19d..3a20002 100644
--- a/schemas/JalviewWsParamSet.xsd
+++ b/schemas/JalviewWsParamSet.xsd
@@ -17,8 +17,14 @@
* You should have received a copy of the GNU General Public License along with Jalview. If not, see .
* The Jalview Authors are detailed in the 'AUTHORS' file.
-->
-
+
+
+
+
+
+
+ Database cross-reference.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Not supported for EMBL-CDS.
+
+
+
+
+
+
+
+ Patent applicant.
+
+
+
+
+
+ The submission date (used only for submission
+ references).
+
+
+
+
+ The journal name (used only for article
+ references).
+
+
+
+
+ The publication year (used only for article
+ references).
+
+
+
+
+ The volume number (used only for article
+ references).
+
+
+
+
+ The issue number (used only for article
+ references).
+
+
+
+
+ The first page (used only for article
+ references).
+
+
+
+
+ The last page (used only for article
+ references).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Sequence feature.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Sequence feature qualifier.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Not supported for EMBL-CDS.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Not supported for EMBL-CDS.
+
+
+
+
+ Not supported for EMBL-CDS.
+
+
+
+
+ Not supported for EMBL-CDS.
+
+
+
+
+ Not supported for EMBL-CDS.
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/schemas/embl_bindings.xml b/schemas/embl_bindings.xml
new file mode 100644
index 0000000..0f6f669
--- /dev/null
+++ b/schemas/embl_bindings.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/schemas/jalview.xsd b/schemas/jalview.xsd
index 48824e7..07dee98 100755
--- a/schemas/jalview.xsd
+++ b/schemas/jalview.xsd
@@ -10,6 +10,11 @@
Public License for more details. * * You should have received a copy of the
GNU General Public License along with Jalview. If not, see .
* The Jalview Authors are detailed in the 'AUTHORS' file. -->
+
+
@@ -493,6 +499,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ endpoints of X, Y and Z axes in that order
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -631,5 +682,46 @@
-
+
+
+
+ parameters that condition a similarity score calculation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The results of a PCA calculation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/schemas/uniprot.xsd b/schemas/uniprot.xsd
new file mode 100644
index 0000000..1744d37
--- /dev/null
+++ b/schemas/uniprot.xsd
@@ -0,0 +1,1077 @@
+
+
+
+
+
+
+
+
+ Describes a collection of UniProtKB entries.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Describes a UniProtKB entry.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Describes the names for the protein and parts thereof.
+ Equivalent to the flat file DE-line.
+
+
+
+
+
+ Describes names of "domains".
+ Equivalent to the flat file DE-line Includes: section.
+
+
+
+
+
+
+
+ Describes names of processed products.
+ Equivalent to the flat file DE-line Contains: section.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Describes a gene.
+ Equivalent to the flat file GN-line.
+
+
+
+
+
+
+
+ Describes different types of gene designations.
+ Equivalent to the flat file GN-line.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Describes the source organism.
+
+
+
+
+ Describes the names of the source organism.
+ Equivalent to the flat file OS-line.
+
+
+
+
+ Describes a cross-reference to the NCBI taxonomy database.
+ Equivalent to the flat file OX-line.
+
+
+
+
+ Describes the lineage of the source organism.
+ Equivalent to the flat file OC-line.
+
+
+
+
+
+
+
+
+
+
+
+
+ Describes different types of source organism names.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Describes non-nuclear gene locations (organelles and plasmids).
+ Equivalent to the flat file OG-line.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Indicates whether the name of a plasmid is known or unknown.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Describes a citation and a summary of its content.
+ Equivalent to the flat file RN-, RP-, RC-, RX-, RG-, RA-, RT- and RL-lines.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Describes different types of citations.
+ Equivalent to the flat file RX-, RG-, RA-, RT- and RL-lines.
+
+
+
+
+ Describes the title of a citation.
+ Equivalent to the flat file RT-line.
+
+
+
+
+ Describes the editors of a book (only used for books).
+ Equivalent to part of the flat file RL-line of books.
+
+
+
+
+ Describes the authors of a citation.
+ Equivalent to the flat file RA-line.
+
+
+
+
+ Describes the location (URL) of an online journal article.
+ No flat file equivalent.
+
+
+
+
+ Describes cross-references to bibliography databases (MEDLINE, PubMed, AGRICOLA) or other online resources (DOI).
+ Equivalent to the flat file RX-line.
+
+
+
+
+
+ Describes the type of a citation.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Describes the name of an (online) journal or book.
+
+
+
+
+ Describes the volume of a journal or book.
+
+
+
+
+ Describes the first page of an article.
+
+
+
+
+ Describes the last page of an article.
+
+
+
+
+ Describes the publisher of a book.
+
+
+
+
+ Describes the city where a book was published.
+
+
+
+
+ Describes the database name of submissions.
+
+
+
+
+ Describes a patent number.
+
+
+
+
+ Describes the institute where a thesis was made.
+
+
+
+
+ Describes the country where a thesis was made.
+
+
+
+
+
+ Describes the authors of a citation when these are represented by a consortium.
+ Equivalent to the flat file RG-line.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Groups a citation's scope and source descriptions.
+
+
+
+
+ Describes the scope of a citation.
+ Equivalent to the flat file RP-line.
+
+
+
+
+
+
+
+ Describes the source of the sequence according to the citation.
+ Equivalent to the flat file RC-line.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Describes different types of general annotations.
+ Equivalent to the flat file CC-line.
+
+
+
+
+
+
+
+
+ Used in 'cofactor' annotations.
+
+
+
+
+
+
+ Used in 'subcellular location' annotations.
+
+
+
+
+
+
+ Used in 'sequence caution' annotations.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Refers to the 'key' attribute of a 'reference' element.
+
+
+
+
+
+
+
+
+ Used in 'online information' annotations.
+
+
+
+
+
+
+
+
+
+ Used in 'alternative products' annotations.
+
+
+
+
+
+
+
+ Used in 'interaction' annotations.
+
+
+
+
+
+
+
+
+ Used in 'disease' annotations.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Used in 'mass spectrometry' and 'sequence caution' annotations.
+
+
+
+
+
+
+
+
+
+ Describes the type of a general annotation.
+ Equivalent to the flat file CC comment topics (except for "DATABASE" which is translated to "online information").
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Describes the type of sequence location in 'RNA editing' annotations. Common values are "Not_applicable" and "Undetermined".
+
+
+
+
+
+ Describes an optional name for an 'online information'.
+
+
+
+
+
+ Describes the molecular mass in 'mass spectrometry' annotations.
+
+
+
+
+ Describes the error of the mass measurement in 'mass spectrometry' annotations.
+
+
+
+
+ Describes the experimental method in 'mass spectrometry' annotations.
+
+
+
+
+
+
+
+
+ Describes different types of biophysicochemical properties.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Describes a cofactor.
+
+
+
+
+
+
+
+
+
+
+ Describes the subcellular location and optionally the topology and orientation of a molecule.
+
+
+
+
+
+
+
+
+
+
+ Describes the type of events that cause alternative products.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Describes isoforms in 'alternative products' annotations.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Describes a database cross-reference.
+ Equivalent to the flat file DR-line.
+
+
+
+
+
+
+
+
+ Describes the name of the database.
+
+
+
+
+ Describes a unique database identifier.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Describes the evidence for the protein's existence.
+ Equivalent to the flat file PE-line.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Describes different types of sequence annotations.
+ Equivalent to the flat file FT-line.
+
+
+
+
+ Describes the original sequence in annotations that describe natural or artifical sequence variations.
+
+
+
+
+ Describes the variant sequence in annotations that describe natural or artifical sequence variations.
+
+
+
+
+ Describes the sequence coordinates of the annotation.
+
+
+
+
+
+ Describes the type of a sequence annotation.
+ Equivalent to the flat file FT feature keys, but using full terms instead of acronyms.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Describes a sequence location as either a range with a begin and end or as a position. The 'sequence' attribute is only used when the location is not on the canonical sequence displayed in the current entry.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Describes a molecule by name or unique identifier.
+
+
+
+
+
+
+
+
+
+
+
+
+ Describes the evidence for an annotation.
+ No flat file equivalent.
+
+
+
+
+
+
+
+ Describes the type of an evidence using the Evidence Code Ontology (http://www.obofoundry.org/cgi-bin/detail.cgi?id=evidence_code).
+
+
+
+
+ A unique key to link annotations (via 'evidence' attributes) to evidences.
+
+
+
+
+
+ Describes the source of the data using a database cross-reference (or a 'ref' attribute when the source cannot be found in a public data source, such as PubMed, and is cited only within the UniProtKB entry).
+
+
+
+
+
+
+
+
+ Describes the source of the evidence, when it is not assigned by UniProt, but imported from an external database.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/schemas/vamsas.xsd b/schemas/vamsas.xsd
index 65a1307..22b7aac 100755
--- a/schemas/vamsas.xsd
+++ b/schemas/vamsas.xsd
@@ -18,6 +18,11 @@
* The Jalview Authors are detailed in the 'AUTHORS' file.
-->
+
diff --git a/src/jalview/analysis/AlignmentUtils.java b/src/jalview/analysis/AlignmentUtils.java
index 0dfd383..0440b24 100644
--- a/src/jalview/analysis/AlignmentUtils.java
+++ b/src/jalview/analysis/AlignmentUtils.java
@@ -1107,7 +1107,7 @@ public class AlignmentUtils
SequenceI prot = mapping.findAlignedSequence(dnaSeq, protein);
if (prot != null)
{
- Mapping seqMap = mapping.getMappingForSequence(dnaSeq);
+ Mapping seqMap = mapping.getMappingForSequence(dnaSeq, false);
addCodonPositions(dnaSeq, prot, protein.getGapCharacter(), seqMap,
alignedCodons);
unmappedProtein.remove(prot);
@@ -2428,7 +2428,8 @@ public class AlignmentUtils
static int computePeptideVariants(SequenceI peptide, int peptidePos,
List[] codonVariants)
{
- String residue = String.valueOf(peptide.getCharAt(peptidePos - 1));
+ String residue = String
+ .valueOf(peptide.getCharAt(peptidePos - peptide.getStart()));
int count = 0;
String base1 = codonVariants[0].get(0).base;
String base2 = codonVariants[1].get(0).base;
diff --git a/src/jalview/analysis/PCA.java b/src/jalview/analysis/PCA.java
index 42a168d..d51f00e 100755
--- a/src/jalview/analysis/PCA.java
+++ b/src/jalview/analysis/PCA.java
@@ -22,7 +22,9 @@ package jalview.analysis;
import jalview.api.analysis.ScoreModelI;
import jalview.api.analysis.SimilarityParamsI;
+import jalview.bin.Cache;
import jalview.datamodel.AlignmentView;
+import jalview.datamodel.Point;
import jalview.math.MatrixI;
import java.io.PrintStream;
@@ -32,28 +34,37 @@ import java.io.PrintStream;
*/
public class PCA implements Runnable
{
- MatrixI symm;
-
- double[] eigenvalue;
+ /*
+ * inputs
+ */
+ final private AlignmentView seqs;
- MatrixI eigenvector;
+ final private ScoreModelI scoreModel;
- StringBuilder details = new StringBuilder(1024);
+ final private SimilarityParamsI similarityParams;
- final private AlignmentView seqs;
+ /*
+ * outputs
+ */
+ private MatrixI pairwiseScores;
- private ScoreModelI scoreModel;
+ private MatrixI tridiagonal;
- private SimilarityParamsI similarityParams;
+ private MatrixI eigenMatrix;
- public PCA(AlignmentView s, ScoreModelI sm, SimilarityParamsI options)
+ /**
+ * Constructor given the sequences to compute for, the similarity model to
+ * use, and a set of parameters for sequence comparison
+ *
+ * @param sequences
+ * @param sm
+ * @param options
+ */
+ public PCA(AlignmentView sequences, ScoreModelI sm, SimilarityParamsI options)
{
- this.seqs = s;
- this.similarityParams = options;
+ this.seqs = sequences;
this.scoreModel = sm;
-
- details.append("PCA calculation using " + sm.getName()
- + " sequence similarity matrix\n========\n\n");
+ this.similarityParams = options;
}
/**
@@ -66,7 +77,7 @@ public class PCA implements Runnable
*/
public double getEigenvalue(int i)
{
- return eigenvector.getD()[i];
+ return eigenMatrix.getD()[i];
}
/**
@@ -83,15 +94,16 @@ public class PCA implements Runnable
*
* @return DOCUMENT ME!
*/
- public float[][] getComponents(int l, int n, int mm, float factor)
+ public Point[] getComponents(int l, int n, int mm, float factor)
{
- float[][] out = new float[getHeight()][3];
+ Point[] out = new Point[getHeight()];
for (int i = 0; i < getHeight(); i++)
{
- out[i][0] = (float) component(i, l) * factor;
- out[i][1] = (float) component(i, n) * factor;
- out[i][2] = (float) component(i, mm) * factor;
+ float x = (float) component(i, l) * factor;
+ float y = (float) component(i, n) * factor;
+ float z = (float) component(i, mm) * factor;
+ out[i] = new Point(x, y, z);
}
return out;
@@ -132,84 +144,111 @@ public class PCA implements Runnable
{
double out = 0.0;
- for (int i = 0; i < symm.width(); i++)
+ for (int i = 0; i < pairwiseScores.width(); i++)
{
- out += (symm.getValue(row, i) * eigenvector.getValue(i, n));
+ out += (pairwiseScores.getValue(row, i) * eigenMatrix.getValue(i, n));
}
- return out / eigenvector.getD()[n];
+ return out / eigenMatrix.getD()[n];
}
+ /**
+ * Answers a formatted text report of the PCA calculation results (matrices
+ * and eigenvalues) suitable for display
+ *
+ * @return
+ */
public String getDetails()
{
- return details.toString();
+ StringBuilder sb = new StringBuilder(1024);
+ sb.append("PCA calculation using ").append(scoreModel.getName())
+ .append(" sequence similarity matrix\n========\n\n");
+ PrintStream ps = wrapOutputBuffer(sb);
+
+ /*
+ * pairwise similarity scores
+ */
+ sb.append(" --- OrigT * Orig ---- \n");
+ pairwiseScores.print(ps, "%8.2f");
+
+ /*
+ * tridiagonal matrix, with D and E vectors
+ */
+ sb.append(" ---Tridiag transform matrix ---\n");
+ sb.append(" --- D vector ---\n");
+ tridiagonal.printD(ps, "%15.4e");
+ ps.println();
+ sb.append("--- E vector ---\n");
+ tridiagonal.printE(ps, "%15.4e");
+ ps.println();
+
+ /*
+ * eigenvalues matrix, with D vector
+ */
+ sb.append(" --- New diagonalization matrix ---\n");
+ eigenMatrix.print(ps, "%8.2f");
+ sb.append(" --- Eigenvalues ---\n");
+ eigenMatrix.printD(ps, "%15.4e");
+ ps.println();
+
+ return sb.toString();
}
/**
- * DOCUMENT ME!
+ * Performs the PCA calculation
*/
@Override
public void run()
{
+ try
+ {
+ /*
+ * sequence pairwise similarity scores
+ */
+ pairwiseScores = scoreModel.findSimilarities(seqs, similarityParams);
+
+ /*
+ * tridiagonal matrix
+ */
+ tridiagonal = pairwiseScores.copy();
+ tridiagonal.tred();
+
+ /*
+ * the diagonalization matrix
+ */
+ eigenMatrix = tridiagonal.copy();
+ eigenMatrix.tqli();
+ } catch (Exception q)
+ {
+ Cache.log.error("Error computing PCA: " + q.getMessage());
+ q.printStackTrace();
+ }
+ }
+
+ /**
+ * Returns a PrintStream that wraps (appends its output to) the given
+ * StringBuilder
+ *
+ * @param sb
+ * @return
+ */
+ protected PrintStream wrapOutputBuffer(StringBuilder sb)
+ {
PrintStream ps = new PrintStream(System.out)
{
@Override
public void print(String x)
{
- details.append(x);
+ sb.append(x);
}
@Override
public void println()
{
- details.append("\n");
+ sb.append("\n");
}
};
-
- // long now = System.currentTimeMillis();
- try
- {
- eigenvector = scoreModel.findSimilarities(seqs, similarityParams);
-
- details.append(" --- OrigT * Orig ---- \n");
- eigenvector.print(ps, "%8.2f");
-
- symm = eigenvector.copy();
-
- eigenvector.tred();
-
- details.append(" ---Tridiag transform matrix ---\n");
- details.append(" --- D vector ---\n");
- eigenvector.printD(ps, "%15.4e");
- ps.println();
- details.append("--- E vector ---\n");
- eigenvector.printE(ps, "%15.4e");
- ps.println();
-
- // Now produce the diagonalization matrix
- eigenvector.tqli();
- } catch (Exception q)
- {
- q.printStackTrace();
- details.append("\n*** Unexpected exception when performing PCA ***\n"
- + q.getLocalizedMessage());
- details.append(
- "*** Matrices below may not be fully diagonalised. ***\n");
- }
-
- details.append(" --- New diagonalization matrix ---\n");
- eigenvector.print(ps, "%8.2f");
- details.append(" --- Eigenvalues ---\n");
- eigenvector.printD(ps, "%15.4e");
- ps.println();
- /*
- * for (int seq=0;seq> findFeatureTypesAtColumn(
SeqCigar[] seqs, int columnPosition)
{
- Map> sfap = new HashMap>();
+ Map> sfap = new HashMap<>();
for (SeqCigar seq : seqs)
{
int spos = seq.findPosition(columnPosition);
@@ -197,9 +197,9 @@ public class FeatureDistanceModel extends DistanceScoreModel
/*
* position is not a gap
*/
- Set types = new HashSet();
+ Set types = new HashSet<>();
List sfs = fr.findFeaturesAtResidue(
- seq.getRefSeq(), spos);
+ seq.getRefSeq(), spos, spos);
for (SequenceFeature sf : sfs)
{
types.add(sf.getType());
diff --git a/src/jalview/analysis/scoremodels/ScoreModels.java b/src/jalview/analysis/scoremodels/ScoreModels.java
index 7262fb8..3cbd5f1 100644
--- a/src/jalview/analysis/scoremodels/ScoreModels.java
+++ b/src/jalview/analysis/scoremodels/ScoreModels.java
@@ -66,11 +66,11 @@ public class ScoreModels
/*
* using LinkedHashMap keeps models ordered as added
*/
- models = new LinkedHashMap();
+ models = new LinkedHashMap<>();
BLOSUM62 = loadScoreMatrix("scoreModel/blosum62.scm");
PAM250 = loadScoreMatrix("scoreModel/pam250.scm");
- registerScoreModel(new PIDModel());
DNA = loadScoreMatrix("scoreModel/dna.scm");
+ registerScoreModel(new PIDModel());
registerScoreModel(new FeatureDistanceModel());
}
diff --git a/src/jalview/analysis/scoremodels/SimilarityParams.java b/src/jalview/analysis/scoremodels/SimilarityParams.java
index 58b08dd..5c47703 100644
--- a/src/jalview/analysis/scoremodels/SimilarityParams.java
+++ b/src/jalview/analysis/scoremodels/SimilarityParams.java
@@ -147,4 +147,57 @@ public class SimilarityParams implements SimilarityParamsI
{
return matchGaps;
}
+
+ /**
+ * IDE-generated hashCode method
+ */
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (denominateByShortestLength ? 1231 : 1237);
+ result = prime * result + (includeGappedColumns ? 1231 : 1237);
+ result = prime * result + (includeGaps ? 1231 : 1237);
+ result = prime * result + (matchGaps ? 1231 : 1237);
+ return result;
+ }
+
+ /**
+ * IDE-generated equals method
+ */
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ SimilarityParams other = (SimilarityParams) obj;
+ if (denominateByShortestLength != other.denominateByShortestLength)
+ {
+ return false;
+ }
+ if (includeGappedColumns != other.includeGappedColumns)
+ {
+ return false;
+ }
+ if (includeGaps != other.includeGaps)
+ {
+ return false;
+ }
+ if (matchGaps != other.matchGaps)
+ {
+ return false;
+ }
+ return true;
+ }
}
diff --git a/src/jalview/api/FeatureRenderer.java b/src/jalview/api/FeatureRenderer.java
index cf3c8da..59e3e61 100644
--- a/src/jalview/api/FeatureRenderer.java
+++ b/src/jalview/api/FeatureRenderer.java
@@ -161,15 +161,18 @@ public interface FeatureRenderer
List findFeaturesAtColumn(SequenceI sequence, int column);
/**
- * Returns features at the specified residue position on the given sequence.
- * Non-positional features are not included.
+ * Returns features at the specified residue positions on the given sequence.
+ * Non-positional features are not included. Features are returned in render
+ * order of their feature type (last is on top). Within feature type, ordering
+ * is undefined.
*
* @param sequence
- * @param resNo
- * residue position (start..)
+ * @param fromResNo
+ * @param toResNo
* @return
*/
- List findFeaturesAtResidue(SequenceI sequence, int resNo);
+ List findFeaturesAtResidue(SequenceI sequence,
+ int fromResNo, int toResNo);
/**
* get current displayed types, in ordering of rendering (on top last)
@@ -255,11 +258,11 @@ public interface FeatureRenderer
*
* Returns null if
*
- * - feature type is not visible, or
* - feature group is not visible, or
* - feature values lie outside any colour threshold, or
* - feature is excluded by filter conditions
*
+ * This method does not check feature type visibility.
*
* @param feature
* @return
diff --git a/src/jalview/api/RotatableCanvasI.java b/src/jalview/api/RotatableCanvasI.java
index a57bcdb..c6eb6de 100644
--- a/src/jalview/api/RotatableCanvasI.java
+++ b/src/jalview/api/RotatableCanvasI.java
@@ -1,6 +1,6 @@
/*
- * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
- * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2b1)
+ * Copyright (C) 2014 The Jalview Authors
*
* This file is part of Jalview.
*
@@ -22,7 +22,7 @@ package jalview.api;
import jalview.datamodel.SequencePoint;
-import java.util.Vector;
+import java.util.List;
/**
* interface implemented by RotatatableCanvas GUI elements (such as point clouds
@@ -33,7 +33,23 @@ import java.util.Vector;
*/
public interface RotatableCanvasI
{
+ void setPoints(List points, int rows);
- void setPoints(Vector points, int rows);
+ /**
+ * Zoom the view in (or out) by the given factor, which should be >= 0. A
+ * factor greater than 1 zooms in (expands the display), a factor less than 1
+ * zooms out (shrinks the display).
+ *
+ * @param factor
+ */
+ void zoom(float factor);
+ /**
+ * Rotates the view by the specified number of degrees about the x and/or y
+ * axis
+ *
+ * @param x
+ * @param y
+ */
+ void rotate(float x, float y);
}
diff --git a/src/jalview/api/ViewStyleI.java b/src/jalview/api/ViewStyleI.java
index 2b554ea..a348300 100644
--- a/src/jalview/api/ViewStyleI.java
+++ b/src/jalview/api/ViewStyleI.java
@@ -24,6 +24,13 @@ import java.awt.Color;
public interface ViewStyleI
{
+ void setShowComplementFeatures(boolean b);
+
+ boolean isShowComplementFeatures();
+
+ void setShowComplementFeaturesOnTop(boolean b);
+
+ boolean isShowComplementFeaturesOnTop();
void setColourAppliesToAllGroups(boolean b);
diff --git a/src/jalview/appletgui/AppletJmolBinding.java b/src/jalview/appletgui/AppletJmolBinding.java
index 2f61b24..e5767b6 100644
--- a/src/jalview/appletgui/AppletJmolBinding.java
+++ b/src/jalview/appletgui/AppletJmolBinding.java
@@ -24,7 +24,6 @@ import jalview.api.AlignmentViewPanel;
import jalview.datamodel.PDBEntry;
import jalview.datamodel.SequenceI;
import jalview.ext.jmol.JalviewJmolBinding;
-import jalview.gui.IProgressIndicator;
import jalview.io.DataSourceType;
import jalview.structure.StructureSelectionManager;
@@ -174,21 +173,12 @@ class AppletJmolBinding extends JalviewJmolBinding
@Override
public int[] resizeInnerPanel(String data)
{
- // TODO Auto-generated method stub
return null;
}
@Override
public Map getJSpecViewProperty(String arg0)
{
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- protected IProgressIndicator getIProgressIndicator()
- {
- // no progress indicators on the applet
return null;
}
}
diff --git a/src/jalview/appletgui/ExtJmol.java b/src/jalview/appletgui/ExtJmol.java
index 89228d5..b0d3f7a 100644
--- a/src/jalview/appletgui/ExtJmol.java
+++ b/src/jalview/appletgui/ExtJmol.java
@@ -26,7 +26,6 @@ import jalview.api.SequenceRenderer;
import jalview.datamodel.PDBEntry;
import jalview.datamodel.SequenceI;
import jalview.ext.jmol.JalviewJmolBinding;
-import jalview.gui.IProgressIndicator;
import jalview.io.DataSourceType;
import java.awt.Container;
@@ -66,18 +65,8 @@ public class ExtJmol extends JalviewJmolBinding
}
@Override
- protected IProgressIndicator getIProgressIndicator()
- {
- // no progress indicators on applet (could access javascript for this)
- return null;
- }
-
- @Override
public void updateColours(Object source)
{
-
- // TODO Auto-generated method stub
-
}
@Override
@@ -190,7 +179,6 @@ public class ExtJmol extends JalviewJmolBinding
protected JmolAppConsoleInterface createJmolConsole(
Container consolePanel, String buttonsToShow)
{
- // TODO Auto-generated method stub
return null;
}
@@ -205,14 +193,11 @@ public class ExtJmol extends JalviewJmolBinding
@Override
public void releaseReferences(Object svl)
{
- // TODO Auto-generated method stub
-
}
@Override
public Map getJSpecViewProperty(String arg0)
{
- // TODO Auto-generated method stub
return null;
}
diff --git a/src/jalview/appletgui/OverviewCanvas.java b/src/jalview/appletgui/OverviewCanvas.java
index e99c021..07f5919 100644
--- a/src/jalview/appletgui/OverviewCanvas.java
+++ b/src/jalview/appletgui/OverviewCanvas.java
@@ -162,4 +162,12 @@ public class OverviewCanvas extends Component
}
}
+ /**
+ * Nulls references to protect against potential memory leaks
+ */
+ void dispose()
+ {
+ od = null;
+ }
+
}
diff --git a/src/jalview/appletgui/OverviewPanel.java b/src/jalview/appletgui/OverviewPanel.java
index 3bbbe95..5081509 100755
--- a/src/jalview/appletgui/OverviewPanel.java
+++ b/src/jalview/appletgui/OverviewPanel.java
@@ -334,6 +334,10 @@ public class OverviewPanel extends Panel implements Runnable,
} finally
{
av = null;
+ if (oviewCanvas != null)
+ {
+ oviewCanvas.dispose();
+ }
oviewCanvas = null;
ap = null;
od = null;
diff --git a/src/jalview/appletgui/PCAPanel.java b/src/jalview/appletgui/PCAPanel.java
index fc1d359..7c0dfa9 100644
--- a/src/jalview/appletgui/PCAPanel.java
+++ b/src/jalview/appletgui/PCAPanel.java
@@ -134,7 +134,7 @@ public class PCAPanel extends EmbmenuFrame
{
nuclSetting.setState(pcaModel.isNucleotide());
protSetting.setState(!pcaModel.isNucleotide());
- pcaModel.run();
+ pcaModel.calculate();
// ////////////////
xCombobox.select(0);
yCombobox.select(1);
@@ -167,9 +167,7 @@ public class PCAPanel extends EmbmenuFrame
int dim2 = top - yCombobox.getSelectedIndex();
int dim3 = top - zCombobox.getSelectedIndex();
pcaModel.updateRcView(dim1, dim2, dim3);
- rc.img = null;
- rc.rotmat.setIdentity();
- rc.initAxes();
+ rc.resetView();
rc.paint(rc.getGraphics());
}
@@ -281,7 +279,7 @@ public class PCAPanel extends EmbmenuFrame
{
}
;
- Object[] alAndColsel = pcaModel.getSeqtrings()
+ Object[] alAndColsel = pcaModel.getInputData()
.getAlignmentAndHiddenColumns(gc);
if (alAndColsel != null && alAndColsel[0] != null)
diff --git a/src/jalview/appletgui/RotatableCanvas.java b/src/jalview/appletgui/RotatableCanvas.java
index afb4e95..34f8ea5 100755
--- a/src/jalview/appletgui/RotatableCanvas.java
+++ b/src/jalview/appletgui/RotatableCanvas.java
@@ -21,11 +21,12 @@
package jalview.appletgui;
import jalview.api.RotatableCanvasI;
+import jalview.datamodel.Point;
import jalview.datamodel.SequenceGroup;
import jalview.datamodel.SequenceI;
import jalview.datamodel.SequencePoint;
import jalview.math.RotatableMatrix;
-import jalview.util.Format;
+import jalview.math.RotatableMatrix.Axis;
import jalview.util.MessageManager;
import jalview.viewmodel.AlignmentViewport;
@@ -40,32 +41,26 @@ import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
-import java.util.Vector;
+import java.util.List;
public class RotatableCanvas extends Panel implements MouseListener,
MouseMotionListener, KeyListener, RotatableCanvasI
{
- RotatableMatrix idmat = new RotatableMatrix(3, 3);
-
- RotatableMatrix objmat = new RotatableMatrix(3, 3);
-
- RotatableMatrix rotmat = new RotatableMatrix(3, 3);
+ private static final int DIMS = 3;
String tooltip;
- int toolx, tooly;
+ int toolx;
+
+ int tooly;
// RubberbandRectangle rubberband;
boolean drawAxes = true;
- int omx = 0;
-
- int mx = 0;
-
- int omy = 0;
+ int mouseX = 0;
- int my = 0;
+ int mouseY = 0;
Image img;
@@ -73,13 +68,13 @@ public class RotatableCanvas extends Panel implements MouseListener,
Dimension prefsize;
- float centre[] = new float[3];
+ Point centre;
- float width[] = new float[3];
+ float[] width = new float[DIMS];
- float max[] = new float[3];
+ float[] max = new float[DIMS];
- float min[] = new float[3];
+ float[] min = new float[DIMS];
float maxwidth;
@@ -87,11 +82,11 @@ public class RotatableCanvas extends Panel implements MouseListener,
int npoint;
- Vector points;
+ List points;
- float[][] orig;
+ Point[] orig;
- float[][] axes;
+ Point[] axisEndPoints;
int startx;
@@ -115,9 +110,10 @@ public class RotatableCanvas extends Panel implements MouseListener,
boolean showLabels = false;
- public RotatableCanvas(AlignmentViewport av)
+ public RotatableCanvas(AlignmentViewport viewport)
{
- this.av = av;
+ this.av = viewport;
+ axisEndPoints = new Point[DIMS];
}
public void showLabels(boolean b)
@@ -126,46 +122,23 @@ public class RotatableCanvas extends Panel implements MouseListener,
repaint();
}
- public void setPoints(Vector points, int npoint)
+ @Override
+ public void setPoints(List points, int npoint)
{
this.points = points;
this.npoint = npoint;
PaintRefresher.Register(this, av.getSequenceSetId());
prefsize = getPreferredSize();
- orig = new float[npoint][3];
+ orig = new Point[npoint];
for (int i = 0; i < npoint; i++)
{
- SequencePoint sp = (SequencePoint) points.elementAt(i);
- for (int j = 0; j < 3; j++)
- {
- orig[i][j] = sp.coord[j];
- }
- }
- // Initialize the matrices to identity
-
- for (int i = 0; i < 3; i++)
- {
- for (int j = 0; j < 3; j++)
- {
- if (i != j)
- {
- idmat.addElement(i, j, 0);
- objmat.addElement(i, j, 0);
- rotmat.addElement(i, j, 0);
- }
- else
- {
- idmat.addElement(i, j, 0);
- objmat.addElement(i, j, 0);
- rotmat.addElement(i, j, 0);
- }
- }
+ SequencePoint sp = points.get(i);
+ orig[i] = sp.coord;
}
- axes = new float[3][3];
- initAxes();
+ resetAxes();
findCentre();
findWidth();
@@ -195,115 +168,93 @@ public class RotatableCanvas extends Panel implements MouseListener,
* super.removeNotify(); }
*/
- public void initAxes()
+ /**
+ * Resets axes to the initial state: x-axis to the right, y-axis up, z-axis to
+ * back (so obscured in a 2-D display)
+ */
+ public void resetAxes()
{
- for (int i = 0; i < 3; i++)
- {
- for (int j = 0; j < 3; j++)
- {
- if (i != j)
- {
- axes[i][j] = 0;
- }
- else
- {
- axes[i][j] = 1;
- }
- }
- }
+ axisEndPoints[0] = new Point(1f, 0f, 0f);
+ axisEndPoints[1] = new Point(0f, 1f, 0f);
+ axisEndPoints[2] = new Point(0f, 0f, 1f);
}
+ /**
+ * Computes and saves the maximum and minimum (x, y, z) positions of any
+ * sequence point, and also the min-max range (width) for each dimension, and
+ * the maximum width for all dimensions
+ */
public void findWidth()
{
max = new float[3];
min = new float[3];
- max[0] = (float) -1e30;
- max[1] = (float) -1e30;
- max[2] = (float) -1e30;
+ max[0] = Float.MIN_VALUE;
+ max[1] = Float.MIN_VALUE;
+ max[2] = Float.MIN_VALUE;
- min[0] = (float) 1e30;
- min[1] = (float) 1e30;
- min[2] = (float) 1e30;
+ min[0] = Float.MAX_VALUE;
+ min[1] = Float.MAX_VALUE;
+ min[2] = Float.MAX_VALUE;
- for (int i = 0; i < 3; i++)
+ for (SequencePoint sp : points)
{
- for (int j = 0; j < npoint; j++)
- {
- SequencePoint sp = (SequencePoint) points.elementAt(j);
- if (sp.coord[i] >= max[i])
- {
- max[i] = sp.coord[i];
- }
- if (sp.coord[i] <= min[i])
- {
- min[i] = sp.coord[i];
- }
- }
+ max[0] = Math.max(max[0], sp.coord.x);
+ max[1] = Math.max(max[1], sp.coord.y);
+ max[2] = Math.max(max[2], sp.coord.z);
+ min[0] = Math.min(min[0], sp.coord.x);
+ min[1] = Math.min(min[1], sp.coord.y);
+ min[2] = Math.min(min[2], sp.coord.z);
}
- // System.out.println("xmax " + max[0] + " min " + min[0]);
- // System.out.println("ymax " + max[1] + " min " + min[1]);
- // System.out.println("zmax " + max[2] + " min " + min[2]);
-
width[0] = Math.abs(max[0] - min[0]);
width[1] = Math.abs(max[1] - min[1]);
width[2] = Math.abs(max[2] - min[2]);
- maxwidth = width[0];
-
- if (width[1] > width[0])
- {
- maxwidth = width[1];
- }
- if (width[2] > width[1])
- {
- maxwidth = width[2];
- }
-
- // System.out.println("Maxwidth = " + maxwidth);
+ maxwidth = Math.max(width[0], Math.max(width[1], width[2]));
}
public float findScale()
{
- int dim, width, height;
+ int dim, w, height;
if (getSize().width != 0)
{
- width = getSize().width;
+ w = getSize().width;
height = getSize().height;
}
else
{
- width = prefsize.width;
+ w = prefsize.width;
height = prefsize.height;
}
- if (width < height)
+ if (w < height)
{
- dim = width;
+ dim = w;
}
else
{
dim = height;
}
- return (float) (dim * scalefactor / (2 * maxwidth));
+ return dim * scalefactor / (2 * maxwidth);
}
+ /**
+ * Computes and saves the position of the centre of the view
+ */
public void findCentre()
{
- // Find centre coordinate
findWidth();
- centre[0] = (max[0] + min[0]) / 2;
- centre[1] = (max[1] + min[1]) / 2;
- centre[2] = (max[2] + min[2]) / 2;
+ float x = (max[0] + min[0]) / 2;
+ float y = (max[1] + min[1]) / 2;
+ float z = (max[2] + min[2]) / 2;
- // System.out.println("Centre x " + centre[0]);
- // System.out.println("Centre y " + centre[1]);
- // System.out.println("Centre z " + centre[2]);
+ centre = new Point(x, y, z);
}
+ @Override
public Dimension getPreferredSize()
{
if (prefsize != null)
@@ -316,16 +267,19 @@ public class RotatableCanvas extends Panel implements MouseListener,
}
}
+ @Override
public Dimension getMinimumSize()
{
return getPreferredSize();
}
+ @Override
public void update(Graphics g)
{
paint(g);
}
+ @Override
public void paint(Graphics g)
{
if (points == null)
@@ -355,7 +309,7 @@ public class RotatableCanvas extends Panel implements MouseListener,
drawBackground(ig, Color.black);
drawScene(ig);
- if (drawAxes == true)
+ if (drawAxes)
{
drawAxes(ig);
}
@@ -377,8 +331,8 @@ public class RotatableCanvas extends Panel implements MouseListener,
for (int i = 0; i < 3; i++)
{
g.drawLine(getSize().width / 2, getSize().height / 2,
- (int) (axes[i][0] * scale * max[0] + getSize().width / 2),
- (int) (axes[i][1] * scale * max[1] + getSize().height / 2));
+ (int) (axisEndPoints[i].x * scale * max[0] + getSize().width / 2),
+ (int) (axisEndPoints[i].y * scale * max[1] + getSize().height / 2));
}
}
@@ -390,81 +344,85 @@ public class RotatableCanvas extends Panel implements MouseListener,
public void drawScene(Graphics g)
{
- // boolean darker = false;
-
- int halfwidth = getSize().width / 2;
- int halfheight = getSize().height / 2;
-
for (int i = 0; i < npoint; i++)
{
- SequencePoint sp = (SequencePoint) points.elementAt(i);
- int x = (int) ((float) (sp.coord[0] - centre[0]) * scale) + halfwidth;
- int y = (int) ((float) (sp.coord[1] - centre[1]) * scale)
- + halfheight;
- float z = sp.coord[1] - centre[2];
-
- if (av.getSequenceColour(sp.sequence) == Color.black)
- {
- g.setColor(Color.white);
- }
- else
- {
- g.setColor(av.getSequenceColour(sp.sequence));
- }
-
+ SequencePoint sp = points.get(i);
+ SequenceI sequence = sp.getSequence();
+ Color sequenceColour = av.getSequenceColour(sequence);
+ g.setColor(
+ sequenceColour == Color.black ? Color.white : sequenceColour);
if (av.getSelectionGroup() != null)
{
if (av.getSelectionGroup().getSequences(null)
- .contains(((SequencePoint) points.elementAt(i)).sequence))
+ .contains(sequence))
{
g.setColor(Color.gray);
}
}
- if (z < 0)
+
+ if (sp.coord.z < centre.z)
{
g.setColor(g.getColor().darker());
}
+ int halfwidth = getSize().width / 2;
+ int halfheight = getSize().height / 2;
+ int x = (int) ((sp.coord.x - centre.x) * scale) + halfwidth;
+ int y = (int) ((sp.coord.y - centre.y) * scale) + halfheight;
g.fillRect(x - 3, y - 3, 6, 6);
+
if (showLabels)
{
g.setColor(Color.red);
- g.drawString(
- ((SequencePoint) points.elementAt(i)).sequence.getName(),
- x - 3, y - 4);
+ g.drawString(sequence.getName(), x - 3, y - 4);
}
}
}
- public Dimension minimumsize()
- {
- return prefsize;
- }
-
- public Dimension preferredsize()
- {
- return prefsize;
- }
-
+ @Override
public void keyTyped(KeyEvent evt)
{
}
+ @Override
public void keyReleased(KeyEvent evt)
{
}
+ @Override
public void keyPressed(KeyEvent evt)
{
- if (evt.getKeyCode() == KeyEvent.VK_UP)
+ boolean shiftDown = evt.isShiftDown();
+ int keyCode = evt.getKeyCode();
+ if (keyCode == KeyEvent.VK_UP)
+ {
+ if (shiftDown)
+ {
+ rotate(0f, -1f);
+ }
+ else
+ {
+ zoom(1.1f);
+ }
+ }
+ else if (keyCode == KeyEvent.VK_DOWN)
{
- scalefactor = (float) (scalefactor * 1.1);
- scale = findScale();
+ if (shiftDown)
+ {
+ rotate(0f, 1f);
+ }
+ else
+ {
+ zoom(0.9f);
+ }
}
- else if (evt.getKeyCode() == KeyEvent.VK_DOWN)
+ else if (shiftDown && keyCode == KeyEvent.VK_LEFT)
{
- scalefactor = (float) (scalefactor * 0.9);
- scale = findScale();
+ rotate(1f, 0f);
+ }
+ else if (shiftDown && keyCode == KeyEvent.VK_RIGHT)
+ {
+ rotate(-1f, 0f);
}
else if (evt.getKeyChar() == 's')
{
@@ -478,46 +436,34 @@ public class RotatableCanvas extends Panel implements MouseListener,
repaint();
}
- public void printPoints()
- {
- for (int i = 0; i < npoint; i++)
- {
- SequencePoint sp = (SequencePoint) points.elementAt(i);
- Format.print(System.out, "%5d ", i);
- for (int j = 0; j < 3; j++)
- {
- Format.print(System.out, "%13.3f ", sp.coord[j]);
- }
- System.out.println();
- }
- }
-
+ @Override
public void mouseClicked(MouseEvent evt)
{
}
+ @Override
public void mouseEntered(MouseEvent evt)
{
}
+ @Override
public void mouseExited(MouseEvent evt)
{
}
+ @Override
public void mouseReleased(MouseEvent evt)
{
}
+ @Override
public void mousePressed(MouseEvent evt)
{
int x = evt.getX();
int y = evt.getY();
- mx = x;
- my = y;
-
- omx = mx;
- omy = my;
+ mouseX = x;
+ mouseY = y;
startx = x;
starty = y;
@@ -528,7 +474,7 @@ public class RotatableCanvas extends Panel implements MouseListener,
rectx2 = -1;
recty2 = -1;
- SequenceI found = findPoint(x, y);
+ SequenceI found = findSequenceAtPoint(x, y);
if (found != null)
{
@@ -552,9 +498,10 @@ public class RotatableCanvas extends Panel implements MouseListener,
repaint();
}
+ @Override
public void mouseMoved(MouseEvent evt)
{
- SequenceI found = findPoint(evt.getX(), evt.getY());
+ SequenceI found = findSequenceAtPoint(evt.getX(), evt.getY());
if (found == null)
{
tooltip = null;
@@ -568,40 +515,22 @@ public class RotatableCanvas extends Panel implements MouseListener,
repaint();
}
+ @Override
public void mouseDragged(MouseEvent evt)
{
- mx = evt.getX();
- my = evt.getY();
-
- rotmat.setIdentity();
+ int xPos = evt.getX();
+ int yPos = evt.getY();
- rotmat.rotate((float) (my - omy), 'x');
- rotmat.rotate((float) (mx - omx), 'y');
-
- for (int i = 0; i < npoint; i++)
+ if (xPos == mouseX && yPos == mouseY)
{
- SequencePoint sp = (SequencePoint) points.elementAt(i);
- sp.coord[0] -= centre[0];
- sp.coord[1] -= centre[1];
- sp.coord[2] -= centre[2];
-
- // Now apply the rotation matrix
- sp.coord = rotmat.vectorMultiply(sp.coord);
-
- // Now translate back again
- sp.coord[0] += centre[0];
- sp.coord[1] += centre[1];
- sp.coord[2] += centre[2];
+ return;
}
- for (int i = 0; i < 3; i++)
- {
- axes[i] = rotmat.vectorMultiply(axes[i]);
- }
- omx = mx;
- omy = my;
+ int xDelta = xPos - mouseX;
+ int yDelta = yPos - mouseY;
- paint(this.getGraphics());
+ rotate(xDelta, yDelta);
+ repaint();
}
public void rectSelect(int x1, int y1, int x2, int y2)
@@ -609,29 +538,38 @@ public class RotatableCanvas extends Panel implements MouseListener,
// boolean changedSel = false;
for (int i = 0; i < npoint; i++)
{
- SequencePoint sp = (SequencePoint) points.elementAt(i);
- int tmp1 = (int) ((sp.coord[0] - centre[0]) * scale
- + (float) getSize().width / 2.0);
- int tmp2 = (int) ((sp.coord[1] - centre[1]) * scale
- + (float) getSize().height / 2.0);
+ SequencePoint sp = points.get(i);
+ int tmp1 = (int) ((sp.coord.x - centre.x) * scale
+ + getSize().width / 2.0);
+ int tmp2 = (int) ((sp.coord.y - centre.y) * scale
+ + getSize().height / 2.0);
+ SequenceI sequence = sp.getSequence();
if (tmp1 > x1 && tmp1 < x2 && tmp2 > y1 && tmp2 < y2)
{
if (av != null)
{
if (!av.getSelectionGroup().getSequences(null)
- .contains(sp.sequence))
+ .contains(sequence))
{
- av.getSelectionGroup().addSequence(sp.sequence, true);
+ av.getSelectionGroup().addSequence(sequence, true);
}
}
}
}
}
- public SequenceI findPoint(int x, int y)
+ /**
+ * Answers the first sequence found whose point on the display is within 2
+ * pixels of the given coordinates, or null if none is found
+ *
+ * @param x
+ * @param y
+ *
+ * @return
+ */
+ public SequenceI findSequenceAtPoint(int x, int y)
{
-
int halfwidth = getSize().width / 2;
int halfheight = getSize().height / 2;
@@ -640,20 +578,22 @@ public class RotatableCanvas extends Panel implements MouseListener,
for (int i = 0; i < npoint; i++)
{
- SequencePoint sp = (SequencePoint) points.elementAt(i);
- int px = (int) ((float) (sp.coord[0] - centre[0]) * scale)
+ SequencePoint sp = points.get(i);
+ int px = (int) ((sp.coord.x - centre.x) * scale)
+ halfwidth;
- int py = (int) ((float) (sp.coord[1] - centre[1]) * scale)
+ int py = (int) ((sp.coord.y - centre.y) * scale)
+ halfheight;
if (Math.abs(px - x) < 3 && Math.abs(py - y) < 3)
{
found = i;
+ break;
}
}
+
if (found != -1)
{
- return ((SequencePoint) points.elementAt(found)).sequence;
+ return points.get(found).getSequence();
}
else
{
@@ -661,4 +601,79 @@ public class RotatableCanvas extends Panel implements MouseListener,
}
}
+ /**
+ * Resets the view to initial state (no rotation)
+ */
+ public void resetView()
+ {
+ img = null;
+ resetAxes();
+ }
+
+ @Override
+ public void zoom(float factor)
+ {
+ if (factor > 0f)
+ {
+ scalefactor *= factor;
+ }
+ scale = findScale();
+ }
+
+ @Override
+ public void rotate(float x, float y)
+ {
+ if (x == 0f && y == 0f)
+ {
+ return;
+ }
+
+ /*
+ * get the identity transformation...
+ */
+ RotatableMatrix rotmat = new RotatableMatrix();
+
+ /*
+ * rotate around the X axis for change in Y
+ * (mouse movement up/down); note we are equating a
+ * number of pixels with degrees of rotation here!
+ */
+ if (y != 0)
+ {
+ rotmat.rotate(y, Axis.X);
+ }
+
+ /*
+ * rotate around the Y axis for change in X
+ * (mouse movement left/right)
+ */
+ if (x != 0)
+ {
+ rotmat.rotate(x, Axis.Y);
+ }
+
+ /*
+ * apply the composite transformation to sequence points
+ */
+ for (int i = 0; i < npoint; i++)
+ {
+ SequencePoint sp = points.get(i);
+ sp.translate(-centre.x, -centre.y, -centre.z);
+
+ // Now apply the rotation matrix
+ sp.coord = rotmat.vectorMultiply(sp.coord);
+
+ // Now translate back again
+ sp.translate(centre.x, centre.y, centre.z);
+ }
+
+ /*
+ * rotate the x/y/z axis positions
+ */
+ for (int i = 0; i < DIMS; i++)
+ {
+ axisEndPoints[i] = rotmat.vectorMultiply(axisEndPoints[i]);
+ }
+ }
+
}
diff --git a/src/jalview/appletgui/SeqPanel.java b/src/jalview/appletgui/SeqPanel.java
index e07dae6..7c85d12 100644
--- a/src/jalview/appletgui/SeqPanel.java
+++ b/src/jalview/appletgui/SeqPanel.java
@@ -744,7 +744,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
}
@Override
- public void highlightSequence(SearchResultsI results)
+ public String highlightSequence(SearchResultsI results)
{
if (av.isFollowHighlight())
{
@@ -761,7 +761,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
}
setStatusMessage(results);
seqCanvas.highlightSearchResults(results);
-
+ return null;
}
@Override
diff --git a/src/jalview/commands/EditCommand.java b/src/jalview/commands/EditCommand.java
index cac843f..1a227c5 100644
--- a/src/jalview/commands/EditCommand.java
+++ b/src/jalview/commands/EditCommand.java
@@ -20,12 +20,16 @@
*/
package jalview.commands;
+import jalview.analysis.AlignSeq;
import jalview.datamodel.AlignmentAnnotation;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.Annotation;
+import jalview.datamodel.Range;
import jalview.datamodel.Sequence;
import jalview.datamodel.SequenceFeature;
import jalview.datamodel.SequenceI;
+import jalview.datamodel.features.SequenceFeaturesI;
+import jalview.util.Comparison;
import jalview.util.ReverseListIterator;
import jalview.util.StringUtils;
@@ -114,7 +118,7 @@ public class EditCommand implements CommandI
public abstract Action getUndoAction();
};
- private List edits = new ArrayList();
+ private List edits = new ArrayList<>();
String description;
@@ -330,20 +334,8 @@ public class EditCommand implements CommandI
int position, int number, AlignmentI al, boolean performEdit,
AlignmentI[] views)
{
- Edit edit = new Edit(command, seqs, position, number,
- al.getGapCharacter());
- if (al.getHeight() == seqs.length)
- {
- edit.al = al;
- edit.fullAlignmentHeight = true;
- }
-
- addEdit(edit);
-
- if (performEdit)
- {
- performEdit(edit, views);
- }
+ Edit edit = new Edit(command, seqs, position, number, al);
+ appendEdit(edit, al, performEdit, views);
}
/**
@@ -534,39 +526,62 @@ public class EditCommand implements CommandI
command.string[i] = sequence.getSequence(command.position,
command.position + command.number);
SequenceI oldds = sequence.getDatasetSequence();
- if (command.oldds != null && command.oldds[i] != null)
- {
- // we are redoing an undone cut.
- sequence.setDatasetSequence(null);
- }
- sequence.deleteChars(command.position,
+ Range cutPositions = sequence.findPositions(command.position + 1,
command.position + command.number);
+ boolean cutIsInternal = cutPositions != null
+ && sequence.getStart() != cutPositions
+ .getBegin() && sequence.getEnd() != cutPositions.getEnd();
+
+ /*
+ * perform the cut; if this results in a new dataset sequence, add
+ * that to the alignment dataset
+ */
+ SequenceI ds = sequence.getDatasetSequence();
+ sequence.deleteChars(command.position, command.position
+ + command.number);
+
if (command.oldds != null && command.oldds[i] != null)
{
- // oldds entry contains the cut dataset sequence.
+ /*
+ * we are Redoing a Cut, or Undoing a Paste - so
+ * oldds entry contains the cut dataset sequence,
+ * with sequence features in expected place
+ */
sequence.setDatasetSequence(command.oldds[i]);
command.oldds[i] = oldds;
}
else
{
- // modify the oldds if necessary
+ /*
+ * new cut operation: save the dataset sequence
+ * so it can be restored in an Undo
+ */
+ if (command.oldds == null)
+ {
+ command.oldds = new SequenceI[command.seqs.length];
+ }
+ command.oldds[i] = oldds;// todo not if !cutIsInternal?
+
+ // do we need to edit sequence features for new sequence ?
if (oldds != sequence.getDatasetSequence()
- || sequence.getFeatures().hasFeatures())
+ || (cutIsInternal
+ && sequence.getFeatures().hasFeatures()))
+ // todo or just test cutIsInternal && cutPositions != null ?
{
- if (command.oldds == null)
+ if (cutPositions != null)
{
- command.oldds = new SequenceI[command.seqs.length];
+ cutFeatures(command, sequence, cutPositions.getBegin(),
+ cutPositions.getEnd(), cutIsInternal);
}
- command.oldds[i] = oldds;
- // FIXME JAL-2541 JAL-2526 get correct positions if on a gap
- adjustFeatures(
- command,
- i,
- sequence.findPosition(command.position),
- sequence.findPosition(command.position + command.number),
- false);
}
}
+ SequenceI newDs = sequence.getDatasetSequence();
+ if (newDs != ds && command.al != null
+ && command.al.getDataset() != null
+ && !command.al.getDataset().getSequences().contains(newDs))
+ {
+ command.al.getDataset().addSequence(newDs);
+ }
}
if (sequence.getLength() < 1)
@@ -588,21 +603,19 @@ public class EditCommand implements CommandI
*/
static void paste(Edit command, AlignmentI[] views)
{
- StringBuffer tmp;
- boolean newDSNeeded;
- boolean newDSWasNeeded;
- int newstart, newend;
boolean seqWasDeleted = false;
- int start = 0, end = 0;
for (int i = 0; i < command.seqs.length; i++)
{
- newDSNeeded = false;
- newDSWasNeeded = command.oldds != null && command.oldds[i] != null;
- if (command.seqs[i].getLength() < 1)
+ boolean newDSNeeded = false;
+ boolean newDSWasNeeded = command.oldds != null
+ && command.oldds[i] != null;
+ SequenceI sequence = command.seqs[i];
+ if (sequence.getLength() < 1)
{
- // ie this sequence was deleted, we need to
- // readd it to the alignment
+ /*
+ * sequence was deleted; re-add it to the alignment
+ */
if (command.alIndex[i] < command.al.getHeight())
{
List sequences;
@@ -610,68 +623,78 @@ public class EditCommand implements CommandI
{
if (!(command.alIndex[i] < 0))
{
- sequences.add(command.alIndex[i], command.seqs[i]);
+ sequences.add(command.alIndex[i], sequence);
}
}
}
else
{
- command.al.addSequence(command.seqs[i]);
+ command.al.addSequence(sequence);
}
seqWasDeleted = true;
}
- newstart = command.seqs[i].getStart();
- newend = command.seqs[i].getEnd();
+ int newStart = sequence.getStart();
+ int newEnd = sequence.getEnd();
- tmp = new StringBuffer();
- tmp.append(command.seqs[i].getSequence());
+ StringBuilder tmp = new StringBuilder();
+ tmp.append(sequence.getSequence());
// Undo of a delete does not replace original dataset sequence on to
// alignment sequence.
+ int start = 0;
+ int length = 0;
+
if (command.string != null && command.string[i] != null)
{
if (command.position >= tmp.length())
{
// This occurs if padding is on, and residues
// are removed from end of alignment
- int length = command.position - tmp.length();
- while (length > 0)
+ int len = command.position - tmp.length();
+ while (len > 0)
{
tmp.append(command.gapChar);
- length--;
+ len--;
}
}
tmp.insert(command.position, command.string[i]);
for (int s = 0; s < command.string[i].length; s++)
{
- if (jalview.schemes.ResidueProperties.aaIndex[command.string[i][s]] != 23)
+ if (!Comparison.isGap(command.string[i][s]))
{
+ length++;
if (!newDSNeeded)
{
newDSNeeded = true;
- start = command.seqs[i].findPosition(command.position);
- end = command.seqs[i]
- .findPosition(command.position + command.number);
+ start = sequence.findPosition(command.position);
+ // end = sequence
+ // .findPosition(command.position + command.number);
}
- if (command.seqs[i].getStart() == start)
+ if (sequence.getStart() == start)
{
- newstart--;
+ newStart--;
}
else
{
- newend++;
+ newEnd++;
}
}
}
command.string[i] = null;
}
- command.seqs[i].setSequence(tmp.toString());
- command.seqs[i].setStart(newstart);
- command.seqs[i].setEnd(newend);
+ sequence.setSequence(tmp.toString());
+ sequence.setStart(newStart);
+ sequence.setEnd(newEnd);
+
+ /*
+ * command and Undo share the same dataset sequence if cut was
+ * at start or end of sequence
+ */
+ boolean sameDatasetSequence = false;
if (newDSNeeded)
{
- if (command.seqs[i].getDatasetSequence() != null)
+ if (sequence.getDatasetSequence() != null)
{
SequenceI ds;
if (newDSWasNeeded)
@@ -682,21 +705,29 @@ public class EditCommand implements CommandI
{
// make a new DS sequence
// use new ds mechanism here
- ds = new Sequence(command.seqs[i].getName(),
- jalview.analysis.AlignSeq.extractGaps(
- jalview.util.Comparison.GapChars,
- command.seqs[i].getSequenceAsString()),
- command.seqs[i].getStart(), command.seqs[i].getEnd());
- ds.setDescription(command.seqs[i].getDescription());
+ String ungapped = AlignSeq.extractGaps(Comparison.GapChars,
+ sequence.getSequenceAsString());
+ ds = new Sequence(sequence.getName(), ungapped,
+ sequence.getStart(), sequence.getEnd());
+ ds.setDescription(sequence.getDescription());
}
if (command.oldds == null)
{
command.oldds = new SequenceI[command.seqs.length];
}
- command.oldds[i] = command.seqs[i].getDatasetSequence();
- command.seqs[i].setDatasetSequence(ds);
+ command.oldds[i] = sequence.getDatasetSequence();
+ sameDatasetSequence = ds == sequence.getDatasetSequence();
+ ds.setSequenceFeatures(sequence.getSequenceFeatures());
+ if (!sameDatasetSequence && command.al.getDataset() != null)
+ {
+ // delete 'undone' sequence from alignment dataset
+ command.al.getDataset()
+ .deleteSequence(sequence.getDatasetSequence());
+ }
+ sequence.setDatasetSequence(ds);
}
- adjustFeatures(command, i, start, end, true);
+ undoCutFeatures(command, command.seqs[i], start, length,
+ sameDatasetSequence);
}
}
adjustAnnotations(command, true, seqWasDeleted, views);
@@ -706,7 +737,7 @@ public class EditCommand implements CommandI
static void replace(Edit command)
{
- StringBuffer tmp;
+ StringBuilder tmp;
String oldstring;
int start = command.position;
int end = command.number;
@@ -719,6 +750,7 @@ public class EditCommand implements CommandI
{
boolean newDSWasNeeded = command.oldds != null
&& command.oldds[i] != null;
+ boolean newStartEndWasNeeded = command.oldStartEnd!=null && command.oldStartEnd[i]!=null;
/**
* cut addHistoryItem(new EditCommand("Cut Sequences", EditCommand.CUT,
@@ -731,49 +763,147 @@ public class EditCommand implements CommandI
* EditCommand.PASTE, sequences, 0, alignment.getWidth(), alignment) );
*
*/
+
+ Range beforeEditedPositions = command.seqs[i].findPositions(1, start);
+ Range afterEditedPositions = command.seqs[i]
+ .findPositions(end + 1, command.seqs[i].getLength());
+
oldstring = command.seqs[i].getSequenceAsString();
- tmp = new StringBuffer(oldstring.substring(0, start));
+ tmp = new StringBuilder(oldstring.substring(0, start));
tmp.append(command.string[i]);
- String nogaprep = jalview.analysis.AlignSeq.extractGaps(
- jalview.util.Comparison.GapChars,
+ String nogaprep = AlignSeq.extractGaps(Comparison.GapChars,
new String(command.string[i]));
- int ipos = command.seqs[i].findPosition(start)
- - command.seqs[i].getStart();
- tmp.append(oldstring.substring(end));
+ if (end < oldstring.length())
+ {
+ tmp.append(oldstring.substring(end));
+ }
+ // stash end prior to updating the sequence object so we can save it if
+ // need be.
+ Range oldstartend = new Range(command.seqs[i].getStart(),
+ command.seqs[i].getEnd());
command.seqs[i].setSequence(tmp.toString());
- command.string[i] = oldstring.substring(start, end).toCharArray();
- String nogapold = jalview.analysis.AlignSeq.extractGaps(
- jalview.util.Comparison.GapChars,
+ command.string[i] = oldstring
+ .substring(start, Math.min(end, oldstring.length()))
+ .toCharArray();
+ String nogapold = AlignSeq.extractGaps(Comparison.GapChars,
new String(command.string[i]));
+
if (!nogaprep.toLowerCase().equals(nogapold.toLowerCase()))
{
- if (newDSWasNeeded)
+ // we may already have dataset and limits stashed...
+ if (newDSWasNeeded || newStartEndWasNeeded)
{
+ if (newDSWasNeeded)
+ {
+ // then just switch the dataset sequence
SequenceI oldds = command.seqs[i].getDatasetSequence();
command.seqs[i].setDatasetSequence(command.oldds[i]);
command.oldds[i] = oldds;
+ }
+ if (newStartEndWasNeeded)
+ {
+ Range newStart = command.oldStartEnd[i];
+ command.oldStartEnd[i] = oldstartend;
+ command.seqs[i].setStart(newStart.getBegin());
+ command.seqs[i].setEnd(newStart.getEnd());
+ }
}
- else
+ else
{
- if (command.oldds == null)
+ // decide if we need a new dataset sequence or modify start/end
+ // first edit the original dataset sequence string
+ SequenceI oldds = command.seqs[i].getDatasetSequence();
+ String osp = oldds.getSequenceAsString();
+ int beforeStartOfEdit = -oldds.getStart() + 1
+ + (beforeEditedPositions == null
+ ? ((afterEditedPositions != null)
+ ? afterEditedPositions.getBegin() - 1
+ : oldstartend.getBegin()
+ + nogapold.length())
+ : beforeEditedPositions.getEnd()
+ );
+ int afterEndOfEdit = -oldds.getStart() + 1
+ + ((afterEditedPositions == null)
+ ? oldstartend.getEnd()
+ : afterEditedPositions.getBegin() - 1);
+ String fullseq = osp.substring(0,
+ beforeStartOfEdit)
+ + nogaprep
+ + osp.substring(afterEndOfEdit);
+
+ // and check if new sequence data is different..
+ if (!fullseq.equalsIgnoreCase(osp))
{
- command.oldds = new SequenceI[command.seqs.length];
- }
- command.oldds[i] = command.seqs[i].getDatasetSequence();
- SequenceI newds = new Sequence(
- command.seqs[i].getDatasetSequence());
- String fullseq, osp = newds.getSequenceAsString();
- fullseq = osp.substring(0, ipos) + nogaprep
- + osp.substring(ipos + nogaprep.length());
- newds.setSequence(fullseq.toUpperCase());
- // TODO: JAL-1131 ensure newly created dataset sequence is added to
- // the set of
- // dataset sequences associated with the alignment.
- // TODO: JAL-1131 fix up any annotation associated with new dataset
- // sequence to ensure that original sequence/annotation relationships
- // are preserved.
- command.seqs[i].setDatasetSequence(newds);
+ // old ds and edited ds are different, so
+ // create the new dataset sequence
+ SequenceI newds = new Sequence(oldds);
+ newds.setSequence(fullseq);
+
+ if (command.oldds == null)
+ {
+ command.oldds = new SequenceI[command.seqs.length];
+ }
+ command.oldds[i] = command.seqs[i].getDatasetSequence();
+
+ // And preserve start/end for good-measure
+ if (command.oldStartEnd == null)
+ {
+ command.oldStartEnd = new Range[command.seqs.length];
+ }
+ command.oldStartEnd[i] = oldstartend;
+ // TODO: JAL-1131 ensure newly created dataset sequence is added to
+ // the set of
+ // dataset sequences associated with the alignment.
+ // TODO: JAL-1131 fix up any annotation associated with new dataset
+ // sequence to ensure that original sequence/annotation
+ // relationships
+ // are preserved.
+ command.seqs[i].setDatasetSequence(newds);
+ }
+ else
+ {
+ if (command.oldStartEnd == null)
+ {
+ command.oldStartEnd = new Range[command.seqs.length];
+ }
+ command.oldStartEnd[i] = new Range(command.seqs[i].getStart(),
+ command.seqs[i].getEnd());
+ if (beforeEditedPositions != null
+ && afterEditedPositions == null)
+ {
+ // modification at end
+ command.seqs[i].setEnd(
+ beforeEditedPositions.getEnd() + nogaprep.length()
+ - nogapold.length());
+ }
+ else if (afterEditedPositions != null
+ && beforeEditedPositions == null)
+ {
+ // modification at start
+ command.seqs[i].setStart(
+ afterEditedPositions.getBegin() - nogaprep.length());
+ }
+ else
+ {
+ // edit covered both start and end. Here we can only guess the
+ // new
+ // start/end
+ String nogapalseq = AlignSeq.extractGaps(Comparison.GapChars,
+ command.seqs[i].getSequenceAsString().toUpperCase());
+ int newStart = command.seqs[i].getDatasetSequence()
+ .getSequenceAsString().indexOf(nogapalseq);
+ if (newStart == -1)
+ {
+ throw new Error(
+ "Implementation Error: could not locate start/end "
+ + "in dataset sequence after an edit of the sequence string");
+ }
+ int newEnd = newStart + nogapalseq.length() - 1;
+ command.seqs[i].setStart(newStart);
+ command.seqs[i].setEnd(newEnd);
+ }
+ }
}
}
tmp = null;
@@ -789,7 +919,7 @@ public class EditCommand implements CommandI
if (modifyVisibility && !insert)
{
// only occurs if a sequence was added or deleted.
- command.deletedAnnotationRows = new Hashtable();
+ command.deletedAnnotationRows = new Hashtable<>();
}
if (command.fullAlignmentHeight)
{
@@ -892,8 +1022,7 @@ public class EditCommand implements CommandI
}
// and then duplicate added annotation on every other alignment
// view
- for (int vnum = 0; views != null
- && vnum < views.length; vnum++)
+ for (int vnum = 0; views != null && vnum < views.length; vnum++)
{
if (views[vnum] != command.al)
{
@@ -948,7 +1077,7 @@ public class EditCommand implements CommandI
if (!insert)
{
- command.deletedAnnotations = new Hashtable();
+ command.deletedAnnotations = new Hashtable<>();
}
int aSize;
@@ -1109,98 +1238,97 @@ public class EditCommand implements CommandI
}
}
- final static void adjustFeatures(Edit command, int index, final int i,
- final int j, boolean insert)
+ /**
+ * Restores features to the state before a Cut.
+ *
+ * - re-add any features deleted by the cut
+ * - remove any truncated features created by the cut
+ * - shift right any features to the right of the cut
+ *
+ *
+ * @param command
+ * the Cut command
+ * @param seq
+ * the sequence the Cut applied to
+ * @param start
+ * the start residue position of the cut
+ * @param length
+ * the number of residues cut
+ * @param sameDatasetSequence
+ * true if dataset sequence and frame of reference were left
+ * unchanged by the Cut
+ */
+ final static void undoCutFeatures(Edit command, SequenceI seq,
+ final int start, final int length, boolean sameDatasetSequence)
{
- SequenceI seq = command.seqs[index];
SequenceI sequence = seq.getDatasetSequence();
if (sequence == null)
{
sequence = seq;
}
- if (insert)
+ /*
+ * shift right features that lie to the right of the restored cut (but not
+ * if dataset sequence unchanged - so coordinates were changed by Cut)
+ */
+ if (!sameDatasetSequence)
{
- if (command.editedFeatures != null
- && command.editedFeatures.containsKey(seq))
+ /*
+ * shift right all features right of and not
+ * contiguous with the cut position
+ */
+ seq.getFeatures().shiftFeatures(start + 1, length);
+
+ /*
+ * shift right any features that start at the cut position,
+ * unless they were truncated
+ */
+ List sfs = seq.getFeatures().findFeatures(start,
+ start);
+ for (SequenceFeature sf : sfs)
{
- sequence.setSequenceFeatures(command.editedFeatures.get(seq));
+ if (sf.getBegin() == start)
+ {
+ if (!command.truncatedFeatures.containsKey(seq)
+ || !command.truncatedFeatures.get(seq).contains(sf))
+ {
+ /*
+ * feature was shifted left to cut position (not truncated),
+ * so shift it back right
+ */
+ SequenceFeature shifted = new SequenceFeature(sf, sf.getBegin()
+ + length, sf.getEnd() + length, sf.getFeatureGroup(),
+ sf.getScore());
+ seq.addSequenceFeature(shifted);
+ seq.deleteFeature(sf);
+ }
+ }
}
-
- return;
}
- List sf = sequence.getFeatures()
- .getPositionalFeatures();
-
- if (sf.isEmpty())
- {
- return;
- }
-
- List oldsf = new ArrayList();
-
- int cSize = j - i;
-
- for (SequenceFeature feature : sf)
+ /*
+ * restore any features that were deleted or truncated
+ */
+ if (command.deletedFeatures != null
+ && command.deletedFeatures.containsKey(seq))
{
- SequenceFeature copy = new SequenceFeature(feature);
-
- oldsf.add(copy);
-
- if (feature.getEnd() < i)
- {
- continue;
- }
-
- if (feature.getBegin() > j)
- {
- int newBegin = copy.getBegin() - cSize;
- int newEnd = copy.getEnd() - cSize;
- SequenceFeature newSf = new SequenceFeature(feature, newBegin,
- newEnd, feature.getFeatureGroup(), feature.getScore());
- sequence.deleteFeature(feature);
- sequence.addSequenceFeature(newSf);
- // feature.setBegin(newBegin);
- // feature.setEnd(newEnd);
- continue;
- }
-
- int newBegin = feature.getBegin();
- int newEnd = feature.getEnd();
- if (newBegin >= i)
- {
- newBegin = i;
- // feature.setBegin(i);
- }
-
- if (newEnd < j)
+ for (SequenceFeature deleted : command.deletedFeatures.get(seq))
{
- newEnd = j - 1;
- // feature.setEnd(j - 1);
+ sequence.addSequenceFeature(deleted);
}
- newEnd = newEnd - cSize;
- // feature.setEnd(feature.getEnd() - (cSize));
-
- sequence.deleteFeature(feature);
- if (newEnd >= newBegin)
- {
- sequence.addSequenceFeature(new SequenceFeature(feature, newBegin,
- newEnd, feature.getFeatureGroup(), feature.getScore()));
- }
- // if (feature.getBegin() > feature.getEnd())
- // {
- // sequence.deleteFeature(feature);
- // }
}
- if (command.editedFeatures == null)
+ /*
+ * delete any truncated features
+ */
+ if (command.truncatedFeatures != null
+ && command.truncatedFeatures.containsKey(seq))
{
- command.editedFeatures = new Hashtable>();
+ for (SequenceFeature amended : command.truncatedFeatures.get(seq))
+ {
+ sequence.deleteFeature(amended);
+ }
}
-
- command.editedFeatures.put(seq, oldsf);
-
}
/**
@@ -1233,7 +1361,7 @@ public class EditCommand implements CommandI
*/
public Map priorState(boolean forUndo)
{
- Map result = new HashMap();
+ Map result = new HashMap<>();
if (getEdits() == null)
{
return result;
@@ -1266,7 +1394,7 @@ public class EditCommand implements CommandI
* Work backwards through the edit list, deriving the sequences before each
* was applied. The final result is the sequence set before any edits.
*/
- Iterator editList = new ReverseListIterator(getEdits());
+ Iterator editList = new ReverseListIterator<>(getEdits());
while (editList.hasNext())
{
Edit oldEdit = editList.next();
@@ -1315,29 +1443,45 @@ public class EditCommand implements CommandI
public class Edit
{
- public SequenceI[] oldds;
+ private SequenceI[] oldds;
+
+ /**
+ * start and end of sequence prior to edit
+ */
+ private Range[] oldStartEnd;
+
+ private boolean fullAlignmentHeight = false;
- boolean fullAlignmentHeight = false;
+ private Map deletedAnnotationRows;
- Hashtable deletedAnnotationRows;
+ private Map deletedAnnotations;
- Hashtable deletedAnnotations;
+ /*
+ * features deleted by the cut (re-add on Undo)
+ * (including the original of any shortened features)
+ */
+ private Map> deletedFeatures;
- Hashtable> editedFeatures;
+ /*
+ * shortened features added by the cut (delete on Undo)
+ */
+ private Map> truncatedFeatures;
- AlignmentI al;
+ private AlignmentI al;
- Action command;
+ final private Action command;
char[][] string;
SequenceI[] seqs;
- int[] alIndex;
+ private int[] alIndex;
- int position, number;
+ private int position;
- char gapChar;
+ private int number;
+
+ private char gapChar;
public Edit(Action cmd, SequenceI[] sqs, int pos, int count,
char gap)
@@ -1352,11 +1496,8 @@ public class EditCommand implements CommandI
Edit(Action cmd, SequenceI[] sqs, int pos, int count,
AlignmentI align)
{
- this.gapChar = align.getGapCharacter();
- this.command = cmd;
- this.seqs = sqs;
- this.position = pos;
- this.number = count;
+ this(cmd, sqs, pos, count, align.getGapCharacter());
+
this.al = align;
alIndex = new int[sqs.length];
@@ -1368,22 +1509,26 @@ public class EditCommand implements CommandI
fullAlignmentHeight = (align.getHeight() == sqs.length);
}
+ /**
+ * Constructor given a REPLACE command and the replacement string
+ *
+ * @param cmd
+ * @param sqs
+ * @param pos
+ * @param count
+ * @param align
+ * @param replace
+ */
Edit(Action cmd, SequenceI[] sqs, int pos, int count,
AlignmentI align, String replace)
{
- this.command = cmd;
- this.seqs = sqs;
- this.position = pos;
- this.number = count;
- this.al = align;
- this.gapChar = align.getGapCharacter();
+ this(cmd, sqs, pos, count, align);
+
string = new char[sqs.length][];
for (int i = 0; i < sqs.length; i++)
{
string[i] = replace.toCharArray();
}
-
- fullAlignmentHeight = (align.getHeight() == sqs.length);
}
public SequenceI[] getSequences()
@@ -1427,7 +1572,163 @@ public class EditCommand implements CommandI
}
else
{
- return new ReverseListIterator(getEdits());
+ return new ReverseListIterator<>(getEdits());
+ }
+ }
+
+ /**
+ * Adjusts features for Cut, and saves details of changes made to allow Undo
+ *
+ * - features left of the cut are unchanged
+ * - features right of the cut are shifted left
+ * - features internal to the cut region are deleted
+ * - features that overlap or span the cut are shortened
+ * - the originals of any deleted or shortened features are saved, to re-add
+ * on Undo
+ * - any added (shortened) features are saved, to delete on Undo
+ *
+ *
+ * @param command
+ * @param seq
+ * @param fromPosition
+ * @param toPosition
+ * @param cutIsInternal
+ */
+ protected static void cutFeatures(Edit command, SequenceI seq,
+ int fromPosition, int toPosition, boolean cutIsInternal)
+ {
+ /*
+ * if the cut is at start or end of sequence
+ * then we don't modify the sequence feature store
+ */
+ if (!cutIsInternal)
+ {
+ return;
+ }
+ List added = new ArrayList<>();
+ List removed = new ArrayList<>();
+
+ SequenceFeaturesI featureStore = seq.getFeatures();
+ if (toPosition < fromPosition || featureStore == null)
+ {
+ return;
+ }
+
+ int cutStartPos = fromPosition;
+ int cutEndPos = toPosition;
+ int cutWidth = cutEndPos - cutStartPos + 1;
+
+ synchronized (featureStore)
+ {
+ /*
+ * get features that overlap the cut region
+ */
+ List toAmend = featureStore.findFeatures(
+ cutStartPos, cutEndPos);
+
+ /*
+ * add any contact features that span the cut region
+ * (not returned by findFeatures)
+ */
+ for (SequenceFeature contact : featureStore.getContactFeatures())
+ {
+ if (contact.getBegin() < cutStartPos
+ && contact.getEnd() > cutEndPos)
+ {
+ toAmend.add(contact);
+ }
+ }
+
+ /*
+ * adjust start-end of overlapping features;
+ * delete features enclosed by the cut;
+ * delete partially overlapping contact features
+ */
+ for (SequenceFeature sf : toAmend)
+ {
+ int sfBegin = sf.getBegin();
+ int sfEnd = sf.getEnd();
+ int newBegin = sfBegin;
+ int newEnd = sfEnd;
+ boolean toDelete = false;
+ boolean follows = false;
+
+ if (sfBegin >= cutStartPos && sfEnd <= cutEndPos)
+ {
+ /*
+ * feature lies within cut region - delete it
+ */
+ toDelete = true;
+ }
+ else if (sfBegin < cutStartPos && sfEnd > cutEndPos)
+ {
+ /*
+ * feature spans cut region - left-shift the end
+ */
+ newEnd -= cutWidth;
+ }
+ else if (sfEnd <= cutEndPos)
+ {
+ /*
+ * feature overlaps left of cut region - truncate right
+ */
+ newEnd = cutStartPos - 1;
+ if (sf.isContactFeature())
+ {
+ toDelete = true;
+ }
+ }
+ else if (sfBegin >= cutStartPos)
+ {
+ /*
+ * remaining case - feature overlaps right
+ * truncate left, adjust end of feature
+ */
+ newBegin = cutIsInternal ? cutStartPos : cutEndPos + 1;
+ newEnd = newBegin + sfEnd - cutEndPos - 1;
+ if (sf.isContactFeature())
+ {
+ toDelete = true;
+ }
+ }
+
+ seq.deleteFeature(sf);
+ if (!follows)
+ {
+ removed.add(sf);
+ }
+ if (!toDelete)
+ {
+ SequenceFeature copy = new SequenceFeature(sf, newBegin, newEnd,
+ sf.getFeatureGroup(), sf.getScore());
+ seq.addSequenceFeature(copy);
+ if (!follows)
+ {
+ added.add(copy);
+ }
+ }
+ }
+
+ /*
+ * and left shift any features lying to the right of the cut region
+ */
+
+ featureStore.shiftFeatures(cutEndPos + 1, -cutWidth);
+ }
+
+ /*
+ * save deleted and amended features, so that Undo can
+ * re-add or delete them respectively
+ */
+ if (command.deletedFeatures == null)
+ {
+ command.deletedFeatures = new HashMap<>();
+ }
+ if (command.truncatedFeatures == null)
+ {
+ command.truncatedFeatures = new HashMap<>();
}
+ command.deletedFeatures.put(seq, removed);
+ command.truncatedFeatures.put(seq, added);
}
}
diff --git a/src/jalview/datamodel/AlignedCodonFrame.java b/src/jalview/datamodel/AlignedCodonFrame.java
index ec11fc1..26f6e2a 100644
--- a/src/jalview/datamodel/AlignedCodonFrame.java
+++ b/src/jalview/datamodel/AlignedCodonFrame.java
@@ -116,7 +116,7 @@ public class AlignedCodonFrame
*/
public AlignedCodonFrame()
{
- mappings = new ArrayList();
+ mappings = new ArrayList<>();
}
/**
@@ -179,7 +179,7 @@ public class AlignedCodonFrame
{
// TODO return a list instead?
// return dnaSeqs;
- List seqs = new ArrayList();
+ List seqs = new ArrayList<>();
for (SequenceToSequenceMapping ssm : mappings)
{
seqs.add(ssm.fromSeq);
@@ -190,7 +190,7 @@ public class AlignedCodonFrame
public SequenceI[] getAaSeqs()
{
// TODO not used - remove?
- List seqs = new ArrayList();
+ List seqs = new ArrayList<>();
for (SequenceToSequenceMapping ssm : mappings)
{
seqs.add(ssm.mapping.to);
@@ -200,7 +200,7 @@ public class AlignedCodonFrame
public MapList[] getdnaToProt()
{
- List maps = new ArrayList();
+ List maps = new ArrayList<>();
for (SequenceToSequenceMapping ssm : mappings)
{
maps.add(ssm.mapping.map);
@@ -210,7 +210,7 @@ public class AlignedCodonFrame
public Mapping[] getProtMappings()
{
- List maps = new ArrayList();
+ List maps = new ArrayList<>();
for (SequenceToSequenceMapping ssm : mappings)
{
maps.add(ssm.mapping);
@@ -225,7 +225,7 @@ public class AlignedCodonFrame
* @param seq
* @return
*/
- public Mapping getMappingForSequence(SequenceI seq)
+ public Mapping getMappingForSequence(SequenceI seq, boolean cdsOnly)
{
SequenceI seqDs = seq.getDatasetSequence();
seqDs = seqDs != null ? seqDs : seq;
@@ -234,7 +234,11 @@ public class AlignedCodonFrame
{
if (ssm.fromSeq == seqDs || ssm.mapping.to == seqDs)
{
- return ssm.mapping;
+ if (!cdsOnly || ssm.fromSeq.getName().startsWith("CDS")
+ || ssm.mapping.to.getName().startsWith("CDS"))
+ {
+ return ssm.mapping;
+ }
}
}
return null;
@@ -485,7 +489,7 @@ public class AlignedCodonFrame
{
MapList ml = null;
SequenceI dnaSeq = null;
- List result = new ArrayList();
+ List result = new ArrayList<>();
for (SequenceToSequenceMapping ssm : mappings)
{
@@ -524,8 +528,8 @@ public class AlignedCodonFrame
*/
public List getMappingsFromSequence(SequenceI seq)
{
- List result = new ArrayList();
- List related = new ArrayList();
+ List result = new ArrayList<>();
+ List related = new ArrayList<>();
SequenceI seqDs = seq.getDatasetSequence();
seqDs = seqDs != null ? seqDs : seq;
diff --git a/src/jalview/datamodel/MappedFeatures.java b/src/jalview/datamodel/MappedFeatures.java
new file mode 100644
index 0000000..b69a103
--- /dev/null
+++ b/src/jalview/datamodel/MappedFeatures.java
@@ -0,0 +1,175 @@
+package jalview.datamodel;
+
+import jalview.io.gff.Gff3Helper;
+import jalview.schemes.ResidueProperties;
+import jalview.util.MappingUtils;
+import jalview.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A data bean to hold a list of mapped sequence features (e.g. CDS features
+ * mapped from protein), and the mapping between the sequences
+ *
+ * @author gmcarstairs
+ */
+public class MappedFeatures
+{
+ /*
+ * the mapping from CDS to peptide
+ */
+ public final Mapping mapping;
+
+ /**
+ * the CDS sequence mapped to
+ */
+ public final SequenceI fromSeq;
+
+ /*
+ * the residue position in the peptide sequence
+ */
+ public final int fromPosition;
+
+ /*
+ * the peptide residue at the position
+ */
+ public final char fromResidue;
+
+ /*
+ * features on CDS that overlap the codon positions
+ */
+ public final List features;
+
+ /**
+ * Constructor
+ *
+ * @param theMapping
+ * @param pos
+ * @param res
+ * @param theFeatures
+ */
+ public MappedFeatures(Mapping theMapping, SequenceI from, int pos,
+ char res,
+ List theFeatures)
+ {
+ mapping = theMapping;
+ fromSeq = from;
+ fromPosition = pos;
+ fromResidue = res;
+ features = theFeatures;
+ }
+
+ /**
+ * Computes and returns a (possibly empty) list of HGVS notation peptide
+ * variants derived from codon allele variants
+ *
+ * @return
+ */
+ public List findProteinVariants()
+ {
+ List vars = new ArrayList<>();
+
+ /*
+ * determine canonical codon
+ */
+ int[] codonPos = MappingUtils.flattenRanges(
+ mapping.getMap().locateInFrom(fromPosition, fromPosition));
+ if (codonPos.length != 3)
+ {
+ // error
+ return vars;
+ }
+ final char[] baseCodon = new char[3];
+ int cdsStart = fromSeq.getStart();
+ baseCodon[0] = fromSeq.getCharAt(codonPos[0] - cdsStart);
+ baseCodon[1] = fromSeq.getCharAt(codonPos[1] - cdsStart);
+ baseCodon[2] = fromSeq.getCharAt(codonPos[2] - cdsStart);
+
+ for (SequenceFeature sf : features)
+ {
+ /*
+ * VCF data may already contain the protein consequence
+ */
+ String hgvsp = sf.getValueAsString("CSQ", "HGVSp");
+ if (hgvsp != null)
+ {
+ int colonPos = hgvsp.indexOf(':');
+ if (colonPos >= 0)
+ {
+ String var = hgvsp.substring(colonPos + 1);
+ if (!vars.contains(var))
+ {
+ vars.add(var);
+ }
+ continue;
+ }
+ }
+
+ /*
+ * otherwise, compute codon and peptide variant
+ */
+ // todo avoid duplication of code in AlignmentUtils.buildDnaVariantsMap
+ int cdsPos = sf.getBegin();
+ if (cdsPos != sf.getEnd())
+ {
+ // not handling multi-locus variant features
+ continue;
+ }
+ if (cdsPos != codonPos[0] && cdsPos != codonPos[1]
+ && cdsPos != codonPos[2])
+ {
+ // e.g. feature on intron within spliced codon!
+ continue;
+ }
+
+ String alls = (String) sf.getValue(Gff3Helper.ALLELES);
+ if (alls == null)
+ {
+ continue;
+ }
+ String from3 = StringUtils.toSentenceCase(
+ ResidueProperties.aa2Triplet
+ .get(String.valueOf(fromResidue)));
+
+ /*
+ * make a peptide variant for each SNP allele
+ * e.g. C,G,T gives variants G and T for base C
+ */
+ String[] alleles = alls.toUpperCase().split(",");
+ for (String allele : alleles)
+ {
+ allele = allele.trim().toUpperCase();
+ if (allele.length() > 1)
+ {
+ continue; // multi-locus variant
+ }
+ char[] variantCodon = new char[3];
+ variantCodon[0] = baseCodon[0];
+ variantCodon[1] = baseCodon[1];
+ variantCodon[2] = baseCodon[2];
+
+ /*
+ * poke variant base into canonical codon
+ */
+ int i = cdsPos == codonPos[0] ? 0 : (cdsPos == codonPos[1] ? 1 : 2);
+ variantCodon[i] = allele.toUpperCase().charAt(0);
+ String codon = new String(variantCodon);
+ String peptide = ResidueProperties.codonTranslate(codon);
+ if (fromResidue != peptide.charAt(0))
+ {
+ String to3 = ResidueProperties.STOP.equals(peptide) ? "STOP"
+ : StringUtils.toSentenceCase(
+ ResidueProperties.aa2Triplet.get(peptide));
+ String var = "p." + from3 + fromPosition + to3;
+ if (!vars.contains(var))
+ {
+ vars.add(var);
+ }
+ }
+ }
+ }
+
+ return vars;
+ }
+}
diff --git a/src/jalview/datamodel/Point.java b/src/jalview/datamodel/Point.java
new file mode 100644
index 0000000..e7c77c0
--- /dev/null
+++ b/src/jalview/datamodel/Point.java
@@ -0,0 +1,71 @@
+package jalview.datamodel;
+
+/**
+ * A bean that models an (x, y, z) position in 3-D space
+ */
+public final class Point
+{
+ public final float x;
+
+ public final float y;
+
+ public final float z;
+
+ public Point(float xVal, float yVal, float zVal)
+ {
+ x = xVal;
+ y = yVal;
+ z = zVal;
+ }
+
+ /**
+ * toString for convenience of inspection in debugging or logging
+ */
+ @Override
+ public String toString()
+ {
+ return String.format("[%f, %f, %f]", x, y, z);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Float.floatToIntBits(x);
+ result = prime * result + Float.floatToIntBits(y);
+ result = prime * result + Float.floatToIntBits(z);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ Point other = (Point) obj;
+ if (Float.floatToIntBits(x) != Float.floatToIntBits(other.x))
+ {
+ return false;
+ }
+ if (Float.floatToIntBits(y) != Float.floatToIntBits(other.y))
+ {
+ return false;
+ }
+ if (Float.floatToIntBits(z) != Float.floatToIntBits(other.z))
+ {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/jalview/datamodel/Sequence.java b/src/jalview/datamodel/Sequence.java
index 33de452..f555855 100755
--- a/src/jalview/datamodel/Sequence.java
+++ b/src/jalview/datamodel/Sequence.java
@@ -43,10 +43,7 @@ import fr.orsay.lri.varna.models.rna.RNA;
/**
*
- * Implements the SequenceI interface for a char[] based sequence object.
- *
- * @author $author$
- * @version $Revision$
+ * Implements the SequenceI interface for a char[] based sequence object
*/
public class Sequence extends ASequence implements SequenceI
{
@@ -797,6 +794,7 @@ public class Sequence extends ASequence implements SequenceI
* preserve end residue column provided cursor was valid
*/
int endColumn = isValidCursor(cursor) ? cursor.lastColumnPosition : 0;
+
if (residuePos == this.end)
{
endColumn = column;
@@ -833,8 +831,7 @@ public class Sequence extends ASequence implements SequenceI
/*
* move left or right to find pos from hint.position
*/
- int col = curs.columnPosition - 1; // convert from base 1 to 0-based array
- // index
+ int col = curs.columnPosition - 1; // convert from base 1 to base 0
int newPos = curs.residuePosition;
int delta = newPos > pos ? -1 : 1;
@@ -1263,12 +1260,13 @@ public class Sequence extends ASequence implements SequenceI
boolean createNewDs = false;
// TODO: take a (second look) at the dataset creation validation method for
// the very large sequence case
+
int startIndex = findIndex(start) - 1;
int endIndex = findIndex(end) - 1;
int startDeleteColumn = -1; // for dataset sequence deletions
int deleteCount = 0;
- for (int s = i; s < j; s++)
+ for (int s = i; s < j && s < sequence.length; s++)
{
if (Comparison.isGap(sequence[s]))
{
@@ -1913,6 +1911,15 @@ public class Sequence extends ASequence implements SequenceI
List result = getFeatures().findFeatures(startPos,
endPos, types);
+ if (datasetSequence != null)
+ {
+ result = datasetSequence.getFeatures().findFeatures(startPos, endPos,
+ types);
+ }
+ else
+ {
+ result = sequenceFeatureStore.findFeatures(startPos, endPos, types);
+ }
/*
* if end column is gapped, endPos may be to the right,
diff --git a/src/jalview/datamodel/SequenceGroup.java b/src/jalview/datamodel/SequenceGroup.java
index 944f263..fc8ac49 100755
--- a/src/jalview/datamodel/SequenceGroup.java
+++ b/src/jalview/datamodel/SequenceGroup.java
@@ -102,11 +102,15 @@ public class SequenceGroup implements AnnotatedCollectionI
*/
public ResidueShaderI cs;
- // start column (base 0)
- int startRes = 0;
+ /**
+ * start column (base 0)
+ */
+ private int startRes = 0;
- // end column (base 0)
- int endRes = 0;
+ /**
+ * end column (base 0)
+ */
+ private int endRes = 0;
public Color outlineColour = Color.black;
@@ -209,6 +213,7 @@ public class SequenceGroup implements AnnotatedCollectionI
displayBoxes = seqsel.displayBoxes;
displayText = seqsel.displayText;
colourText = seqsel.colourText;
+
startRes = seqsel.startRes;
endRes = seqsel.endRes;
cs = new ResidueShader((ResidueShader) seqsel.cs);
@@ -752,13 +757,16 @@ public class SequenceGroup implements AnnotatedCollectionI
/**
* Set the first column selected by this group. Runs from 0<=i 0)
+ if (sf.getBegin() >= fromPosition)
{
- newBegin = Math.max(1, newBegin);
- SequenceFeature sf2 = new SequenceFeature(sf, newBegin, newEnd,
- sf.getFeatureGroup(), sf.getScore());
- addFeature(sf2);
+ modified = true;
+ int newBegin = sf.getBegin() + shiftBy;
+ int newEnd = sf.getEnd() + shiftBy;
+
+ /*
+ * sanity check: don't shift left of the first residue
+ */
+ if (newEnd > 0)
+ {
+ newBegin = Math.max(1, newBegin);
+ SequenceFeature sf2 = new SequenceFeature(sf, newBegin, newEnd,
+ sf.getFeatureGroup(), sf.getScore());
+ addFeature(sf2);
+ }
+ delete(sf);
}
- delete(sf);
}
return modified;
}
diff --git a/src/jalview/datamodel/features/SequenceFeatures.java b/src/jalview/datamodel/features/SequenceFeatures.java
index 727d3ef..1120291 100644
--- a/src/jalview/datamodel/features/SequenceFeatures.java
+++ b/src/jalview/datamodel/features/SequenceFeatures.java
@@ -26,7 +26,6 @@ import jalview.io.gff.SequenceOntologyFactory;
import jalview.io.gff.SequenceOntologyI;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
@@ -228,7 +227,9 @@ public class SequenceFeatures implements SequenceFeaturesI
/**
* A convenience method that converts a vararg for feature types to an
- * Iterable over matched feature sets in key order
+ * Iterable over matched feature sets. If no types are specified, all feature
+ * sets are returned. If one or more types are specified, feature sets for
+ * those types are returned, preserving the order of the types.
*
* @param type
* @return
@@ -244,12 +245,11 @@ public class SequenceFeatures implements SequenceFeaturesI
}
List types = new ArrayList<>();
- List args = Arrays.asList(type);
- for (Entry featureType : featureStore.entrySet())
+ for (String theType : type)
{
- if (args.contains(featureType.getKey()))
+ if (theType != null && featureStore.containsKey(theType))
{
- types.add(featureType.getValue());
+ types.add(featureStore.get(theType));
}
}
return types;
@@ -472,13 +472,22 @@ public class SequenceFeatures implements SequenceFeaturesI
* {@inheritDoc}
*/
@Override
- public boolean shiftFeatures(int shift)
+ public boolean shiftFeatures(int fromPosition, int shiftBy)
{
boolean modified = false;
for (FeatureStore fs : featureStore.values())
{
- modified |= fs.shiftFeatures(shift);
+ modified |= fs.shiftFeatures(fromPosition, shiftBy);
}
return modified;
}
-}
\ No newline at end of file
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void deleteAll()
+ {
+ featureStore.clear();
+ }
+}
diff --git a/src/jalview/datamodel/features/SequenceFeaturesI.java b/src/jalview/datamodel/features/SequenceFeaturesI.java
index 31712b9..7213cba 100644
--- a/src/jalview/datamodel/features/SequenceFeaturesI.java
+++ b/src/jalview/datamodel/features/SequenceFeaturesI.java
@@ -42,7 +42,8 @@ public interface SequenceFeaturesI
/**
* Returns a (possibly empty) list of features, optionally restricted to
* specified types, which overlap the given (inclusive) sequence position
- * range
+ * range. If types are specified, features are returned in the order of the
+ * types given.
*
* @param from
* @param to
@@ -215,10 +216,17 @@ public interface SequenceFeaturesI
float getMaximumScore(String type, boolean positional);
/**
- * Adds the shift amount to the start and end of all positional features,
- * returning true if at least one feature was shifted, else false
+ * Adds the shift amount to the start and end of all positional features whose
+ * start position is at or after fromPosition. Returns true if at least one
+ * feature was shifted, else false.
*
- * @param shift
+ * @param fromPosition
+ * @param shiftBy
*/
- abstract boolean shiftFeatures(int shift);
-}
\ No newline at end of file
+ boolean shiftFeatures(int fromPosition, int shiftBy);
+
+ /**
+ * Deletes all positional and non-positional features
+ */
+ void deleteAll();
+}
diff --git a/src/jalview/datamodel/xdb/embl/EmblEntry.java b/src/jalview/datamodel/xdb/embl/EmblEntry.java
deleted file mode 100644
index bbe6a20..0000000
--- a/src/jalview/datamodel/xdb/embl/EmblEntry.java
+++ /dev/null
@@ -1,872 +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 .
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.datamodel.xdb.embl;
-
-import jalview.analysis.SequenceIdMatcher;
-import jalview.bin.Cache;
-import jalview.datamodel.DBRefEntry;
-import jalview.datamodel.DBRefSource;
-import jalview.datamodel.FeatureProperties;
-import jalview.datamodel.Mapping;
-import jalview.datamodel.Sequence;
-import jalview.datamodel.SequenceFeature;
-import jalview.datamodel.SequenceI;
-import jalview.util.DBRefUtils;
-import jalview.util.DnaUtils;
-import jalview.util.MapList;
-import jalview.util.MappingUtils;
-import jalview.util.StringUtils;
-
-import java.text.ParseException;
-import java.util.Arrays;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Vector;
-import java.util.regex.Pattern;
-
-/**
- * Data model for one entry returned from an EMBL query, as marshalled by a
- * Castor binding file
- *
- * For example: http://www.ebi.ac.uk/ena/data/view/J03321&display=xml
- *
- * @see embl_mapping.xml
- */
-public class EmblEntry
-{
- private static final Pattern SPACE_PATTERN = Pattern.compile(" ");
-
- String accession;
-
- String entryVersion;
-
- String sequenceVersion;
-
- String dataClass;
-
- String moleculeType;
-
- String topology;
-
- String sequenceLength;
-
- String taxonomicDivision;
-
- String description;
-
- String firstPublicDate;
-
- String firstPublicRelease;
-
- String lastUpdatedDate;
-
- String lastUpdatedRelease;
-
- Vector keywords;
-
- Vector dbRefs;
-
- Vector features;
-
- EmblSequence sequence;
-
- /**
- * @return the accession
- */
- public String getAccession()
- {
- return accession;
- }
-
- /**
- * @param accession
- * the accession to set
- */
- public void setAccession(String accession)
- {
- this.accession = accession;
- }
-
- /**
- * @return the dbRefs
- */
- public Vector getDbRefs()
- {
- return dbRefs;
- }
-
- /**
- * @param dbRefs
- * the dbRefs to set
- */
- public void setDbRefs(Vector dbRefs)
- {
- this.dbRefs = dbRefs;
- }
-
- /**
- * @return the features
- */
- public Vector getFeatures()
- {
- return features;
- }
-
- /**
- * @param features
- * the features to set
- */
- public void setFeatures(Vector features)
- {
- this.features = features;
- }
-
- /**
- * @return the keywords
- */
- public Vector getKeywords()
- {
- return keywords;
- }
-
- /**
- * @param keywords
- * the keywords to set
- */
- public void setKeywords(Vector keywords)
- {
- this.keywords = keywords;
- }
-
- /**
- * @return the sequence
- */
- public EmblSequence getSequence()
- {
- return sequence;
- }
-
- /**
- * @param sequence
- * the sequence to set
- */
- public void setSequence(EmblSequence sequence)
- {
- this.sequence = sequence;
- }
-
- /**
- * Recover annotated sequences from EMBL file
- *
- * @param sourceDb
- * @param peptides
- * a list of protein products found so far (to add to)
- * @return dna dataset sequence with DBRefs and features
- */
- public SequenceI getSequence(String sourceDb, List peptides)
- {
- SequenceI dna = makeSequence(sourceDb);
- if (dna == null)
- {
- return null;
- }
- dna.setDescription(description);
- DBRefEntry retrievedref = new DBRefEntry(sourceDb, getSequenceVersion(),
- accession);
- dna.addDBRef(retrievedref);
- // add map to indicate the sequence is a valid coordinate frame for the
- // dbref
- retrievedref
- .setMap(new Mapping(null, new int[]
- { 1, dna.getLength() }, new int[] { 1, dna.getLength() }, 1,
- 1));
-
- /*
- * transform EMBL Database refs to canonical form
- */
- if (dbRefs != null)
- {
- for (DBRefEntry dbref : dbRefs)
- {
- dbref.setSource(DBRefUtils.getCanonicalName(dbref.getSource()));
- dna.addDBRef(dbref);
- }
- }
-
- SequenceIdMatcher matcher = new SequenceIdMatcher(peptides);
- try
- {
- for (EmblFeature feature : features)
- {
- if (FeatureProperties.isCodingFeature(sourceDb, feature.getName()))
- {
- parseCodingFeature(feature, sourceDb, dna, peptides, matcher);
- }
- }
- } catch (Exception e)
- {
- System.err.println("EMBL Record Features parsing error!");
- System.err
- .println("Please report the following to help@jalview.org :");
- System.err.println("EMBL Record " + accession);
- System.err.println("Resulted in exception: " + e.getMessage());
- e.printStackTrace(System.err);
- }
-
- return dna;
- }
-
- /**
- * @param sourceDb
- * @return
- */
- SequenceI makeSequence(String sourceDb)
- {
- if (sequence == null)
- {
- System.err.println(
- "No sequence was returned for ENA accession " + accession);
- return null;
- }
- SequenceI dna = new Sequence(sourceDb + "|" + accession,
- sequence.getSequence());
- return dna;
- }
-
- /**
- * Extracts coding region and product from a CDS feature and properly decorate
- * it with annotations.
- *
- * @param feature
- * coding feature
- * @param sourceDb
- * source database for the EMBLXML
- * @param dna
- * parent dna sequence for this record
- * @param peptides
- * list of protein product sequences for Embl entry
- * @param matcher
- * helper to match xrefs in already retrieved sequences
- */
- void parseCodingFeature(EmblFeature feature, String sourceDb,
- SequenceI dna, List peptides,
- SequenceIdMatcher matcher)
- {
- boolean isEmblCdna = sourceDb.equals(DBRefSource.EMBLCDS);
-
- int[] exons = getCdsRanges(feature);
-
- String translation = null;
- String proteinName = "";
- String proteinId = null;
- Map vals = new Hashtable<>();
-
- /*
- * codon_start 1/2/3 in EMBL corresponds to phase 0/1/2 in CDS
- * (phase is required for CDS features in GFF3 format)
- */
- int codonStart = 1;
-
- /*
- * parse qualifiers, saving protein translation, protein id,
- * codon start position, product (name), and 'other values'
- */
- if (feature.getQualifiers() != null)
- {
- for (Qualifier q : feature.getQualifiers())
- {
- String qname = q.getName();
- if (qname.equals("translation"))
- {
- // remove all spaces (precompiled String.replaceAll(" ", ""))
- translation = SPACE_PATTERN.matcher(q.getValues()[0])
- .replaceAll("");
- }
- else if (qname.equals("protein_id"))
- {
- proteinId = q.getValues()[0].trim();
- }
- else if (qname.equals("codon_start"))
- {
- try
- {
- codonStart = Integer.parseInt(q.getValues()[0].trim());
- } catch (NumberFormatException e)
- {
- System.err.println("Invalid codon_start in XML for " + accession
- + ": " + e.getMessage());
- }
- }
- else if (qname.equals("product"))
- {
- // sometimes name is returned e.g. for V00488
- proteinName = q.getValues()[0].trim();
- }
- else
- {
- // throw anything else into the additional properties hash
- String[] qvals = q.getValues();
- if (qvals != null)
- {
- String commaSeparated = StringUtils.arrayToSeparatorList(qvals,
- ",");
- vals.put(qname, commaSeparated);
- }
- }
- }
- }
-
- DBRefEntry proteinToEmblProteinRef = null;
- exons = MappingUtils.removeStartPositions(codonStart - 1, exons);
-
- SequenceI product = null;
- Mapping dnaToProteinMapping = null;
- if (translation != null && proteinName != null && proteinId != null)
- {
- int translationLength = translation.length();
-
- /*
- * look for product in peptides list, if not found, add it
- */
- product = matcher.findIdMatch(proteinId);
- if (product == null)
- {
- product = new Sequence(proteinId, translation, 1,
- translationLength);
- product.setDescription(((proteinName.length() == 0)
- ? "Protein Product from " + sourceDb
- : proteinName));
- peptides.add(product);
- matcher.add(product);
- }
-
- // we have everything - create the mapping and perhaps the protein
- // sequence
- if (exons == null || exons.length == 0)
- {
- /*
- * workaround until we handle dna location for CDS sequence
- * e.g. location="X53828.1:60..1058" correctly
- */
- System.err.println(
- "Implementation Notice: EMBLCDS records not properly supported yet - Making up the CDNA region of this sequence... may be incorrect ("
- + sourceDb + ":" + getAccession() + ")");
- int dnaLength = dna.getLength();
- if (translationLength * 3 == (1 - codonStart + dnaLength))
- {
- System.err.println(
- "Not allowing for additional stop codon at end of cDNA fragment... !");
- // this might occur for CDS sequences where no features are marked
- exons = new int[] { dna.getStart() + (codonStart - 1),
- dna.getEnd() };
- dnaToProteinMapping = new Mapping(product, exons,
- new int[]
- { 1, translationLength }, 3, 1);
- }
- if ((translationLength + 1) * 3 == (1 - codonStart + dnaLength))
- {
- System.err.println(
- "Allowing for additional stop codon at end of cDNA fragment... will probably cause an error in VAMSAs!");
- exons = new int[] { dna.getStart() + (codonStart - 1),
- dna.getEnd() - 3 };
- dnaToProteinMapping = new Mapping(product, exons,
- new int[]
- { 1, translationLength }, 3, 1);
- }
- }
- else
- {
- // Trim the exon mapping if necessary - the given product may only be a
- // fragment of a larger protein. (EMBL:AY043181 is an example)
-
- if (isEmblCdna)
- {
- // TODO: Add a DbRef back to the parent EMBL sequence with the exon
- // map
- // if given a dataset reference, search dataset for parent EMBL
- // sequence if it exists and set its map
- // make a new feature annotating the coding contig
- }
- else
- {
- // final product length truncation check
- int[] cdsRanges = adjustForProteinLength(translationLength,
- exons);
- dnaToProteinMapping = new Mapping(product, cdsRanges,
- new int[]
- { 1, translationLength }, 3, 1);
- if (product != null)
- {
- /*
- * make xref with mapping from protein to EMBL dna
- */
- DBRefEntry proteinToEmblRef = new DBRefEntry(DBRefSource.EMBL,
- getSequenceVersion(), proteinId,
- new Mapping(dnaToProteinMapping.getMap().getInverse()));
- product.addDBRef(proteinToEmblRef);
-
- /*
- * make xref from protein to EMBLCDS; we assume here that the
- * CDS sequence version is same as dna sequence (?!)
- */
- MapList proteinToCdsMapList = new MapList(
- new int[]
- { 1, translationLength },
- new int[]
- { 1 + (codonStart - 1),
- (codonStart - 1) + 3 * translationLength },
- 1, 3);
- DBRefEntry proteinToEmblCdsRef = new DBRefEntry(
- DBRefSource.EMBLCDS, getSequenceVersion(), proteinId,
- new Mapping(proteinToCdsMapList));
- product.addDBRef(proteinToEmblCdsRef);
-
- /*
- * make 'direct' xref from protein to EMBLCDSPROTEIN
- */
- proteinToEmblProteinRef = new DBRefEntry(proteinToEmblCdsRef);
- proteinToEmblProteinRef.setSource(DBRefSource.EMBLCDSProduct);
- proteinToEmblProteinRef.setMap(null);
- product.addDBRef(proteinToEmblProteinRef);
- }
- }
- }
-
- /*
- * add cds features to dna sequence
- */
- String cds = feature.getName(); // "CDS"
- for (int xint = 0; exons != null && xint < exons.length - 1; xint += 2)
- {
- int exonStart = exons[xint];
- int exonEnd = exons[xint + 1];
- int begin = Math.min(exonStart, exonEnd);
- int end = Math.max(exonStart, exonEnd);
- int exonNumber = xint / 2 + 1;
- String desc = String.format("Exon %d for protein '%s' EMBLCDS:%s",
- exonNumber, proteinName, proteinId);
-
- SequenceFeature sf = makeCdsFeature(cds, desc, begin, end,
- sourceDb, vals);
-
- sf.setEnaLocation(feature.getLocation());
- boolean forwardStrand = exonStart <= exonEnd;
- sf.setStrand(forwardStrand ? "+" : "-");
- sf.setPhase(String.valueOf(codonStart - 1));
- sf.setValue(FeatureProperties.EXONPOS, exonNumber);
- sf.setValue(FeatureProperties.EXONPRODUCT, proteinName);
-
- dna.addSequenceFeature(sf);
- }
- }
-
- /*
- * add feature dbRefs to sequence, and mappings for Uniprot xrefs
- */
- boolean hasUniprotDbref = false;
- if (feature.dbRefs != null)
- {
- boolean mappingUsed = false;
- for (DBRefEntry ref : feature.dbRefs)
- {
- /*
- * ensure UniProtKB/Swiss-Prot converted to UNIPROT
- */
- String source = DBRefUtils.getCanonicalName(ref.getSource());
- ref.setSource(source);
- DBRefEntry proteinDbRef = new DBRefEntry(ref.getSource(),
- ref.getVersion(), ref.getAccessionId());
- if (source.equals(DBRefSource.UNIPROT))
- {
- String proteinSeqName = DBRefSource.UNIPROT + "|"
- + ref.getAccessionId();
- if (dnaToProteinMapping != null
- && dnaToProteinMapping.getTo() != null)
- {
- if (mappingUsed)
- {
- /*
- * two or more Uniprot xrefs for the same CDS -
- * each needs a distinct Mapping (as to a different sequence)
- */
- dnaToProteinMapping = new Mapping(dnaToProteinMapping);
- }
- mappingUsed = true;
-
- /*
- * try to locate the protein mapped to (possibly by a
- * previous CDS feature); if not found, construct it from
- * the EMBL translation
- */
- SequenceI proteinSeq = matcher.findIdMatch(proteinSeqName);
- if (proteinSeq == null)
- {
- proteinSeq = new Sequence(proteinSeqName,
- product.getSequenceAsString());
- matcher.add(proteinSeq);
- peptides.add(proteinSeq);
- }
- dnaToProteinMapping.setTo(proteinSeq);
- dnaToProteinMapping.setMappedFromId(proteinId);
- proteinSeq.addDBRef(proteinDbRef);
- ref.setMap(dnaToProteinMapping);
- }
- hasUniprotDbref = true;
- }
- if (product != null)
- {
- /*
- * copy feature dbref to our protein product
- */
- DBRefEntry pref = proteinDbRef;
- pref.setMap(null); // reference is direct
- product.addDBRef(pref);
- // Add converse mapping reference
- if (dnaToProteinMapping != null)
- {
- Mapping pmap = new Mapping(dna,
- dnaToProteinMapping.getMap().getInverse());
- pref = new DBRefEntry(sourceDb, getSequenceVersion(),
- this.getAccession());
- pref.setMap(pmap);
- if (dnaToProteinMapping.getTo() != null)
- {
- dnaToProteinMapping.getTo().addDBRef(pref);
- }
- }
- }
- dna.addDBRef(ref);
- }
- }
-
- /*
- * if we have a product (translation) but no explicit Uniprot dbref
- * (example: EMBL AAFI02000057 protein_id EAL65544.1)
- * then construct mappings to an assumed EMBLCDSPROTEIN accession
- */
- if (!hasUniprotDbref && product != null)
- {
- if (proteinToEmblProteinRef == null)
- {
- // assuming CDSPROTEIN sequence version = dna version (?!)
- proteinToEmblProteinRef = new DBRefEntry(DBRefSource.EMBLCDSProduct,
- getSequenceVersion(), proteinId);
- }
- product.addDBRef(proteinToEmblProteinRef);
-
- if (dnaToProteinMapping != null
- && dnaToProteinMapping.getTo() != null)
- {
- DBRefEntry dnaToEmblProteinRef = new DBRefEntry(
- DBRefSource.EMBLCDSProduct, getSequenceVersion(),
- proteinId);
- dnaToEmblProteinRef.setMap(dnaToProteinMapping);
- dnaToProteinMapping.setMappedFromId(proteinId);
- dna.addDBRef(dnaToEmblProteinRef);
- }
- }
- }
-
- /**
- * Helper method to construct a SequenceFeature for one cds range
- *
- * @param type
- * feature type ("CDS")
- * @param desc
- * description
- * @param begin
- * start position
- * @param end
- * end position
- * @param group
- * feature group
- * @param vals
- * map of 'miscellaneous values' for feature
- * @return
- */
- protected SequenceFeature makeCdsFeature(String type, String desc,
- int begin, int end, String group, Map vals)
- {
- SequenceFeature sf = new SequenceFeature(type, desc, begin, end, group);
- if (!vals.isEmpty())
- {
- StringBuilder sb = new StringBuilder();
- boolean first = true;
- for (Entry val : vals.entrySet())
- {
- if (!first)
- {
- sb.append(";");
- }
- sb.append(val.getKey()).append("=").append(val.getValue());
- first = false;
- sf.setValue(val.getKey(), val.getValue());
- }
- sf.setAttributes(sb.toString());
- }
- return sf;
- }
-
- /**
- * Returns the CDS positions as a single array of [start, end, start, end...]
- * positions. If on the reverse strand, these will be in descending order.
- *
- * @param feature
- * @return
- */
- protected int[] getCdsRanges(EmblFeature feature)
- {
- if (feature.location == null)
- {
- return new int[] {};
- }
-
- try
- {
- List ranges = DnaUtils.parseLocation(feature.location);
- return listToArray(ranges);
- } catch (ParseException e)
- {
- Cache.log.warn(
- String.format("Not parsing inexact CDS location %s in ENA %s",
- feature.location, this.accession));
- return new int[] {};
- }
- }
-
- /**
- * Converts a list of [start, end] ranges to a single array of [start, end,
- * start, end ...]
- *
- * @param ranges
- * @return
- */
- int[] listToArray(List ranges)
- {
- int[] result = new int[ranges.size() * 2];
- int i = 0;
- for (int[] range : ranges)
- {
- result[i++] = range[0];
- result[i++] = range[1];
- }
- return result;
- }
-
- /**
- * Truncates (if necessary) the exon intervals to match 3 times the length of
- * the protein; also accepts 3 bases longer (for stop codon not included in
- * protein)
- *
- * @param proteinLength
- * @param exon
- * an array of [start, end, start, end...] intervals
- * @return the same array (if unchanged) or a truncated copy
- */
- static int[] adjustForProteinLength(int proteinLength, int[] exon)
- {
- if (proteinLength <= 0 || exon == null)
- {
- return exon;
- }
- int expectedCdsLength = proteinLength * 3;
- int exonLength = MappingUtils.getLength(Arrays.asList(exon));
-
- /*
- * if exon length matches protein, or is shorter, or longer by the
- * length of a stop codon (3 bases), then leave it unchanged
- */
- if (expectedCdsLength >= exonLength
- || expectedCdsLength == exonLength - 3)
- {
- return exon;
- }
-
- int origxon[];
- int sxpos = -1;
- int endxon = 0;
- origxon = new int[exon.length];
- System.arraycopy(exon, 0, origxon, 0, exon.length);
- int cdspos = 0;
- for (int x = 0; x < exon.length; x += 2)
- {
- cdspos += Math.abs(exon[x + 1] - exon[x]) + 1;
- if (expectedCdsLength <= cdspos)
- {
- // advanced beyond last codon.
- sxpos = x;
- if (expectedCdsLength != cdspos)
- {
- // System.err
- // .println("Truncating final exon interval on region by "
- // + (cdspos - cdslength));
- }
-
- /*
- * shrink the final exon - reduce end position if forward
- * strand, increase it if reverse
- */
- if (exon[x + 1] >= exon[x])
- {
- endxon = exon[x + 1] - cdspos + expectedCdsLength;
- }
- else
- {
- endxon = exon[x + 1] + cdspos - expectedCdsLength;
- }
- break;
- }
- }
-
- if (sxpos != -1)
- {
- // and trim the exon interval set if necessary
- int[] nxon = new int[sxpos + 2];
- System.arraycopy(exon, 0, nxon, 0, sxpos + 2);
- nxon[sxpos + 1] = endxon; // update the end boundary for the new exon
- // set
- exon = nxon;
- }
- return exon;
- }
-
- public String getSequenceVersion()
- {
- return sequenceVersion;
- }
-
- public void setSequenceVersion(String sequenceVersion)
- {
- this.sequenceVersion = sequenceVersion;
- }
-
- public String getSequenceLength()
- {
- return sequenceLength;
- }
-
- public void setSequenceLength(String sequenceLength)
- {
- this.sequenceLength = sequenceLength;
- }
-
- public String getEntryVersion()
- {
- return entryVersion;
- }
-
- public void setEntryVersion(String entryVersion)
- {
- this.entryVersion = entryVersion;
- }
-
- public String getMoleculeType()
- {
- return moleculeType;
- }
-
- public void setMoleculeType(String moleculeType)
- {
- this.moleculeType = moleculeType;
- }
-
- public String getTopology()
- {
- return topology;
- }
-
- public void setTopology(String topology)
- {
- this.topology = topology;
- }
-
- public String getTaxonomicDivision()
- {
- return taxonomicDivision;
- }
-
- public void setTaxonomicDivision(String taxonomicDivision)
- {
- this.taxonomicDivision = taxonomicDivision;
- }
-
- public String getDescription()
- {
- return description;
- }
-
- public void setDescription(String description)
- {
- this.description = description;
- }
-
- public String getFirstPublicDate()
- {
- return firstPublicDate;
- }
-
- public void setFirstPublicDate(String firstPublicDate)
- {
- this.firstPublicDate = firstPublicDate;
- }
-
- public String getFirstPublicRelease()
- {
- return firstPublicRelease;
- }
-
- public void setFirstPublicRelease(String firstPublicRelease)
- {
- this.firstPublicRelease = firstPublicRelease;
- }
-
- public String getLastUpdatedDate()
- {
- return lastUpdatedDate;
- }
-
- public void setLastUpdatedDate(String lastUpdatedDate)
- {
- this.lastUpdatedDate = lastUpdatedDate;
- }
-
- public String getLastUpdatedRelease()
- {
- return lastUpdatedRelease;
- }
-
- public void setLastUpdatedRelease(String lastUpdatedRelease)
- {
- this.lastUpdatedRelease = lastUpdatedRelease;
- }
-
- public String getDataClass()
- {
- return dataClass;
- }
-
- public void setDataClass(String dataClass)
- {
- this.dataClass = dataClass;
- }
-}
diff --git a/src/jalview/datamodel/xdb/embl/EmblError.java b/src/jalview/datamodel/xdb/embl/EmblError.java
deleted file mode 100644
index 94de28f..0000000
--- a/src/jalview/datamodel/xdb/embl/EmblError.java
+++ /dev/null
@@ -1,48 +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 .
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.datamodel.xdb.embl;
-
-/**
- * Data model mapped from any <error> elements returned from an EMBL query
- *
- * @see embl_mapping.xml
- */
-public class EmblError
-{
- String accession;
-
- /**
- * @return the accession
- */
- public String getAccession()
- {
- return accession;
- }
-
- /**
- * @param accession
- * the accession to set
- */
- public void setAccession(String accession)
- {
- this.accession = accession;
- }
-}
diff --git a/src/jalview/datamodel/xdb/embl/EmblFeature.java b/src/jalview/datamodel/xdb/embl/EmblFeature.java
deleted file mode 100644
index 51d740b..0000000
--- a/src/jalview/datamodel/xdb/embl/EmblFeature.java
+++ /dev/null
@@ -1,108 +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 .
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.datamodel.xdb.embl;
-
-import jalview.datamodel.DBRefEntry;
-
-import java.util.Vector;
-
-/**
- * Data model for a <feature> element returned from an EMBL query reply
- *
- * @see embl_mapping.xml
- */
-public class EmblFeature
-{
- String name;
-
- Vector dbRefs;
-
- Vector qualifiers;
-
- String location;
-
- /**
- * @return the dbRefs
- */
- public Vector getDbRefs()
- {
- return dbRefs;
- }
-
- /**
- * @param dbRefs
- * the dbRefs to set
- */
- public void setDbRefs(Vector dbRefs)
- {
- this.dbRefs = dbRefs;
- }
-
- /**
- * @return the location
- */
- public String getLocation()
- {
- return location;
- }
-
- /**
- * @param loc
- */
- public void setLocation(String loc)
- {
- this.location = loc;
- }
-
- /**
- * @return the name
- */
- public String getName()
- {
- return name;
- }
-
- /**
- * @param name
- * the name to set
- */
- public void setName(String name)
- {
- this.name = name;
- }
-
- /**
- * @return the qualifiers
- */
- public Vector getQualifiers()
- {
- return qualifiers;
- }
-
- /**
- * @param qualifiers
- * the qualifiers to set
- */
- public void setQualifiers(Vector qualifiers)
- {
- this.qualifiers = qualifiers;
- }
-}
diff --git a/src/jalview/datamodel/xdb/embl/EmblFile.java b/src/jalview/datamodel/xdb/embl/EmblFile.java
deleted file mode 100644
index 8a32c13..0000000
--- a/src/jalview/datamodel/xdb/embl/EmblFile.java
+++ /dev/null
@@ -1,203 +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 .
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.datamodel.xdb.embl;
-
-import jalview.datamodel.DBRefEntry;
-import jalview.ws.dbsources.Uniprot;
-
-import java.io.File;
-import java.io.FileReader;
-import java.io.PrintWriter;
-import java.io.Reader;
-import java.util.Vector;
-
-import org.exolab.castor.mapping.Mapping;
-import org.exolab.castor.xml.Unmarshaller;
-
-/**
- * Data model for entries returned from an EMBL query, as marshalled by a Castor
- * binding file
- *
- * For example: http://www.ebi.ac.uk/Tools/dbfetch/dbfetch/embl/x53828/emblxml
- *
- * @see embl_mapping.xml
- */
-public class EmblFile
-{
- Vector entries;
-
- Vector errors;
-
- String text;
-
- /**
- * @return the entries
- */
- public Vector getEntries()
- {
- return entries;
- }
-
- /**
- * @param entries
- * the entries to set
- */
- public void setEntries(Vector entries)
- {
- this.entries = entries;
- }
-
- /**
- * @return the errors
- */
- public Vector getErrors()
- {
- return errors;
- }
-
- /**
- * @param errors
- * the errors to set
- */
- public void setErrors(Vector errors)
- {
- this.errors = errors;
- }
-
- /**
- * Parse an EmblXML file into an EmblFile object
- *
- * @param file
- * @return parsed EmblXML or null if exceptions were raised
- */
- public static EmblFile getEmblFile(File file)
- {
- if (file == null)
- {
- return null;
- }
- try
- {
- return EmblFile.getEmblFile(new FileReader(file));
- } catch (Exception e)
- {
- System.err.println("Exception whilst reading EMBLfile from " + file);
- e.printStackTrace(System.err);
- }
- return null;
- }
-
- public static EmblFile getEmblFile(Reader file)
- {
- EmblFile record = new EmblFile();
- try
- {
- // 1. Load the mapping information from the file
- Mapping map = new Mapping(record.getClass().getClassLoader());
-
- java.net.URL url = record.getClass().getResource("/embl_mapping.xml");
- map.loadMapping(url);
-
- // 2. Unmarshal the data
- Unmarshaller unmar = new Unmarshaller(record);
- try
- {
- // uncomment to DEBUG EMBLFile reading
- if (jalview.bin.Cache
- .getDefault(jalview.bin.Cache.CASTORLOGLEVEL, "debug")
- .equalsIgnoreCase("DEBUG"))
- {
- unmar.setDebug(jalview.bin.Cache.log.isDebugEnabled());
- }
- } catch (Exception e)
- {
- }
- unmar.setIgnoreExtraElements(true);
- unmar.setIgnoreExtraAttributes(true);
- unmar.setMapping(map);
- unmar.setLogWriter(new PrintWriter(System.out));
- record = (EmblFile) unmar.unmarshal(file);
-
- canonicaliseDbRefs(record);
- } catch (Exception e)
- {
- e.printStackTrace(System.err);
- record = null;
- }
-
- return record;
- }
-
- /**
- * Change blank version to "0" in any DBRefEntry, to ensure consistent
- * comparison with other DBRefEntry in Jalview
- *
- * @param record
- * @see Uniprot#getDbVersion
- */
- static void canonicaliseDbRefs(EmblFile record)
- {
- if (record.getEntries() == null)
- {
- return;
- }
- for (EmblEntry entry : record.getEntries())
- {
- if (entry.getDbRefs() != null)
- {
- for (DBRefEntry dbref : entry.getDbRefs())
- {
- if ("".equals(dbref.getVersion()))
- {
- dbref.setVersion("0");
- }
- }
- }
-
- if (entry.getFeatures() != null)
- {
- for (EmblFeature feature : entry.getFeatures())
- {
- if (feature.getDbRefs() != null)
- {
- for (DBRefEntry dbref : feature.getDbRefs())
- {
- if ("".equals(dbref.getVersion()))
- {
- dbref.setVersion("0");
- }
- }
- }
- }
- }
- }
- }
-
- public String getText()
- {
- return text;
- }
-
- public void setText(String text)
- {
- this.text = text;
- }
-}
diff --git a/src/jalview/datamodel/xdb/embl/EmblSequence.java b/src/jalview/datamodel/xdb/embl/EmblSequence.java
deleted file mode 100644
index 92c424b..0000000
--- a/src/jalview/datamodel/xdb/embl/EmblSequence.java
+++ /dev/null
@@ -1,49 +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 .
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.datamodel.xdb.embl;
-
-/**
- * Data model for the sequence extracted from an EMBL query reply
- *
- * @see embl_mapping.xml
- */
-public class EmblSequence
-{
- String sequence;
-
- /**
- * @return the sequence
- */
- public String getSequence()
- {
- return sequence;
- }
-
- /**
- * @param sequence
- * the sequence to set
- */
- public void setSequence(String sequence)
- {
- // remove spaces introduced by unmarshalling of newline characters
- this.sequence = sequence.replace(" ", "");
- }
-}
diff --git a/src/jalview/datamodel/xdb/embl/Qualifier.java b/src/jalview/datamodel/xdb/embl/Qualifier.java
deleted file mode 100644
index 851dd48..0000000
--- a/src/jalview/datamodel/xdb/embl/Qualifier.java
+++ /dev/null
@@ -1,119 +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 .
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.datamodel.xdb.embl;
-
-/**
- * Data model for a <qualifier> child element of a <feature> read
- * from an EMBL query reply
- *
- * @see embl_mapping.xml
- */
-public class Qualifier
-{
- String name;
-
- String[] values;
-
- String[] evidence;
-
- /**
- * @return the name
- */
- public String getName()
- {
- return name;
- }
-
- /**
- * @param name
- * the name to set
- */
- public void setName(String name)
- {
- this.name = name;
- }
-
- /**
- * @return the values
- */
- public String[] getValues()
- {
- return values;
- }
-
- /**
- * @param values
- * the values to set
- */
- public void setValues(String[] values)
- {
- this.values = values;
- }
-
- public void addEvidence(String qevidence)
- {
- // TODO - not used? can remove?
- if (evidence == null)
- {
- evidence = new String[1];
- }
- else
- {
- String[] temp = new String[evidence.length + 1];
- System.arraycopy(evidence, 0, temp, 0, evidence.length);
- evidence = temp;
- }
- evidence[evidence.length - 1] = qevidence;
- }
-
- public void addValues(String value)
- {
- // TODO - not used? can remove?
- if (values == null)
- {
- values = new String[1];
- }
- else
- {
- String[] temp = new String[values.length + 1];
- System.arraycopy(values, 0, temp, 0, values.length);
- values = temp;
- }
- values[values.length - 1] = value;
- }
-
- /**
- * @return the evidence
- */
- public String[] getEvidence()
- {
- return evidence;
- }
-
- /**
- * @param evidence
- * the evidence to set
- */
- public void setEvidence(String[] evidence)
- {
- this.evidence = evidence;
- }
-}
diff --git a/src/jalview/datamodel/xdb/uniprot/UniprotEntry.java b/src/jalview/datamodel/xdb/uniprot/UniprotEntry.java
deleted file mode 100755
index a3537c9..0000000
--- a/src/jalview/datamodel/xdb/uniprot/UniprotEntry.java
+++ /dev/null
@@ -1,107 +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 .
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.datamodel.xdb.uniprot;
-
-import jalview.datamodel.PDBEntry;
-
-import java.util.Vector;
-
-/**
- * Data model for an entry returned from a Uniprot query
- *
- * @see uniprot_mapping.xml
- */
-public class UniprotEntry
-{
-
- UniprotSequence sequence;
-
- Vector name;
-
- Vector accession;
-
- Vector feature;
-
- Vector dbrefs;
-
- UniprotProteinName protName;
-
- public void setAccession(Vector items)
- {
- accession = items;
- }
-
- public void setFeature(Vector items)
- {
- feature = items;
- }
-
- public Vector getFeature()
- {
- return feature;
- }
-
- public Vector getAccession()
- {
- return accession;
- }
-
- public void setProtein(UniprotProteinName names)
- {
- protName = names;
- }
-
- public UniprotProteinName getProtein()
- {
- return protName;
- }
-
- public void setName(Vector na)
- {
- name = na;
- }
-
- public Vector getName()
- {
- return name;
- }
-
- public UniprotSequence getUniprotSequence()
- {
- return sequence;
- }
-
- public void setUniprotSequence(UniprotSequence seq)
- {
- sequence = seq;
- }
-
- public Vector getDbReference()
- {
- return dbrefs;
- }
-
- public void setDbReference(Vector dbref)
- {
- this.dbrefs = dbref;
- }
-
-}
diff --git a/src/jalview/datamodel/xdb/uniprot/UniprotFeature.java b/src/jalview/datamodel/xdb/uniprot/UniprotFeature.java
deleted file mode 100644
index 8bd5652..0000000
--- a/src/jalview/datamodel/xdb/uniprot/UniprotFeature.java
+++ /dev/null
@@ -1,124 +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 .
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.datamodel.xdb.uniprot;
-
-import java.util.Vector;
-
-/**
- * A data model class for binding from Uniprot XML via uniprot_mapping.xml
- */
-public class UniprotFeature
-{
- private String type;
-
- private String description = null;
-
- private String original = null;
-
- private Vector variation = null;
-
- private String status;
-
- private int begin;
-
- private int end;
-
- public String getType()
- {
- return type;
- }
-
- public void setType(String t)
- {
- this.type = t;
- }
-
- public String getDescription()
- {
- return description;
- }
-
- public void setDescription(String d)
- {
- this.description = d;
- }
-
- public String getStatus()
- {
- return status;
- }
-
- public void setStatus(String s)
- {
- this.status = s;
- }
-
- public int getBegin()
- {
- return begin;
- }
-
- public void setBegin(int b)
- {
- this.begin = b;
- }
-
- public int getEnd()
- {
- return end;
- }
-
- public void setEnd(int e)
- {
- this.end = e;
- }
-
- public int getPosition()
- {
- return begin;
- }
-
- public void setPosition(int p)
- {
- this.begin = p;
- this.end = p;
- }
-
- public String getOriginal()
- {
- return original;
- }
-
- public void setOriginal(String original)
- {
- this.original = original;
- }
-
- public Vector getVariation()
- {
- return variation;
- }
-
- public void setVariation(Vector variant)
- {
- this.variation = variant;
- }
-}
diff --git a/src/jalview/datamodel/xdb/uniprot/UniprotFile.java b/src/jalview/datamodel/xdb/uniprot/UniprotFile.java
deleted file mode 100755
index 9cc0391..0000000
--- a/src/jalview/datamodel/xdb/uniprot/UniprotFile.java
+++ /dev/null
@@ -1,42 +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 .
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.datamodel.xdb.uniprot;
-
-import java.util.Vector;
-
-/**
- * Data model of a retrieved Uniprot entry, as unmarshalled by Castor using a
- * binding file (uniprot_mapping.xml)
- */
-public class UniprotFile
-{
- Vector _items;
-
- public void setUniprotEntries(Vector items)
- {
- _items = items;
- }
-
- public Vector getUniprotEntries()
- {
- return _items;
- }
-}
diff --git a/src/jalview/datamodel/xdb/uniprot/UniprotProteinName.java b/src/jalview/datamodel/xdb/uniprot/UniprotProteinName.java
deleted file mode 100755
index 2335e71..0000000
--- a/src/jalview/datamodel/xdb/uniprot/UniprotProteinName.java
+++ /dev/null
@@ -1,47 +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 .
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.datamodel.xdb.uniprot;
-
-import java.util.Vector;
-
-/**
- * Data model for protein name returned from a Uniprot query
- *
- * Protein names are read from the Uniprot XML element
- * uniprot/entry/protein/recommendedName/fullName
- *
- * @see uniprot_mapping.xml
- */
-public class UniprotProteinName
-{
- private Vector names;
-
- public void setName(Vector names)
- {
- this.names = names;
- }
-
- public Vector getName()
- {
- return names;
- }
-
-}
diff --git a/src/jalview/datamodel/xdb/uniprot/UniprotSequence.java b/src/jalview/datamodel/xdb/uniprot/UniprotSequence.java
deleted file mode 100755
index bdba73f..0000000
--- a/src/jalview/datamodel/xdb/uniprot/UniprotSequence.java
+++ /dev/null
@@ -1,58 +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 .
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.datamodel.xdb.uniprot;
-
-/**
- * Data model for the sequence returned by a Uniprot query
- *
- * @see uniprot_mapping.xml
- */
-public class UniprotSequence
-{
- private String _content = "";
-
- /**
- * Sets the content string, omitting any space characters
- *
- * @param seq
- */
- public void setContent(String seq)
- {
- if (seq != null)
- {
- StringBuilder sb = new StringBuilder(seq.length());
- for (int i = 0; i < seq.length(); i++)
- {
- if (seq.charAt(i) != ' ')
- {
- sb.append(seq.charAt(i));
- }
- }
- _content = sb.toString();
- }
- }
-
- public String getContent()
- {
- return _content;
- }
-
-}
diff --git a/src/jalview/ext/ensembl/EnsemblSeqProxy.java b/src/jalview/ext/ensembl/EnsemblSeqProxy.java
index 5dc701d..001e18e 100644
--- a/src/jalview/ext/ensembl/EnsemblSeqProxy.java
+++ b/src/jalview/ext/ensembl/EnsemblSeqProxy.java
@@ -330,8 +330,8 @@ public abstract class EnsemblSeqProxy extends EnsemblRestClient
* copy exon features to protein, compute peptide variants from dna
* variants and add as features on the protein sequence ta-da
*/
- AlignmentUtils.computeProteinFeatures(querySeq, proteinSeq,
- mapList);
+ // JAL-3187 render on the fly instead
+ // AlignmentUtils.computeProteinFeatures(querySeq, proteinSeq, mapList);
}
} catch (Exception e)
{
diff --git a/src/jalview/ext/jmol/JalviewJmolBinding.java b/src/jalview/ext/jmol/JalviewJmolBinding.java
index 8832278..e85d387 100644
--- a/src/jalview/ext/jmol/JalviewJmolBinding.java
+++ b/src/jalview/ext/jmol/JalviewJmolBinding.java
@@ -50,6 +50,7 @@ import java.util.BitSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
+import java.util.StringTokenizer;
import java.util.Vector;
import org.jmol.adapter.smarter.SmarterJmolAdapter;
@@ -65,6 +66,8 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
implements JmolStatusListener, JmolSelectionListener,
ComponentListener
{
+ private String lastMessage;
+
boolean allChainsSelected = false;
/*
@@ -89,8 +92,6 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
String lastCommand;
- String lastMessage;
-
boolean loadedInline;
StringBuffer resetLastRes = new StringBuffer();
@@ -822,7 +823,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
viewer.openStringInline(string);
}
- public void mouseOverStructure(int atomIndex, String strInfo)
+ protected void mouseOverStructure(int atomIndex, final String strInfo)
{
int pdbResNum;
int alocsep = strInfo.indexOf("^");
@@ -876,7 +877,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
try
{
// recover PDB filename for the model hovered over.
- int mnumber = new Integer(mdlId).intValue() - 1;
+ int mnumber = Integer.valueOf(mdlId).intValue() - 1;
if (_modelFileNameMap != null)
{
int _mp = _modelFileNameMap.length - 1;
@@ -903,18 +904,34 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
} catch (Exception e)
{
}
- ;
}
- if (lastMessage == null || !lastMessage.equals(strInfo))
+
+ /*
+ * highlight position on alignment(s); if some text is returned,
+ * show this as a second line on the structure hover tooltip
+ */
+ String label = getSsm().mouseOverStructure(pdbResNum, chainId,
+ pdbfilename);
+ if (label != null)
{
- getSsm().mouseOverStructure(pdbResNum, chainId, pdbfilename);
+ StringTokenizer toks = new StringTokenizer(strInfo, " ");
+ StringBuilder sb = new StringBuilder();
+ sb.append("select ").append(String.valueOf(pdbResNum)).append(":")
+ .append(chainId).append("/1");
+ sb.append(";set hoverLabel \"").append(toks.nextToken()).append(" ")
+ .append(toks.nextToken());
+ sb.append("|").append(label).append("\"");
+ evalStateCommand(sb.toString());
}
-
- lastMessage = strInfo;
}
public void notifyAtomHovered(int atomIndex, String strInfo, String data)
{
+ if (strInfo.equals(lastMessage))
+ {
+ return;
+ }
+ lastMessage = strInfo;
if (data != null)
{
System.err.println("Ignoring additional hover info: " + data
@@ -1254,7 +1271,10 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
return chainNames;
}
- protected abstract IProgressIndicator getIProgressIndicator();
+ protected IProgressIndicator getIProgressIndicator()
+ {
+ return null;
+ }
public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
{
diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java
index 94b38ed..477db0c 100644
--- a/src/jalview/gui/AlignFrame.java
+++ b/src/jalview/gui/AlignFrame.java
@@ -733,9 +733,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
int aSize = alignPanels.size();
- tabbedPane.setVisible(aSize > 1 || ap.av.viewName != null);
+ tabbedPane.setVisible(aSize > 1 || ap.av.getViewName() != null);
- if (aSize == 1 && ap.av.viewName == null)
+ if (aSize == 1 && ap.av.getViewName() == null)
{
this.getContentPane().add(ap, BorderLayout.CENTER);
}
@@ -748,7 +748,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
expandViews.setEnabled(true);
gatherViews.setEnabled(true);
- tabbedPane.addTab(ap.av.viewName, ap);
+ tabbedPane.addTab(ap.av.getViewName(), ap);
ap.setVisible(false);
}
@@ -771,7 +771,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
gatherViews.setEnabled(true);
tabbedPane.setVisible(true);
AlignmentPanel first = alignPanels.get(0);
- tabbedPane.addTab(first.av.viewName, first);
+ tabbedPane.addTab(first.av.getViewName(), first);
this.getContentPane().add(tabbedPane, BorderLayout.CENTER);
}
@@ -872,7 +872,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
* @param av
* AlignViewport
*/
- void setMenusFromViewport(AlignViewport av)
+ public void setMenusFromViewport(AlignViewport av)
{
padGapsMenuitem.setSelected(av.isPadGaps());
colourTextMenuItem.setSelected(av.isShowColourText());
@@ -1154,7 +1154,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
shortName.lastIndexOf(java.io.File.separatorChar) + 1);
}
- success = new Jalview2XML().saveAlignment(this, file, shortName);
+ success = new jalview.project.Jalview2XML().saveAlignment(this, file,
+ shortName);
statusBar.setText(MessageManager.formatMessage(
"label.successfully_saved_to_file_in_format", new Object[]
@@ -2701,7 +2702,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
/*
* Create a new AlignmentPanel (with its own, new Viewport)
*/
- AlignmentPanel newap = new Jalview2XML().copyAlignPanel(alignPanel);
+ AlignmentPanel newap = new jalview.project.Jalview2XML()
+ .copyAlignPanel(alignPanel);
if (!copyAnnotation)
{
/*
@@ -2713,10 +2715,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
newap.av.setGatherViewsHere(false);
- if (viewport.viewName == null)
+ if (viewport.getViewName() == null)
{
- viewport.viewName = MessageManager
- .getString("label.view_name_original");
+ viewport.setViewName(MessageManager
+ .getString("label.view_name_original"));
}
/*
@@ -2740,7 +2742,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
newap.refresh(true); // adjust layout of annotations
}
- newap.av.viewName = getNewViewName(viewTitle);
+ newap.av.setViewName(getNewViewName(viewTitle));
addAlignmentPanel(newap, true);
newap.alignmentChanged();
@@ -2803,9 +2805,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
if (comp instanceof AlignmentPanel)
{
AlignmentPanel ap = (AlignmentPanel) comp;
- if (!existingNames.contains(ap.av.viewName))
+ if (!existingNames.contains(ap.av.getViewName()))
{
- existingNames.add(ap.av.viewName);
+ existingNames.add(ap.av.getViewName());
}
}
}
@@ -3597,9 +3599,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
frameTitle += " from ";
- if (viewport.viewName != null)
+ if (viewport.getViewName() != null)
{
- frameTitle += viewport.viewName + " of ";
+ frameTitle += viewport.getViewName() + " of ";
}
frameTitle += this.title;
@@ -4767,7 +4769,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
if (reply != null)
{
- viewport.viewName = reply;
+ viewport.setViewName(reply);
// TODO warn if reply is in getExistingViewNames()?
tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), reply);
}
@@ -5399,7 +5401,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
*/
public List extends AlignmentViewPanel> getAlignPanels()
{
- return alignPanels == null ? Arrays.asList(alignPanel) : alignPanels;
+ // alignPanels is never null
+ // return alignPanels == null ? Arrays.asList(alignPanel) : alignPanels;
+ return alignPanels;
}
/**
diff --git a/src/jalview/gui/AlignViewport.java b/src/jalview/gui/AlignViewport.java
index 7e77bec..cc533ce 100644
--- a/src/jalview/gui/AlignViewport.java
+++ b/src/jalview/gui/AlignViewport.java
@@ -79,7 +79,7 @@ public class AlignViewport extends AlignmentViewport
private Rectangle explodedGeometry;
- String viewName;
+ private String viewName;
/*
* Flag set true on the view that should 'gather' multiple views of the same
@@ -1032,4 +1032,14 @@ public class AlignViewport extends AlignmentViewport
}
fr.setTransparency(featureSettings.getTransparency());
}
+
+ public String getViewName()
+ {
+ return viewName;
+ }
+
+ public void setViewName(String viewName)
+ {
+ this.viewName = viewName;
+ }
}
diff --git a/src/jalview/gui/AlignmentPanel.java b/src/jalview/gui/AlignmentPanel.java
index 60ef480..c03b56d 100644
--- a/src/jalview/gui/AlignmentPanel.java
+++ b/src/jalview/gui/AlignmentPanel.java
@@ -1567,7 +1567,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
@Override
public String getViewName()
{
- return av.viewName;
+ return av.getViewName();
}
/**
diff --git a/src/jalview/gui/CalculationChooser.java b/src/jalview/gui/CalculationChooser.java
index f674c7e..336a312 100644
--- a/src/jalview/gui/CalculationChooser.java
+++ b/src/jalview/gui/CalculationChooser.java
@@ -25,6 +25,7 @@ import jalview.analysis.scoremodels.ScoreModels;
import jalview.analysis.scoremodels.SimilarityParams;
import jalview.api.analysis.ScoreModelI;
import jalview.api.analysis.SimilarityParamsI;
+import jalview.bin.Cache;
import jalview.datamodel.SequenceGroup;
import jalview.util.MessageManager;
@@ -103,7 +104,7 @@ public class CalculationChooser extends JPanel
final ComboBoxTooltipRenderer renderer = new ComboBoxTooltipRenderer();
- List tips = new ArrayList();
+ List tips = new ArrayList<>();
/*
* the most recently opened PCA results panel
@@ -270,9 +271,9 @@ public class CalculationChooser extends JPanel
setMinimumSize(new Dimension(325, height - 10));
String title = MessageManager.getString("label.choose_calculation");
- if (af.getViewport().viewName != null)
+ if (af.getViewport().getViewName() != null)
{
- title = title + " (" + af.getViewport().viewName + ")";
+ title = title + " (" + af.getViewport().getViewName() + ")";
}
Desktop.addInternalFrame(frame, title, width, height, false);
@@ -375,7 +376,7 @@ public class CalculationChooser extends JPanel
*/
protected JComboBox buildModelOptionsList()
{
- final JComboBox scoreModelsCombo = new JComboBox();
+ final JComboBox scoreModelsCombo = new JComboBox<>();
scoreModelsCombo.setRenderer(renderer);
/*
@@ -418,39 +419,42 @@ public class CalculationChooser extends JPanel
{
Object curSel = comboBox.getSelectedItem();
toolTips.clear();
- DefaultComboBoxModel model = new DefaultComboBoxModel();
+ DefaultComboBoxModel model = new DefaultComboBoxModel<>();
+
+ /*
+ * select the score models applicable to the alignment type
+ */
+ boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
+ List models = getApplicableScoreModels(nucleotide,
+ pca.isSelected());
/*
* now we can actually add entries to the combobox,
* remembering their descriptions for tooltips
*/
- ScoreModels scoreModels = ScoreModels.getInstance();
boolean selectedIsPresent = false;
- for (ScoreModelI sm : scoreModels.getModels())
+ for (ScoreModelI sm : models)
{
- boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
- if (sm.isDNA() && nucleotide || sm.isProtein() && !nucleotide)
+ if (curSel != null && sm.getName().equals(curSel))
+ {
+ selectedIsPresent = true;
+ curSel = sm.getName();
+ }
+ model.addElement(sm.getName());
+
+ /*
+ * tooltip is description if provided, else text lookup with
+ * fallback on the model name
+ */
+ String tooltip = sm.getDescription();
+ if (tooltip == null)
{
- if (curSel != null && sm.getName().equals(curSel))
- {
- selectedIsPresent = true;
- curSel = sm.getName();
- }
- model.addElement(sm.getName());
-
- /*
- * tooltip is description if provided, else text lookup with
- * fallback on the model name
- */
- String tooltip = sm.getDescription();
- if (tooltip == null)
- {
- tooltip = MessageManager.getStringOrReturn("label.score_model_",
- sm.getName());
- }
- toolTips.add(tooltip);
+ tooltip = MessageManager.getStringOrReturn("label.score_model_",
+ sm.getName());
}
+ toolTips.add(tooltip);
}
+
if (selectedIsPresent)
{
model.setSelectedItem(curSel);
@@ -460,6 +464,47 @@ public class CalculationChooser extends JPanel
}
/**
+ * Builds a list of score models which are applicable for the alignment and
+ * calculation type (peptide or generic models for protein, nucleotide or
+ * generic models for nucleotide).
+ *
+ * As a special case, includes BLOSUM62 as an extra option for nucleotide PCA.
+ * This is for backwards compatibility with Jalview prior to 2.8 when BLOSUM62
+ * was the only score matrix supported. This is included if property
+ * BLOSUM62_PCA_FOR_NUCLEOTIDE is set to true in the Jalview properties file.
+ *
+ * @param nucleotide
+ * @param forPca
+ * @return
+ */
+ protected static List getApplicableScoreModels(
+ boolean nucleotide, boolean forPca)
+ {
+ List filtered = new ArrayList<>();
+
+ ScoreModels scoreModels = ScoreModels.getInstance();
+ for (ScoreModelI sm : scoreModels.getModels())
+ {
+ if (!nucleotide && sm.isProtein() || nucleotide && sm.isDNA())
+ {
+ filtered.add(sm);
+ }
+ }
+
+ /*
+ * special case: add BLOSUM62 as last option for nucleotide PCA,
+ * for backwards compatibility with Jalview < 2.8 (JAL-2962)
+ */
+ if (nucleotide && forPca
+ && Cache.getDefault("BLOSUM62_PCA_FOR_NUCLEOTIDE", false))
+ {
+ filtered.add(scoreModels.getBlosum62());
+ }
+
+ return filtered;
+ }
+
+ /**
* Open and calculate the selected tree or PCA on 'OK'
*/
protected void calculate_actionPerformed()
@@ -539,7 +584,13 @@ public class CalculationChooser extends JPanel
JvOptionPane.WARNING_MESSAGE);
return;
}
+
+ /*
+ * construct the panel and kick off its calculation thread
+ */
pcaPanel = new PCAPanel(af.alignPanel, modelName, params);
+ new Thread(pcaPanel).start();
+
}
/**
diff --git a/src/jalview/gui/CrossRefAction.java b/src/jalview/gui/CrossRefAction.java
index 85f2498..37f11df 100644
--- a/src/jalview/gui/CrossRefAction.java
+++ b/src/jalview/gui/CrossRefAction.java
@@ -39,6 +39,7 @@ import jalview.util.DBRefUtils;
import jalview.util.MapList;
import jalview.util.MappingUtils;
import jalview.util.MessageManager;
+import jalview.viewmodel.seqfeatures.FeatureRendererModel;
import jalview.ws.SequenceFetcher;
import java.util.ArrayList;
@@ -170,17 +171,17 @@ public class CrossRefAction implements Runnable
.isShowSequenceFeatures();
newFrame.setShowSeqFeatures(showSequenceFeatures);
copyThis.setShowSeqFeatures(showSequenceFeatures);
- FeatureRenderer myFeatureStyling = alignFrame.alignPanel
+ FeatureRendererModel myFeatureStyling = alignFrame.alignPanel
.getSeqPanel().seqCanvas.getFeatureRenderer();
/*
* copy feature rendering settings to split frame
*/
- FeatureRenderer fr1 = newFrame.alignPanel.getSeqPanel().seqCanvas
+ FeatureRendererModel fr1 = newFrame.alignPanel.getSeqPanel().seqCanvas
.getFeatureRenderer();
fr1.transferSettings(myFeatureStyling);
fr1.findAllFeatures(true);
- FeatureRenderer fr2 = copyThis.alignPanel.getSeqPanel().seqCanvas
+ FeatureRendererModel fr2 = copyThis.alignPanel.getSeqPanel().seqCanvas
.getFeatureRenderer();
fr2.transferSettings(myFeatureStyling);
fr2.findAllFeatures(true);
diff --git a/src/jalview/gui/Desktop.java b/src/jalview/gui/Desktop.java
index 8d9e366..bbdddbb 100644
--- a/src/jalview/gui/Desktop.java
+++ b/src/jalview/gui/Desktop.java
@@ -1605,9 +1605,10 @@ public class Desktop extends jalview.jbgui.GDesktop
* DOCUMENT ME!
*/
@Override
- public void saveState_actionPerformed(ActionEvent e)
+ public void saveState_actionPerformed(boolean asCastor)
{
- JalviewFileChooser chooser = new JalviewFileChooser("jvp",
+ JalviewFileChooser chooser = new JalviewFileChooser(
+ asCastor ? "jvp" : "jvx",
"Jalview Project");
chooser.setFileView(new JalviewFileView());
@@ -1636,7 +1637,14 @@ public class Desktop extends jalview.jbgui.GDesktop
// TODO prevent user from messing with the Desktop whilst we're saving
try
{
- new Jalview2XML().saveState(choice);
+ if (asCastor)
+ {
+ new Jalview2XML().saveState(choice);
+ }
+ else
+ {
+ new jalview.project.Jalview2XML().saveState(choice);
+ }
} catch (OutOfMemoryError oom)
{
new OOMWarning(
@@ -1678,13 +1686,19 @@ public class Desktop extends jalview.jbgui.GDesktop
* DOCUMENT ME!
*/
@Override
- public void loadState_actionPerformed(ActionEvent e)
- {
+ public void loadState_actionPerformed(boolean asCastor)
+ {
+ final String[] suffix = asCastor ? new String[] { "jvp", "jar" }
+ : new String[]
+ { "jvx" };
+ final String[] desc = asCastor
+ ? new String[]
+ { "Jalview Project", "Jalview Project (old)" }
+ : new String[]
+ { "Jalview Project" };
JalviewFileChooser chooser = new JalviewFileChooser(
- Cache.getProperty("LAST_DIRECTORY"), new String[]
- { "jvp", "jar" },
- new String[]
- { "Jalview Project", "Jalview Project (old)" },
+ Cache.getProperty("LAST_DIRECTORY"), suffix,
+ desc,
"Jalview Project");
chooser.setFileView(new JalviewFileView());
chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
@@ -1707,7 +1721,14 @@ public class Desktop extends jalview.jbgui.GDesktop
{ choice }), choice.hashCode());
try
{
- new Jalview2XML().loadJalviewAlign(choice);
+ if (asCastor)
+ {
+ new Jalview2XML().loadJalviewAlign(choice);
+ }
+ else
+ {
+ new jalview.project.Jalview2XML().loadJalviewAlign(choice);
+ }
} catch (OutOfMemoryError oom)
{
new OOMWarning("Whilst loading project from " + choice, oom);
diff --git a/src/jalview/gui/FeatureSettings.java b/src/jalview/gui/FeatureSettings.java
index 1358c8f..47c0cc5 100644
--- a/src/jalview/gui/FeatureSettings.java
+++ b/src/jalview/gui/FeatureSettings.java
@@ -20,8 +20,10 @@
*/
package jalview.gui;
+import jalview.api.AlignViewportI;
import jalview.api.FeatureColourI;
import jalview.api.FeatureSettingsControllerI;
+import jalview.api.ViewStyleI;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.SequenceI;
import jalview.datamodel.features.FeatureMatcherI;
@@ -30,18 +32,21 @@ import jalview.datamodel.features.FeatureMatcherSetI;
import jalview.gui.Help.HelpId;
import jalview.io.JalviewFileChooser;
import jalview.io.JalviewFileView;
-import jalview.schemabinding.version2.Filter;
-import jalview.schemabinding.version2.JalviewUserColours;
-import jalview.schemabinding.version2.MatcherSet;
import jalview.schemes.FeatureColour;
import jalview.util.MessageManager;
import jalview.util.Platform;
import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
+import jalview.viewmodel.styles.ViewStyle;
+import jalview.xml.binding.jalview.JalviewUserColours;
+import jalview.xml.binding.jalview.JalviewUserColours.Colour;
+import jalview.xml.binding.jalview.JalviewUserColours.Filter;
+import jalview.xml.binding.jalview.ObjectFactory;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
+import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridLayout;
@@ -78,7 +83,6 @@ import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
-import javax.swing.JCheckBoxMenuItem;
import javax.swing.JColorChooser;
import javax.swing.JDialog;
import javax.swing.JInternalFrame;
@@ -98,6 +102,11 @@ import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.Marshaller;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamReader;
public class FeatureSettings extends JPanel
implements FeatureSettingsControllerI
@@ -133,6 +142,8 @@ public class FeatureSettings extends JPanel
private float originalTransparency;
+ private ViewStyleI originalViewStyle;
+
private Map originalFilters;
final JInternalFrame frame;
@@ -145,6 +156,10 @@ public class FeatureSettings extends JPanel
JSlider transparency = new JSlider();
+ JCheckBox showComplement;
+
+ JCheckBox showComplementOnTop;
+
/*
* when true, constructor is still executing - so ignore UI events
*/
@@ -186,6 +201,7 @@ public class FeatureSettings extends JPanel
transparency.setMaximum(100 - originalTransparencyAsPercent);
originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
+ originalViewStyle = new ViewStyle(af.viewport.getViewStyle());
try
{
@@ -213,7 +229,8 @@ public class FeatureSettings extends JPanel
FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
column);
tip = o.isEmpty()
- ? MessageManager.getString("label.filters_tooltip")
+ ? MessageManager
+ .getString("label.configure_feature_tooltip")
: o.toString();
break;
default:
@@ -409,69 +426,6 @@ public class FeatureSettings extends JPanel
});
men.add(dens);
- /*
- * variable colour options include colour by label, by score,
- * by selected attribute text, or attribute value
- */
- final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
- MessageManager.getString("label.variable_colour"));
- mxcol.setSelected(!featureColour.isSimpleColour());
- men.add(mxcol);
- mxcol.addActionListener(new ActionListener()
- {
- JColorChooser colorChooser;
-
- @Override
- public void actionPerformed(ActionEvent e)
- {
- if (e.getSource() == mxcol)
- {
- if (featureColour.isSimpleColour())
- {
- FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
- fc.addActionListener(this);
- }
- else
- {
- // bring up simple color chooser
- colorChooser = new JColorChooser();
- String title = MessageManager
- .getString("label.select_colour");
- JDialog dialog = JColorChooser.createDialog(me,
- title, true, // modal
- colorChooser, this, // OK button handler
- null); // no CANCEL button handler
- colorChooser.setColor(featureColour.getMaxColour());
- dialog.setVisible(true);
- }
- }
- else
- {
- if (e.getSource() instanceof FeatureTypeSettings)
- {
- /*
- * update after OK in feature colour dialog; the updated
- * colour will have already been set in the FeatureRenderer
- */
- FeatureColourI fci = fr.getFeatureColours().get(type);
- table.setValueAt(fci, rowSelected, 1);
- table.validate();
- }
- else
- {
- // probably the color chooser!
- table.setValueAt(new FeatureColour(colorChooser.getColor()),
- rowSelected, 1);
- table.validate();
- me.updateFeatureRenderer(
- ((FeatureTableModel) table.getModel()).getData(),
- false);
- }
- }
- }
-
- });
-
JMenuItem selCols = new JMenuItem(
MessageManager.getString("label.select_columns_containing"));
selCols.addActionListener(new ActionListener()
@@ -575,7 +529,7 @@ public class FeatureSettings extends JPanel
{
fr.setGroupVisibility(check.getText(), check.isSelected());
resetTable(new String[] { grp });
- af.alignPanel.paintAlignment(true, true);
+ refreshDisplay();
}
});
groupPanel.add(check);
@@ -874,30 +828,39 @@ public class FeatureSettings extends JPanel
InputStreamReader in = new InputStreamReader(
new FileInputStream(file), "UTF-8");
- JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
+ JAXBContext jc = JAXBContext
+ .newInstance("jalview.xml.binding.jalview");
+ javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
+ XMLStreamReader streamReader = XMLInputFactory.newInstance()
+ .createXMLStreamReader(in);
+ JAXBElement jbe = um.unmarshal(streamReader,
+ JalviewUserColours.class);
+ JalviewUserColours jucs = jbe.getValue();
+
+ // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
/*
* load feature colours
*/
- for (int i = jucs.getColourCount() - 1; i >= 0; i--)
+ for (int i = jucs.getColour().size() - 1; i >= 0; i--)
{
- jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
- FeatureColourI colour = Jalview2XML.unmarshalColour(newcol);
+ Colour newcol = jucs.getColour().get(i);
+ FeatureColourI colour = jalview.project.Jalview2XML
+ .parseColour(newcol);
fr.setColour(newcol.getName(), colour);
- fr.setOrder(newcol.getName(), i / (float) jucs.getColourCount());
+ fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
}
/*
* load feature filters; loaded filters will replace any that are
* currently defined, other defined filters are left unchanged
*/
- for (int i = 0; i < jucs.getFilterCount(); i++)
+ for (int i = 0; i < jucs.getFilter().size(); i++)
{
- jalview.schemabinding.version2.Filter filterModel = jucs
- .getFilter(i);
+ Filter filterModel = jucs.getFilter().get(i);
String featureType = filterModel.getFeatureType();
- FeatureMatcherSetI filter = Jalview2XML.unmarshalFilter(featureType,
- filterModel.getMatcherSet());
+ FeatureMatcherSetI filter = jalview.project.Jalview2XML
+ .parseFilter(featureType, filterModel.getMatcherSet());
if (!filter.isEmpty())
{
fr.setFeatureFilter(featureType, filter);
@@ -979,9 +942,9 @@ public class FeatureSettings extends JPanel
for (String featureType : sortedTypes)
{
FeatureColourI fcol = fr.getFeatureStyle(featureType);
- jalview.schemabinding.version2.Colour col = Jalview2XML.marshalColour(
- featureType, fcol);
- ucs.addColour(col);
+ Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
+ fcol);
+ ucs.getColour().add(col);
}
/*
@@ -994,16 +957,26 @@ public class FeatureSettings extends JPanel
{
Iterator iterator = filter.getMatchers().iterator();
FeatureMatcherI firstMatcher = iterator.next();
- MatcherSet ms = Jalview2XML.marshalFilter(firstMatcher, iterator,
+ jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
+ .marshalFilter(firstMatcher, iterator,
filter.isAnded());
Filter filterModel = new Filter();
filterModel.setFeatureType(featureType);
filterModel.setMatcherSet(ms);
- ucs.addFilter(filterModel);
+ ucs.getFilter().add(filterModel);
}
}
+ JAXBContext jaxbContext = JAXBContext
+ .newInstance(JalviewUserColours.class);
+ Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
+ jaxbMarshaller.marshal(
+ new ObjectFactory().createJalviewUserColours(ucs), out);
- ucs.marshal(out);
+ // jaxbMarshaller.marshal(object, pout);
+ // marshaller.marshal(object);
+ out.flush();
+
+ // ucs.marshal(out);
out.close();
} catch (Exception ex)
{
@@ -1114,7 +1087,7 @@ public class FeatureSettings extends JPanel
if (fr.setFeaturePriority(rowData, visibleNew))
{
- af.alignPanel.paintAlignment(true, true);
+ refreshDisplay();
}
}
@@ -1239,6 +1212,7 @@ public class FeatureSettings extends JPanel
fr.setTransparency(originalTransparency);
fr.setFeatureFilters(originalFilters);
updateFeatureRenderer(originalData);
+ af.getViewport().setViewStyle(originalViewStyle);
close();
}
});
@@ -1289,7 +1263,7 @@ public class FeatureSettings extends JPanel
if (!inConstruction)
{
fr.setTransparency((100 - transparency.getValue()) / 100f);
- af.alignPanel.paintAlignment(true, true);
+ refreshDisplay();
}
}
});
@@ -1298,8 +1272,37 @@ public class FeatureSettings extends JPanel
transparency.setToolTipText(
MessageManager.getString("label.transparency_tip"));
- JPanel transPanel = new JPanel(new GridLayout(1, 2));
- bigPanel.add(transPanel, BorderLayout.SOUTH);
+ boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
+ showComplement = new JCheckBox(
+ "Show " + (nucleotide ? "protein" : "CDS") + " features");
+ showComplement.setSelected(af.getViewport().isShowComplementFeatures());
+ showComplement.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ af.getViewport()
+ .setShowComplementFeatures(showComplement.isSelected());
+ refreshDisplay();
+ }
+ });
+
+ showComplementOnTop = new JCheckBox("on top");
+ showComplementOnTop
+ .setSelected(af.getViewport().isShowComplementFeaturesOnTop());
+ showComplementOnTop.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ af.getViewport().setShowComplementFeaturesOnTop(
+ showComplementOnTop.isSelected());
+ refreshDisplay();
+ }
+ });
+
+ JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
+ bigPanel.add(lowerPanel, BorderLayout.SOUTH);
JPanel transbuttons = new JPanel(new GridLayout(5, 1));
transbuttons.add(optimizeOrder);
@@ -1307,8 +1310,21 @@ public class FeatureSettings extends JPanel
transbuttons.add(sortByScore);
transbuttons.add(sortByDens);
transbuttons.add(help);
- transPanel.add(transparency);
- transPanel.add(transbuttons);
+
+ boolean hasComplement = af.getViewport().getCodingComplement() != null;
+ JPanel transPanelLeft = new JPanel(
+ new GridLayout(hasComplement ? 3 : 2, 1));
+ transPanelLeft.add(new JLabel(" Colour transparency" + ":"));
+ transPanelLeft.add(transparency);
+ if (hasComplement)
+ {
+ JPanel cp = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ cp.add(showComplement);
+ cp.add(showComplementOnTop);
+ transPanelLeft.add(cp);
+ }
+ lowerPanel.add(transPanelLeft);
+ lowerPanel.add(transbuttons);
JPanel buttonPanel = new JPanel();
buttonPanel.add(ok);
@@ -1321,6 +1337,22 @@ public class FeatureSettings extends JPanel
this.add(settingsPane);
}
+ /**
+ * Repaints alignment, structure and overview (if shown). If there is a
+ * complementary view which is showing this view's features, then also
+ * repaints that.
+ */
+ void refreshDisplay()
+ {
+ af.alignPanel.paintAlignment(true, true);
+ AlignViewportI complement = af.getViewport().getCodingComplement();
+ if (complement != null && complement.isShowComplementFeatures())
+ {
+ AlignFrame af2 = Desktop.getAlignFrameFor(complement);
+ af2.alignPanel.paintAlignment(true, true);
+ }
+ }
+
// ///////////////////////////////////////////////////////////////////////
// http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
// ///////////////////////////////////////////////////////////////////////
@@ -1329,7 +1361,7 @@ public class FeatureSettings extends JPanel
private String[] columnNames = {
MessageManager.getString("label.feature_type"),
MessageManager.getString("action.colour"),
- MessageManager.getString("label.filter"),
+ MessageManager.getString("label.configuration"),
MessageManager.getString("label.show") };
private Object[][] data;
@@ -1379,13 +1411,22 @@ public class FeatureSettings extends JPanel
}
/**
- * Answers the class of the object in column c of the first row of the table
+ * Answers the class of column c of the table
*/
@Override
public Class> getColumnClass(int c)
{
- Object v = getValueAt(0, c);
- return v == null ? null : v.getClass();
+ switch (c)
+ {
+ case TYPE_COLUMN:
+ return String.class;
+ case COLOUR_COLUMN:
+ return FeatureColour.class;
+ case FILTER_COLUMN:
+ return FeatureMatcherSet.class;
+ default:
+ return Boolean.class;
+ }
}
@Override
diff --git a/src/jalview/gui/FeatureTypeSettings.java b/src/jalview/gui/FeatureTypeSettings.java
index 55bc519..ceb38b0 100644
--- a/src/jalview/gui/FeatureTypeSettings.java
+++ b/src/jalview/gui/FeatureTypeSettings.java
@@ -20,6 +20,7 @@
*/
package jalview.gui;
+import jalview.api.AlignViewportI;
import jalview.api.AlignmentViewPanel;
import jalview.api.FeatureColourI;
import jalview.datamodel.GraphLine;
@@ -242,8 +243,7 @@ public class FeatureTypeSettings extends JalviewDialog
String title = MessageManager
.formatMessage("label.display_settings_for", new String[]
{ theType });
- initDialogFrame(this, true, false, title, 500, 500);
-
+ initDialogFrame(this, true, false, title, 580, 500);
waitForInput();
}
@@ -366,7 +366,7 @@ public class FeatureTypeSettings extends JalviewDialog
: BELOW_THRESHOLD_OPTION);
slider.setEnabled(true);
slider.setValue((int) (fc.getThreshold() * scaleFactor));
- thresholdValue.setText(String.valueOf(getRoundedSliderValue()));
+ thresholdValue.setText(String.valueOf(fc.getThreshold()));
thresholdValue.setEnabled(true);
thresholdIsMin.setEnabled(true);
}
@@ -652,6 +652,7 @@ public class FeatureTypeSettings extends JalviewDialog
{
thresholdValue
.setText(String.valueOf(slider.getValue() / scaleFactor));
+ thresholdValue.setBackground(Color.white); // to reset red for invalid
sliderValueChanged();
}
}
@@ -667,7 +668,7 @@ public class FeatureTypeSettings extends JalviewDialog
*/
if (ap != null)
{
- ap.paintAlignment(true, true);
+ refreshDisplay(true);
}
}
});
@@ -868,7 +869,7 @@ public class FeatureTypeSettings extends JalviewDialog
* save the colour, and repaint stuff
*/
fr.setColour(featureType, acg);
- ap.paintAlignment(updateStructsAndOverview, updateStructsAndOverview);
+ refreshDisplay(updateStructsAndOverview);
updateColoursTab();
}
@@ -1009,7 +1010,7 @@ public class FeatureTypeSettings extends JalviewDialog
{
fr.setColour(featureType, originalColour);
fr.setFeatureFilter(featureType, originalFilter);
- ap.paintAlignment(true, true);
+ refreshDisplay(true);
}
/**
@@ -1019,21 +1020,23 @@ public class FeatureTypeSettings extends JalviewDialog
{
try
{
+ /*
+ * set 'adjusting' flag while moving the slider, so it
+ * doesn't then in turn change the value (with rounding)
+ */
adjusting = true;
float f = Float.parseFloat(thresholdValue.getText());
+ f = Float.max(f, this.min);
+ f = Float.min(f, this.max);
+ thresholdValue.setText(String.valueOf(f));
slider.setValue((int) (f * scaleFactor));
threshline.value = f;
thresholdValue.setBackground(Color.white); // ok
-
- /*
- * force repaint of any Overview window or structure
- */
- ap.paintAlignment(true, true);
+ adjusting = false;
+ colourChanged(true);
} catch (NumberFormatException ex)
{
thresholdValue.setBackground(Color.red); // not ok
- } finally
- {
adjusting = false;
}
}
@@ -1736,8 +1739,26 @@ public class FeatureTypeSettings extends JalviewDialog
* (note this might now be an empty filter with no conditions)
*/
fr.setFeatureFilter(featureType, combined.isEmpty() ? null : combined);
- ap.paintAlignment(true, true);
+ refreshDisplay(true);
updateFiltersTab();
}
+
+ /**
+ * Repaints alignment, structure and overview (if shown). If there is a
+ * complementary view which is showing this view's features, then also
+ * repaints that.
+ *
+ * @param updateStructsAndOverview
+ */
+ void refreshDisplay(boolean updateStructsAndOverview)
+ {
+ ap.paintAlignment(true, updateStructsAndOverview);
+ AlignViewportI complement = ap.getAlignViewport().getCodingComplement();
+ if (complement != null && complement.isShowComplementFeatures())
+ {
+ AlignFrame af2 = Desktop.getAlignFrameFor(complement);
+ af2.alignPanel.paintAlignment(true, updateStructsAndOverview);
+ }
+ }
}
diff --git a/src/jalview/gui/FontChooser.java b/src/jalview/gui/FontChooser.java
index c66f304..92cc4c6 100755
--- a/src/jalview/gui/FontChooser.java
+++ b/src/jalview/gui/FontChooser.java
@@ -87,7 +87,7 @@ public class FontChooser extends GFontChooser
public FontChooser(TreePanel treePanel)
{
this.tp = treePanel;
- ap = treePanel.treeCanvas.ap;
+ ap = treePanel.getTreeCanvas().getAssociatedPanel();
oldFont = treePanel.getTreeFont();
defaultButton.setVisible(false);
smoothFont.setEnabled(false);
diff --git a/src/jalview/gui/Jalview2XML.java b/src/jalview/gui/Jalview2XML.java
index 9285754..ceec19d 100644
--- a/src/jalview/gui/Jalview2XML.java
+++ b/src/jalview/gui/Jalview2XML.java
@@ -20,9 +20,18 @@
*/
package jalview.gui;
+import static jalview.math.RotatableMatrix.Axis.X;
+import static jalview.math.RotatableMatrix.Axis.Y;
+import static jalview.math.RotatableMatrix.Axis.Z;
+
import jalview.analysis.Conservation;
+import jalview.analysis.PCA;
+import jalview.analysis.scoremodels.ScoreModels;
+import jalview.analysis.scoremodels.SimilarityParams;
import jalview.api.FeatureColourI;
import jalview.api.ViewStyleI;
+import jalview.api.analysis.ScoreModelI;
+import jalview.api.analysis.SimilarityParamsI;
import jalview.api.structures.JalviewStructureDisplayI;
import jalview.bin.Cache;
import jalview.datamodel.AlignedCodonFrame;
@@ -31,6 +40,7 @@ import jalview.datamodel.AlignmentAnnotation;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.GraphLine;
import jalview.datamodel.PDBEntry;
+import jalview.datamodel.Point;
import jalview.datamodel.RnaViewerModel;
import jalview.datamodel.SequenceFeature;
import jalview.datamodel.SequenceGroup;
@@ -45,16 +55,22 @@ import jalview.ext.varna.RnaModel;
import jalview.gui.StructureViewer.ViewerType;
import jalview.io.DataSourceType;
import jalview.io.FileFormat;
+import jalview.math.Matrix;
+import jalview.math.MatrixI;
import jalview.renderer.ResidueShaderI;
import jalview.schemabinding.version2.AlcodMap;
import jalview.schemabinding.version2.AlcodonFrame;
import jalview.schemabinding.version2.Annotation;
import jalview.schemabinding.version2.AnnotationColours;
import jalview.schemabinding.version2.AnnotationElement;
+import jalview.schemabinding.version2.Axis;
import jalview.schemabinding.version2.CalcIdParam;
-import jalview.schemabinding.version2.Colour;
import jalview.schemabinding.version2.CompoundMatcher;
+import jalview.schemabinding.version2.D;
import jalview.schemabinding.version2.DBRef;
+import jalview.schemabinding.version2.DoubleMatrix;
+import jalview.schemabinding.version2.E;
+import jalview.schemabinding.version2.EigenMatrix;
import jalview.schemabinding.version2.Features;
import jalview.schemabinding.version2.Group;
import jalview.schemabinding.version2.HiddenColumns;
@@ -69,18 +85,26 @@ import jalview.schemabinding.version2.MappingChoice;
import jalview.schemabinding.version2.MatchCondition;
import jalview.schemabinding.version2.MatcherSet;
import jalview.schemabinding.version2.OtherData;
+import jalview.schemabinding.version2.PairwiseMatrix;
+import jalview.schemabinding.version2.PcaData;
+import jalview.schemabinding.version2.PcaViewer;
import jalview.schemabinding.version2.PdbentryItem;
import jalview.schemabinding.version2.Pdbids;
import jalview.schemabinding.version2.Property;
import jalview.schemabinding.version2.RnaViewer;
+import jalview.schemabinding.version2.Row;
import jalview.schemabinding.version2.SecondaryStructure;
+import jalview.schemabinding.version2.SeqPointMax;
+import jalview.schemabinding.version2.SeqPointMin;
import jalview.schemabinding.version2.Sequence;
+import jalview.schemabinding.version2.SequencePoint;
import jalview.schemabinding.version2.SequenceSet;
import jalview.schemabinding.version2.SequenceSetProperties;
import jalview.schemabinding.version2.Setting;
import jalview.schemabinding.version2.StructureState;
import jalview.schemabinding.version2.ThresholdLine;
import jalview.schemabinding.version2.Tree;
+import jalview.schemabinding.version2.TridiagonalMatrix;
import jalview.schemabinding.version2.UserColours;
import jalview.schemabinding.version2.Viewport;
import jalview.schemabinding.version2.types.ColourThreshTypeType;
@@ -101,7 +125,9 @@ import jalview.util.StringUtils;
import jalview.util.jarInputStreamProvider;
import jalview.util.matcher.Condition;
import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.PCAModel;
import jalview.viewmodel.ViewportRanges;
+import jalview.viewmodel.seqfeatures.FeatureRendererModel;
import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
import jalview.viewmodel.seqfeatures.FeaturesDisplayed;
import jalview.ws.jws2.Jws2Discoverer;
@@ -169,6 +195,12 @@ public class Jalview2XML
private static final String UTF_8 = "UTF-8";
+ /**
+ * prefix for recovering datasets for alignments with multiple views where
+ * non-existent dataset IDs were written for some views
+ */
+ private static final String UNIQSEQSETID = "uniqueSeqSetId.";
+
// use this with nextCounter() to make unique names for entities
private int counter = 0;
@@ -1102,13 +1134,13 @@ public class Jalview2XML
{
TreePanel tp = (TreePanel) frames[t];
- if (tp.treeCanvas.av.getAlignment() == jal)
+ if (tp.getTreeCanvas().getViewport().getAlignment() == jal)
{
Tree tree = new Tree();
tree.setTitle(tp.getTitle());
tree.setCurrentTree((av.getCurrentTree() == tp.getTree()));
tree.setNewick(tp.getTree().print());
- tree.setThreshold(tp.treeCanvas.threshold);
+ tree.setThreshold(tp.getTreeCanvas().getThreshold());
tree.setFitToWindow(tp.fitToWindow.getState());
tree.setFontName(tp.getTreeFont().getName());
@@ -1124,6 +1156,7 @@ public class Jalview2XML
tree.setXpos(tp.getX());
tree.setYpos(tp.getY());
tree.setId(makeHashCode(tp, null));
+ tree.setLinkToAllViews(tp.getTreeCanvas().applyToAllViews);
jms.addTree(tree);
}
}
@@ -1131,6 +1164,24 @@ public class Jalview2XML
}
}
+ /*
+ * save PCA viewers
+ */
+ if (!storeDS && Desktop.desktop != null)
+ {
+ for (JInternalFrame frame : Desktop.desktop.getAllFrames())
+ {
+ if (frame instanceof PCAPanel)
+ {
+ PCAPanel panel = (PCAPanel) frame;
+ if (panel.av.getAlignment() == jal)
+ {
+ savePCA(panel, jms);
+ }
+ }
+ }
+ }
+
// SAVE ANNOTATIONS
/**
* store forward refs from an annotationRow to any groups
@@ -1247,7 +1298,7 @@ public class Jalview2XML
{
view.setComplementId(av.getCodingComplement().getViewId());
}
- view.setViewName(av.viewName);
+ view.setViewName(av.getViewName());
view.setGatheredViews(av.isGatherViewsHere());
Rectangle size = ap.av.getExplodedGeometry();
@@ -1345,7 +1396,7 @@ public class Jalview2XML
{
jalview.schemabinding.version2.FeatureSettings fs = new jalview.schemabinding.version2.FeatureSettings();
- FeatureRenderer fr = ap.getSeqPanel().seqCanvas
+ FeatureRendererModel fr = ap.getSeqPanel().seqCanvas
.getFeatureRenderer();
String[] renderOrder = fr.getRenderOrder().toArray(new String[0]);
@@ -1508,6 +1559,165 @@ public class Jalview2XML
}
/**
+ * Writes PCA viewer attributes and computed values to an XML model object and adds it to the JalviewModel. Any exceptions are reported by logging.
+ */
+ protected void savePCA(PCAPanel panel, JalviewModelSequence jms)
+ {
+ try
+ {
+ PcaViewer viewer = new PcaViewer();
+ viewer.setHeight(panel.getHeight());
+ viewer.setWidth(panel.getWidth());
+ viewer.setXpos(panel.getX());
+ viewer.setYpos(panel.getY());
+ viewer.setTitle(panel.getTitle());
+ PCAModel pcaModel = panel.getPcaModel();
+ viewer.setScoreModelName(pcaModel.getScoreModelName());
+ viewer.setXDim(panel.getSelectedDimensionIndex(X));
+ viewer.setYDim(panel.getSelectedDimensionIndex(Y));
+ viewer.setZDim(panel.getSelectedDimensionIndex(Z));
+ viewer.setBgColour(panel.getRotatableCanvas().getBackgroundColour().getRGB());
+ viewer.setScaleFactor(panel.getRotatableCanvas().getScaleFactor());
+ float[] spMin = panel.getRotatableCanvas().getSeqMin();
+ SeqPointMin spmin = new SeqPointMin();
+ spmin.setXPos(spMin[0]);
+ spmin.setYPos(spMin[1]);
+ spmin.setZPos(spMin[2]);
+ viewer.setSeqPointMin(spmin);
+ float[] spMax = panel.getRotatableCanvas().getSeqMax();
+ SeqPointMax spmax = new SeqPointMax();
+ spmax.setXPos(spMax[0]);
+ spmax.setYPos(spMax[1]);
+ spmax.setZPos(spMax[2]);
+ viewer.setSeqPointMax(spmax);
+ viewer.setShowLabels(panel.getRotatableCanvas().isShowLabels());
+ viewer.setLinkToAllViews(panel.getRotatableCanvas().isApplyToAllViews());
+ SimilarityParamsI sp = pcaModel.getSimilarityParameters();
+ viewer.setIncludeGaps(sp.includeGaps());
+ viewer.setMatchGaps(sp.matchGaps());
+ viewer.setIncludeGappedColumns(sp.includeGappedColumns());
+ viewer.setDenominateByShortestLength(sp.denominateByShortestLength());
+
+ /*
+ * sequence points on display
+ */
+ for (jalview.datamodel.SequencePoint spt : pcaModel
+ .getSequencePoints())
+ {
+ SequencePoint point = new SequencePoint();
+ point.setSequenceRef(seqHash(spt.getSequence()));
+ point.setXPos(spt.coord.x);
+ point.setYPos(spt.coord.y);
+ point.setZPos(spt.coord.z);
+ viewer.addSequencePoint(point);
+ }
+
+ /*
+ * (end points of) axes on display
+ */
+ for (Point p : panel.getRotatableCanvas().getAxisEndPoints())
+ {
+ Axis axis = new Axis();
+ axis.setXPos(p.x);
+ axis.setYPos(p.y);
+ axis.setZPos(p.z);
+ viewer.addAxis(axis);
+ }
+
+ /*
+ * raw PCA data (note we are not restoring PCA inputs here -
+ * alignment view, score model, similarity parameters)
+ */
+ PcaData data = new PcaData();
+ viewer.setPcaData(data);
+ PCA pca = pcaModel.getPcaData();
+
+ PairwiseMatrix pm = new PairwiseMatrix();
+ saveDoubleMatrix(pca.getPairwiseScores(), pm);
+ data.setPairwiseMatrix(pm);
+
+ TridiagonalMatrix tm = new TridiagonalMatrix();
+ saveDoubleMatrix(pca.getTridiagonal(), tm);
+ data.setTridiagonalMatrix(tm);
+
+ EigenMatrix eigenMatrix = new EigenMatrix();
+ data.setEigenMatrix(eigenMatrix);
+ saveDoubleMatrix(pca.getEigenmatrix(), eigenMatrix);
+
+ jms.addPcaViewer(viewer);
+ } catch (Throwable t)
+ {
+ Cache.log.error("Error saving PCA: " + t.getMessage());
+ }
+ }
+
+ /**
+ * Stores values from a matrix into an XML element, including (if present) the
+ * D or E vectors
+ *
+ * @param m
+ * @param xmlMatrix
+ * @see #loadDoubleMatrix(DoubleMatrix)
+ */
+ protected void saveDoubleMatrix(MatrixI m, DoubleMatrix xmlMatrix)
+ {
+ xmlMatrix.setRows(m.height());
+ xmlMatrix.setColumns(m.width());
+ for (int i = 0; i < m.height(); i++)
+ {
+ Row row = new Row();
+ for (int j = 0; j < m.width(); j++)
+ {
+ row.addV(m.getValue(i, j));
+ }
+ xmlMatrix.addRow(row);
+ }
+ if (m.getD() != null)
+ {
+ D dVector = new D();
+ dVector.setV(m.getD());
+ xmlMatrix.setD(dVector);
+ }
+ if (m.getE() != null)
+ {
+ E eVector = new E();
+ eVector.setV(m.getE());
+ xmlMatrix.setE(eVector);
+ }
+ }
+
+ /**
+ * Loads XML matrix data into a new Matrix object, including the D and/or E
+ * vectors (if present)
+ *
+ * @param mData
+ * @return
+ * @see Jalview2XML#saveDoubleMatrix(MatrixI, DoubleMatrix)
+ */
+ protected MatrixI loadDoubleMatrix(DoubleMatrix mData)
+ {
+ int rows = mData.getRows();
+ double[][] vals = new double[rows][];
+
+ for (int i = 0; i < rows; i++)
+ {
+ vals[i] = mData.getRow(i).getV();
+ }
+
+ MatrixI m = new Matrix(vals);
+
+ if (mData.getD() != null) {
+ m.setD(mData.getD().getV());
+ }
+ if (mData.getE() != null)
+ {
+ m.setE(mData.getE().getV());
+ }
+
+ return m;
+ }
+
+ /**
* Save any Varna viewers linked to this sequence. Writes an rnaViewer element
* for each viewer, with
*
@@ -2868,6 +3078,28 @@ public class Jalview2XML
: null;
// ////////////////////////////////
+ // INITIALISE ALIGNMENT SEQUENCESETID AND VIEWID
+ //
+ //
+ // If we just load in the same jar file again, the sequenceSetId
+ // will be the same, and we end up with multiple references
+ // to the same sequenceSet. We must modify this id on load
+ // so that each load of the file gives a unique id
+
+ /**
+ * used to resolve correct alignment dataset for alignments with multiple
+ * views
+ */
+ String uniqueSeqSetId = null;
+ String viewId = null;
+ if (view != null)
+ {
+ uniqueSeqSetId = view.getSequenceSetId() + uniqueSetSuffix;
+ viewId = (view.getId() == null ? null
+ : view.getId() + uniqueSetSuffix);
+ }
+
+ // ////////////////////////////////
// LOAD SEQUENCES
List hiddenSeqs = null;
@@ -2985,7 +3217,7 @@ public class Jalview2XML
// finally, verify all data in vamsasSet is actually present in al
// passing on flag indicating if it is actually a stored dataset
- recoverDatasetFor(vamsasSet, al, isdsal);
+ recoverDatasetFor(vamsasSet, al, isdsal, uniqueSeqSetId);
}
if (referenceseqForView != null)
@@ -3533,13 +3765,6 @@ public class Jalview2XML
// ///////////////////////////////
// LOAD VIEWPORT
- // If we just load in the same jar file again, the sequenceSetId
- // will be the same, and we end up with multiple references
- // to the same sequenceSet. We must modify this id on load
- // so that each load of the file gives a unique id
- String uniqueSeqSetId = view.getSequenceSetId() + uniqueSetSuffix;
- String viewId = (view.getId() == null ? null
- : view.getId() + uniqueSetSuffix);
AlignFrame af = null;
AlignViewport av = null;
// now check to see if we really need to create a new viewport.
@@ -3621,6 +3846,7 @@ public class Jalview2XML
if (loadTreesAndStructures)
{
loadTrees(jms, view, af, av, ap);
+ loadPCAViewers(jms, ap);
loadPDBStructures(jprovider, jseqs, af, ap);
loadRnaViewers(jprovider, jseqs, ap);
}
@@ -3763,12 +3989,11 @@ public class Jalview2XML
tp.setTitle(tree.getTitle());
tp.setBounds(new Rectangle(tree.getXpos(), tree.getYpos(),
tree.getWidth(), tree.getHeight()));
- tp.av = av; // af.viewport; // TODO: verify 'associate with all
+ tp.setViewport(av); // af.viewport; // TODO: verify 'associate with all
// views'
// works still
- tp.treeCanvas.av = av; // af.viewport;
- tp.treeCanvas.ap = ap; // af.alignPanel;
-
+ tp.getTreeCanvas().setViewport(av); // af.viewport;
+ tp.getTreeCanvas().setAssociatedPanel(ap); // af.alignPanel;
}
if (tp == null)
{
@@ -3795,7 +4020,8 @@ public class Jalview2XML
tp.showBootstrap(tree.getShowBootstrap());
tp.showDistances(tree.getShowDistances());
- tp.treeCanvas.threshold = tree.getThreshold();
+ tp.getTreeCanvas().setThreshold(tree.getThreshold());
+ tp.getTreeCanvas().applyToAllViews = tree.isLinkToAllViews();
if (tree.getCurrentTree())
{
@@ -4528,7 +4754,7 @@ public class Jalview2XML
if (view.getViewName() != null)
{
- af.viewport.viewName = view.getViewName();
+ af.viewport.setViewName(view.getViewName());
af.setInitialTabVisible();
}
af.setBounds(view.getXpos(), view.getYpos(), view.getWidth(),
@@ -4642,7 +4868,7 @@ public class Jalview2XML
// recover feature settings
if (jms.getFeatureSettings() != null)
{
- FeatureRenderer fr = af.alignPanel.getSeqPanel().seqCanvas
+ FeatureRendererModel fr = af.alignPanel.getSeqPanel().seqCanvas
.getFeatureRenderer();
FeaturesDisplayed fdi;
af.viewport.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
@@ -5083,13 +5309,25 @@ public class Jalview2XML
}
private void recoverDatasetFor(SequenceSet vamsasSet, AlignmentI al,
- boolean ignoreUnrefed)
+ boolean ignoreUnrefed, String uniqueSeqSetId)
{
jalview.datamodel.AlignmentI ds = getDatasetFor(
vamsasSet.getDatasetId());
Vector dseqs = null;
if (ds == null)
{
+ if (!ignoreUnrefed)
+ {
+ // try to resolve the dataset via uniqueSeqSetId
+ ds = getDatasetFor(UNIQSEQSETID + uniqueSeqSetId);
+ if (ds != null)
+ {
+ addDatasetRef(vamsasSet.getDatasetId(), ds);
+ }
+ }
+ }
+ if (ds == null)
+ {
// create a list of new dataset sequences
dseqs = new Vector();
}
@@ -5112,6 +5350,8 @@ public class Jalview2XML
if (al.getDataset() == null && !ignoreUnrefed)
{
al.setDataset(ds);
+ // register dataset for the alignment's uniqueSeqSetId for legacy projects
+ addDatasetRef(UNIQSEQSETID + uniqueSeqSetId, ds);
}
}
@@ -5453,6 +5693,9 @@ public class Jalview2XML
initSeqRefs();
JalviewModel jm = saveState(ap, null, null, null);
+ addDatasetRef(jm.getVamsasModel().getSequenceSet()[0].getDatasetId(),
+ ap.getAlignment().getDataset());
+
uniqueSetSuffix = "";
jm.getJalviewModelSequence().getViewport(0).setId(null);
// we don't overwrite the view we just copied
@@ -5713,6 +5956,126 @@ public class Jalview2XML
}
/**
+ * Loads any saved PCA viewers
+ *
+ * @param jms
+ * @param ap
+ */
+ protected void loadPCAViewers(JalviewModelSequence jms, AlignmentPanel ap)
+ {
+ try
+ {
+ for (int t = 0; t < jms.getPcaViewerCount(); t++)
+ {
+ PcaViewer viewer = jms.getPcaViewer(t);
+ String modelName = viewer.getScoreModelName();
+ SimilarityParamsI params = new SimilarityParams(
+ viewer.isIncludeGappedColumns(),
+ viewer.isMatchGaps(), viewer.isIncludeGaps(),
+ viewer.isDenominateByShortestLength());
+
+ /*
+ * create the panel (without computing the PCA)
+ */
+ PCAPanel panel = new PCAPanel(ap, modelName, params);
+
+ panel.setTitle(viewer.getTitle());
+ panel.setBounds(new Rectangle(viewer.getXpos(), viewer.getYpos(),
+ viewer.getWidth(), viewer.getHeight()));
+
+ boolean showLabels = viewer.isShowLabels();
+ panel.setShowLabels(showLabels);
+ panel.getRotatableCanvas().setShowLabels(showLabels);
+ panel.getRotatableCanvas().setBgColour(new Color(viewer.getBgColour()));
+ panel.getRotatableCanvas().setApplyToAllViews(viewer.isLinkToAllViews());
+
+ /*
+ * load PCA output data
+ */
+ ScoreModelI scoreModel = ScoreModels.getInstance()
+ .getScoreModel(modelName, ap);
+ PCA pca = new PCA(null, scoreModel, params);
+ PcaData pcaData = viewer.getPcaData();
+
+ MatrixI pairwise = loadDoubleMatrix(pcaData.getPairwiseMatrix());
+ pca.setPairwiseScores(pairwise);
+
+ MatrixI triDiag = loadDoubleMatrix(pcaData.getTridiagonalMatrix());
+ pca.setTridiagonal(triDiag);
+
+ MatrixI result = loadDoubleMatrix(pcaData.getEigenMatrix());
+ pca.setEigenmatrix(result);
+
+ panel.getPcaModel().setPCA(pca);
+
+ /*
+ * we haven't saved the input data! (JAL-2647 to do)
+ */
+ panel.setInputData(null);
+
+ /*
+ * add the sequence points for the PCA display
+ */
+ List seqPoints = new ArrayList<>();
+ for (SequencePoint sp : viewer.getSequencePoint())
+ {
+ String seqId = sp.getSequenceRef();
+ SequenceI seq = seqRefIds.get(seqId);
+ if (seq == null)
+ {
+ throw new IllegalStateException(
+ "Unmatched seqref for PCA: " + seqId);
+ }
+ Point pt = new Point(sp.getXPos(), sp.getYPos(), sp.getZPos());
+ jalview.datamodel.SequencePoint seqPoint = new jalview.datamodel.SequencePoint(
+ seq, pt);
+ seqPoints.add(seqPoint);
+ }
+ panel.getRotatableCanvas().setPoints(seqPoints, seqPoints.size());
+
+ /*
+ * set min-max ranges and scale after setPoints (which recomputes them)
+ */
+ panel.getRotatableCanvas().setScaleFactor(viewer.getScaleFactor());
+ SeqPointMin spMin = viewer.getSeqPointMin();
+ float[] min = new float[] { spMin.getXPos(), spMin.getYPos(),
+ spMin.getZPos() };
+ SeqPointMax spMax = viewer.getSeqPointMax();
+ float[] max = new float[] { spMax.getXPos(), spMax.getYPos(),
+ spMax.getZPos() };
+ panel.getRotatableCanvas().setSeqMinMax(min, max);
+
+ // todo: hold points list in PCAModel only
+ panel.getPcaModel().setSequencePoints(seqPoints);
+
+ panel.setSelectedDimensionIndex(viewer.getXDim(), X);
+ panel.setSelectedDimensionIndex(viewer.getYDim(), Y);
+ panel.setSelectedDimensionIndex(viewer.getZDim(), Z);
+
+ // is this duplication needed?
+ panel.setTop(seqPoints.size() - 1);
+ panel.getPcaModel().setTop(seqPoints.size() - 1);
+
+ /*
+ * add the axes' end points for the display
+ */
+ for (int i = 0; i < 3; i++)
+ {
+ Axis axis = viewer.getAxis(i);
+ panel.getRotatableCanvas().getAxisEndPoints()[i] = new Point(axis.getXPos(),
+ axis.getYPos(), axis.getZPos());
+ }
+
+ Desktop.addInternalFrame(panel, MessageManager.formatMessage(
+ "label.calc_title", "PCA", modelName), 475, 450);
+ }
+ } catch (Exception ex)
+ {
+ Cache.log.error("Error loading PCA: " + ex.toString());
+ }
+ }
+
+ /**
* Populates an XML model of the feature colour scheme for one feature type
*
* @param featureType
diff --git a/src/jalview/gui/Jalview2XML_V1.java b/src/jalview/gui/Jalview2XML_V1.java
index 331e738..9548839 100755
--- a/src/jalview/gui/Jalview2XML_V1.java
+++ b/src/jalview/gui/Jalview2XML_V1.java
@@ -493,7 +493,7 @@ public class Jalview2XML_V1
tp.showBootstrap(tree.getShowBootstrap());
tp.showDistances(tree.getShowDistances());
- tp.treeCanvas.threshold = tree.getThreshold();
+ tp.getTreeCanvas().setThreshold(tree.getThreshold());
if (tree.getCurrentTree())
{
diff --git a/src/jalview/gui/JalviewChimeraBindingModel.java b/src/jalview/gui/JalviewChimeraBindingModel.java
index 2f11c30..9d63c6a 100644
--- a/src/jalview/gui/JalviewChimeraBindingModel.java
+++ b/src/jalview/gui/JalviewChimeraBindingModel.java
@@ -27,6 +27,7 @@ import jalview.datamodel.SequenceI;
import jalview.ext.rbvi.chimera.JalviewChimeraBinding;
import jalview.io.DataSourceType;
import jalview.structure.StructureSelectionManager;
+import jalview.viewmodel.seqfeatures.FeatureRendererModel;
import javax.swing.SwingUtilities;
@@ -43,7 +44,7 @@ public class JalviewChimeraBindingModel extends JalviewChimeraBinding
}
@Override
- public FeatureRenderer getFeatureRenderer(AlignmentViewPanel alignment)
+ public FeatureRendererModel getFeatureRenderer(AlignmentViewPanel alignment)
{
AlignmentPanel ap = (alignment == null) ? cvf.getAlignmentPanel()
: (AlignmentPanel) alignment;
diff --git a/src/jalview/gui/OverviewCanvas.java b/src/jalview/gui/OverviewCanvas.java
index cc361a5..0f49381 100644
--- a/src/jalview/gui/OverviewCanvas.java
+++ b/src/jalview/gui/OverviewCanvas.java
@@ -25,6 +25,7 @@ import jalview.bin.Cache;
import jalview.renderer.OverviewRenderer;
import jalview.renderer.OverviewResColourFinder;
import jalview.viewmodel.OverviewDimensions;
+import jalview.viewmodel.seqfeatures.FeatureRendererModel;
import java.awt.Color;
import java.awt.Dimension;
@@ -132,7 +133,7 @@ public class OverviewCanvas extends JComponent
* the renderer to transfer feature colouring from
*/
public void draw(boolean showSequenceFeatures, boolean showAnnotation,
- FeatureRenderer transferRenderer)
+ FeatureRendererModel transferRenderer)
{
miniMe = null;
@@ -257,6 +258,7 @@ public class OverviewCanvas extends JComponent
public void dispose()
{
dispose = true;
+ od = null;
synchronized (this)
{
restart = true;
diff --git a/src/jalview/gui/PCAPanel.java b/src/jalview/gui/PCAPanel.java
index 7ceceee..3388d4d 100644
--- a/src/jalview/gui/PCAPanel.java
+++ b/src/jalview/gui/PCAPanel.java
@@ -21,15 +21,18 @@
package jalview.gui;
import jalview.analysis.scoremodels.ScoreModels;
-import jalview.analysis.scoremodels.SimilarityParams;
+import jalview.api.AlignViewportI;
import jalview.api.analysis.ScoreModelI;
import jalview.api.analysis.SimilarityParamsI;
+import jalview.bin.Cache;
import jalview.datamodel.Alignment;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.AlignmentView;
import jalview.datamodel.HiddenColumns;
import jalview.datamodel.SequenceI;
import jalview.jbgui.GPCAPanel;
+import jalview.math.RotatableMatrix.Axis;
+import jalview.util.ImageMaker;
import jalview.util.MessageManager;
import jalview.viewmodel.AlignmentViewport;
import jalview.viewmodel.PCAModel;
@@ -46,7 +49,6 @@ import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import javax.swing.ButtonGroup;
-import javax.swing.JCheckBoxMenuItem;
import javax.swing.JColorChooser;
import javax.swing.JMenuItem;
import javax.swing.JRadioButtonMenuItem;
@@ -54,49 +56,30 @@ import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
/**
- * DOCUMENT ME!
- *
- * @author $author$
- * @version $Revision$
+ * The panel holding the Principal Component Analysis 3-D visualisation
*/
public class PCAPanel extends GPCAPanel
implements Runnable, IProgressIndicator
{
+ private static final int MIN_WIDTH = 470;
- private IProgressIndicator progressBar;
+ private static final int MIN_HEIGHT = 250;
- RotatableCanvas rc;
+ private RotatableCanvas rc;
AlignmentPanel ap;
AlignmentViewport av;
- PCAModel pcaModel;
+ private PCAModel pcaModel;
- private static final int MIN_WIDTH = 470;
-
- private static final int MIN_HEIGHT = 250;
+ private int top = 0;
- int top = 0;
+ private IProgressIndicator progressBar;
private boolean working;
/**
- * Creates a new PCAPanel object using default score model and parameters
- *
- * @param alignPanel
- */
- public PCAPanel(AlignmentPanel alignPanel)
- {
- this(alignPanel,
- ScoreModels.getInstance()
- .getDefaultModel(
- !alignPanel.av.getAlignment().isNucleotide())
- .getName(),
- SimilarityParams.SeqSpace);
- }
-
- /**
* Constructor given sequence data, a similarity (or distance) score model
* name, and score calculation parameters
*
@@ -138,14 +121,17 @@ public class PCAPanel extends GPCAPanel
ScoreModelI scoreModel = ScoreModels.getInstance()
.getScoreModel(modelName, ap);
- pcaModel = new PCAModel(seqstrings, seqs, nucleotide, scoreModel,
- params);
+ setPcaModel(new PCAModel(seqstrings, seqs, nucleotide, scoreModel,
+ params));
PaintRefresher.Register(this, av.getSequenceSetId());
- rc = new RotatableCanvas(alignPanel);
- this.getContentPane().add(rc, BorderLayout.CENTER);
- Thread worker = new Thread(this);
- worker.start();
+ setRotatableCanvas(new RotatableCanvas(alignPanel));
+ this.getContentPane().add(getRotatableCanvas(), BorderLayout.CENTER);
+
+ addKeyListener(getRotatableCanvas());
+ validate();
+
+ this.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
}
/**
@@ -154,79 +140,30 @@ public class PCAPanel extends GPCAPanel
*/
protected void close_actionPerformed()
{
- pcaModel = null;
- }
-
- /**
- * Repopulate the options and actions under the score model menu when it is
- * selected. Options will depend on whether 'nucleotide' or 'peptide'
- * modelling is selected (and also possibly on whether any additional score
- * models have been added).
- */
- @Override
- protected void scoreModel_menuSelected()
- {
- scoreModelMenu.removeAll();
- for (final ScoreModelI sm : ScoreModels.getInstance().getModels())
- {
- final String name = sm.getName();
- JCheckBoxMenuItem jm = new JCheckBoxMenuItem(name);
-
- /*
- * if the score model doesn't provide a description, try to look one
- * up in the text bundle, falling back on its name
- */
- String tooltip = sm.getDescription();
- if (tooltip == null)
- {
- tooltip = MessageManager.getStringOrReturn("label.score_model_",
- name);
- }
- jm.setToolTipText(tooltip);
- jm.setSelected(pcaModel.getScoreModelName().equals(name));
- if ((pcaModel.isNucleotide() && sm.isDNA())
- || (!pcaModel.isNucleotide() && sm.isProtein()))
- {
- jm.addActionListener(new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent e)
- {
- if (!pcaModel.getScoreModelName().equals(name))
- {
- ScoreModelI sm2 = ScoreModels.getInstance()
- .getScoreModel(name, ap);
- pcaModel.setScoreModel(sm2);
- Thread worker = new Thread(PCAPanel.this);
- worker.start();
- }
- }
- });
- scoreModelMenu.add(jm);
- }
- }
+ setPcaModel(null);
}
@Override
- public void bgcolour_actionPerformed(ActionEvent e)
+ protected void bgcolour_actionPerformed()
{
Color col = JColorChooser.showDialog(this,
MessageManager.getString("label.select_background_colour"),
- rc.bgColour);
+ getRotatableCanvas().getBgColour());
if (col != null)
{
- rc.bgColour = col;
+ getRotatableCanvas().setBgColour(col);
}
- rc.repaint();
+ getRotatableCanvas().repaint();
}
/**
- * DOCUMENT ME!
+ * Calculates the PCA and displays the results
*/
@Override
public void run()
{
+ working = true;
long progId = System.currentTimeMillis();
IProgressIndicator progress = this;
String message = MessageManager.getString("label.pca_recalculating");
@@ -236,21 +173,17 @@ public class PCAPanel extends GPCAPanel
message = MessageManager.getString("label.pca_calculating");
}
progress.setProgressBar(message, progId);
- working = true;
try
{
- calcSettings.setEnabled(false);
- pcaModel.run();
- // ////////////////
+ getPcaModel().calculate();
+
xCombobox.setSelectedIndex(0);
yCombobox.setSelectedIndex(1);
zCombobox.setSelectedIndex(2);
- pcaModel.updateRc(rc);
+ getPcaModel().updateRc(getRotatableCanvas());
// rc.invalidate();
- nuclSetting.setSelected(pcaModel.isNucleotide());
- protSetting.setSelected(!pcaModel.isNucleotide());
- top = pcaModel.getTop();
+ setTop(getPcaModel().getTop());
} catch (OutOfMemoryError er)
{
@@ -261,109 +194,68 @@ public class PCAPanel extends GPCAPanel
{
progress.setProgressBar("", progId);
}
- calcSettings.setEnabled(true);
+
repaint();
if (getParent() == null)
{
- addKeyListener(rc);
- Desktop.addInternalFrame(this, MessageManager
- .getString("label.principal_component_analysis"), 475, 450);
- this.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
+ Desktop.addInternalFrame(this,
+ MessageManager.formatMessage("label.calc_title", "PCA",
+ getPcaModel().getScoreModelName()),
+ 475, 450);
}
working = false;
}
- @Override
- protected void nuclSetting_actionPerfomed(ActionEvent arg0)
- {
- if (!pcaModel.isNucleotide())
- {
- pcaModel.setNucleotide(true);
- pcaModel.setScoreModel(
- ScoreModels.getInstance().getDefaultModel(false));
- Thread worker = new Thread(this);
- worker.start();
- }
-
- }
-
- @Override
- protected void protSetting_actionPerfomed(ActionEvent arg0)
- {
-
- if (pcaModel.isNucleotide())
- {
- pcaModel.setNucleotide(false);
- pcaModel.setScoreModel(
- ScoreModels.getInstance().getDefaultModel(true));
- Thread worker = new Thread(this);
- worker.start();
- }
- }
-
/**
- * DOCUMENT ME!
+ * Updates the PCA display after a change of component to use for x, y or z
+ * axis
*/
- void doDimensionChange()
+ @Override
+ protected void doDimensionChange()
{
- if (top == 0)
+ if (getTop() == 0)
{
return;
}
- int dim1 = top - xCombobox.getSelectedIndex();
- int dim2 = top - yCombobox.getSelectedIndex();
- int dim3 = top - zCombobox.getSelectedIndex();
- pcaModel.updateRcView(dim1, dim2, dim3);
- rc.img = null;
- rc.rotmat.setIdentity();
- rc.initAxes();
- rc.paint(rc.getGraphics());
- }
-
- /**
- * DOCUMENT ME!
- *
- * @param e
- * DOCUMENT ME!
- */
- @Override
- protected void xCombobox_actionPerformed(ActionEvent e)
- {
- doDimensionChange();
- }
-
- /**
- * DOCUMENT ME!
- *
- * @param e
- * DOCUMENT ME!
- */
- @Override
- protected void yCombobox_actionPerformed(ActionEvent e)
- {
- doDimensionChange();
+ int dim1 = getTop() - xCombobox.getSelectedIndex();
+ int dim2 = getTop() - yCombobox.getSelectedIndex();
+ int dim3 = getTop() - zCombobox.getSelectedIndex();
+ getPcaModel().updateRcView(dim1, dim2, dim3);
+ getRotatableCanvas().resetView();
}
/**
- * DOCUMENT ME!
+ * Sets the selected checkbox item index for PCA dimension (1, 2, 3...) for
+ * the given axis (X/Y/Z)
*
- * @param e
- * DOCUMENT ME!
+ * @param index
+ * @param axis
*/
- @Override
- protected void zCombobox_actionPerformed(ActionEvent e)
+ public void setSelectedDimensionIndex(int index, Axis axis)
{
- doDimensionChange();
+ switch (axis)
+ {
+ case X:
+ xCombobox.setSelectedIndex(index);
+ break;
+ case Y:
+ yCombobox.setSelectedIndex(index);
+ break;
+ case Z:
+ zCombobox.setSelectedIndex(index);
+ break;
+ default:
+ }
}
@Override
- public void outputValues_actionPerformed(ActionEvent e)
+ protected void outputValues_actionPerformed()
{
CutAndPasteTransfer cap = new CutAndPasteTransfer();
try
{
- cap.setText(pcaModel.getDetails());
+ cap.setText(getPcaModel().getDetails());
Desktop.addInternalFrame(cap,
MessageManager.getString("label.pca_details"), 500, 500);
} catch (OutOfMemoryError oom)
@@ -374,32 +266,35 @@ public class PCAPanel extends GPCAPanel
}
@Override
- public void showLabels_actionPerformed(ActionEvent e)
+ protected void showLabels_actionPerformed()
{
- rc.showLabels(showLabels.getState());
+ getRotatableCanvas().showLabels(showLabels.getState());
}
@Override
- public void print_actionPerformed(ActionEvent e)
+ protected void print_actionPerformed()
{
PCAPrinter printer = new PCAPrinter();
printer.start();
}
+ /**
+ * If available, shows the data which formed the inputs for the PCA as a new
+ * alignment
+ */
@Override
- public void originalSeqData_actionPerformed(ActionEvent e)
+ public void originalSeqData_actionPerformed()
{
- // this was cut'n'pasted from the equivalent TreePanel method - we should
- // make this an abstract function of all jalview analysis windows
- if (pcaModel.getSeqtrings() == null)
+ // JAL-2647 disabled after load from project (until save to project done)
+ if (getPcaModel().getInputData() == null)
{
- jalview.bin.Cache.log.info(
+ Cache.log.info(
"Unexpected call to originalSeqData_actionPerformed - should have hidden this menu action.");
return;
}
// decide if av alignment is sufficiently different to original data to
// warrant a new window to be created
- // create new alignmnt window with hidden regions (unhiding hidden regions
+ // create new alignment window with hidden regions (unhiding hidden regions
// yields unaligned seqs)
// or create a selection box around columns in alignment view
// test Alignment(SeqCigar[])
@@ -412,8 +307,8 @@ public class PCAPanel extends GPCAPanel
} catch (Exception ex)
{
}
- ;
- Object[] alAndColsel = pcaModel.getSeqtrings()
+
+ Object[] alAndColsel = getPcaModel().getInputData()
.getAlignmentAndHiddenColumns(gc);
if (alAndColsel != null && alAndColsel[0] != null)
@@ -495,11 +390,11 @@ public class PCAPanel extends GPCAPanel
{
pg.translate((int) pf.getImageableX(), (int) pf.getImageableY());
- rc.drawBackground(pg, rc.bgColour);
- rc.drawScene(pg);
- if (rc.drawAxes == true)
+ getRotatableCanvas().drawBackground(pg);
+ getRotatableCanvas().drawScene(pg);
+ if (getRotatableCanvas().drawAxes)
{
- rc.drawAxes(pg);
+ getRotatableCanvas().drawAxes(pg);
}
if (pi == 0)
@@ -514,79 +409,75 @@ public class PCAPanel extends GPCAPanel
}
/**
- * DOCUMENT ME!
- *
- * @param e
- * DOCUMENT ME!
+ * Handler for 'Save as EPS' option
*/
@Override
- public void eps_actionPerformed(ActionEvent e)
+ protected void eps_actionPerformed()
{
- makePCAImage(jalview.util.ImageMaker.TYPE.EPS);
+ makePCAImage(ImageMaker.TYPE.EPS);
}
/**
- * DOCUMENT ME!
- *
- * @param e
- * DOCUMENT ME!
+ * Handler for 'Save as PNG' option
*/
@Override
- public void png_actionPerformed(ActionEvent e)
+ protected void png_actionPerformed()
{
- makePCAImage(jalview.util.ImageMaker.TYPE.PNG);
+ makePCAImage(ImageMaker.TYPE.PNG);
}
- void makePCAImage(jalview.util.ImageMaker.TYPE type)
+ void makePCAImage(ImageMaker.TYPE type)
{
- int width = rc.getWidth();
- int height = rc.getHeight();
-
- jalview.util.ImageMaker im;
-
- if (type == jalview.util.ImageMaker.TYPE.PNG)
- {
- im = new jalview.util.ImageMaker(this,
- jalview.util.ImageMaker.TYPE.PNG, "Make PNG image from PCA",
- width, height, null, null, null, 0, false);
- }
- else if (type == jalview.util.ImageMaker.TYPE.EPS)
- {
- im = new jalview.util.ImageMaker(this,
- jalview.util.ImageMaker.TYPE.EPS, "Make EPS file from PCA",
- width, height, null, this.getTitle(), null, 0, false);
- }
- else
- {
- im = new jalview.util.ImageMaker(this,
- jalview.util.ImageMaker.TYPE.SVG, "Make SVG file from PCA",
- width, height, null, this.getTitle(), null, 0, false);
-
+ int width = getRotatableCanvas().getWidth();
+ int height = getRotatableCanvas().getHeight();
+
+ ImageMaker im;
+
+ switch (type)
+ {
+ case PNG:
+ im = new ImageMaker(this, ImageMaker.TYPE.PNG,
+ "Make PNG image from PCA", width, height, null, null, null, 0,
+ false);
+ break;
+ case EPS:
+ im = new ImageMaker(this, ImageMaker.TYPE.EPS,
+ "Make EPS file from PCA", width, height, null,
+ this.getTitle(), null, 0, false);
+ break;
+ default:
+ im = new ImageMaker(this, ImageMaker.TYPE.SVG,
+ "Make SVG file from PCA", width, height, null,
+ this.getTitle(), null, 0, false);
}
if (im.getGraphics() != null)
{
- rc.drawBackground(im.getGraphics(), Color.black);
- rc.drawScene(im.getGraphics());
- if (rc.drawAxes == true)
+ getRotatableCanvas().drawBackground(im.getGraphics());
+ getRotatableCanvas().drawScene(im.getGraphics());
+ if (getRotatableCanvas().drawAxes)
{
- rc.drawAxes(im.getGraphics());
+ getRotatableCanvas().drawAxes(im.getGraphics());
}
im.writeImage();
}
}
@Override
- public void viewMenu_menuSelected()
+ protected void viewMenu_menuSelected()
{
buildAssociatedViewMenu();
}
+ /**
+ * Builds the menu showing the choice of possible views (for the associated
+ * sequence data) to which the PCA may be linked
+ */
void buildAssociatedViewMenu()
{
AlignmentPanel[] aps = PaintRefresher
.getAssociatedPanels(av.getSequenceSetId());
- if (aps.length == 1 && rc.av == aps[0].av)
+ if (aps.length == 1 && getRotatableCanvas().av == aps[0].av)
{
associateViewsMenu.setVisible(false);
return;
@@ -604,22 +495,20 @@ public class PCAPanel extends GPCAPanel
JRadioButtonMenuItem item;
ButtonGroup buttonGroup = new ButtonGroup();
- int i, iSize = aps.length;
- final PCAPanel thisPCAPanel = this;
- for (i = 0; i < iSize; i++)
+ int iSize = aps.length;
+
+ for (int i = 0; i < iSize; i++)
{
- final AlignmentPanel ap = aps[i];
- item = new JRadioButtonMenuItem(ap.av.viewName, ap.av == rc.av);
+ final AlignmentPanel panel = aps[i];
+ item = new JRadioButtonMenuItem(panel.av.getViewName(),
+ panel.av == getRotatableCanvas().av);
buttonGroup.add(item);
item.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent evt)
{
- rc.applyToAllViews = false;
- rc.av = ap.av;
- rc.ap = ap;
- PaintRefresher.Register(thisPCAPanel, ap.av.getSequenceSetId());
+ selectAssociatedView(panel);
}
});
@@ -631,13 +520,13 @@ public class PCAPanel extends GPCAPanel
buttonGroup.add(itemf);
- itemf.setSelected(rc.applyToAllViews);
+ itemf.setSelected(getRotatableCanvas().isApplyToAllViews());
itemf.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent evt)
{
- rc.applyToAllViews = itemf.isSelected();
+ getRotatableCanvas().setApplyToAllViews(itemf.isSelected());
}
});
associateViewsMenu.add(itemf);
@@ -652,12 +541,12 @@ public class PCAPanel extends GPCAPanel
* )
*/
@Override
- protected void outputPoints_actionPerformed(ActionEvent e)
+ protected void outputPoints_actionPerformed()
{
CutAndPasteTransfer cap = new CutAndPasteTransfer();
try
{
- cap.setText(pcaModel.getPointsasCsv(false,
+ cap.setText(getPcaModel().getPointsasCsv(false,
xCombobox.getSelectedIndex(), yCombobox.getSelectedIndex(),
zCombobox.getSelectedIndex()));
Desktop.addInternalFrame(cap, MessageManager
@@ -678,12 +567,12 @@ public class PCAPanel extends GPCAPanel
* .ActionEvent)
*/
@Override
- protected void outputProjPoints_actionPerformed(ActionEvent e)
+ protected void outputProjPoints_actionPerformed()
{
CutAndPasteTransfer cap = new CutAndPasteTransfer();
try
{
- cap.setText(pcaModel.getPointsasCsv(true,
+ cap.setText(getPcaModel().getPointsasCsv(true,
xCombobox.getSelectedIndex(), yCombobox.getSelectedIndex(),
zCombobox.getSelectedIndex()));
Desktop.addInternalFrame(cap, MessageManager.formatMessage(
@@ -793,13 +682,13 @@ public class PCAPanel extends GPCAPanel
}
@Override
- protected void resetButton_actionPerformed(ActionEvent e)
+ protected void resetButton_actionPerformed()
{
- int t = top;
- top = 0; // ugly - prevents dimensionChanged events from being processed
+ int t = getTop();
+ setTop(0); // ugly - prevents dimensionChanged events from being processed
xCombobox.setSelectedIndex(0);
yCombobox.setSelectedIndex(1);
- top = t;
+ setTop(t);
zCombobox.setSelectedIndex(2);
}
@@ -812,4 +701,94 @@ public class PCAPanel extends GPCAPanel
{
return working;
}
+
+ /**
+ * Answers the selected checkbox item index for PCA dimension for the X, Y or
+ * Z axis of the display
+ *
+ * @param axis
+ * @return
+ */
+ public int getSelectedDimensionIndex(Axis axis)
+ {
+ switch (axis)
+ {
+ case X:
+ return xCombobox.getSelectedIndex();
+ case Y:
+ return yCombobox.getSelectedIndex();
+ default:
+ return zCombobox.getSelectedIndex();
+ }
+ }
+
+ public void setShowLabels(boolean show)
+ {
+ showLabels.setSelected(show);
+ }
+
+ /**
+ * Sets the input data used to calculate the PCA. This is provided for
+ * 'restore from project', which does not currently support this (AL-2647), so
+ * sets the value to null, and hides the menu option for "Input Data...". J
+ *
+ * @param data
+ */
+ public void setInputData(AlignmentView data)
+ {
+ getPcaModel().setInputData(data);
+ originalSeqData.setVisible(data != null);
+ }
+
+ public AlignViewportI getAlignViewport()
+ {
+ return av;
+ }
+
+ public PCAModel getPcaModel()
+ {
+ return pcaModel;
+ }
+
+ public void setPcaModel(PCAModel pcaModel)
+ {
+ this.pcaModel = pcaModel;
+ }
+
+ public RotatableCanvas getRotatableCanvas()
+ {
+ return rc;
+ }
+
+ public void setRotatableCanvas(RotatableCanvas rc)
+ {
+ this.rc = rc;
+ }
+
+ public int getTop()
+ {
+ return top;
+ }
+
+ public void setTop(int top)
+ {
+ this.top = top;
+ }
+
+ /**
+ * set the associated view for this PCA.
+ *
+ * @param panel
+ */
+ public void selectAssociatedView(AlignmentPanel panel)
+ {
+ getRotatableCanvas().setApplyToAllViews(false);
+
+ ap = panel;
+ av = panel.av;
+
+ getRotatableCanvas().av = panel.av;
+ getRotatableCanvas().ap = panel;
+ PaintRefresher.Register(PCAPanel.this, panel.av.getSequenceSetId());
+ }
}
diff --git a/src/jalview/gui/RotatableCanvas.java b/src/jalview/gui/RotatableCanvas.java
index 02368df..dc0cdb4 100755
--- a/src/jalview/gui/RotatableCanvas.java
+++ b/src/jalview/gui/RotatableCanvas.java
@@ -21,10 +21,13 @@
package jalview.gui;
import jalview.api.RotatableCanvasI;
+import jalview.datamodel.Point;
import jalview.datamodel.SequenceGroup;
import jalview.datamodel.SequenceI;
import jalview.datamodel.SequencePoint;
import jalview.math.RotatableMatrix;
+import jalview.math.RotatableMatrix.Axis;
+import jalview.util.ColorUtils;
import jalview.util.MessageManager;
import jalview.viewmodel.AlignmentViewport;
@@ -43,328 +46,192 @@ import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
-import java.util.Vector;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
import javax.swing.JPanel;
import javax.swing.ToolTipManager;
/**
- * DOCUMENT ME!
- *
- * @author $author$
- * @version $Revision$
+ * Models a Panel on which a set of points, and optionally x/y/z axes, can be
+ * drawn, and rotated or zoomed with the mouse
*/
public class RotatableCanvas extends JPanel implements MouseListener,
- MouseMotionListener, KeyListener, RotatableCanvasI
+ MouseMotionListener, KeyListener, RotatableCanvasI,
+ MouseWheelListener
{
- RotatableMatrix idmat = new RotatableMatrix(3, 3);
+ private static final float ZOOM_OUT = 0.9f;
- RotatableMatrix objmat = new RotatableMatrix(3, 3);
+ private static final float ZOOM_IN = 1.1f;
- RotatableMatrix rotmat = new RotatableMatrix(3, 3);
+ /*
+ * pixels distance within which tooltip shows sequence name
+ */
+ private static final int NEARBY = 3;
- // RubberbandRectangle rubberband;
- boolean drawAxes = true;
+ private static final List AXES = Arrays.asList("x", "y", "z");
- int omx = 0;
+ private static final Color AXIS_COLOUR = Color.yellow;
- int mx = 0;
+ private static final int DIMS = 3;
- int omy = 0;
+ boolean drawAxes = true;
+
+ int mouseX;
- int my = 0;
+ int mouseY;
Image img;
Graphics ig;
- Dimension prefsize;
-
- float[] centre = new float[3];
+ Dimension prefSize;
- float[] width = new float[3];
-
- float[] max = new float[3];
-
- float[] min = new float[3];
+ /*
+ * the min-max [x, y, z] values of sequence points when the points
+ * were set on the object, or when the view is reset;
+ * x and y ranges are not recomputed as points are rotated, as this
+ * would make scaling (zoom) unstable, but z ranges are (for correct
+ * graduated colour brightness based on z-coordinate)
+ */
+ float[] seqMin;
- float maxwidth;
+ float[] seqMax;
- float scale;
+ /*
+ * a scale factor used in drawing; when equal to 1, the points span
+ * half the available width or height (whichever is less); increase this
+ * factor to zoom in, decrease it to zoom out
+ */
+ private float scaleFactor;
int npoint;
- Vector points;
-
- float[][] orig;
-
- float[][] axes;
-
- int startx;
-
- int starty;
-
- int lastx;
-
- int lasty;
-
- int rectx1;
-
- int recty1;
-
- int rectx2;
+ /*
+ * sequences and their (x, y, z) PCA dimension values
+ */
+ List sequencePoints;
- int recty2;
+ /*
+ * x, y, z axis end points (PCA dimension values)
+ */
+ private Point[] axisEndPoints;
- float scalefactor = 1;
+ // fields for 'select rectangle' (JAL-1124)
+ // int rectx1;
+ // int recty1;
+ // int rectx2;
+ // int recty2;
AlignmentViewport av;
AlignmentPanel ap;
- boolean showLabels = false;
+ private boolean showLabels;
- Color bgColour = Color.black;
+ private Color bgColour;
- boolean applyToAllViews = false;
+ private boolean applyToAllViews;
- public RotatableCanvas(AlignmentPanel ap)
+ /**
+ * Constructor
+ *
+ * @param panel
+ */
+ public RotatableCanvas(AlignmentPanel panel)
{
- this.av = ap.av;
- this.ap = ap;
-
- addMouseWheelListener(new MouseWheelListener()
- {
- @Override
- public void mouseWheelMoved(MouseWheelEvent e)
- {
- double wheelRotation = e.getPreciseWheelRotation();
- if (wheelRotation > 0)
- {
- /*
- * zoom in
- */
- scale = (float) (scale * 1.1);
- repaint();
- }
- else if (wheelRotation < 0)
- {
- /*
- * zoom out
- */
- scale = (float) (scale * 0.9);
- repaint();
- }
- }
- });
-
+ this.av = panel.av;
+ this.ap = panel;
+ setAxisEndPoints(new Point[DIMS]);
+ setShowLabels(false);
+ setApplyToAllViews(false);
+ setBgColour(Color.BLACK);
+ resetAxes();
+
+ ToolTipManager.sharedInstance().registerComponent(this);
+
+ addMouseListener(this);
+ addMouseMotionListener(this);
+ addMouseWheelListener(this);
}
- public void showLabels(boolean b)
+ /**
+ * Refreshes the display with labels shown (or not)
+ *
+ * @param show
+ */
+ public void showLabels(boolean show)
{
- showLabels = b;
+ setShowLabels(show);
repaint();
}
- boolean first = true;
-
@Override
- public void setPoints(Vector points, int npoint)
+ public void setPoints(List points, int np)
{
- this.points = points;
- this.npoint = npoint;
- if (first)
- {
- ToolTipManager.sharedInstance().registerComponent(this);
- ToolTipManager.sharedInstance().setInitialDelay(0);
- ToolTipManager.sharedInstance().setDismissDelay(10000);
- }
- prefsize = getPreferredSize();
- orig = new float[npoint][3];
+ this.sequencePoints = points;
+ this.npoint = np;
+ prefSize = getPreferredSize();
- for (int i = 0; i < npoint; i++)
- {
- SequencePoint sp = (SequencePoint) points.elementAt(i);
+ findWidths();
- for (int j = 0; j < 3; j++)
- {
- orig[i][j] = sp.coord[j];
- }
- }
-
- // Initialize the matrices to identity
- for (int i = 0; i < 3; i++)
- {
- for (int j = 0; j < 3; j++)
- {
- if (i != j)
- {
- idmat.addElement(i, j, 0);
- objmat.addElement(i, j, 0);
- rotmat.addElement(i, j, 0);
- }
- else
- {
- idmat.addElement(i, j, 0);
- objmat.addElement(i, j, 0);
- rotmat.addElement(i, j, 0);
- }
- }
- }
-
- axes = new float[3][3];
- initAxes();
-
- findCentre();
- findWidth();
-
- scale = findScale();
- if (first)
- {
-
- addMouseListener(this);
-
- addMouseMotionListener(this);
- }
- first = false;
- }
-
- public void initAxes()
- {
- for (int i = 0; i < 3; i++)
- {
- for (int j = 0; j < 3; j++)
- {
- if (i != j)
- {
- axes[i][j] = 0;
- }
- else
- {
- axes[i][j] = 1;
- }
- }
- }
+ setScaleFactor(1f);
}
/**
- * DOCUMENT ME!
+ * Resets axes to the initial state: x-axis to the right, y-axis up, z-axis to
+ * back (so obscured in a 2-D display)
*/
- public void findWidth()
+ protected void resetAxes()
{
- max = new float[3];
- min = new float[3];
-
- max[0] = (float) -1e30;
- max[1] = (float) -1e30;
- max[2] = (float) -1e30;
-
- min[0] = (float) 1e30;
- min[1] = (float) 1e30;
- min[2] = (float) 1e30;
-
- for (int i = 0; i < 3; i++)
- {
- for (int j = 0; j < npoint; j++)
- {
- SequencePoint sp = (SequencePoint) points.elementAt(j);
-
- if (sp.coord[i] >= max[i])
- {
- max[i] = sp.coord[i];
- }
-
- if (sp.coord[i] <= min[i])
- {
- min[i] = sp.coord[i];
- }
- }
- }
-
- // System.out.println("xmax " + max[0] + " min " + min[0]);
- // System.out.println("ymax " + max[1] + " min " + min[1]);
- // System.out.println("zmax " + max[2] + " min " + min[2]);
- width[0] = Math.abs(max[0] - min[0]);
- width[1] = Math.abs(max[1] - min[1]);
- width[2] = Math.abs(max[2] - min[2]);
-
- maxwidth = width[0];
-
- if (width[1] > width[0])
- {
- maxwidth = width[1];
- }
-
- if (width[2] > width[1])
- {
- maxwidth = width[2];
- }
-
- // System.out.println("Maxwidth = " + maxwidth);
- }
-
- /**
- * DOCUMENT ME!
- *
- * @return DOCUMENT ME!
- */
- public float findScale()
- {
- int dim;
- int width;
- int height;
-
- if (getWidth() != 0)
- {
- width = getWidth();
- height = getHeight();
- }
- else
- {
- width = prefsize.width;
- height = prefsize.height;
- }
-
- if (width < height)
- {
- dim = width;
- }
- else
- {
- dim = height;
- }
-
- return (dim * scalefactor) / (2 * maxwidth);
+ getAxisEndPoints()[0] = new Point(1f, 0f, 0f);
+ getAxisEndPoints()[1] = new Point(0f, 1f, 0f);
+ getAxisEndPoints()[2] = new Point(0f, 0f, 1f);
}
/**
- * DOCUMENT ME!
+ * Computes and saves the min-max ranges of x/y/z positions of the sequence
+ * points
*/
- public void findCentre()
+ protected void findWidths()
{
- // Find centre coordinate
- findWidth();
-
- centre[0] = (max[0] + min[0]) / 2;
- centre[1] = (max[1] + min[1]) / 2;
- centre[2] = (max[2] + min[2]) / 2;
-
- // System.out.println("Centre x " + centre[0]);
- // System.out.println("Centre y " + centre[1]);
- // System.out.println("Centre z " + centre[2]);
+ float[] max = new float[DIMS];
+ float[] min = new float[DIMS];
+
+ max[0] = -Float.MAX_VALUE;
+ max[1] = -Float.MAX_VALUE;
+ max[2] = -Float.MAX_VALUE;
+
+ min[0] = Float.MAX_VALUE;
+ min[1] = Float.MAX_VALUE;
+ min[2] = Float.MAX_VALUE;
+
+ for (SequencePoint sp : sequencePoints)
+ {
+ max[0] = Math.max(max[0], sp.coord.x);
+ max[1] = Math.max(max[1], sp.coord.y);
+ max[2] = Math.max(max[2], sp.coord.z);
+ min[0] = Math.min(min[0], sp.coord.x);
+ min[1] = Math.min(min[1], sp.coord.y);
+ min[2] = Math.min(min[2], sp.coord.z);
+ }
+
+ seqMin = min;
+ seqMax = max;
}
/**
- * DOCUMENT ME!
+ * Answers the preferred size if it has been set, else 400 x 400
*
- * @return DOCUMENT ME!
+ * @return
*/
@Override
public Dimension getPreferredSize()
{
- if (prefsize != null)
+ if (prefSize != null)
{
- return prefsize;
+ return prefSize;
}
else
{
@@ -373,9 +240,10 @@ public class RotatableCanvas extends JPanel implements MouseListener,
}
/**
- * DOCUMENT ME!
+ * Answers the preferred size
*
- * @return DOCUMENT ME!
+ * @return
+ * @see RotatableCanvas#getPreferredSize()
*/
@Override
public Dimension getMinimumSize()
@@ -384,10 +252,9 @@ public class RotatableCanvas extends JPanel implements MouseListener,
}
/**
- * DOCUMENT ME!
+ * Repaints the panel
*
* @param g
- * DOCUMENT ME!
*/
@Override
public void paintComponent(Graphics g1)
@@ -397,7 +264,7 @@ public class RotatableCanvas extends JPanel implements MouseListener,
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
- if (points == null)
+ if (sequencePoints == null)
{
g.setFont(new Font("Verdana", Font.PLAIN, 18));
g.drawString(
@@ -406,24 +273,24 @@ public class RotatableCanvas extends JPanel implements MouseListener,
}
else
{
- // Only create the image at the beginning -
- if ((img == null) || (prefsize.width != getWidth())
- || (prefsize.height != getHeight()))
+ /*
+ * create the image at the beginning or after a resize
+ */
+ boolean resized = prefSize.width != getWidth()
+ || prefSize.height != getHeight();
+ if (img == null || resized)
{
- prefsize.width = getWidth();
- prefsize.height = getHeight();
+ prefSize.width = getWidth();
+ prefSize.height = getHeight();
- scale = findScale();
-
- // System.out.println("New scale = " + scale);
img = createImage(getWidth(), getHeight());
ig = img.getGraphics();
}
- drawBackground(ig, bgColour);
+ drawBackground(ig);
drawScene(ig);
- if (drawAxes == true)
+ if (drawAxes)
{
drawAxes(ig);
}
@@ -433,96 +300,110 @@ public class RotatableCanvas extends JPanel implements MouseListener,
}
/**
- * DOCUMENT ME!
+ * Resets the rotation and choice of axes to the initial state (without change
+ * of scale factor)
+ */
+ public void resetView()
+ {
+ img = null;
+ findWidths();
+ resetAxes();
+ repaint();
+ }
+
+ /**
+ * Draws lines for the x, y, z axes
*
* @param g
- * DOCUMENT ME!
*/
public void drawAxes(Graphics g)
{
+ g.setColor(AXIS_COLOUR);
- g.setColor(Color.yellow);
+ int midX = getWidth() / 2;
+ int midY = getHeight() / 2;
+ float maxWidth = Math.max(Math.abs(seqMax[0] - seqMin[0]),
+ Math.abs(seqMax[1] - seqMin[1]));
+ int pix = Math.min(getWidth(), getHeight());
+ float scaleBy = pix * getScaleFactor() / (2f * maxWidth);
- for (int i = 0; i < 3; i++)
+ for (int i = 0; i < DIMS; i++)
{
- g.drawLine(getWidth() / 2, getHeight() / 2,
- (int) ((axes[i][0] * scale * max[0]) + (getWidth() / 2)),
- (int) ((axes[i][1] * scale * max[1]) + (getHeight() / 2)));
+ g.drawLine(midX, midY,
+ midX + (int) (getAxisEndPoints()[i].x * scaleBy * seqMax[0]),
+ midY + (int) (getAxisEndPoints()[i].y * scaleBy * seqMax[1]));
}
}
/**
- * DOCUMENT ME!
+ * Fills the background with the currently configured background colour
*
* @param g
- * DOCUMENT ME!
- * @param col
- * DOCUMENT ME!
*/
- public void drawBackground(Graphics g, Color col)
+ public void drawBackground(Graphics g)
{
- g.setColor(col);
- g.fillRect(0, 0, prefsize.width, prefsize.height);
+ g.setColor(getBgColour());
+ g.fillRect(0, 0, prefSize.width, prefSize.height);
}
/**
- * DOCUMENT ME!
+ * Draws points (6x6 squares) for the sequences of the PCA, and labels
+ * (sequence names) if configured to do so. The sequence points colours are
+ * taken from the sequence ids in the alignment (converting black to white).
+ * Sequences 'at the back' (z-coordinate is negative) are shaded slightly
+ * darker to help give a 3-D sensation.
*
* @param g
- * DOCUMENT ME!
*/
public void drawScene(Graphics g1)
{
-
Graphics2D g = (Graphics2D) g1;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
+ int pix = Math.min(getWidth(), getHeight());
+ float xWidth = Math.abs(seqMax[0] - seqMin[0]);
+ float yWidth = Math.abs(seqMax[1] - seqMin[1]);
+ float maxWidth = Math.max(xWidth, yWidth);
+ float scaleBy = pix * getScaleFactor() / (2f * maxWidth);
- int halfwidth = getWidth() / 2;
- int halfheight = getHeight() / 2;
+ float[] centre = getCentre();
for (int i = 0; i < npoint; i++)
{
- SequencePoint sp = (SequencePoint) points.elementAt(i);
- int x = (int) ((sp.coord[0] - centre[0]) * scale) + halfwidth;
- int y = (int) ((sp.coord[1] - centre[1]) * scale)
- + halfheight;
- float z = sp.coord[1] - centre[2];
-
- if (av.getSequenceColour(sp.sequence) == Color.black)
- {
- g.setColor(Color.white);
- }
- else
- {
- g.setColor(av.getSequenceColour(sp.sequence));
- }
-
- if (av.getSelectionGroup() != null)
- {
- if (av.getSelectionGroup().getSequences(null)
- .contains(((SequencePoint) points.elementAt(i)).sequence))
- {
- g.setColor(Color.gray);
- }
- }
+ /*
+ * sequence point colour as sequence id, but
+ * gray if sequence is currently selected
+ */
+ SequencePoint sp = sequencePoints.get(i);
+ Color sequenceColour = getSequencePointColour(sp);
+ g.setColor(sequenceColour);
+
+ int halfwidth = getWidth() / 2;
+ int halfheight = getHeight() / 2;
+ int x = (int) ((sp.coord.x - centre[0]) * scaleBy) + halfwidth;
+ int y = (int) ((sp.coord.y - centre[1]) * scaleBy) + halfheight;
+ g.fillRect(x - 3, y - 3, 6, 6);
- if (z < 0)
+ if (isShowLabels())
{
- g.setColor(g.getColor().darker());
+ g.setColor(Color.red);
+ g.drawString(sp.getSequence().getName(), x - 3, y - 4);
}
-
- g.fillRect(x - 3, y - 3, 6, 6);
- if (showLabels)
+ }
+ if (isShowLabels())
+ {
+ g.setColor(AXIS_COLOUR);
+ int midX = getWidth() / 2;
+ int midY = getHeight() / 2;
+ Iterator axes = AXES.iterator();
+ for (Point p : getAxisEndPoints())
{
- g.setColor(Color.red);
- g.drawString(
- ((SequencePoint) points.elementAt(i)).sequence.getName(),
- x - 3, y - 4);
+ int x = midX + (int) (p.x * scaleBy * seqMax[0]);
+ int y = midY + (int) (p.y * scaleBy * seqMax[1]);
+ g.drawString(axes.next(), x - 3, y - 4);
}
}
-
// //Now the rectangle
// if (rectx2 != -1 && recty2 != -1) {
// g.setColor(Color.white);
@@ -532,128 +413,147 @@ public class RotatableCanvas extends JPanel implements MouseListener,
}
/**
- * DOCUMENT ME!
+ * Determines the colour to use when drawing a sequence point. The colour is
+ * taken from the sequence id, with black converted to white, and then
+ * graduated from darker (at the back) to brighter (at the front) based on the
+ * z-axis coordinate of the point.
*
- * @return DOCUMENT ME!
+ * @param sp
+ * @return
*/
- public Dimension minimumsize()
+ protected Color getSequencePointColour(SequencePoint sp)
{
- return prefsize;
- }
+ SequenceI sequence = sp.getSequence();
+ Color sequenceColour = av.getSequenceColour(sequence);
+ if (sequenceColour == Color.black)
+ {
+ sequenceColour = Color.white;
+ }
+ if (av.getSelectionGroup() != null)
+ {
+ if (av.getSelectionGroup().getSequences(null).contains(sequence))
+ {
+ sequenceColour = Color.gray;
+ }
+ }
- /**
- * DOCUMENT ME!
- *
- * @return DOCUMENT ME!
- */
- public Dimension preferredsize()
- {
- return prefsize;
+ /*
+ * graduate brighter for point in front of centre, darker if behind centre
+ */
+ float zCentre = (seqMin[2] + seqMax[2]) / 2f;
+ if (sp.coord.z > zCentre)
+ {
+ sequenceColour = ColorUtils.getGraduatedColour(sp.coord.z, 0,
+ sequenceColour, seqMax[2], sequenceColour.brighter());
+ }
+ else if (sp.coord.z < zCentre)
+ {
+ sequenceColour = ColorUtils.getGraduatedColour(sp.coord.z, seqMin[2],
+ sequenceColour.darker(), 0, sequenceColour);
+ }
+
+ return sequenceColour;
}
- /**
- * DOCUMENT ME!
- *
- * @param evt
- * DOCUMENT ME!
- */
@Override
public void keyTyped(KeyEvent evt)
{
}
- /**
- * DOCUMENT ME!
- *
- * @param evt
- * DOCUMENT ME!
- */
@Override
public void keyReleased(KeyEvent evt)
{
}
/**
- * DOCUMENT ME!
+ * Responds to up or down arrow key by zooming in or out, respectively
*
* @param evt
- * DOCUMENT ME!
*/
@Override
public void keyPressed(KeyEvent evt)
{
- if (evt.getKeyCode() == KeyEvent.VK_UP)
+ int keyCode = evt.getKeyCode();
+ boolean shiftDown = evt.isShiftDown();
+
+ if (keyCode == KeyEvent.VK_UP)
+ {
+ if (shiftDown)
+ {
+ rotate(0f, -1f);
+ }
+ else
+ {
+ zoom(ZOOM_IN);
+ }
+ }
+ else if (keyCode == KeyEvent.VK_DOWN)
+ {
+ if (shiftDown)
+ {
+ rotate(0f, 1f);
+ }
+ else
+ {
+ zoom(ZOOM_OUT);
+ }
+ }
+ else if (shiftDown && keyCode == KeyEvent.VK_LEFT)
{
- scalefactor = (float) (scalefactor * 1.1);
- scale = findScale();
+ rotate(1f, 0f);
}
- else if (evt.getKeyCode() == KeyEvent.VK_DOWN)
+ else if (shiftDown && keyCode == KeyEvent.VK_RIGHT)
{
- scalefactor = (float) (scalefactor * 0.9);
- scale = findScale();
+ rotate(-1f, 0f);
}
else if (evt.getKeyChar() == 's')
{
- System.err.println("DEBUG: Rectangle selection"); // log.debug
-
- if ((rectx2 != -1) && (recty2 != -1))
- {
- rectSelect(rectx1, recty1, rectx2, recty2);
- }
+ // Cache.log.warn("DEBUG: Rectangle selection");
+ // todo not yet enabled as rectx2, recty2 are always -1
+ // need to set them in mouseDragged; JAL-1124
+ // if ((rectx2 != -1) && (recty2 != -1))
+ // {
+ // rectSelect(rectx1, recty1, rectx2, recty2);
+ // }
}
repaint();
}
- /**
- * DOCUMENT ME!
- *
- * @param evt
- * DOCUMENT ME!
- */
+ @Override
+ public void zoom(float factor)
+ {
+ if (factor > 0f)
+ {
+ setScaleFactor(getScaleFactor() * factor);
+ }
+ }
+
@Override
public void mouseClicked(MouseEvent evt)
{
}
- /**
- * DOCUMENT ME!
- *
- * @param evt
- * DOCUMENT ME!
- */
@Override
public void mouseEntered(MouseEvent evt)
{
}
- /**
- * DOCUMENT ME!
- *
- * @param evt
- * DOCUMENT ME!
- */
@Override
public void mouseExited(MouseEvent evt)
{
}
- /**
- * DOCUMENT ME!
- *
- * @param evt
- * DOCUMENT ME!
- */
@Override
public void mouseReleased(MouseEvent evt)
{
}
/**
- * DOCUMENT ME!
- *
- * @param evt
- * DOCUMENT ME!
+ * If the mouse press is at (within 2 pixels of) a sequence point, toggles
+ * (adds or removes) the corresponding sequence as a member of the viewport
+ * selection group. This supports configuring a group in the alignment by
+ * clicking on points in the PCA display.
*/
@Override
public void mousePressed(MouseEvent evt)
@@ -661,22 +561,15 @@ public class RotatableCanvas extends JPanel implements MouseListener,
int x = evt.getX();
int y = evt.getY();
- mx = x;
- my = y;
-
- omx = mx;
- omy = my;
-
- startx = x;
- starty = y;
+ mouseX = x;
+ mouseY = y;
- rectx1 = x;
- recty1 = y;
+ // rectx1 = x;
+ // recty1 = y;
+ // rectx2 = -1;
+ // recty2 = -1;
- rectx2 = -1;
- recty2 = -1;
-
- SequenceI found = findPoint(x, y);
+ SequenceI found = findSequenceAtPoint(x, y);
if (found != null)
{
@@ -704,36 +597,37 @@ public class RotatableCanvas extends JPanel implements MouseListener,
repaint();
}
- // private void fireSequenceSelectionEvent(Selection sel) {
- // controller.handleSequenceSelectionEvent(new
- // SequenceSelectionEvent(this,sel));
- // }
+ /**
+ * Sets the tooltip to the name of the sequence within 2 pixels of the mouse
+ * position, or clears the tooltip if none found
+ */
@Override
public void mouseMoved(MouseEvent evt)
{
- SequenceI found = findPoint(evt.getX(), evt.getY());
+ SequenceI found = findSequenceAtPoint(evt.getX(), evt.getY());
- if (found != null)
- {
- this.setToolTipText(found.getName());
- }
- else
- {
- this.setToolTipText(null);
- }
+ this.setToolTipText(found == null ? null : found.getName());
}
/**
- * DOCUMENT ME!
+ * Action handler for a mouse drag. Rotates the display around the X axis (for
+ * up/down mouse movement) and/or the Y axis (for left/right mouse movement).
*
* @param evt
- * DOCUMENT ME!
*/
@Override
public void mouseDragged(MouseEvent evt)
{
- mx = evt.getX();
- my = evt.getY();
+ int xPos = evt.getX();
+ int yPos = evt.getY();
+
+ if (xPos == mouseX && yPos == mouseY)
+ {
+ return;
+ }
+
+ int xDelta = xPos - mouseX;
+ int yDelta = yPos - mouseY;
// Check if this is a rectangle drawing drag
if ((evt.getModifiers() & InputEvent.BUTTON2_MASK) != 0)
@@ -743,113 +637,178 @@ public class RotatableCanvas extends JPanel implements MouseListener,
}
else
{
- rotmat.setIdentity();
+ rotate(xDelta, yDelta);
- rotmat.rotate(my - omy, 'x');
- rotmat.rotate(mx - omx, 'y');
+ mouseX = xPos;
+ mouseY = yPos;
- for (int i = 0; i < npoint; i++)
- {
- SequencePoint sp = (SequencePoint) points.elementAt(i);
- sp.coord[0] -= centre[0];
- sp.coord[1] -= centre[1];
- sp.coord[2] -= centre[2];
-
- // Now apply the rotation matrix
- sp.coord = rotmat.vectorMultiply(sp.coord);
-
- // Now translate back again
- sp.coord[0] += centre[0];
- sp.coord[1] += centre[1];
- sp.coord[2] += centre[2];
- }
+ // findWidths();
- for (int i = 0; i < 3; i++)
- {
- axes[i] = rotmat.vectorMultiply(axes[i]);
- }
+ repaint();
+ }
+ }
+
+ @Override
+ public void rotate(float x, float y)
+ {
+ if (x == 0f && y == 0f)
+ {
+ return;
+ }
+
+ /*
+ * get the identity transformation...
+ */
+ RotatableMatrix rotmat = new RotatableMatrix();
+
+ /*
+ * rotate around the X axis for change in Y
+ * (mouse movement up/down); note we are equating a
+ * number of pixels with degrees of rotation here!
+ */
+ if (y != 0)
+ {
+ rotmat.rotate(y, Axis.X);
+ }
+
+ /*
+ * rotate around the Y axis for change in X
+ * (mouse movement left/right)
+ */
+ if (x != 0)
+ {
+ rotmat.rotate(x, Axis.Y);
+ }
+
+ /*
+ * apply the composite transformation to sequence points;
+ * update z min-max range (affects colour graduation), but not
+ * x or y min-max (as this would affect axis scaling)
+ */
+ float[] centre = getCentre();
+ float zMin = Float.MAX_VALUE;
+ float zMax = -Float.MAX_VALUE;
+
+ for (int i = 0; i < npoint; i++)
+ {
+ SequencePoint sp = sequencePoints.get(i);
+ sp.translate(-centre[0], -centre[1], -centre[2]);
+
+ // Now apply the rotation matrix
+ sp.coord = rotmat.vectorMultiply(sp.coord);
+
+ // Now translate back again
+ sp.translate(centre[0], centre[1], centre[2]);
+
+ zMin = Math.min(zMin, sp.coord.z);
+ zMax = Math.max(zMax, sp.coord.z);
+ }
- omx = mx;
- omy = my;
+ seqMin[2] = zMin;
+ seqMax[2] = zMax;
- paint(this.getGraphics());
+ /*
+ * rotate the x/y/z axis positions
+ */
+ for (int i = 0; i < DIMS; i++)
+ {
+ getAxisEndPoints()[i] = rotmat.vectorMultiply(getAxisEndPoints()[i]);
}
}
/**
- * DOCUMENT ME!
+ * Answers the x/y/z coordinates that are midway between the maximum and
+ * minimum sequence point values
+ *
+ * @return
+ */
+ private float[] getCentre()
+ {
+ float xCentre = (seqMin[0] + seqMax[0]) / 2f;
+ float yCentre = (seqMin[1] + seqMax[1]) / 2f;
+ float zCentre = (seqMin[2] + seqMax[2]) / 2f;
+
+ return new float[] { xCentre, yCentre, zCentre };
+ }
+
+ /**
+ * Adds any sequences whose displayed points are within the given rectangle to
+ * the viewport's current selection. Intended for key 's' after dragging to
+ * select a region of the PCA.
*
* @param x1
- * DOCUMENT ME!
* @param y1
- * DOCUMENT ME!
* @param x2
- * DOCUMENT ME!
* @param y2
- * DOCUMENT ME!
*/
- public void rectSelect(int x1, int y1, int x2, int y2)
+ protected void rectSelect(int x1, int y1, int x2, int y2)
{
+ float[] centre = getCentre();
+
for (int i = 0; i < npoint; i++)
{
- SequencePoint sp = (SequencePoint) points.elementAt(i);
- int tmp1 = (int) (((sp.coord[0] - centre[0]) * scale)
+ SequencePoint sp = sequencePoints.get(i);
+ int tmp1 = (int) (((sp.coord.x - centre[0]) * getScaleFactor())
+ (getWidth() / 2.0));
- int tmp2 = (int) (((sp.coord[1] - centre[1]) * scale)
+ int tmp2 = (int) (((sp.coord.y - centre[1]) * getScaleFactor())
+ (getHeight() / 2.0));
if ((tmp1 > x1) && (tmp1 < x2) && (tmp2 > y1) && (tmp2 < y2))
{
if (av != null)
{
+ SequenceI sequence = sp.getSequence();
if (!av.getSelectionGroup().getSequences(null)
- .contains(sp.sequence))
+ .contains(sequence))
{
- av.getSelectionGroup().addSequence(sp.sequence, true);
+ av.getSelectionGroup().addSequence(sequence, true);
}
}
}
}
-
- // if (changedSel) {
- // fireSequenceSelectionEvent(av.getSelection());
- // }
}
/**
- * DOCUMENT ME!
+ * Answers the first sequence found whose point on the display is within 2
+ * pixels of the given coordinates, or null if none is found
*
* @param x
- * DOCUMENT ME!
* @param y
- * DOCUMENT ME!
*
- * @return DOCUMENT ME!
+ * @return
*/
- public SequenceI findPoint(int x, int y)
+ protected SequenceI findSequenceAtPoint(int x, int y)
{
int halfwidth = getWidth() / 2;
int halfheight = getHeight() / 2;
int found = -1;
+ int pix = Math.min(getWidth(), getHeight());
+ float xWidth = Math.abs(seqMax[0] - seqMin[0]);
+ float yWidth = Math.abs(seqMax[1] - seqMin[1]);
+ float maxWidth = Math.max(xWidth, yWidth);
+ float scaleBy = pix * getScaleFactor() / (2f * maxWidth);
+
+ float[] centre = getCentre();
for (int i = 0; i < npoint; i++)
{
- SequencePoint sp = (SequencePoint) points.elementAt(i);
- int px = (int) ((sp.coord[0] - centre[0]) * scale)
+ SequencePoint sp = sequencePoints.get(i);
+ int px = (int) ((sp.coord.x - centre[0]) * scaleBy)
+ halfwidth;
- int py = (int) ((sp.coord[1] - centre[1]) * scale)
+ int py = (int) ((sp.coord.y - centre[1]) * scaleBy)
+ halfheight;
- if ((Math.abs(px - x) < 3) && (Math.abs(py - y) < 3))
+ if ((Math.abs(px - x) < NEARBY) && (Math.abs(py - y) < NEARBY))
{
found = i;
+ break;
}
}
if (found != -1)
{
- return ((SequencePoint) points.elementAt(found)).sequence;
+ return sequencePoints.get(found).getSequence();
}
else
{
@@ -857,9 +816,15 @@ public class RotatableCanvas extends JPanel implements MouseListener,
}
}
+ /**
+ * Answers the panel the PCA is associated with (all panels for this alignment
+ * if 'associate with all panels' is selected).
+ *
+ * @return
+ */
AlignmentPanel[] getAssociatedPanels()
{
- if (applyToAllViews)
+ if (isApplyToAllViews())
{
return PaintRefresher.getAssociatedPanels(av.getSequenceSetId());
}
@@ -869,19 +834,114 @@ public class RotatableCanvas extends JPanel implements MouseListener,
}
}
+ public Color getBackgroundColour()
+ {
+ return getBgColour();
+ }
+
+ /**
+ * Zooms in or out in response to mouse wheel movement
+ */
+ @Override
+ public void mouseWheelMoved(MouseWheelEvent e)
+ {
+ double wheelRotation = e.getPreciseWheelRotation();
+ if (wheelRotation > 0)
+ {
+ zoom(ZOOM_IN);
+ repaint();
+ }
+ else if (wheelRotation < 0)
+ {
+ zoom(ZOOM_OUT);
+ repaint();
+ }
+ }
+
+ /**
+ * Answers the sequence point minimum [x, y, z] values. Note these are derived
+ * when sequence points are set, but x and y values are not updated on
+ * rotation (because this would result in changes to scaling).
+ *
+ * @return
+ */
+ public float[] getSeqMin()
+ {
+ return seqMin;
+ }
+
+ /**
+ * Answers the sequence point maximum [x, y, z] values. Note these are derived
+ * when sequence points are set, but x and y values are not updated on
+ * rotation (because this would result in changes to scaling).
+ *
+ * @return
+ */
+ public float[] getSeqMax()
+ {
+ return seqMax;
+ }
+
/**
+ * Sets the minimum and maximum [x, y, z] positions for sequence points. For
+ * use when restoring a saved PCA from state data.
*
- * @return x,y,z positions of point s (index into points) under current
- * transform.
+ * @param min
+ * @param max
*/
- public double[] getPointPosition(int s)
+ public void setSeqMinMax(float[] min, float[] max)
+ {
+ seqMin = min;
+ seqMax = max;
+ }
+
+ public float getScaleFactor()
+ {
+ return scaleFactor;
+ }
+
+ public void setScaleFactor(float scaleFactor)
+ {
+ this.scaleFactor = scaleFactor;
+ }
+
+ public boolean isShowLabels()
{
- double pts[] = new double[3];
- float[] p = ((SequencePoint) points.elementAt(s)).coord;
- pts[0] = p[0];
- pts[1] = p[1];
- pts[2] = p[2];
- return pts;
+ return showLabels;
}
+ public void setShowLabels(boolean showLabels)
+ {
+ this.showLabels = showLabels;
+ }
+
+ public boolean isApplyToAllViews()
+ {
+ return applyToAllViews;
+ }
+
+ public void setApplyToAllViews(boolean applyToAllViews)
+ {
+ this.applyToAllViews = applyToAllViews;
+ }
+
+ public Point[] getAxisEndPoints()
+ {
+ return axisEndPoints;
+ }
+
+ public void setAxisEndPoints(Point[] axisEndPoints)
+ {
+ this.axisEndPoints = axisEndPoints;
+ }
+
+ public Color getBgColour()
+ {
+ return bgColour;
+ }
+
+ public void setBgColour(Color bgColour)
+ {
+ this.bgColour = bgColour;
+ }
}
diff --git a/src/jalview/gui/ScalePanel.java b/src/jalview/gui/ScalePanel.java
index e6bba02..6abee08 100755
--- a/src/jalview/gui/ScalePanel.java
+++ b/src/jalview/gui/ScalePanel.java
@@ -276,7 +276,9 @@ public class ScalePanel extends JPanel
{
mouseDragging = false;
- int res = (evt.getX() / av.getCharWidth())
+ int xCords = Math.max(0, evt.getX()); // prevent negative X coordinates
+
+ int res = (xCords / av.getCharWidth())
+ av.getRanges().getStartRes();
if (av.hasHiddenColumns())
diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java
index 8b2e7bc..c794e57 100644
--- a/src/jalview/gui/SeqPanel.java
+++ b/src/jalview/gui/SeqPanel.java
@@ -28,6 +28,7 @@ import jalview.commands.EditCommand.Edit;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.ColumnSelection;
import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.MappedFeatures;
import jalview.datamodel.SearchResultMatchI;
import jalview.datamodel.SearchResults;
import jalview.datamodel.SearchResultsI;
@@ -48,6 +49,7 @@ import jalview.util.MappingUtils;
import jalview.util.MessageManager;
import jalview.util.Platform;
import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.seqfeatures.FeatureRendererModel;
import java.awt.BorderLayout;
import java.awt.Color;
@@ -59,6 +61,7 @@ import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -714,11 +717,11 @@ public class SeqPanel extends JPanel
* the start of the highlighted region.
*/
@Override
- public void highlightSequence(SearchResultsI results)
+ public String highlightSequence(SearchResultsI results)
{
if (results == null || results.equals(lastSearchResults))
{
- return;
+ return null;
}
lastSearchResults = results;
@@ -744,6 +747,74 @@ public class SeqPanel extends JPanel
{
setStatusMessage(results);
}
+ return results.isEmpty() ? null : getHighlightInfo(results);
+ }
+
+ /**
+ * temporary hack: answers a message suitable to show on structure hover
+ * label. This is normally null. It is a peptide variation description if
+ *
+ * - results are a single residue in a protein alignment
+ * - there is a mapping to a coding sequence (codon)
+ * - there are one or more SNP variant features on the codon
+ *
+ * in which case the answer is of the format (e.g.) "p.Glu388Asp"
+ *
+ * @param results
+ * @return
+ */
+ private String getHighlightInfo(SearchResultsI results)
+ {
+ /*
+ * ideally, just find mapped CDS (as we don't care about render style here);
+ * for now, go via split frame complement's FeatureRenderer
+ */
+ AlignViewportI complement = ap.getAlignViewport().getCodingComplement();
+ if (complement == null)
+ {
+ return null;
+ }
+ AlignFrame af = Desktop.getAlignFrameFor(complement);
+ FeatureRendererModel fr2 = af.getFeatureRenderer();
+
+ int j = results.getSize();
+ List infos = new ArrayList<>();
+ for (int i = 0; i < j; i++)
+ {
+ SearchResultMatchI match = results.getResults().get(i);
+ int pos = match.getStart();
+ if (pos == match.getEnd())
+ {
+ SequenceI seq = match.getSequence();
+ SequenceI ds = seq.getDatasetSequence() == null ? seq
+ : seq.getDatasetSequence();
+ MappedFeatures mf = fr2
+ .findComplementFeaturesAtResidue(ds, pos);
+ List pv = mf.findProteinVariants();
+ for (String s : pv)
+ {
+ if (!infos.contains(s))
+ {
+ infos.addAll(pv);
+ }
+ }
+ }
+ }
+
+ if (infos.isEmpty())
+ {
+ return null;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (String info : infos)
+ {
+ if (sb.length() > 0)
+ {
+ sb.append("|");
+ }
+ sb.append(info);
+ }
+ return sb.toString();
}
@Override
@@ -847,6 +918,24 @@ public class SeqPanel extends JPanel
.findFeaturesAtColumn(sequence, column + 1);
seqARep.appendFeatures(tooltipText, pos, features,
this.ap.getSeqPanel().seqCanvas.fr);
+
+ /*
+ * add features in CDS/protein complement at the corresponding
+ * position if configured to do so
+ */
+ if (av.isShowComplementFeatures())
+ {
+ if (!Comparison.isGap(sequence.getCharAt(column)))
+ {
+ AlignViewportI complement = ap.getAlignViewport()
+ .getCodingComplement();
+ AlignFrame af = Desktop.getAlignFrameFor(complement);
+ FeatureRendererModel fr2 = af.getFeatureRenderer();
+ MappedFeatures mf = fr2.findComplementFeaturesAtResidue(sequence,
+ pos);
+ seqARep.appendFeatures(tooltipText, pos, mf.features, fr2);
+ }
+ }
}
if (tooltipText.length() == 6) //
{
diff --git a/src/jalview/gui/SplitFrame.java b/src/jalview/gui/SplitFrame.java
index 5bff407..a0d31cf 100644
--- a/src/jalview/gui/SplitFrame.java
+++ b/src/jalview/gui/SplitFrame.java
@@ -539,7 +539,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
topFrame.setDisplayedView(newTopPanel);
}
- newBottomPanel.av.viewName = newTopPanel.av.viewName;
+ newBottomPanel.av.setViewName(newTopPanel.av.getViewName());
newTopPanel.av.setCodingComplement(newBottomPanel.av);
/*
diff --git a/src/jalview/gui/TreeCanvas.java b/src/jalview/gui/TreeCanvas.java
index fa30e13..c195c80 100755
--- a/src/jalview/gui/TreeCanvas.java
+++ b/src/jalview/gui/TreeCanvas.java
@@ -80,9 +80,9 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
TreePanel tp;
- AlignViewport av;
+ private AlignViewport av;
- AlignmentPanel ap;
+ private AlignmentPanel ap;
Font font;
@@ -100,7 +100,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
int offy;
- float threshold;
+ private float threshold;
String longestName;
@@ -130,7 +130,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
{
this.tp = tp;
this.av = ap.av;
- this.ap = ap;
+ this.setAssociatedPanel(ap);
font = av.getFont();
scrollPane = scroller;
addMouseListener(this);
@@ -847,7 +847,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
if (col != null)
{
setColor(highlightNode, col);
- PaintRefresher.Refresh(tp, ap.av.getSequenceSetId());
+ PaintRefresher.Refresh(tp, getAssociatedPanel().av.getSequenceSetId());
repaint();
}
}
@@ -928,7 +928,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
if (ob instanceof SequenceI)
{
treeSelectionChanged((Sequence) ob);
- PaintRefresher.Refresh(tp, ap.av.getSequenceSetId());
+ PaintRefresher.Refresh(tp, getAssociatedPanel().av.getSequenceSetId());
repaint();
av.sendSelection();
return;
@@ -973,7 +973,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
}
}
- PaintRefresher.Refresh(tp, ap.av.getSequenceSetId());
+ PaintRefresher.Refresh(tp, getAssociatedPanel().av.getSequenceSetId());
repaint();
}
@@ -1127,7 +1127,47 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
}
else
{
- return new AlignmentPanel[] { ap };
+ return new AlignmentPanel[] { getAssociatedPanel() };
}
}
+
+ public AlignmentPanel getAssociatedPanel()
+ {
+ return ap;
+ }
+
+ public void setAssociatedPanel(AlignmentPanel ap)
+ {
+ this.ap = ap;
+ }
+
+ public AlignViewport getViewport()
+ {
+ return av;
+ }
+
+ public void setViewport(AlignViewport av)
+ {
+ this.av = av;
+ }
+
+ public float getThreshold()
+ {
+ return threshold;
+ }
+
+ public void setThreshold(float threshold)
+ {
+ this.threshold = threshold;
+ }
+
+ public boolean isApplyToAllViews()
+ {
+ return this.applyToAllViews;
+ }
+
+ public void setApplyToAllViews(boolean applyToAllViews)
+ {
+ this.applyToAllViews = applyToAllViews;
+ }
}
diff --git a/src/jalview/gui/TreePanel.java b/src/jalview/gui/TreePanel.java
index 2727db1..ca4f84e 100755
--- a/src/jalview/gui/TreePanel.java
+++ b/src/jalview/gui/TreePanel.java
@@ -85,11 +85,11 @@ public class TreePanel extends GTreePanel
SimilarityParamsI similarityParams;
- TreeCanvas treeCanvas;
+ private TreeCanvas treeCanvas;
TreeModel tree;
- AlignViewport av;
+ private AlignViewport av;
/**
* Creates a new TreePanel object.
@@ -121,24 +121,24 @@ public class TreePanel extends GTreePanel
public AlignmentI getAlignment()
{
- return treeCanvas.av.getAlignment();
+ return getTreeCanvas().getViewport().getAlignment();
}
public AlignmentViewport getViewPort()
{
- return treeCanvas.av;
+ return getTreeCanvas().getViewport();
}
void initTreePanel(AlignmentPanel ap, String type, String modelName,
NewickFile newTree, AlignmentView inputData)
{
- av = ap.av;
+ setViewport(ap.av);
this.treeType = type;
this.scoreModelName = modelName;
treeCanvas = new TreeCanvas(this, ap, scrollPane);
- scrollPane.setViewportView(treeCanvas);
+ scrollPane.setViewportView(getTreeCanvas());
PaintRefresher.Register(this, ap.av.getSequenceSetId());
@@ -155,9 +155,9 @@ public class TreePanel extends GTreePanel
@Override
public void internalFrameClosed(InternalFrameEvent evt)
{
- if (av != null)
+ if (getViewport() != null)
{
- av.removePropertyChangeListener(listener);
+ getViewport().removePropertyChangeListener(listener);
}
}
});
@@ -194,13 +194,13 @@ public class TreePanel extends GTreePanel
}
tree.updatePlaceHolders((List) evt.getNewValue());
- treeCanvas.nameHash.clear(); // reset the mapping between canvas
+ getTreeCanvas().nameHash.clear(); // reset the mapping between canvas
// rectangles and leafnodes
repaint();
}
}
};
- av.addPropertyChangeListener(listener);
+ getViewport().addPropertyChangeListener(listener);
return listener;
}
@@ -213,8 +213,8 @@ public class TreePanel extends GTreePanel
void buildAssociatedViewMenu()
{
AlignmentPanel[] aps = PaintRefresher
- .getAssociatedPanels(av.getSequenceSetId());
- if (aps.length == 1 && treeCanvas.ap == aps[0])
+ .getAssociatedPanels(getViewport().getSequenceSetId());
+ if (aps.length == 1 && getTreeCanvas().getAssociatedPanel() == aps[0])
{
associateLeavesMenu.setVisible(false);
return;
@@ -237,16 +237,16 @@ public class TreePanel extends GTreePanel
for (i = 0; i < iSize; i++)
{
final AlignmentPanel ap = aps[i];
- item = new JRadioButtonMenuItem(ap.av.viewName, ap == treeCanvas.ap);
+ item = new JRadioButtonMenuItem(ap.av.getViewName(), ap == getTreeCanvas().getAssociatedPanel());
buttonGroup.add(item);
item.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent evt)
{
- treeCanvas.applyToAllViews = false;
- treeCanvas.ap = ap;
- treeCanvas.av = ap.av;
+ getTreeCanvas().applyToAllViews = false;
+ getTreeCanvas().setAssociatedPanel(ap);
+ getTreeCanvas().setViewport(ap.av);
PaintRefresher.Register(thisTreePanel, ap.av.getSequenceSetId());
}
});
@@ -257,13 +257,13 @@ public class TreePanel extends GTreePanel
final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem(
MessageManager.getString("label.all_views"));
buttonGroup.add(itemf);
- itemf.setSelected(treeCanvas.applyToAllViews);
+ itemf.setSelected(getTreeCanvas().applyToAllViews);
itemf.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent evt)
{
- treeCanvas.applyToAllViews = itemf.isSelected();
+ getTreeCanvas().applyToAllViews = itemf.isSelected();
}
});
associateLeavesMenu.add(itemf);
@@ -296,7 +296,7 @@ public class TreePanel extends GTreePanel
if (newtree != null)
{
- tree = new TreeModel(av.getAlignment().getSequencesArray(), odata,
+ tree = new TreeModel(getViewport().getAlignment().getSequencesArray(), odata,
newtree);
if (tree.getOriginalData() == null)
{
@@ -306,20 +306,20 @@ public class TreePanel extends GTreePanel
else
{
ScoreModelI sm = ScoreModels.getInstance()
- .getScoreModel(scoreModelName, treeCanvas.ap);
+ .getScoreModel(scoreModelName, getTreeCanvas().getAssociatedPanel());
TreeBuilder njtree = treeType.equals(TreeBuilder.NEIGHBOUR_JOINING)
- ? new NJTree(av, sm, similarityParams)
- : new AverageDistanceTree(av, sm, similarityParams);
+ ? new NJTree(getViewport(), sm, similarityParams)
+ : new AverageDistanceTree(getViewport(), sm, similarityParams);
tree = new TreeModel(njtree);
showDistances(true);
}
tree.reCount(tree.getTopNode());
tree.findHeight(tree.getTopNode());
- treeCanvas.setTree(tree);
- treeCanvas.repaint();
- av.setCurrentTree(tree);
- if (av.getSortByTree())
+ getTreeCanvas().setTree(tree);
+ getTreeCanvas().repaint();
+ getViewport().setCurrentTree(tree);
+ if (getViewport().getSortByTree())
{
sortByTree_actionPerformed();
}
@@ -328,20 +328,20 @@ public class TreePanel extends GTreePanel
public void showDistances(boolean b)
{
- treeCanvas.setShowDistances(b);
+ getTreeCanvas().setShowDistances(b);
distanceMenu.setSelected(b);
}
public void showBootstrap(boolean b)
{
- treeCanvas.setShowBootstrap(b);
+ getTreeCanvas().setShowBootstrap(b);
bootstrapMenu.setSelected(b);
}
public void showPlaceholders(boolean b)
{
placeholdersMenu.setState(b);
- treeCanvas.setMarkPlaceholders(b);
+ getTreeCanvas().setMarkPlaceholders(b);
}
/**
@@ -432,7 +432,7 @@ public class TreePanel extends GTreePanel
public void printMenu_actionPerformed(ActionEvent e)
{
// Putting in a thread avoids Swing painting problems
- treeCanvas.startPrinting();
+ getTreeCanvas().startPrinting();
}
@Override
@@ -456,7 +456,7 @@ public class TreePanel extends GTreePanel
{
// we try to get the associated view's gap character
// but this may fail if the view was closed...
- gc = av.getGapCharacter();
+ gc = getViewport().getGapCharacter();
} catch (Exception ex)
{
@@ -469,8 +469,8 @@ public class TreePanel extends GTreePanel
// AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]);
AlignmentI al = new Alignment((SequenceI[]) alAndColsel[0]);
- AlignmentI dataset = (av != null && av.getAlignment() != null)
- ? av.getAlignment().getDataset()
+ AlignmentI dataset = (getViewport() != null && getViewport().getAlignment() != null)
+ ? getViewport().getAlignment().getDataset()
: null;
if (dataset != null)
{
@@ -507,7 +507,7 @@ public class TreePanel extends GTreePanel
@Override
public void fitToWindow_actionPerformed(ActionEvent e)
{
- treeCanvas.fitToWindow = fitToWindow.isSelected();
+ getTreeCanvas().fitToWindow = fitToWindow.isSelected();
repaint();
}
@@ -520,15 +520,15 @@ public class TreePanel extends GTreePanel
public void sortByTree_actionPerformed()
{
- if (treeCanvas.applyToAllViews)
+ if (getTreeCanvas().applyToAllViews)
{
final ArrayList commands = new ArrayList<>();
for (AlignmentPanel ap : PaintRefresher
- .getAssociatedPanels(av.getSequenceSetId()))
+ .getAssociatedPanels(getViewport().getSequenceSetId()))
{
commands.add(sortAlignmentIn(ap.av.getAlignPanel()));
}
- av.getAlignPanel().alignFrame.addHistoryItem(new CommandI()
+ getViewport().getAlignPanel().alignFrame.addHistoryItem(new CommandI()
{
@Override
@@ -563,7 +563,7 @@ public class TreePanel extends GTreePanel
}
});
for (AlignmentPanel ap : PaintRefresher
- .getAssociatedPanels(av.getSequenceSetId()))
+ .getAssociatedPanels(getViewport().getSequenceSetId()))
{
// ensure all the alignFrames refresh their GI after adding an undo item
ap.alignFrame.updateEditMenuBar();
@@ -571,8 +571,8 @@ public class TreePanel extends GTreePanel
}
else
{
- treeCanvas.ap.alignFrame
- .addHistoryItem(sortAlignmentIn(treeCanvas.ap));
+ getTreeCanvas().getAssociatedPanel().alignFrame
+ .addHistoryItem(sortAlignmentIn(getTreeCanvas().getAssociatedPanel()));
}
}
@@ -599,7 +599,7 @@ public class TreePanel extends GTreePanel
@Override
public void font_actionPerformed(ActionEvent e)
{
- if (treeCanvas == null)
+ if (getTreeCanvas() == null)
{
return;
}
@@ -609,14 +609,14 @@ public class TreePanel extends GTreePanel
public Font getTreeFont()
{
- return treeCanvas.font;
+ return getTreeCanvas().font;
}
public void setTreeFont(Font f)
{
- if (treeCanvas != null)
+ if (getTreeCanvas() != null)
{
- treeCanvas.setFont(f);
+ getTreeCanvas().setFont(f);
}
}
@@ -629,7 +629,7 @@ public class TreePanel extends GTreePanel
@Override
public void distanceMenu_actionPerformed(ActionEvent e)
{
- treeCanvas.setShowDistances(distanceMenu.isSelected());
+ getTreeCanvas().setShowDistances(distanceMenu.isSelected());
}
/**
@@ -641,7 +641,7 @@ public class TreePanel extends GTreePanel
@Override
public void bootstrapMenu_actionPerformed(ActionEvent e)
{
- treeCanvas.setShowBootstrap(bootstrapMenu.isSelected());
+ getTreeCanvas().setShowBootstrap(bootstrapMenu.isSelected());
}
/**
@@ -653,7 +653,7 @@ public class TreePanel extends GTreePanel
@Override
public void placeholdersMenu_actionPerformed(ActionEvent e)
{
- treeCanvas.setMarkPlaceholders(placeholdersMenu.isSelected());
+ getTreeCanvas().setMarkPlaceholders(placeholdersMenu.isSelected());
}
/**
@@ -691,8 +691,8 @@ public class TreePanel extends GTreePanel
accurateText = false;
}
- int width = treeCanvas.getWidth();
- int height = treeCanvas.getHeight();
+ int width = getTreeCanvas().getWidth();
+ int height = getTreeCanvas().getHeight();
try
{
@@ -720,7 +720,7 @@ public class TreePanel extends GTreePanel
pg.setAccurateTextMode(accurateText);
- treeCanvas.draw(pg, width, height);
+ getTreeCanvas().draw(pg, width, height);
pg.flush();
pg.close();
@@ -739,8 +739,8 @@ public class TreePanel extends GTreePanel
@Override
public void pngTree_actionPerformed(ActionEvent e)
{
- int width = treeCanvas.getWidth();
- int height = treeCanvas.getHeight();
+ int width = getTreeCanvas().getWidth();
+ int height = getTreeCanvas().getHeight();
try
{
@@ -769,7 +769,7 @@ public class TreePanel extends GTreePanel
BufferedImage.TYPE_INT_RGB);
Graphics png = bi.getGraphics();
- treeCanvas.draw(png, width, height);
+ getTreeCanvas().draw(png, width, height);
ImageIO.write(bi, "png", out);
out.close();
@@ -879,8 +879,23 @@ public class TreePanel extends GTreePanel
/*
* put them together as Using
*/
- final String ttl = MessageManager.formatMessage("label.treecalc_title",
+ final String ttl = MessageManager.formatMessage("label.calc_title",
treecalcnm, smn);
return ttl;
}
+
+ public AlignViewport getViewport()
+ {
+ return av;
+ }
+
+ public void setViewport(AlignViewport av)
+ {
+ this.av = av;
+ }
+
+ public TreeCanvas getTreeCanvas()
+ {
+ return treeCanvas;
+ }
}
diff --git a/src/jalview/gui/UserDefinedColours.java b/src/jalview/gui/UserDefinedColours.java
index 3290500..b1f6d1b 100755
--- a/src/jalview/gui/UserDefinedColours.java
+++ b/src/jalview/gui/UserDefinedColours.java
@@ -24,8 +24,6 @@ import jalview.bin.Cache;
import jalview.io.JalviewFileChooser;
import jalview.io.JalviewFileView;
import jalview.jbgui.GUserDefinedColours;
-import jalview.schemabinding.version2.Colour;
-import jalview.schemabinding.version2.JalviewUserColours;
import jalview.schemes.ColourSchemeI;
import jalview.schemes.ColourSchemeLoader;
import jalview.schemes.ColourSchemes;
@@ -34,6 +32,9 @@ import jalview.schemes.UserColourScheme;
import jalview.util.ColorUtils;
import jalview.util.Format;
import jalview.util.MessageManager;
+import jalview.xml.binding.jalview.JalviewUserColours;
+import jalview.xml.binding.jalview.JalviewUserColours.Colour;
+import jalview.xml.binding.jalview.ObjectFactory;
import java.awt.Color;
import java.awt.Font;
@@ -51,6 +52,8 @@ import javax.swing.JButton;
import javax.swing.JInternalFrame;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Marshaller;
/**
* This panel allows the user to assign colours to Amino Acid residue codes, and
@@ -858,9 +861,14 @@ public class UserDefinedColours extends GUserDefinedColours
Colour col = new Colour();
col.setName(button.getText());
col.setRGB(Format.getHexString(button.getBackground()));
- ucs.addColour(col);
+ ucs.getColour().add(col);
}
- ucs.marshal(out);
+ JAXBContext jaxbContext = JAXBContext
+ .newInstance(JalviewUserColours.class);
+ Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
+ jaxbMarshaller.marshal(
+ new ObjectFactory().createJalviewUserColours(ucs), out);
+ // ucs.marshal(out);
out.close();
} catch (Exception ex)
{
diff --git a/src/jalview/gui/VamsasApplication.java b/src/jalview/gui/VamsasApplication.java
index 973cfe8..c094b1c 100644
--- a/src/jalview/gui/VamsasApplication.java
+++ b/src/jalview/gui/VamsasApplication.java
@@ -484,8 +484,8 @@ public class VamsasApplication implements SelectionSource, VamsasSource
errorsDuringUpdate = true;
Cache.log.error("Exception synchronizing " + af.getTitle()
+ " "
- + (af.getViewport().viewName == null ? ""
- : " view " + af.getViewport().viewName)
+ + (af.getViewport().getViewName() == null ? ""
+ : " view " + af.getViewport().getViewName())
+ " to document.", e);
stored = false;
}
diff --git a/src/jalview/gui/WsParamSetManager.java b/src/jalview/gui/WsParamSetManager.java
index d91775c..cb98856 100644
--- a/src/jalview/gui/WsParamSetManager.java
+++ b/src/jalview/gui/WsParamSetManager.java
@@ -27,8 +27,11 @@ import jalview.util.MessageManager;
import jalview.ws.params.ParamDatastoreI;
import jalview.ws.params.ParamManager;
import jalview.ws.params.WsParamSetI;
+import jalview.xml.binding.jalview.ObjectFactory;
+import jalview.xml.binding.jalview.WebServiceParameterSet;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -39,6 +42,12 @@ import java.util.Hashtable;
import java.util.List;
import java.util.StringTokenizer;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.Marshaller;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamReader;
+
/**
* store and retrieve web service parameter sets.
*
@@ -47,7 +56,7 @@ import java.util.StringTokenizer;
*/
public class WsParamSetManager implements ParamManager
{
- Hashtable paramparsers = new Hashtable();
+ Hashtable paramparsers = new Hashtable<>();
@Override
public WsParamSetI[] getParameterSet(String name, String serviceUrl,
@@ -60,7 +69,7 @@ public class WsParamSetManager implements ParamManager
}
StringTokenizer st = new StringTokenizer(files, "|");
String pfile = null;
- ArrayList params = new ArrayList();
+ List params = new ArrayList<>();
while (st.hasMoreTokens())
{
pfile = st.nextToken();
@@ -107,33 +116,39 @@ public class WsParamSetManager implements ParamManager
private WsParamSetI[] parseParamFile(String filename) throws IOException
{
- List psets = new ArrayList();
+ List psets = new ArrayList<>();
InputStreamReader is = new InputStreamReader(
- new java.io.FileInputStream(new File(filename)), "UTF-8");
-
- jalview.schemabinding.version2.WebServiceParameterSet wspset = new jalview.schemabinding.version2.WebServiceParameterSet();
+ new FileInputStream(new File(filename)), "UTF-8");
- org.exolab.castor.xml.Unmarshaller unmar = new org.exolab.castor.xml.Unmarshaller(
- wspset);
- unmar.setWhitespacePreserve(true);
+ WebServiceParameterSet wspset = null;
try
{
- wspset = (jalview.schemabinding.version2.WebServiceParameterSet) unmar
- .unmarshal(is);
+ JAXBContext jc = JAXBContext
+ .newInstance("jalview.xml.binding.jalview");
+ javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
+ XMLStreamReader streamReader = XMLInputFactory.newInstance()
+ .createXMLStreamReader(is);
+ JAXBElement jbe = um.unmarshal(streamReader,
+ WebServiceParameterSet.class);
+ wspset = jbe.getValue();
} catch (Exception ex)
{
throw new IOException(ex);
}
+
if (wspset != null && wspset.getParameters().length() > 0)
{
- for (String url : wspset.getServiceURL())
+ List urls = wspset.getServiceURL();
+ final String[] urlArray = urls.toArray(new String[urls.size()]);
+
+ for (String url : urls)
{
ParamDatastoreI parser = paramparsers.get(url);
if (parser != null)
{
WsParamSetI pset = parser.parseServiceParameterFile(
wspset.getName(), wspset.getDescription(),
- wspset.getServiceURL(), wspset.getParameters());
+ urlArray, wspset.getParameters());
if (pset != null)
{
pset.setSourceFile(filename);
@@ -190,8 +205,7 @@ public class WsParamSetManager implements ParamManager
if (value == JalviewFileChooser.APPROVE_OPTION)
{
outfile = chooser.getSelectedFile();
- jalview.bin.Cache.setProperty("LAST_DIRECTORY",
- outfile.getParent());
+ Cache.setProperty("LAST_DIRECTORY", outfile.getParent());
filename = outfile.getAbsolutePath();
if (!filename.endsWith(".wsparams"))
{
@@ -212,13 +226,16 @@ public class WsParamSetManager implements ParamManager
}
paramFiles = paramFiles.concat(filename);
}
- jalview.bin.Cache.setProperty("WS_PARAM_FILES", paramFiles);
+ Cache.setProperty("WS_PARAM_FILES", paramFiles);
- jalview.schemabinding.version2.WebServiceParameterSet paramxml = new jalview.schemabinding.version2.WebServiceParameterSet();
+ WebServiceParameterSet paramxml = new WebServiceParameterSet();
paramxml.setName(parameterSet.getName());
paramxml.setDescription(parameterSet.getDescription());
- paramxml.setServiceURL(parameterSet.getApplicableUrls().clone());
+ for (String url : parameterSet.getApplicableUrls())
+ {
+ paramxml.getServiceURL().add(url);
+ }
paramxml.setVersion("1.0");
try
{
@@ -226,7 +243,12 @@ public class WsParamSetManager implements ParamManager
parser.generateServiceParameterFile(parameterSet));
PrintWriter out = new PrintWriter(new OutputStreamWriter(
new FileOutputStream(outfile), "UTF-8"));
- paramxml.marshal(out);
+ JAXBContext jaxbContext = JAXBContext
+ .newInstance(WebServiceParameterSet.class);
+ Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
+ jaxbMarshaller.marshal(
+ new ObjectFactory().createWebServiceParameterSet(paramxml),
+ out);
out.close();
parameterSet.setSourceFile(filename);
} catch (Exception e)
diff --git a/src/jalview/io/FileLoader.java b/src/jalview/io/FileLoader.java
index f26d6da..2a18b0b 100755
--- a/src/jalview/io/FileLoader.java
+++ b/src/jalview/io/FileLoader.java
@@ -33,9 +33,9 @@ import jalview.datamodel.SequenceI;
import jalview.gui.AlignFrame;
import jalview.gui.AlignViewport;
import jalview.gui.Desktop;
-import jalview.gui.Jalview2XML;
import jalview.gui.JvOptionPane;
import jalview.json.binding.biojson.v1.ColourSchemeMapper;
+import jalview.project.Jalview2XML;
import jalview.schemes.ColourSchemeI;
import jalview.structure.StructureSelectionManager;
import jalview.util.MessageManager;
diff --git a/src/jalview/io/vcf/VCFLoader.java b/src/jalview/io/vcf/VCFLoader.java
index de2f18a..ff57d82 100644
--- a/src/jalview/io/vcf/VCFLoader.java
+++ b/src/jalview/io/vcf/VCFLoader.java
@@ -1,6 +1,5 @@
package jalview.io.vcf;
-import jalview.analysis.AlignmentUtils;
import jalview.analysis.Dna;
import jalview.api.AlignViewControllerGuiI;
import jalview.bin.Cache;
@@ -563,7 +562,8 @@ public class VCFLoader
/*
* dna-to-peptide product mapping
*/
- AlignmentUtils.computeProteinFeatures(seq, mapTo, map);
+ // JAL-3187 render on the fly instead
+ // AlignmentUtils.computeProteinFeatures(seq, mapTo, map);
}
else
{
diff --git a/src/jalview/jbgui/GDesktop.java b/src/jalview/jbgui/GDesktop.java
index a4afb74..290c4a4 100755
--- a/src/jalview/jbgui/GDesktop.java
+++ b/src/jalview/jbgui/GDesktop.java
@@ -245,7 +245,16 @@ public class GDesktop extends JFrame
@Override
public void actionPerformed(ActionEvent e)
{
- saveState_actionPerformed(e);
+ saveState_actionPerformed(true);
+ }
+ });
+ JMenuItem saveAsJaxb = new JMenuItem("Save Project as JAXB");
+ saveAsJaxb.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ saveState_actionPerformed(false);
}
});
loadState.setText(MessageManager.getString("action.load_project"));
@@ -254,7 +263,16 @@ public class GDesktop extends JFrame
@Override
public void actionPerformed(ActionEvent e)
{
- loadState_actionPerformed(e);
+ loadState_actionPerformed(true);
+ }
+ });
+ JMenuItem loadAsJaxb = new JMenuItem("Load Project as JAXB");
+ loadAsJaxb.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ loadState_actionPerformed(false);
}
});
inputMenu.setText(MessageManager.getString("label.input_alignment"));
@@ -424,7 +442,9 @@ public class GDesktop extends JFrame
FileMenu.add(inputSequence);
FileMenu.addSeparator();
FileMenu.add(saveState);
+ FileMenu.add(saveAsJaxb);
FileMenu.add(loadState);
+ FileMenu.add(loadAsJaxb);
FileMenu.addSeparator();
FileMenu.add(quit);
HelpMenu.add(aboutMenuItem);
@@ -586,7 +606,7 @@ public class GDesktop extends JFrame
* @param e
* DOCUMENT ME!
*/
- public void saveState_actionPerformed(ActionEvent e)
+ public void saveState_actionPerformed(boolean asCastor)
{
}
@@ -596,7 +616,7 @@ public class GDesktop extends JFrame
* @param e
* DOCUMENT ME!
*/
- public void loadState_actionPerformed(ActionEvent e)
+ public void loadState_actionPerformed(boolean asCastor)
{
}
diff --git a/src/jalview/jbgui/GPCAPanel.java b/src/jalview/jbgui/GPCAPanel.java
index a183794..122ad0f 100755
--- a/src/jalview/jbgui/GPCAPanel.java
+++ b/src/jalview/jbgui/GPCAPanel.java
@@ -46,13 +46,11 @@ public class GPCAPanel extends JInternalFrame
{
private static final Font VERDANA_12 = new Font("Verdana", 0, 12);
- protected JComboBox xCombobox = new JComboBox();
+ protected JComboBox xCombobox = new JComboBox<>();
- protected JComboBox yCombobox = new JComboBox();
+ protected JComboBox yCombobox = new JComboBox<>();
- protected JComboBox zCombobox = new JComboBox();
-
- protected JMenu scoreModelMenu = new JMenu();
+ protected JComboBox zCombobox = new JComboBox<>();
protected JMenu viewMenu = new JMenu();
@@ -60,16 +58,15 @@ public class GPCAPanel extends JInternalFrame
protected JMenu associateViewsMenu = new JMenu();
- protected JMenu calcSettings = new JMenu();
-
- protected JCheckBoxMenuItem nuclSetting = new JCheckBoxMenuItem();
-
- protected JCheckBoxMenuItem protSetting = new JCheckBoxMenuItem();
-
protected JLabel statusBar = new JLabel();
protected JPanel statusPanel = new JPanel();
+ protected JMenuItem originalSeqData;
+
+ /**
+ * Constructor
+ */
public GPCAPanel()
{
try
@@ -110,7 +107,7 @@ public class GPCAPanel extends JInternalFrame
@Override
public void actionPerformed(ActionEvent e)
{
- zCombobox_actionPerformed(e);
+ doDimensionChange();
}
});
yCombobox.setFont(VERDANA_12);
@@ -119,7 +116,7 @@ public class GPCAPanel extends JInternalFrame
@Override
public void actionPerformed(ActionEvent e)
{
- yCombobox_actionPerformed(e);
+ doDimensionChange();
}
});
xCombobox.setFont(VERDANA_12);
@@ -128,7 +125,7 @@ public class GPCAPanel extends JInternalFrame
@Override
public void actionPerformed(ActionEvent e)
{
- xCombobox_actionPerformed(e);
+ doDimensionChange();
}
});
JButton resetButton = new JButton();
@@ -139,7 +136,7 @@ public class GPCAPanel extends JInternalFrame
@Override
public void actionPerformed(ActionEvent e)
{
- resetButton_actionPerformed(e);
+ resetButton_actionPerformed();
}
});
JMenu fileMenu = new JMenu();
@@ -152,7 +149,7 @@ public class GPCAPanel extends JInternalFrame
@Override
public void actionPerformed(ActionEvent e)
{
- eps_actionPerformed(e);
+ eps_actionPerformed();
}
});
JMenuItem png = new JMenuItem("PNG");
@@ -161,7 +158,7 @@ public class GPCAPanel extends JInternalFrame
@Override
public void actionPerformed(ActionEvent e)
{
- png_actionPerformed(e);
+ png_actionPerformed();
}
});
JMenuItem outputValues = new JMenuItem();
@@ -171,7 +168,7 @@ public class GPCAPanel extends JInternalFrame
@Override
public void actionPerformed(ActionEvent e)
{
- outputValues_actionPerformed(e);
+ outputValues_actionPerformed();
}
});
JMenuItem outputPoints = new JMenuItem();
@@ -181,7 +178,7 @@ public class GPCAPanel extends JInternalFrame
@Override
public void actionPerformed(ActionEvent e)
{
- outputPoints_actionPerformed(e);
+ outputPoints_actionPerformed();
}
});
JMenuItem outputProjPoints = new JMenuItem();
@@ -192,7 +189,7 @@ public class GPCAPanel extends JInternalFrame
@Override
public void actionPerformed(ActionEvent e)
{
- outputProjPoints_actionPerformed(e);
+ outputProjPoints_actionPerformed();
}
});
JMenuItem print = new JMenuItem();
@@ -202,7 +199,7 @@ public class GPCAPanel extends JInternalFrame
@Override
public void actionPerformed(ActionEvent e)
{
- print_actionPerformed(e);
+ print_actionPerformed();
}
});
viewMenu.setText(MessageManager.getString("action.view"));
@@ -224,33 +221,13 @@ public class GPCAPanel extends JInternalFrame
{
}
});
- scoreModelMenu
- .setText(MessageManager.getString("label.select_score_model"));
- scoreModelMenu.addMenuListener(new MenuListener()
- {
- @Override
- public void menuSelected(MenuEvent e)
- {
- scoreModel_menuSelected();
- }
-
- @Override
- public void menuDeselected(MenuEvent e)
- {
- }
-
- @Override
- public void menuCanceled(MenuEvent e)
- {
- }
- });
showLabels.setText(MessageManager.getString("label.show_labels"));
showLabels.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
- showLabels_actionPerformed(e);
+ showLabels_actionPerformed();
}
});
JMenuItem bgcolour = new JMenuItem();
@@ -260,47 +237,22 @@ public class GPCAPanel extends JInternalFrame
@Override
public void actionPerformed(ActionEvent e)
{
- bgcolour_actionPerformed(e);
+ bgcolour_actionPerformed();
}
});
- JMenuItem originalSeqData = new JMenuItem();
+ originalSeqData = new JMenuItem();
originalSeqData.setText(MessageManager.getString("label.input_data"));
originalSeqData.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
- originalSeqData_actionPerformed(e);
+ originalSeqData_actionPerformed();
}
});
associateViewsMenu.setText(
MessageManager.getString("label.associate_nodes_with"));
- calcSettings.setText(MessageManager.getString("action.change_params"));
- nuclSetting
- .setText(MessageManager.getString("label.nucleotide_matrix"));
- protSetting.setText(MessageManager.getString("label.protein_matrix"));
- nuclSetting.addActionListener(new ActionListener()
- {
-
- @Override
- public void actionPerformed(ActionEvent arg0)
- {
- nuclSetting_actionPerfomed(arg0);
- }
- });
- protSetting.addActionListener(new ActionListener()
- {
-
- @Override
- public void actionPerformed(ActionEvent arg0)
- {
- protSetting_actionPerfomed(arg0);
- }
- });
- calcSettings.add(nuclSetting);
- calcSettings.add(protSetting);
- calcSettings.add(scoreModelMenu);
statusPanel.setLayout(new GridLayout());
statusBar.setFont(VERDANA_12);
// statusPanel.setBackground(Color.lightGray);
@@ -321,7 +273,6 @@ public class GPCAPanel extends JInternalFrame
JMenuBar jMenuBar1 = new JMenuBar();
jMenuBar1.add(fileMenu);
jMenuBar1.add(viewMenu);
- jMenuBar1.add(calcSettings);
setJMenuBar(jMenuBar1);
fileMenu.add(saveMenu);
fileMenu.add(outputValues);
@@ -336,91 +287,51 @@ public class GPCAPanel extends JInternalFrame
viewMenu.add(associateViewsMenu);
}
- protected void scoreModel_menuSelected()
- {
- // TODO Auto-generated method stub
-
- }
-
- protected void resetButton_actionPerformed(ActionEvent e)
+ protected void resetButton_actionPerformed()
{
- // TODO Auto-generated method stub
-
}
- protected void protSetting_actionPerfomed(ActionEvent arg0)
+ protected void outputPoints_actionPerformed()
{
- // TODO Auto-generated method stub
-
}
- protected void nuclSetting_actionPerfomed(ActionEvent arg0)
+ protected void outputProjPoints_actionPerformed()
{
- // TODO Auto-generated method stub
-
}
- protected void outputPoints_actionPerformed(ActionEvent e)
+ protected void eps_actionPerformed()
{
- // TODO Auto-generated method stub
-
}
- protected void outputProjPoints_actionPerformed(ActionEvent e)
+ protected void png_actionPerformed()
{
- // TODO Auto-generated method stub
-
}
- protected void xCombobox_actionPerformed(ActionEvent e)
+ protected void outputValues_actionPerformed()
{
}
- protected void yCombobox_actionPerformed(ActionEvent e)
+ protected void print_actionPerformed()
{
}
- protected void zCombobox_actionPerformed(ActionEvent e)
+ protected void showLabels_actionPerformed()
{
}
- public void eps_actionPerformed(ActionEvent e)
+ protected void bgcolour_actionPerformed()
{
-
}
- public void png_actionPerformed(ActionEvent e)
+ protected void originalSeqData_actionPerformed()
{
-
}
- public void outputValues_actionPerformed(ActionEvent e)
+ protected void viewMenu_menuSelected()
{
-
}
- public void print_actionPerformed(ActionEvent e)
+ protected void doDimensionChange()
{
-
- }
-
- public void showLabels_actionPerformed(ActionEvent e)
- {
-
- }
-
- public void bgcolour_actionPerformed(ActionEvent e)
- {
-
- }
-
- public void originalSeqData_actionPerformed(ActionEvent e)
- {
-
- }
-
- public void viewMenu_menuSelected()
- {
-
}
}
diff --git a/src/jalview/math/Matrix.java b/src/jalview/math/Matrix.java
index 8910c67..77862c8 100755
--- a/src/jalview/math/Matrix.java
+++ b/src/jalview/math/Matrix.java
@@ -24,6 +24,7 @@ import jalview.util.Format;
import jalview.util.MessageManager;
import java.io.PrintStream;
+import java.util.Arrays;
/**
* A class to model rectangular matrices of double values and operations on them
@@ -31,37 +32,40 @@ import java.io.PrintStream;
public class Matrix implements MatrixI
{
/*
- * the cell values in row-major order
+ * maximum number of iterations for tqli
*/
- private double[][] value;
+ private static final int MAX_ITER = 45;
+ // fudge - add 15 iterations, just in case
/*
* the number of rows
*/
- protected int rows;
+ final protected int rows;
/*
* the number of columns
*/
- protected int cols;
+ final protected int cols;
+
+ /*
+ * the cell values in row-major order
+ */
+ private double[][] value;
protected double[] d; // Diagonal
protected double[] e; // off diagonal
/**
- * maximum number of iterations for tqli
+ * Constructor given number of rows and columns
*
+ * @param colCount
+ * @param rowCount
*/
- private static final int maxIter = 45; // fudge - add 15 iterations, just in
- // case
-
- /**
- * Default constructor
- */
- public Matrix()
+ protected Matrix(int rowCount, int colCount)
{
-
+ rows = rowCount;
+ cols = colCount;
}
/**
@@ -103,11 +107,6 @@ public class Matrix implements MatrixI
}
}
- /**
- * Returns a new matrix which is the transpose of this one
- *
- * @return
- */
@Override
public MatrixI transpose()
{
@@ -145,18 +144,6 @@ public class Matrix implements MatrixI
}
}
- /**
- * Returns a new matrix which is the result of premultiplying this matrix by
- * the supplied argument. If this of size AxB (A rows and B columns), and the
- * argument is CxA (C rows and A columns), the result is of size CxB.
- *
- * @param in
- *
- * @return
- * @throws IllegalArgumentException
- * if the number of columns in the pre-multiplier is not equal to
- * the number of rows in the multiplicand (this)
- */
@Override
public MatrixI preMultiply(MatrixI in)
{
@@ -208,21 +195,6 @@ public class Matrix implements MatrixI
return out;
}
- /**
- * Returns a new matrix which is the result of postmultiplying this matrix by
- * the supplied argument. If this of size AxB (A rows and B columns), and the
- * argument is BxC (B rows and C columns), the result is of size AxC.
- *
- * This method simply returns the result of in.preMultiply(this)
- *
- * @param in
- *
- * @return
- * @throws IllegalArgumentException
- * if the number of rows in the post-multiplier is not equal to the
- * number of columns in the multiplicand (this)
- * @see #preMultiply(Matrix)
- */
@Override
public MatrixI postMultiply(MatrixI in)
{
@@ -234,11 +206,6 @@ public class Matrix implements MatrixI
return in.preMultiply(this);
}
- /**
- * Answers a new matrix with a copy of the values in this one
- *
- * @return
- */
@Override
public MatrixI copy()
{
@@ -249,7 +216,17 @@ public class Matrix implements MatrixI
System.arraycopy(value[i], 0, newmat[i], 0, value[i].length);
}
- return new Matrix(newmat);
+ Matrix m = new Matrix(newmat);
+ if (this.d != null)
+ {
+ m.d = Arrays.copyOf(this.d, this.d.length);
+ }
+ if (this.e != null)
+ {
+ m.e = Arrays.copyOf(this.e, this.e.length);
+ }
+
+ return m;
}
/**
@@ -479,11 +456,11 @@ public class Matrix implements MatrixI
{
iter++;
- if (iter == maxIter)
+ if (iter == MAX_ITER)
{
throw new Exception(MessageManager.formatMessage(
"exception.matrix_too_many_iteration", new String[]
- { "tqli", Integer.valueOf(maxIter).toString() }));
+ { "tqli", Integer.valueOf(MAX_ITER).toString() }));
}
else
{
@@ -743,11 +720,11 @@ public class Matrix implements MatrixI
{
iter++;
- if (iter == maxIter)
+ if (iter == MAX_ITER)
{
throw new Exception(MessageManager.formatMessage(
"exception.matrix_too_many_iteration", new String[]
- { "tqli2", Integer.valueOf(maxIter).toString() }));
+ { "tqli2", Integer.valueOf(MAX_ITER).toString() }));
}
else
{
@@ -995,4 +972,16 @@ public class Matrix implements MatrixI
}
}
}
+
+ @Override
+ public void setD(double[] v)
+ {
+ d = v;
+ }
+
+ @Override
+ public void setE(double[] v)
+ {
+ e = v;
+ }
}
diff --git a/src/jalview/math/MatrixI.java b/src/jalview/math/MatrixI.java
index 5b93c76..d72890a 100644
--- a/src/jalview/math/MatrixI.java
+++ b/src/jalview/math/MatrixI.java
@@ -22,6 +22,10 @@ package jalview.math;
import java.io.PrintStream;
+/**
+ * An interface that describes a rectangular matrix of double values and
+ * operations on it
+ */
public interface MatrixI
{
/**
@@ -63,18 +67,59 @@ public interface MatrixI
*/
double[] getRow(int i);
+ /**
+ * Answers a new matrix with a copy of the values in this one
+ *
+ * @return
+ */
MatrixI copy();
+ /**
+ * Returns a new matrix which is the transpose of this one
+ *
+ * @return
+ */
MatrixI transpose();
+ /**
+ * Returns a new matrix which is the result of premultiplying this matrix by
+ * the supplied argument. If this of size AxB (A rows and B columns), and the
+ * argument is CxA (C rows and A columns), the result is of size CxB.
+ *
+ * @param in
+ *
+ * @return
+ * @throws IllegalArgumentException
+ * if the number of columns in the pre-multiplier is not equal to
+ * the number of rows in the multiplicand (this)
+ */
MatrixI preMultiply(MatrixI m);
+ /**
+ * Returns a new matrix which is the result of postmultiplying this matrix by
+ * the supplied argument. If this of size AxB (A rows and B columns), and the
+ * argument is BxC (B rows and C columns), the result is of size AxC.
+ *
+ * This method simply returns the result of in.preMultiply(this)
+ *
+ * @param in
+ *
+ * @return
+ * @throws IllegalArgumentException
+ * if the number of rows in the post-multiplier is not equal to the
+ * number of columns in the multiplicand (this)
+ * @see #preMultiply(Matrix)
+ */
MatrixI postMultiply(MatrixI m);
double[] getD();
double[] getE();
+ void setD(double[] v);
+
+ void setE(double[] v);
+
void print(PrintStream ps, String format);
void printD(PrintStream ps, String format);
diff --git a/src/jalview/math/RotatableMatrix.java b/src/jalview/math/RotatableMatrix.java
index 5971227..602c5e4 100755
--- a/src/jalview/math/RotatableMatrix.java
+++ b/src/jalview/math/RotatableMatrix.java
@@ -20,54 +20,86 @@
*/
package jalview.math;
+import jalview.datamodel.Point;
+
+import java.util.HashMap;
+import java.util.Map;
+
/**
- * DOCUMENT ME!
- *
- * @author $author$
- * @version $Revision$
+ * Model for a 3x3 matrix which provides methods for rotation in 3-D space
*/
public class RotatableMatrix
{
- float[][] matrix;
+ private static final int DIMS = 3;
- float[] temp;
+ /*
+ * cache the most used rotations: +/- 1, 2, 3, 4 degrees around x or y axis
+ */
+ private static Map> cachedRotations;
- float[][] rot;
+ static
+ {
+ cachedRotations = new HashMap<>();
+ for (Axis axis : Axis.values())
+ {
+ HashMap map = new HashMap<>();
+ cachedRotations.put(axis, map);
+ for (int deg = 1; deg < 5; deg++)
+ {
+ float[][] rotation = getRotation(deg, axis);
+ map.put(Float.valueOf(deg), rotation);
+ rotation = getRotation(-deg, axis);
+ map.put(Float.valueOf(-deg), rotation);
+ }
+ }
+ }
- /**
- * Creates a new RotatableMatrix object.
- *
- * @param rows
- * DOCUMENT ME!
- * @param cols
- * DOCUMENT ME!
- */
- public RotatableMatrix(int rows, int cols)
+ public enum Axis
{
- matrix = new float[rows][cols];
+ X, Y, Z
+ };
- temp = new float[3];
+ float[][] matrix;
- rot = new float[3][3];
+ /**
+ * Constructor creates a new identity matrix (all values zero except for 1 on
+ * the diagonal)
+ */
+ public RotatableMatrix()
+ {
+ matrix = new float[DIMS][DIMS];
+ for (int j = 0; j < DIMS; j++)
+ {
+ matrix[j][j] = 1f;
+ }
}
/**
- * DOCUMENT ME!
+ * Sets the value at position (i, j) of the matrix
*
* @param i
- * DOCUMENT ME!
* @param j
- * DOCUMENT ME!
* @param value
- * DOCUMENT ME!
*/
- public void addElement(int i, int j, float value)
+ public void setValue(int i, int j, float value)
{
matrix[i][j] = value;
}
/**
- * DOCUMENT ME!
+ * Answers the value at position (i, j) of the matrix
+ *
+ * @param i
+ * @param j
+ * @return
+ */
+ public float getValue(int i, int j)
+ {
+ return matrix[i][j];
+ }
+
+ /**
+ * Prints the matrix in rows of space-delimited values
*/
public void print()
{
@@ -82,174 +114,137 @@ public class RotatableMatrix
}
/**
- * DOCUMENT ME!
+ * Rotates the matrix through the specified number of degrees around the
+ * specified axis
*
* @param degrees
- * DOCUMENT ME!
* @param axis
- * DOCUMENT ME!
*/
- public void rotate(float degrees, char axis)
+ public void rotate(float degrees, Axis axis)
{
- float costheta = (float) Math.cos((degrees * Math.PI) / (float) 180.0);
+ float[][] rot = getRotation(degrees, axis);
- float sintheta = (float) Math.sin((degrees * Math.PI) / (float) 180.0);
+ preMultiply(rot);
+ }
- if (axis == 'z')
+ /**
+ * Answers a matrix which, when it pre-multiplies another matrix, applies a
+ * rotation of the specified number of degrees around the specified axis
+ *
+ * @param degrees
+ * @param axis
+ * @return
+ * @see https://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations
+ */
+ protected static float[][] getRotation(float degrees, Axis axis)
+ {
+ Float floatValue = Float.valueOf(degrees);
+ if (cachedRotations.get(axis).containsKey(floatValue))
{
- rot[0][0] = (float) costheta;
-
- rot[0][1] = (float) -sintheta;
-
- rot[0][2] = (float) 0.0;
-
- rot[1][0] = (float) sintheta;
-
- rot[1][1] = (float) costheta;
-
- rot[1][2] = (float) 0.0;
-
- rot[2][0] = (float) 0.0;
-
- rot[2][1] = (float) 0.0;
-
- rot[2][2] = (float) 1.0;
-
- preMultiply(rot);
+ // System.out.println("getRotation from cache: " + (int) degrees);
+ return cachedRotations.get(axis).get(floatValue);
}
- if (axis == 'x')
- {
- rot[0][0] = (float) 1.0;
-
- rot[0][1] = (float) 0.0;
-
- rot[0][2] = (float) 0.0;
-
- rot[1][0] = (float) 0.0;
+ float costheta = (float) Math.cos(degrees * Math.PI / 180f);
- rot[1][1] = (float) costheta;
+ float sintheta = (float) Math.sin(degrees * Math.PI / 180f);
- rot[1][2] = (float) sintheta;
+ float[][] rot = new float[DIMS][DIMS];
- rot[2][0] = (float) 0.0;
-
- rot[2][1] = (float) -sintheta;
-
- rot[2][2] = (float) costheta;
-
- preMultiply(rot);
- }
-
- if (axis == 'y')
+ switch (axis)
{
- rot[0][0] = (float) costheta;
-
- rot[0][1] = (float) 0.0;
-
- rot[0][2] = (float) -sintheta;
-
- rot[1][0] = (float) 0.0;
-
- rot[1][1] = (float) 1.0;
-
- rot[1][2] = (float) 0.0;
-
- rot[2][0] = (float) sintheta;
-
- rot[2][1] = (float) 0.0;
-
- rot[2][2] = (float) costheta;
-
- preMultiply(rot);
+ case X:
+ rot[0][0] = 1f;
+ rot[1][1] = costheta;
+ rot[1][2] = sintheta;
+ rot[2][1] = -sintheta;
+ rot[2][2] = costheta;
+ break;
+ case Y:
+ rot[0][0] = costheta;
+ rot[0][2] = -sintheta;
+ rot[1][1] = 1f;
+ rot[2][0] = sintheta;
+ rot[2][2] = costheta;
+ break;
+ case Z:
+ rot[0][0] = costheta;
+ rot[0][1] = -sintheta;
+ rot[1][0] = sintheta;
+ rot[1][1] = costheta;
+ rot[2][2] = 1f;
+ break;
}
+ return rot;
}
/**
- * DOCUMENT ME!
+ * Answers a new array of float values which is the result of pre-multiplying
+ * this matrix by the given vector. Each value of the result is the dot
+ * product of the vector with one column of this matrix. The matrix and input
+ * vector are not modified.
*
* @param vect
- * DOCUMENT ME!
*
- * @return DOCUMENT ME!
+ * @return
*/
public float[] vectorMultiply(float[] vect)
{
- temp[0] = vect[0];
-
- temp[1] = vect[1];
-
- temp[2] = vect[2];
+ float[] result = new float[DIMS];
- for (int i = 0; i < 3; i++)
+ for (int i = 0; i < DIMS; i++)
{
- temp[i] = (matrix[i][0] * vect[0]) + (matrix[i][1] * vect[1])
+ result[i] = (matrix[i][0] * vect[0]) + (matrix[i][1] * vect[1])
+ (matrix[i][2] * vect[2]);
}
- vect[0] = temp[0];
-
- vect[1] = temp[1];
-
- vect[2] = temp[2];
-
- return vect;
+ return result;
}
/**
- * DOCUMENT ME!
+ * Performs pre-multiplication of this matrix by the given one. Value (i, j)
+ * of the result is the dot product of the i'th row of mat with
+ * the j'th column of this matrix.
*
* @param mat
- * DOCUMENT ME!
*/
public void preMultiply(float[][] mat)
{
- float[][] tmp = new float[3][3];
+ float[][] tmp = new float[DIMS][DIMS];
- for (int i = 0; i < 3; i++)
+ for (int i = 0; i < DIMS; i++)
{
- for (int j = 0; j < 3; j++)
+ for (int j = 0; j < DIMS; j++)
{
tmp[i][j] = (mat[i][0] * matrix[0][j]) + (mat[i][1] * matrix[1][j])
+ (mat[i][2] * matrix[2][j]);
}
}
- for (int i = 0; i < 3; i++)
- {
- for (int j = 0; j < 3; j++)
- {
- matrix[i][j] = tmp[i][j];
- }
- }
+ matrix = tmp;
}
/**
- * DOCUMENT ME!
+ * Performs post-multiplication of this matrix by the given one. Value (i, j)
+ * of the result is the dot product of the i'th row of this matrix with the
+ * j'th column of mat .
*
* @param mat
- * DOCUMENT ME!
*/
public void postMultiply(float[][] mat)
{
- float[][] tmp = new float[3][3];
+ float[][] tmp = new float[DIMS][DIMS];
- for (int i = 0; i < 3; i++)
+ for (int i = 0; i < DIMS; i++)
{
- for (int j = 0; j < 3; j++)
+ for (int j = 0; j < DIMS; j++)
{
tmp[i][j] = (matrix[i][0] * mat[0][j]) + (matrix[i][1] * mat[1][j])
+ (matrix[i][2] * mat[2][j]);
}
}
- for (int i = 0; i < 3; i++)
- {
- for (int j = 0; j < 3; j++)
- {
- matrix[i][j] = tmp[i][j];
- }
- }
+ matrix = tmp;
}
/**
@@ -260,47 +255,47 @@ public class RotatableMatrix
*/
public static void main(String[] args)
{
- RotatableMatrix m = new RotatableMatrix(3, 3);
+ RotatableMatrix m = new RotatableMatrix();
- m.addElement(0, 0, 1);
+ m.setValue(0, 0, 1);
- m.addElement(0, 1, 0);
+ m.setValue(0, 1, 0);
- m.addElement(0, 2, 0);
+ m.setValue(0, 2, 0);
- m.addElement(1, 0, 0);
+ m.setValue(1, 0, 0);
- m.addElement(1, 1, 2);
+ m.setValue(1, 1, 2);
- m.addElement(1, 2, 0);
+ m.setValue(1, 2, 0);
- m.addElement(2, 0, 0);
+ m.setValue(2, 0, 0);
- m.addElement(2, 1, 0);
+ m.setValue(2, 1, 0);
- m.addElement(2, 2, 1);
+ m.setValue(2, 2, 1);
m.print();
- RotatableMatrix n = new RotatableMatrix(3, 3);
+ RotatableMatrix n = new RotatableMatrix();
- n.addElement(0, 0, 2);
+ n.setValue(0, 0, 2);
- n.addElement(0, 1, 1);
+ n.setValue(0, 1, 1);
- n.addElement(0, 2, 1);
+ n.setValue(0, 2, 1);
- n.addElement(1, 0, 2);
+ n.setValue(1, 0, 2);
- n.addElement(1, 1, 1);
+ n.setValue(1, 1, 1);
- n.addElement(1, 2, 1);
+ n.setValue(1, 2, 1);
- n.addElement(2, 0, 2);
+ n.setValue(2, 0, 2);
- n.addElement(2, 1, 1);
+ n.setValue(2, 1, 1);
- n.addElement(2, 2, 1);
+ n.setValue(2, 2, 1);
n.print();
@@ -321,26 +316,15 @@ public class RotatableMatrix
}
/**
- * DOCUMENT ME!
+ * Performs a vector multiplication whose result is the Point representing the
+ * input point's value vector post-multiplied by this matrix.
+ *
+ * @param coord
+ * @return
*/
- public void setIdentity()
+ public Point vectorMultiply(Point coord)
{
- matrix[0][0] = (float) 1.0;
-
- matrix[1][1] = (float) 1.0;
-
- matrix[2][2] = (float) 1.0;
-
- matrix[0][1] = (float) 0.0;
-
- matrix[0][2] = (float) 0.0;
-
- matrix[1][0] = (float) 0.0;
-
- matrix[1][2] = (float) 0.0;
-
- matrix[2][0] = (float) 0.0;
-
- matrix[2][1] = (float) 0.0;
+ float[] v = vectorMultiply(new float[] { coord.x, coord.y, coord.z });
+ return new Point(v[0], v[1], v[2]);
}
}
diff --git a/src/jalview/math/SparseMatrix.java b/src/jalview/math/SparseMatrix.java
index 86592a0..e24cda5 100644
--- a/src/jalview/math/SparseMatrix.java
+++ b/src/jalview/math/SparseMatrix.java
@@ -45,11 +45,8 @@ public class SparseMatrix extends Matrix
*/
public SparseMatrix(double[][] v)
{
- rows = v.length;
- if (rows > 0)
- {
- cols = v[0].length;
- }
+ super(v.length, v.length > 0 ? v[0].length : 0);
+
sparseColumns = new SparseDoubleArray[cols];
/*
diff --git a/src/jalview/project/Jalview2XML.java b/src/jalview/project/Jalview2XML.java
new file mode 100644
index 0000000..2ce6ea7
--- /dev/null
+++ b/src/jalview/project/Jalview2XML.java
@@ -0,0 +1,6600 @@
+/*
+ * 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 .
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.project;
+
+import static jalview.math.RotatableMatrix.Axis.X;
+import static jalview.math.RotatableMatrix.Axis.Y;
+import static jalview.math.RotatableMatrix.Axis.Z;
+
+import jalview.analysis.Conservation;
+import jalview.analysis.PCA;
+import jalview.analysis.scoremodels.ScoreModels;
+import jalview.analysis.scoremodels.SimilarityParams;
+import jalview.api.FeatureColourI;
+import jalview.api.ViewStyleI;
+import jalview.api.analysis.ScoreModelI;
+import jalview.api.analysis.SimilarityParamsI;
+import jalview.api.structures.JalviewStructureDisplayI;
+import jalview.bin.Cache;
+import jalview.datamodel.AlignedCodonFrame;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.GraphLine;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.Point;
+import jalview.datamodel.RnaViewerModel;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.StructureViewerModel;
+import jalview.datamodel.StructureViewerModel.StructureData;
+import jalview.datamodel.features.FeatureMatcher;
+import jalview.datamodel.features.FeatureMatcherI;
+import jalview.datamodel.features.FeatureMatcherSet;
+import jalview.datamodel.features.FeatureMatcherSetI;
+import jalview.ext.varna.RnaModel;
+import jalview.gui.AlignFrame;
+import jalview.gui.AlignViewport;
+import jalview.gui.AlignmentPanel;
+import jalview.gui.AppVarna;
+import jalview.gui.ChimeraViewFrame;
+import jalview.gui.Desktop;
+import jalview.gui.Jalview2XML_V1;
+import jalview.gui.JvOptionPane;
+import jalview.gui.OOMWarning;
+import jalview.gui.PCAPanel;
+import jalview.gui.PaintRefresher;
+import jalview.gui.SplitFrame;
+import jalview.gui.StructureViewer;
+import jalview.gui.StructureViewer.ViewerType;
+import jalview.gui.StructureViewerBase;
+import jalview.gui.TreePanel;
+import jalview.io.DataSourceType;
+import jalview.io.FileFormat;
+import jalview.io.NewickFile;
+import jalview.math.Matrix;
+import jalview.math.MatrixI;
+import jalview.renderer.ResidueShaderI;
+import jalview.schemes.AnnotationColourGradient;
+import jalview.schemes.ColourSchemeI;
+import jalview.schemes.ColourSchemeProperty;
+import jalview.schemes.FeatureColour;
+import jalview.schemes.ResidueProperties;
+import jalview.schemes.UserColourScheme;
+import jalview.structure.StructureSelectionManager;
+import jalview.structures.models.AAStructureBindingModel;
+import jalview.util.Format;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+import jalview.util.StringUtils;
+import jalview.util.jarInputStreamProvider;
+import jalview.util.matcher.Condition;
+import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.PCAModel;
+import jalview.viewmodel.ViewportRanges;
+import jalview.viewmodel.seqfeatures.FeatureRendererModel;
+import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
+import jalview.viewmodel.seqfeatures.FeaturesDisplayed;
+import jalview.ws.jws2.Jws2Discoverer;
+import jalview.ws.jws2.dm.AAConSettings;
+import jalview.ws.jws2.jabaws2.Jws2Instance;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.AutoCalcSetting;
+import jalview.ws.params.WsParamSetI;
+import jalview.xml.binding.jalview.AlcodonFrame;
+import jalview.xml.binding.jalview.AlcodonFrame.AlcodMap;
+import jalview.xml.binding.jalview.Annotation;
+import jalview.xml.binding.jalview.Annotation.ThresholdLine;
+import jalview.xml.binding.jalview.AnnotationColourScheme;
+import jalview.xml.binding.jalview.AnnotationElement;
+import jalview.xml.binding.jalview.DoubleMatrix;
+import jalview.xml.binding.jalview.DoubleVector;
+import jalview.xml.binding.jalview.Feature;
+import jalview.xml.binding.jalview.Feature.OtherData;
+import jalview.xml.binding.jalview.FeatureMatcherSet.CompoundMatcher;
+import jalview.xml.binding.jalview.FilterBy;
+import jalview.xml.binding.jalview.JalviewModel;
+import jalview.xml.binding.jalview.JalviewModel.FeatureSettings;
+import jalview.xml.binding.jalview.JalviewModel.FeatureSettings.Group;
+import jalview.xml.binding.jalview.JalviewModel.FeatureSettings.Setting;
+import jalview.xml.binding.jalview.JalviewModel.JGroup;
+import jalview.xml.binding.jalview.JalviewModel.JSeq;
+import jalview.xml.binding.jalview.JalviewModel.JSeq.Pdbids;
+import jalview.xml.binding.jalview.JalviewModel.JSeq.Pdbids.StructureState;
+import jalview.xml.binding.jalview.JalviewModel.JSeq.RnaViewer;
+import jalview.xml.binding.jalview.JalviewModel.JSeq.RnaViewer.SecondaryStructure;
+import jalview.xml.binding.jalview.JalviewModel.PcaViewer;
+import jalview.xml.binding.jalview.JalviewModel.PcaViewer.Axis;
+import jalview.xml.binding.jalview.JalviewModel.PcaViewer.SeqPointMax;
+import jalview.xml.binding.jalview.JalviewModel.PcaViewer.SeqPointMin;
+import jalview.xml.binding.jalview.JalviewModel.PcaViewer.SequencePoint;
+import jalview.xml.binding.jalview.JalviewModel.Tree;
+import jalview.xml.binding.jalview.JalviewModel.UserColours;
+import jalview.xml.binding.jalview.JalviewModel.Viewport;
+import jalview.xml.binding.jalview.JalviewModel.Viewport.CalcIdParam;
+import jalview.xml.binding.jalview.JalviewModel.Viewport.HiddenColumns;
+import jalview.xml.binding.jalview.JalviewUserColours;
+import jalview.xml.binding.jalview.JalviewUserColours.Colour;
+import jalview.xml.binding.jalview.MapListType.MapListFrom;
+import jalview.xml.binding.jalview.MapListType.MapListTo;
+import jalview.xml.binding.jalview.Mapping;
+import jalview.xml.binding.jalview.NoValueColour;
+import jalview.xml.binding.jalview.ObjectFactory;
+import jalview.xml.binding.jalview.PcaDataType;
+import jalview.xml.binding.jalview.Pdbentry.Property;
+import jalview.xml.binding.jalview.Sequence;
+import jalview.xml.binding.jalview.Sequence.DBRef;
+import jalview.xml.binding.jalview.SequenceSet;
+import jalview.xml.binding.jalview.SequenceSet.SequenceSetProperties;
+import jalview.xml.binding.jalview.ThresholdType;
+import jalview.xml.binding.jalview.VAMSAS;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Rectangle;
+import java.io.BufferedReader;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.math.BigInteger;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.Vector;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import java.util.jar.JarOutputStream;
+
+import javax.swing.JInternalFrame;
+import javax.swing.SwingUtilities;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.Marshaller;
+import javax.xml.datatype.DatatypeConfigurationException;
+import javax.xml.datatype.DatatypeFactory;
+import javax.xml.datatype.XMLGregorianCalendar;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamReader;
+
+/**
+ * Write out the current jalview desktop state as a Jalview XML stream.
+ *
+ * Note: the vamsas objects referred to here are primitive versions of the
+ * VAMSAS project schema elements - they are not the same and most likely never
+ * will be :)
+ *
+ * @author $author$
+ * @version $Revision: 1.134 $
+ */
+public class Jalview2XML
+{
+ private static final String VIEWER_PREFIX = "viewer_";
+
+ private static final String RNA_PREFIX = "rna_";
+
+ private static final String UTF_8 = "UTF-8";
+
+ /**
+ * prefix for recovering datasets for alignments with multiple views where
+ * non-existent dataset IDs were written for some views
+ */
+ private static final String UNIQSEQSETID = "uniqueSeqSetId.";
+
+ // use this with nextCounter() to make unique names for entities
+ private int counter = 0;
+
+ /*
+ * SequenceI reference -> XML ID string in jalview XML. Populated as XML reps
+ * of sequence objects are created.
+ */
+ IdentityHashMap seqsToIds = null;
+
+ /**
+ * jalview XML Sequence ID to jalview sequence object reference (both dataset
+ * and alignment sequences. Populated as XML reps of sequence objects are
+ * created.)
+ */
+ Map seqRefIds = null;
+
+ Map incompleteSeqs = null;
+
+ List frefedSequence = null;
+
+ boolean raiseGUI = true; // whether errors are raised in dialog boxes or not
+
+ /*
+ * Map of reconstructed AlignFrame objects that appear to have come from
+ * SplitFrame objects (have a dna/protein complement view).
+ */
+ private Map splitFrameCandidates = new HashMap<>();
+
+ /*
+ * Map from displayed rna structure models to their saved session state jar
+ * entry names
+ */
+ private Map rnaSessions = new HashMap<>();
+
+ /**
+ * A helper method for safely using the value of an optional attribute that
+ * may be null if not present in the XML. Answers the boolean value, or false
+ * if null.
+ *
+ * @param b
+ * @return
+ */
+ public static boolean safeBoolean(Boolean b)
+ {
+ return b == null ? false : b.booleanValue();
+ }
+
+ /**
+ * A helper method for safely using the value of an optional attribute that
+ * may be null if not present in the XML. Answers the integer value, or zero
+ * if null.
+ *
+ * @param i
+ * @return
+ */
+ public static int safeInt(Integer i)
+ {
+ return i == null ? 0 : i.intValue();
+ }
+
+ /**
+ * A helper method for safely using the value of an optional attribute that
+ * may be null if not present in the XML. Answers the float value, or zero if
+ * null.
+ *
+ * @param f
+ * @return
+ */
+ public static float safeFloat(Float f)
+ {
+ return f == null ? 0f : f.floatValue();
+ }
+
+ /**
+ * create/return unique hash string for sq
+ *
+ * @param sq
+ * @return new or existing unique string for sq
+ */
+ String seqHash(SequenceI sq)
+ {
+ if (seqsToIds == null)
+ {
+ initSeqRefs();
+ }
+ if (seqsToIds.containsKey(sq))
+ {
+ return seqsToIds.get(sq);
+ }
+ else
+ {
+ // create sequential key
+ String key = "sq" + (seqsToIds.size() + 1);
+ key = makeHashCode(sq, key); // check we don't have an external reference
+ // for it already.
+ seqsToIds.put(sq, key);
+ return key;
+ }
+ }
+
+ void initSeqRefs()
+ {
+ if (seqsToIds == null)
+ {
+ seqsToIds = new IdentityHashMap<>();
+ }
+ if (seqRefIds == null)
+ {
+ seqRefIds = new HashMap<>();
+ }
+ if (incompleteSeqs == null)
+ {
+ incompleteSeqs = new HashMap<>();
+ }
+ if (frefedSequence == null)
+ {
+ frefedSequence = new ArrayList<>();
+ }
+ }
+
+ public Jalview2XML()
+ {
+ }
+
+ public Jalview2XML(boolean raiseGUI)
+ {
+ this.raiseGUI = raiseGUI;
+ }
+
+ /**
+ * base class for resolving forward references to sequences by their ID
+ *
+ * @author jprocter
+ *
+ */
+ abstract class SeqFref
+ {
+ String sref;
+
+ String type;
+
+ public SeqFref(String _sref, String type)
+ {
+ sref = _sref;
+ this.type = type;
+ }
+
+ public String getSref()
+ {
+ return sref;
+ }
+
+ public SequenceI getSrefSeq()
+ {
+ return seqRefIds.get(sref);
+ }
+
+ public boolean isResolvable()
+ {
+ return seqRefIds.get(sref) != null;
+ }
+
+ public SequenceI getSrefDatasetSeq()
+ {
+ SequenceI sq = seqRefIds.get(sref);
+ if (sq != null)
+ {
+ while (sq.getDatasetSequence() != null)
+ {
+ sq = sq.getDatasetSequence();
+ }
+ }
+ return sq;
+ }
+
+ /**
+ * @return true if the forward reference was fully resolved
+ */
+ abstract boolean resolve();
+
+ @Override
+ public String toString()
+ {
+ return type + " reference to " + sref;
+ }
+ }
+
+ /**
+ * create forward reference for a mapping
+ *
+ * @param sref
+ * @param _jmap
+ * @return
+ */
+ public SeqFref newMappingRef(final String sref,
+ final jalview.datamodel.Mapping _jmap)
+ {
+ SeqFref fref = new SeqFref(sref, "Mapping")
+ {
+ public jalview.datamodel.Mapping jmap = _jmap;
+
+ @Override
+ boolean resolve()
+ {
+ SequenceI seq = getSrefDatasetSeq();
+ if (seq == null)
+ {
+ return false;
+ }
+ jmap.setTo(seq);
+ return true;
+ }
+ };
+ return fref;
+ }
+
+ public SeqFref newAlcodMapRef(final String sref,
+ final AlignedCodonFrame _cf,
+ final jalview.datamodel.Mapping _jmap)
+ {
+
+ SeqFref fref = new SeqFref(sref, "Codon Frame")
+ {
+ AlignedCodonFrame cf = _cf;
+
+ public jalview.datamodel.Mapping mp = _jmap;
+
+ @Override
+ public boolean isResolvable()
+ {
+ return super.isResolvable() && mp.getTo() != null;
+ };
+
+ @Override
+ boolean resolve()
+ {
+ SequenceI seq = getSrefDatasetSeq();
+ if (seq == null)
+ {
+ return false;
+ }
+ cf.addMap(seq, mp.getTo(), mp.getMap());
+ return true;
+ }
+ };
+ return fref;
+ }
+
+ public void resolveFrefedSequences()
+ {
+ Iterator nextFref = frefedSequence.iterator();
+ int toresolve = frefedSequence.size();
+ int unresolved = 0, failedtoresolve = 0;
+ while (nextFref.hasNext())
+ {
+ SeqFref ref = nextFref.next();
+ if (ref.isResolvable())
+ {
+ try
+ {
+ if (ref.resolve())
+ {
+ nextFref.remove();
+ }
+ else
+ {
+ failedtoresolve++;
+ }
+ } catch (Exception x)
+ {
+ System.err.println(
+ "IMPLEMENTATION ERROR: Failed to resolve forward reference for sequence "
+ + ref.getSref());
+ x.printStackTrace();
+ failedtoresolve++;
+ }
+ }
+ else
+ {
+ unresolved++;
+ }
+ }
+ if (unresolved > 0)
+ {
+ System.err.println("Jalview Project Import: There were " + unresolved
+ + " forward references left unresolved on the stack.");
+ }
+ if (failedtoresolve > 0)
+ {
+ System.err.println("SERIOUS! " + failedtoresolve
+ + " resolvable forward references failed to resolve.");
+ }
+ if (incompleteSeqs != null && incompleteSeqs.size() > 0)
+ {
+ System.err.println(
+ "Jalview Project Import: There are " + incompleteSeqs.size()
+ + " sequences which may have incomplete metadata.");
+ if (incompleteSeqs.size() < 10)
+ {
+ for (SequenceI s : incompleteSeqs.values())
+ {
+ System.err.println(s.toString());
+ }
+ }
+ else
+ {
+ System.err.println(
+ "Too many to report. Skipping output of incomplete sequences.");
+ }
+ }
+ }
+
+ /**
+ * This maintains a map of viewports, the key being the seqSetId. Important to
+ * set historyItem and redoList for multiple views
+ */
+ Map viewportsAdded = new HashMap<>();
+
+ Map annotationIds = new HashMap<>();
+
+ String uniqueSetSuffix = "";
+
+ /**
+ * List of pdbfiles added to Jar
+ */
+ List pdbfiles = null;
+
+ // SAVES SEVERAL ALIGNMENT WINDOWS TO SAME JARFILE
+ public void saveState(File statefile)
+ {
+ FileOutputStream fos = null;
+ try
+ {
+ fos = new FileOutputStream(statefile);
+ JarOutputStream jout = new JarOutputStream(fos);
+ saveState(jout);
+
+ } catch (Exception e)
+ {
+ // TODO: inform user of the problem - they need to know if their data was
+ // not saved !
+ if (errorMessage == null)
+ {
+ errorMessage = "Couldn't write Jalview Archive to output file '"
+ + statefile + "' - See console error log for details";
+ }
+ else
+ {
+ errorMessage += "(output file was '" + statefile + "')";
+ }
+ e.printStackTrace();
+ } finally
+ {
+ if (fos != null)
+ {
+ try
+ {
+ fos.close();
+ } catch (IOException e)
+ {
+ // ignore
+ }
+ }
+ }
+ reportErrors();
+ }
+
+ /**
+ * Writes a jalview project archive to the given Jar output stream.
+ *
+ * @param jout
+ */
+ public void saveState(JarOutputStream jout)
+ {
+ AlignFrame[] frames = Desktop.getAlignFrames();
+
+ if (frames == null)
+ {
+ return;
+ }
+ saveAllFrames(Arrays.asList(frames), jout);
+ }
+
+ /**
+ * core method for storing state for a set of AlignFrames.
+ *
+ * @param frames
+ * - frames involving all data to be exported (including containing
+ * splitframes)
+ * @param jout
+ * - project output stream
+ */
+ private void saveAllFrames(List frames, JarOutputStream jout)
+ {
+ Hashtable dsses = new Hashtable<>();
+
+ /*
+ * ensure cached data is clear before starting
+ */
+ // todo tidy up seqRefIds, seqsToIds initialisation / reset
+ rnaSessions.clear();
+ splitFrameCandidates.clear();
+
+ try
+ {
+
+ // NOTE UTF-8 MUST BE USED FOR WRITING UNICODE CHARS
+ // //////////////////////////////////////////////////
+
+ List shortNames = new ArrayList<>();
+ List viewIds = new ArrayList<>();
+
+ // REVERSE ORDER
+ for (int i = frames.size() - 1; i > -1; i--)
+ {
+ AlignFrame af = frames.get(i);
+ // skip ?
+ if (skipList != null && skipList
+ .containsKey(af.getViewport().getSequenceSetId()))
+ {
+ continue;
+ }
+
+ String shortName = makeFilename(af, shortNames);
+
+ int apSize = af.getAlignPanels().size();
+
+ for (int ap = 0; ap < apSize; ap++)
+ {
+ AlignmentPanel apanel = (AlignmentPanel) af.getAlignPanels()
+ .get(ap);
+ String fileName = apSize == 1 ? shortName : ap + shortName;
+ if (!fileName.endsWith(".xml"))
+ {
+ fileName = fileName + ".xml";
+ }
+
+ saveState(apanel, fileName, jout, viewIds);
+
+ String dssid = getDatasetIdRef(
+ af.getViewport().getAlignment().getDataset());
+ if (!dsses.containsKey(dssid))
+ {
+ dsses.put(dssid, af);
+ }
+ }
+ }
+
+ writeDatasetFor(dsses, "" + jout.hashCode() + " " + uniqueSetSuffix,
+ jout);
+
+ try
+ {
+ jout.flush();
+ } catch (Exception foo)
+ {
+ }
+ ;
+ jout.close();
+ } catch (Exception ex)
+ {
+ // TODO: inform user of the problem - they need to know if their data was
+ // not saved !
+ if (errorMessage == null)
+ {
+ errorMessage = "Couldn't write Jalview Archive - see error output for details";
+ }
+ ex.printStackTrace();
+ }
+ }
+
+ /**
+ * Generates a distinct file name, based on the title of the AlignFrame, by
+ * appending _n for increasing n until an unused name is generated. The new
+ * name (without its extension) is added to the list.
+ *
+ * @param af
+ * @param namesUsed
+ * @return the generated name, with .xml extension
+ */
+ protected String makeFilename(AlignFrame af, List namesUsed)
+ {
+ String shortName = af.getTitle();
+
+ if (shortName.indexOf(File.separatorChar) > -1)
+ {
+ shortName = shortName
+ .substring(shortName.lastIndexOf(File.separatorChar) + 1);
+ }
+
+ int count = 1;
+
+ while (namesUsed.contains(shortName))
+ {
+ if (shortName.endsWith("_" + (count - 1)))
+ {
+ shortName = shortName.substring(0, shortName.lastIndexOf("_"));
+ }
+
+ shortName = shortName.concat("_" + count);
+ count++;
+ }
+
+ namesUsed.add(shortName);
+
+ if (!shortName.endsWith(".xml"))
+ {
+ shortName = shortName + ".xml";
+ }
+ return shortName;
+ }
+
+ // USE THIS METHOD TO SAVE A SINGLE ALIGNMENT WINDOW
+ public boolean saveAlignment(AlignFrame af, String jarFile,
+ String fileName)
+ {
+ try
+ {
+ FileOutputStream fos = new FileOutputStream(jarFile);
+ JarOutputStream jout = new JarOutputStream(fos);
+ List frames = new ArrayList<>();
+
+ // resolve splitframes
+ if (af.getViewport().getCodingComplement() != null)
+ {
+ frames = ((SplitFrame) af.getSplitViewContainer()).getAlignFrames();
+ }
+ else
+ {
+ frames.add(af);
+ }
+ saveAllFrames(frames, jout);
+ try
+ {
+ jout.flush();
+ } catch (Exception foo)
+ {
+ }
+ ;
+ jout.close();
+ return true;
+ } catch (Exception ex)
+ {
+ errorMessage = "Couldn't Write alignment view to Jalview Archive - see error output for details";
+ ex.printStackTrace();
+ return false;
+ }
+ }
+
+ private void writeDatasetFor(Hashtable dsses,
+ String fileName, JarOutputStream jout)
+ {
+
+ for (String dssids : dsses.keySet())
+ {
+ AlignFrame _af = dsses.get(dssids);
+ String jfileName = fileName + " Dataset for " + _af.getTitle();
+ if (!jfileName.endsWith(".xml"))
+ {
+ jfileName = jfileName + ".xml";
+ }
+ saveState(_af.alignPanel, jfileName, true, jout, null);
+ }
+ }
+
+ /**
+ * create a JalviewModel from an alignment view and marshall it to a
+ * JarOutputStream
+ *
+ * @param ap
+ * panel to create jalview model for
+ * @param fileName
+ * name of alignment panel written to output stream
+ * @param jout
+ * jar output stream
+ * @param viewIds
+ * @param out
+ * jar entry name
+ */
+ public JalviewModel saveState(AlignmentPanel ap, String fileName,
+ JarOutputStream jout, List viewIds)
+ {
+ return saveState(ap, fileName, false, jout, viewIds);
+ }
+
+ /**
+ * create a JalviewModel from an alignment view and marshall it to a
+ * JarOutputStream
+ *
+ * @param ap
+ * panel to create jalview model for
+ * @param fileName
+ * name of alignment panel written to output stream
+ * @param storeDS
+ * when true, only write the dataset for the alignment, not the data
+ * associated with the view.
+ * @param jout
+ * jar output stream
+ * @param out
+ * jar entry name
+ */
+ public JalviewModel saveState(AlignmentPanel ap, String fileName,
+ boolean storeDS, JarOutputStream jout, List viewIds)
+ {
+ if (viewIds == null)
+ {
+ viewIds = new ArrayList<>();
+ }
+
+ initSeqRefs();
+
+ List userColours = new ArrayList<>();
+
+ AlignViewport av = ap.av;
+ ViewportRanges vpRanges = av.getRanges();
+
+ final ObjectFactory objectFactory = new ObjectFactory();
+ JalviewModel object = objectFactory.createJalviewModel();
+ object.setVamsasModel(new VAMSAS());
+
+ // object.setCreationDate(new java.util.Date(System.currentTimeMillis()));
+ try
+ {
+ GregorianCalendar c = new GregorianCalendar();
+ DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();
+ XMLGregorianCalendar now = datatypeFactory.newXMLGregorianCalendar(c);// gregorianCalendar);
+ object.setCreationDate(now);
+ } catch (DatatypeConfigurationException e)
+ {
+ System.err.println("error writing date: " + e.toString());
+ }
+ object.setVersion(
+ jalview.bin.Cache.getDefault("VERSION", "Development Build"));
+
+ /**
+ * rjal is full height alignment, jal is actual alignment with full metadata
+ * but excludes hidden sequences.
+ */
+ jalview.datamodel.AlignmentI rjal = av.getAlignment(), jal = rjal;
+
+ if (av.hasHiddenRows())
+ {
+ rjal = jal.getHiddenSequences().getFullAlignment();
+ }
+
+ SequenceSet vamsasSet = new SequenceSet();
+ Sequence vamsasSeq;
+ // JalviewModelSequence jms = new JalviewModelSequence();
+
+ vamsasSet.setGapChar(jal.getGapCharacter() + "");
+
+ if (jal.getDataset() != null)
+ {
+ // dataset id is the dataset's hashcode
+ vamsasSet.setDatasetId(getDatasetIdRef(jal.getDataset()));
+ if (storeDS)
+ {
+ // switch jal and the dataset
+ jal = jal.getDataset();
+ rjal = jal;
+ }
+ }
+ if (jal.getProperties() != null)
+ {
+ Enumeration en = jal.getProperties().keys();
+ while (en.hasMoreElements())
+ {
+ String key = en.nextElement().toString();
+ SequenceSetProperties ssp = new SequenceSetProperties();
+ ssp.setKey(key);
+ ssp.setValue(jal.getProperties().get(key).toString());
+ // vamsasSet.addSequenceSetProperties(ssp);
+ vamsasSet.getSequenceSetProperties().add(ssp);
+ }
+ }
+
+ JSeq jseq;
+ Set calcIdSet = new HashSet<>();
+ // record the set of vamsas sequence XML POJO we create.
+ HashMap vamsasSetIds = new HashMap<>();
+ // SAVE SEQUENCES
+ for (final SequenceI jds : rjal.getSequences())
+ {
+ final SequenceI jdatasq = jds.getDatasetSequence() == null ? jds
+ : jds.getDatasetSequence();
+ String id = seqHash(jds);
+ if (vamsasSetIds.get(id) == null)
+ {
+ if (seqRefIds.get(id) != null && !storeDS)
+ {
+ // This happens for two reasons: 1. multiple views are being
+ // serialised.
+ // 2. the hashCode has collided with another sequence's code. This
+ // DOES
+ // HAPPEN! (PF00072.15.stk does this)
+ // JBPNote: Uncomment to debug writing out of files that do not read
+ // back in due to ArrayOutOfBoundExceptions.
+ // System.err.println("vamsasSeq backref: "+id+"");
+ // System.err.println(jds.getName()+"
+ // "+jds.getStart()+"-"+jds.getEnd()+" "+jds.getSequenceAsString());
+ // System.err.println("Hashcode: "+seqHash(jds));
+ // SequenceI rsq = (SequenceI) seqRefIds.get(id + "");
+ // System.err.println(rsq.getName()+"
+ // "+rsq.getStart()+"-"+rsq.getEnd()+" "+rsq.getSequenceAsString());
+ // System.err.println("Hashcode: "+seqHash(rsq));
+ }
+ else
+ {
+ vamsasSeq = createVamsasSequence(id, jds);
+// vamsasSet.addSequence(vamsasSeq);
+ vamsasSet.getSequence().add(vamsasSeq);
+ vamsasSetIds.put(id, vamsasSeq);
+ seqRefIds.put(id, jds);
+ }
+ }
+ jseq = new JSeq();
+ jseq.setStart(jds.getStart());
+ jseq.setEnd(jds.getEnd());
+ jseq.setColour(av.getSequenceColour(jds).getRGB());
+
+ jseq.setId(id); // jseq id should be a string not a number
+ if (!storeDS)
+ {
+ // Store any sequences this sequence represents
+ if (av.hasHiddenRows())
+ {
+ // use rjal, contains the full height alignment
+ jseq.setHidden(
+ av.getAlignment().getHiddenSequences().isHidden(jds));
+
+ if (av.isHiddenRepSequence(jds))
+ {
+ jalview.datamodel.SequenceI[] reps = av
+ .getRepresentedSequences(jds).getSequencesInOrder(rjal);
+
+ for (int h = 0; h < reps.length; h++)
+ {
+ if (reps[h] != jds)
+ {
+ // jseq.addHiddenSequences(rjal.findIndex(reps[h]));
+ jseq.getHiddenSequences().add(rjal.findIndex(reps[h]));
+ }
+ }
+ }
+ }
+ // mark sequence as reference - if it is the reference for this view
+ if (jal.hasSeqrep())
+ {
+ jseq.setViewreference(jds == jal.getSeqrep());
+ }
+ }
+
+ // TODO: omit sequence features from each alignment view's XML dump if we
+ // are storing dataset
+ List sfs = jds.getSequenceFeatures();
+ for (SequenceFeature sf : sfs)
+ {
+ // Features features = new Features();
+ Feature features = new Feature();
+
+ features.setBegin(sf.getBegin());
+ features.setEnd(sf.getEnd());
+ features.setDescription(sf.getDescription());
+ features.setType(sf.getType());
+ features.setFeatureGroup(sf.getFeatureGroup());
+ features.setScore(sf.getScore());
+ if (sf.links != null)
+ {
+ for (int l = 0; l < sf.links.size(); l++)
+ {
+ OtherData keyValue = new OtherData();
+ keyValue.setKey("LINK_" + l);
+ keyValue.setValue(sf.links.elementAt(l).toString());
+ // features.addOtherData(keyValue);
+ features.getOtherData().add(keyValue);
+ }
+ }
+ if (sf.otherDetails != null)
+ {
+ /*
+ * save feature attributes, which may be simple strings or
+ * map valued (have sub-attributes)
+ */
+ for (Entry entry : sf.otherDetails.entrySet())
+ {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+ if (value instanceof Map, ?>)
+ {
+ for (Entry subAttribute : ((Map) value)
+ .entrySet())
+ {
+ OtherData otherData = new OtherData();
+ otherData.setKey(key);
+ otherData.setKey2(subAttribute.getKey());
+ otherData.setValue(subAttribute.getValue().toString());
+ // features.addOtherData(otherData);
+ features.getOtherData().add(otherData);
+ }
+ }
+ else
+ {
+ OtherData otherData = new OtherData();
+ otherData.setKey(key);
+ otherData.setValue(value.toString());
+ // features.addOtherData(otherData);
+ features.getOtherData().add(otherData);
+ }
+ }
+ }
+
+ // jseq.addFeatures(features);
+ jseq.getFeatures().add(features);
+ }
+
+ if (jdatasq.getAllPDBEntries() != null)
+ {
+ Enumeration en = jdatasq.getAllPDBEntries().elements();
+ while (en.hasMoreElements())
+ {
+ Pdbids pdb = new Pdbids();
+ jalview.datamodel.PDBEntry entry = en.nextElement();
+
+ String pdbId = entry.getId();
+ pdb.setId(pdbId);
+ pdb.setType(entry.getType());
+
+ /*
+ * Store any structure views associated with this sequence. This
+ * section copes with duplicate entries in the project, so a dataset
+ * only view *should* be coped with sensibly.
+ */
+ // This must have been loaded, is it still visible?
+ JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+ String matchedFile = null;
+ for (int f = frames.length - 1; f > -1; f--)
+ {
+ if (frames[f] instanceof StructureViewerBase)
+ {
+ StructureViewerBase viewFrame = (StructureViewerBase) frames[f];
+ matchedFile = saveStructureState(ap, jds, pdb, entry, viewIds,
+ matchedFile, viewFrame);
+ /*
+ * Only store each structure viewer's state once in the project
+ * jar. First time through only (storeDS==false)
+ */
+ String viewId = viewFrame.getViewId();
+ if (!storeDS && !viewIds.contains(viewId))
+ {
+ viewIds.add(viewId);
+ try
+ {
+ String viewerState = viewFrame.getStateInfo();
+ writeJarEntry(jout, getViewerJarEntryName(viewId),
+ viewerState.getBytes());
+ } catch (IOException e)
+ {
+ System.err.println(
+ "Error saving viewer state: " + e.getMessage());
+ }
+ }
+ }
+ }
+
+ if (matchedFile != null || entry.getFile() != null)
+ {
+ if (entry.getFile() != null)
+ {
+ // use entry's file
+ matchedFile = entry.getFile();
+ }
+ pdb.setFile(matchedFile); // entry.getFile());
+ if (pdbfiles == null)
+ {
+ pdbfiles = new ArrayList<>();
+ }
+
+ if (!pdbfiles.contains(pdbId))
+ {
+ pdbfiles.add(pdbId);
+ copyFileToJar(jout, matchedFile, pdbId);
+ }
+ }
+
+ Enumeration props = entry.getProperties();
+ if (props.hasMoreElements())
+ {
+ // PdbentryItem item = new PdbentryItem();
+ while (props.hasMoreElements())
+ {
+ Property prop = new Property();
+ String key = props.nextElement();
+ prop.setName(key);
+ prop.setValue(entry.getProperty(key).toString());
+ // item.addProperty(prop);
+ pdb.getProperty().add(prop);
+ }
+ // pdb.addPdbentryItem(item);
+ }
+
+ // jseq.addPdbids(pdb);
+ jseq.getPdbids().add(pdb);
+ }
+ }
+
+ saveRnaViewers(jout, jseq, jds, viewIds, ap, storeDS);
+
+ // jms.addJSeq(jseq);
+ object.getJSeq().add(jseq);
+ }
+
+ if (!storeDS && av.hasHiddenRows())
+ {
+ jal = av.getAlignment();
+ }
+ // SAVE MAPPINGS
+ // FOR DATASET
+ if (storeDS && jal.getCodonFrames() != null)
+ {
+ List jac = jal.getCodonFrames();
+ for (AlignedCodonFrame acf : jac)
+ {
+ AlcodonFrame alc = new AlcodonFrame();
+ if (acf.getProtMappings() != null
+ && acf.getProtMappings().length > 0)
+ {
+ boolean hasMap = false;
+ SequenceI[] dnas = acf.getdnaSeqs();
+ jalview.datamodel.Mapping[] pmaps = acf.getProtMappings();
+ for (int m = 0; m < pmaps.length; m++)
+ {
+ AlcodMap alcmap = new AlcodMap();
+ alcmap.setDnasq(seqHash(dnas[m]));
+ alcmap.setMapping(
+ createVamsasMapping(pmaps[m], dnas[m], null, false));
+ // alc.addAlcodMap(alcmap);
+ alc.getAlcodMap().add(alcmap);
+ hasMap = true;
+ }
+ if (hasMap)
+ {
+ // vamsasSet.addAlcodonFrame(alc);
+ vamsasSet.getAlcodonFrame().add(alc);
+ }
+ }
+ // TODO: delete this ? dead code from 2.8.3->2.9 ?
+ // {
+ // AlcodonFrame alc = new AlcodonFrame();
+ // vamsasSet.addAlcodonFrame(alc);
+ // for (int p = 0; p < acf.aaWidth; p++)
+ // {
+ // Alcodon cmap = new Alcodon();
+ // if (acf.codons[p] != null)
+ // {
+ // // Null codons indicate a gapped column in the translated peptide
+ // // alignment.
+ // cmap.setPos1(acf.codons[p][0]);
+ // cmap.setPos2(acf.codons[p][1]);
+ // cmap.setPos3(acf.codons[p][2]);
+ // }
+ // alc.addAlcodon(cmap);
+ // }
+ // if (acf.getProtMappings() != null
+ // && acf.getProtMappings().length > 0)
+ // {
+ // SequenceI[] dnas = acf.getdnaSeqs();
+ // jalview.datamodel.Mapping[] pmaps = acf.getProtMappings();
+ // for (int m = 0; m < pmaps.length; m++)
+ // {
+ // AlcodMap alcmap = new AlcodMap();
+ // alcmap.setDnasq(seqHash(dnas[m]));
+ // alcmap.setMapping(createVamsasMapping(pmaps[m], dnas[m], null,
+ // false));
+ // alc.addAlcodMap(alcmap);
+ // }
+ // }
+ }
+ }
+
+ // SAVE TREES
+ // /////////////////////////////////
+ if (!storeDS && av.getCurrentTree() != null)
+ {
+ // FIND ANY ASSOCIATED TREES
+ // NOT IMPLEMENTED FOR HEADLESS STATE AT PRESENT
+ if (Desktop.desktop != null)
+ {
+ JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+
+ for (int t = 0; t < frames.length; t++)
+ {
+ if (frames[t] instanceof TreePanel)
+ {
+ TreePanel tp = (TreePanel) frames[t];
+
+ if (tp.getTreeCanvas().getViewport().getAlignment() == jal)
+ {
+ JalviewModel.Tree tree = new JalviewModel.Tree();
+ tree.setTitle(tp.getTitle());
+ tree.setCurrentTree((av.getCurrentTree() == tp.getTree()));
+ tree.setNewick(tp.getTree().print());
+ tree.setThreshold(tp.getTreeCanvas().getThreshold());
+
+ tree.setFitToWindow(tp.fitToWindow.getState());
+ tree.setFontName(tp.getTreeFont().getName());
+ tree.setFontSize(tp.getTreeFont().getSize());
+ tree.setFontStyle(tp.getTreeFont().getStyle());
+ tree.setMarkUnlinked(tp.placeholdersMenu.getState());
+
+ tree.setShowBootstrap(tp.bootstrapMenu.getState());
+ tree.setShowDistances(tp.distanceMenu.getState());
+
+ tree.setHeight(tp.getHeight());
+ tree.setWidth(tp.getWidth());
+ tree.setXpos(tp.getX());
+ tree.setYpos(tp.getY());
+ tree.setId(makeHashCode(tp, null));
+ tree.setLinkToAllViews(
+ tp.getTreeCanvas().isApplyToAllViews());
+
+ // jms.addTree(tree);
+ object.getTree().add(tree);
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * save PCA viewers
+ */
+ if (!storeDS && Desktop.desktop != null)
+ {
+ for (JInternalFrame frame : Desktop.desktop.getAllFrames())
+ {
+ if (frame instanceof PCAPanel)
+ {
+ PCAPanel panel = (PCAPanel) frame;
+ if (panel.getAlignViewport().getAlignment() == jal)
+ {
+ savePCA(panel, object);
+ }
+ }
+ }
+ }
+
+ // SAVE ANNOTATIONS
+ /**
+ * store forward refs from an annotationRow to any groups
+ */
+ IdentityHashMap groupRefs = new IdentityHashMap<>();
+ if (storeDS)
+ {
+ for (SequenceI sq : jal.getSequences())
+ {
+ // Store annotation on dataset sequences only
+ AlignmentAnnotation[] aa = sq.getAnnotation();
+ if (aa != null && aa.length > 0)
+ {
+ storeAlignmentAnnotation(aa, groupRefs, av, calcIdSet, storeDS,
+ vamsasSet);
+ }
+ }
+ }
+ else
+ {
+ if (jal.getAlignmentAnnotation() != null)
+ {
+ // Store the annotation shown on the alignment.
+ AlignmentAnnotation[] aa = jal.getAlignmentAnnotation();
+ storeAlignmentAnnotation(aa, groupRefs, av, calcIdSet, storeDS,
+ vamsasSet);
+ }
+ }
+ // SAVE GROUPS
+ if (jal.getGroups() != null)
+ {
+ JGroup[] groups = new JGroup[jal.getGroups().size()];
+ int i = -1;
+ for (jalview.datamodel.SequenceGroup sg : jal.getGroups())
+ {
+ JGroup jGroup = new JGroup();
+ groups[++i] = jGroup;
+
+ jGroup.setStart(sg.getStartRes());
+ jGroup.setEnd(sg.getEndRes());
+ jGroup.setName(sg.getName());
+ if (groupRefs.containsKey(sg))
+ {
+ // group has references so set its ID field
+ jGroup.setId(groupRefs.get(sg));
+ }
+ ColourSchemeI colourScheme = sg.getColourScheme();
+ if (colourScheme != null)
+ {
+ ResidueShaderI groupColourScheme = sg.getGroupColourScheme();
+ if (groupColourScheme.conservationApplied())
+ {
+ jGroup.setConsThreshold(groupColourScheme.getConservationInc());
+
+ if (colourScheme instanceof jalview.schemes.UserColourScheme)
+ {
+ jGroup.setColour(
+ setUserColourScheme(colourScheme, userColours,
+ object));
+ }
+ else
+ {
+ jGroup.setColour(colourScheme.getSchemeName());
+ }
+ }
+ else if (colourScheme instanceof jalview.schemes.AnnotationColourGradient)
+ {
+ jGroup.setColour("AnnotationColourGradient");
+ jGroup.setAnnotationColours(constructAnnotationColours(
+ (jalview.schemes.AnnotationColourGradient) colourScheme,
+ userColours, object));
+ }
+ else if (colourScheme instanceof jalview.schemes.UserColourScheme)
+ {
+ jGroup.setColour(
+ setUserColourScheme(colourScheme, userColours, object));
+ }
+ else
+ {
+ jGroup.setColour(colourScheme.getSchemeName());
+ }
+
+ jGroup.setPidThreshold(groupColourScheme.getThreshold());
+ }
+
+ jGroup.setOutlineColour(sg.getOutlineColour().getRGB());
+ jGroup.setDisplayBoxes(sg.getDisplayBoxes());
+ jGroup.setDisplayText(sg.getDisplayText());
+ jGroup.setColourText(sg.getColourText());
+ jGroup.setTextCol1(sg.textColour.getRGB());
+ jGroup.setTextCol2(sg.textColour2.getRGB());
+ jGroup.setTextColThreshold(sg.thresholdTextColour);
+ jGroup.setShowUnconserved(sg.getShowNonconserved());
+ jGroup.setIgnoreGapsinConsensus(sg.getIgnoreGapsConsensus());
+ jGroup.setShowConsensusHistogram(sg.isShowConsensusHistogram());
+ jGroup.setShowSequenceLogo(sg.isShowSequenceLogo());
+ jGroup.setNormaliseSequenceLogo(sg.isNormaliseSequenceLogo());
+ for (SequenceI seq : sg.getSequences())
+ {
+ // jGroup.addSeq(seqHash(seq));
+ jGroup.getSeq().add(seqHash(seq));
+ }
+ }
+
+ //jms.setJGroup(groups);
+ Object group;
+ for (JGroup grp : groups)
+ {
+ object.getJGroup().add(grp);
+ }
+ }
+ if (!storeDS)
+ {
+ // /////////SAVE VIEWPORT
+ Viewport view = new Viewport();
+ view.setTitle(ap.alignFrame.getTitle());
+ view.setSequenceSetId(
+ makeHashCode(av.getSequenceSetId(), av.getSequenceSetId()));
+ view.setId(av.getViewId());
+ if (av.getCodingComplement() != null)
+ {
+ view.setComplementId(av.getCodingComplement().getViewId());
+ }
+ view.setViewName(av.getViewName());
+ view.setGatheredViews(av.isGatherViewsHere());
+
+ Rectangle size = ap.av.getExplodedGeometry();
+ Rectangle position = size;
+ if (size == null)
+ {
+ size = ap.alignFrame.getBounds();
+ if (av.getCodingComplement() != null)
+ {
+ position = ((SplitFrame) ap.alignFrame.getSplitViewContainer())
+ .getBounds();
+ }
+ else
+ {
+ position = size;
+ }
+ }
+ view.setXpos(position.x);
+ view.setYpos(position.y);
+
+ view.setWidth(size.width);
+ view.setHeight(size.height);
+
+ view.setStartRes(vpRanges.getStartRes());
+ view.setStartSeq(vpRanges.getStartSeq());
+
+ if (av.getGlobalColourScheme() instanceof jalview.schemes.UserColourScheme)
+ {
+ view.setBgColour(setUserColourScheme(av.getGlobalColourScheme(),
+ userColours, object));
+ }
+ else if (av
+ .getGlobalColourScheme() instanceof jalview.schemes.AnnotationColourGradient)
+ {
+ AnnotationColourScheme ac = constructAnnotationColours(
+ (jalview.schemes.AnnotationColourGradient) av
+ .getGlobalColourScheme(),
+ userColours, object);
+
+ view.setAnnotationColours(ac);
+ view.setBgColour("AnnotationColourGradient");
+ }
+ else
+ {
+ view.setBgColour(ColourSchemeProperty
+ .getColourName(av.getGlobalColourScheme()));
+ }
+
+ ResidueShaderI vcs = av.getResidueShading();
+ ColourSchemeI cs = av.getGlobalColourScheme();
+
+ if (cs != null)
+ {
+ if (vcs.conservationApplied())
+ {
+ view.setConsThreshold(vcs.getConservationInc());
+ if (cs instanceof jalview.schemes.UserColourScheme)
+ {
+ view.setBgColour(setUserColourScheme(cs, userColours, object));
+ }
+ }
+ view.setPidThreshold(vcs.getThreshold());
+ }
+
+ view.setConservationSelected(av.getConservationSelected());
+ view.setPidSelected(av.getAbovePIDThreshold());
+ final Font font = av.getFont();
+ view.setFontName(font.getName());
+ view.setFontSize(font.getSize());
+ view.setFontStyle(font.getStyle());
+ view.setScaleProteinAsCdna(av.getViewStyle().isScaleProteinAsCdna());
+ view.setRenderGaps(av.isRenderGaps());
+ view.setShowAnnotation(av.isShowAnnotation());
+ view.setShowBoxes(av.getShowBoxes());
+ view.setShowColourText(av.getColourText());
+ view.setShowFullId(av.getShowJVSuffix());
+ view.setRightAlignIds(av.isRightAlignIds());
+ view.setShowSequenceFeatures(av.isShowSequenceFeatures());
+ view.setShowText(av.getShowText());
+ view.setShowUnconserved(av.getShowUnconserved());
+ view.setWrapAlignment(av.getWrapAlignment());
+ view.setTextCol1(av.getTextColour().getRGB());
+ view.setTextCol2(av.getTextColour2().getRGB());
+ view.setTextColThreshold(av.getThresholdTextColour());
+ view.setShowConsensusHistogram(av.isShowConsensusHistogram());
+ view.setShowSequenceLogo(av.isShowSequenceLogo());
+ view.setNormaliseSequenceLogo(av.isNormaliseSequenceLogo());
+ view.setShowGroupConsensus(av.isShowGroupConsensus());
+ view.setShowGroupConservation(av.isShowGroupConservation());
+ view.setShowNPfeatureTooltip(av.isShowNPFeats());
+ view.setShowDbRefTooltip(av.isShowDBRefs());
+ view.setFollowHighlight(av.isFollowHighlight());
+ view.setFollowSelection(av.followSelection);
+ view.setIgnoreGapsinConsensus(av.isIgnoreGapsConsensus());
+ if (av.getFeaturesDisplayed() != null)
+ {
+ FeatureSettings fs = new FeatureSettings();
+
+ FeatureRendererModel fr = ap.getSeqPanel().seqCanvas
+ .getFeatureRenderer();
+ String[] renderOrder = fr.getRenderOrder().toArray(new String[0]);
+
+ Vector settingsAdded = new Vector<>();
+ if (renderOrder != null)
+ {
+ for (String featureType : renderOrder)
+ {
+ FeatureSettings.Setting setting = new FeatureSettings.Setting();
+ setting.setType(featureType);
+
+ /*
+ * save any filter for the feature type
+ */
+ FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
+ if (filter != null) {
+ Iterator filters = filter.getMatchers().iterator();
+ FeatureMatcherI firstFilter = filters.next();
+ setting.setMatcherSet(Jalview2XML.marshalFilter(
+ firstFilter, filters, filter.isAnded()));
+ }
+
+ /*
+ * save colour scheme for the feature type
+ */
+ FeatureColourI fcol = fr.getFeatureStyle(featureType);
+ if (!fcol.isSimpleColour())
+ {
+ setting.setColour(fcol.getMaxColour().getRGB());
+ setting.setMincolour(fcol.getMinColour().getRGB());
+ setting.setMin(fcol.getMin());
+ setting.setMax(fcol.getMax());
+ setting.setColourByLabel(fcol.isColourByLabel());
+ if (fcol.isColourByAttribute())
+ {
+ String[] attName = fcol.getAttributeName();
+ setting.getAttributeName().add(attName[0]);
+ if (attName.length > 1)
+ {
+ setting.getAttributeName().add(attName[1]);
+ }
+ }
+ setting.setAutoScale(fcol.isAutoScaled());
+ setting.setThreshold(fcol.getThreshold());
+ Color noColour = fcol.getNoColour();
+ if (noColour == null)
+ {
+ setting.setNoValueColour(NoValueColour.NONE);
+ }
+ else if (noColour.equals(fcol.getMaxColour()))
+ {
+ setting.setNoValueColour(NoValueColour.MAX);
+ }
+ else
+ {
+ setting.setNoValueColour(NoValueColour.MIN);
+ }
+ // -1 = No threshold, 0 = Below, 1 = Above
+ setting.setThreshstate(fcol.isAboveThreshold() ? 1
+ : (fcol.isBelowThreshold() ? 0 : -1));
+ }
+ else
+ {
+ setting.setColour(fcol.getColour().getRGB());
+ }
+
+ setting.setDisplay(
+ av.getFeaturesDisplayed().isVisible(featureType));
+ float rorder = fr
+ .getOrder(featureType);
+ if (rorder > -1)
+ {
+ setting.setOrder(rorder);
+ }
+ /// fs.addSetting(setting);
+ fs.getSetting().add(setting);
+ settingsAdded.addElement(featureType);
+ }
+ }
+
+ // is groups actually supposed to be a map here ?
+ Iterator en = fr.getFeatureGroups().iterator();
+ Vector groupsAdded = new Vector<>();
+ while (en.hasNext())
+ {
+ String grp = en.next();
+ if (groupsAdded.contains(grp))
+ {
+ continue;
+ }
+ Group g = new Group();
+ g.setName(grp);
+ g.setDisplay(((Boolean) fr.checkGroupVisibility(grp, false))
+ .booleanValue());
+ // fs.addGroup(g);
+ fs.getGroup().add(g);
+ groupsAdded.addElement(grp);
+ }
+ // jms.setFeatureSettings(fs);
+ object.setFeatureSettings(fs);
+ }
+
+ if (av.hasHiddenColumns())
+ {
+ jalview.datamodel.HiddenColumns hidden = av.getAlignment()
+ .getHiddenColumns();
+ if (hidden == null)
+ {
+ warn("REPORT BUG: avoided null columnselection bug (DMAM reported). Please contact Jim about this.");
+ }
+ else
+ {
+ Iterator hiddenRegions = hidden.iterator();
+ while (hiddenRegions.hasNext())
+ {
+ int[] region = hiddenRegions.next();
+ HiddenColumns hc = new HiddenColumns();
+ hc.setStart(region[0]);
+ hc.setEnd(region[1]);
+ // view.addHiddenColumns(hc);
+ view.getHiddenColumns().add(hc);
+ }
+ }
+ }
+ if (calcIdSet.size() > 0)
+ {
+ for (String calcId : calcIdSet)
+ {
+ if (calcId.trim().length() > 0)
+ {
+ CalcIdParam cidp = createCalcIdParam(calcId, av);
+ // Some calcIds have no parameters.
+ if (cidp != null)
+ {
+ // view.addCalcIdParam(cidp);
+ view.getCalcIdParam().add(cidp);
+ }
+ }
+ }
+ }
+
+ // jms.addViewport(view);
+ object.getViewport().add(view);
+ }
+ // object.setJalviewModelSequence(jms);
+ // object.getVamsasModel().addSequenceSet(vamsasSet);
+ object.getVamsasModel().getSequenceSet().add(vamsasSet);
+
+ if (jout != null && fileName != null)
+ {
+ // We may not want to write the object to disk,
+ // eg we can copy the alignViewport to a new view object
+ // using save and then load
+ try
+ {
+ System.out.println("Writing jar entry " + fileName);
+ JarEntry entry = new JarEntry(fileName);
+ jout.putNextEntry(entry);
+ PrintWriter pout = new PrintWriter(
+ new OutputStreamWriter(jout, UTF_8));
+ JAXBContext jaxbContext = JAXBContext
+ .newInstance(JalviewModel.class);
+ Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
+
+ // output pretty printed
+ // jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
+ jaxbMarshaller.marshal(
+ new ObjectFactory().createJalviewModel(object), pout);
+
+ // jaxbMarshaller.marshal(object, pout);
+ // marshaller.marshal(object);
+ pout.flush();
+ jout.closeEntry();
+ } catch (Exception ex)
+ {
+ // TODO: raise error in GUI if marshalling failed.
+ System.err.println("Error writing Jalview project");
+ ex.printStackTrace();
+ }
+ }
+ return object;
+ }
+
+ /**
+ * Writes PCA viewer attributes and computed values to an XML model object and
+ * adds it to the JalviewModel. Any exceptions are reported by logging.
+ */
+ protected void savePCA(PCAPanel panel, JalviewModel object)
+ {
+ try
+ {
+ PcaViewer viewer = new PcaViewer();
+ viewer.setHeight(panel.getHeight());
+ viewer.setWidth(panel.getWidth());
+ viewer.setXpos(panel.getX());
+ viewer.setYpos(panel.getY());
+ viewer.setTitle(panel.getTitle());
+ PCAModel pcaModel = panel.getPcaModel();
+ viewer.setScoreModelName(pcaModel.getScoreModelName());
+ viewer.setXDim(panel.getSelectedDimensionIndex(X));
+ viewer.setYDim(panel.getSelectedDimensionIndex(Y));
+ viewer.setZDim(panel.getSelectedDimensionIndex(Z));
+ viewer.setBgColour(
+ panel.getRotatableCanvas().getBackgroundColour().getRGB());
+ viewer.setScaleFactor(panel.getRotatableCanvas().getScaleFactor());
+ float[] spMin = panel.getRotatableCanvas().getSeqMin();
+ SeqPointMin spmin = new SeqPointMin();
+ spmin.setXPos(spMin[0]);
+ spmin.setYPos(spMin[1]);
+ spmin.setZPos(spMin[2]);
+ viewer.setSeqPointMin(spmin);
+ float[] spMax = panel.getRotatableCanvas().getSeqMax();
+ SeqPointMax spmax = new SeqPointMax();
+ spmax.setXPos(spMax[0]);
+ spmax.setYPos(spMax[1]);
+ spmax.setZPos(spMax[2]);
+ viewer.setSeqPointMax(spmax);
+ viewer.setShowLabels(panel.getRotatableCanvas().isShowLabels());
+ viewer.setLinkToAllViews(
+ panel.getRotatableCanvas().isApplyToAllViews());
+ SimilarityParamsI sp = pcaModel.getSimilarityParameters();
+ viewer.setIncludeGaps(sp.includeGaps());
+ viewer.setMatchGaps(sp.matchGaps());
+ viewer.setIncludeGappedColumns(sp.includeGappedColumns());
+ viewer.setDenominateByShortestLength(sp.denominateByShortestLength());
+
+ /*
+ * sequence points on display
+ */
+ for (jalview.datamodel.SequencePoint spt : pcaModel
+ .getSequencePoints())
+ {
+ SequencePoint point = new SequencePoint();
+ point.setSequenceRef(seqHash(spt.getSequence()));
+ point.setXPos(spt.coord.x);
+ point.setYPos(spt.coord.y);
+ point.setZPos(spt.coord.z);
+ viewer.getSequencePoint().add(point);
+ }
+
+ /*
+ * (end points of) axes on display
+ */
+ for (Point p : panel.getRotatableCanvas().getAxisEndPoints())
+ {
+
+ Axis axis = new Axis();
+ axis.setXPos(p.x);
+ axis.setYPos(p.y);
+ axis.setZPos(p.z);
+ viewer.getAxis().add(axis);
+ }
+
+ /*
+ * raw PCA data (note we are not restoring PCA inputs here -
+ * alignment view, score model, similarity parameters)
+ */
+ PcaDataType data = new PcaDataType();
+ viewer.setPcaData(data);
+ PCA pca = pcaModel.getPcaData();
+
+ DoubleMatrix pm = new DoubleMatrix();
+ saveDoubleMatrix(pca.getPairwiseScores(), pm);
+ data.setPairwiseMatrix(pm);
+
+ DoubleMatrix tm = new DoubleMatrix();
+ saveDoubleMatrix(pca.getTridiagonal(), tm);
+ data.setTridiagonalMatrix(tm);
+
+ DoubleMatrix eigenMatrix = new DoubleMatrix();
+ data.setEigenMatrix(eigenMatrix);
+ saveDoubleMatrix(pca.getEigenmatrix(), eigenMatrix);
+
+ object.getPcaViewer().add(viewer);
+ } catch (Throwable t)
+ {
+ Cache.log.error("Error saving PCA: " + t.getMessage());
+ }
+ }
+
+ /**
+ * Stores values from a matrix into an XML element, including (if present) the
+ * D or E vectors
+ *
+ * @param m
+ * @param xmlMatrix
+ * @see #loadDoubleMatrix(DoubleMatrix)
+ */
+ protected void saveDoubleMatrix(MatrixI m, DoubleMatrix xmlMatrix)
+ {
+ xmlMatrix.setRows(m.height());
+ xmlMatrix.setColumns(m.width());
+ for (int i = 0; i < m.height(); i++)
+ {
+ DoubleVector row = new DoubleVector();
+ for (int j = 0; j < m.width(); j++)
+ {
+ row.getV().add(m.getValue(i, j));
+ }
+ xmlMatrix.getRow().add(row);
+ }
+ if (m.getD() != null)
+ {
+ DoubleVector dVector = new DoubleVector();
+ for (double d : m.getD())
+ {
+ dVector.getV().add(d);
+ }
+ xmlMatrix.setD(dVector);
+ }
+ if (m.getE() != null)
+ {
+ DoubleVector eVector = new DoubleVector();
+ for (double e : m.getE())
+ {
+ eVector.getV().add(e);
+ }
+ xmlMatrix.setE(eVector);
+ }
+ }
+
+ /**
+ * Loads XML matrix data into a new Matrix object, including the D and/or E
+ * vectors (if present)
+ *
+ * @param mData
+ * @return
+ * @see Jalview2XML#saveDoubleMatrix(MatrixI, DoubleMatrix)
+ */
+ protected MatrixI loadDoubleMatrix(DoubleMatrix mData)
+ {
+ int rows = mData.getRows();
+ double[][] vals = new double[rows][];
+
+ for (int i = 0; i < rows; i++)
+ {
+ List dVector = mData.getRow().get(i).getV();
+ vals[i] = new double[dVector.size()];
+ int dvi = 0;
+ for (Double d : dVector)
+ {
+ vals[i][dvi++] = d;
+ }
+ }
+
+ MatrixI m = new Matrix(vals);
+
+ if (mData.getD() != null)
+ {
+ List dVector = mData.getD().getV();
+ double[] vec = new double[dVector.size()];
+ int dvi = 0;
+ for (Double d : dVector)
+ {
+ vec[dvi++] = d;
+ }
+ m.setD(vec);
+ }
+ if (mData.getE() != null)
+ {
+ List dVector = mData.getE().getV();
+ double[] vec = new double[dVector.size()];
+ int dvi = 0;
+ for (Double d : dVector)
+ {
+ vec[dvi++] = d;
+ }
+ m.setE(vec);
+ }
+
+ return m;
+ }
+
+ /**
+ * Save any Varna viewers linked to this sequence. Writes an rnaViewer element
+ * for each viewer, with
+ *
+ * - viewer geometry (position, size, split pane divider location)
+ * - index of the selected structure in the viewer (currently shows gapped
+ * or ungapped)
+ * - the id of the annotation holding RNA secondary structure
+ * - (currently only one SS is shown per viewer, may be more in future)
+ *
+ * Varna viewer state is also written out (in native Varna XML) to separate
+ * project jar entries. A separate entry is written for each RNA structure
+ * displayed, with the naming convention
+ *
+ * - rna_viewId_sequenceId_annotationId_[gapped|trimmed]
+ *
+ *
+ * @param jout
+ * @param jseq
+ * @param jds
+ * @param viewIds
+ * @param ap
+ * @param storeDataset
+ */
+ protected void saveRnaViewers(JarOutputStream jout, JSeq jseq,
+ final SequenceI jds, List viewIds, AlignmentPanel ap,
+ boolean storeDataset)
+ {
+ if (Desktop.desktop == null)
+ {
+ return;
+ }
+ JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+ for (int f = frames.length - 1; f > -1; f--)
+ {
+ if (frames[f] instanceof AppVarna)
+ {
+ AppVarna varna = (AppVarna) frames[f];
+ /*
+ * link the sequence to every viewer that is showing it and is linked to
+ * its alignment panel
+ */
+ if (varna.isListeningFor(jds) && ap == varna.getAlignmentPanel())
+ {
+ String viewId = varna.getViewId();
+ RnaViewer rna = new RnaViewer();
+ rna.setViewId(viewId);
+ rna.setTitle(varna.getTitle());
+ rna.setXpos(varna.getX());
+ rna.setYpos(varna.getY());
+ rna.setWidth(varna.getWidth());
+ rna.setHeight(varna.getHeight());
+ rna.setDividerLocation(varna.getDividerLocation());
+ rna.setSelectedRna(varna.getSelectedIndex());
+ // jseq.addRnaViewer(rna);
+ jseq.getRnaViewer().add(rna);
+
+ /*
+ * Store each Varna panel's state once in the project per sequence.
+ * First time through only (storeDataset==false)
+ */
+ // boolean storeSessions = false;
+ // String sequenceViewId = viewId + seqsToIds.get(jds);
+ // if (!storeDataset && !viewIds.contains(sequenceViewId))
+ // {
+ // viewIds.add(sequenceViewId);
+ // storeSessions = true;
+ // }
+ for (RnaModel model : varna.getModels())
+ {
+ if (model.seq == jds)
+ {
+ /*
+ * VARNA saves each view (sequence or alignment secondary
+ * structure, gapped or trimmed) as a separate XML file
+ */
+ String jarEntryName = rnaSessions.get(model);
+ if (jarEntryName == null)
+ {
+
+ String varnaStateFile = varna.getStateInfo(model.rna);
+ jarEntryName = RNA_PREFIX + viewId + "_" + nextCounter();
+ copyFileToJar(jout, varnaStateFile, jarEntryName);
+ rnaSessions.put(model, jarEntryName);
+ }
+ SecondaryStructure ss = new SecondaryStructure();
+ String annotationId = varna.getAnnotation(jds).annotationId;
+ ss.setAnnotationId(annotationId);
+ ss.setViewerState(jarEntryName);
+ ss.setGapped(model.gapped);
+ ss.setTitle(model.title);
+ // rna.addSecondaryStructure(ss);
+ rna.getSecondaryStructure().add(ss);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Copy the contents of a file to a new entry added to the output jar
+ *
+ * @param jout
+ * @param infilePath
+ * @param jarEntryName
+ */
+ protected void copyFileToJar(JarOutputStream jout, String infilePath,
+ String jarEntryName)
+ {
+ DataInputStream dis = null;
+ try
+ {
+ File file = new File(infilePath);
+ if (file.exists() && jout != null)
+ {
+ dis = new DataInputStream(new FileInputStream(file));
+ byte[] data = new byte[(int) file.length()];
+ dis.readFully(data);
+ writeJarEntry(jout, jarEntryName, data);
+ }
+ } catch (Exception ex)
+ {
+ ex.printStackTrace();
+ } finally
+ {
+ if (dis != null)
+ {
+ try
+ {
+ dis.close();
+ } catch (IOException e)
+ {
+ // ignore
+ }
+ }
+ }
+ }
+
+ /**
+ * Write the data to a new entry of given name in the output jar file
+ *
+ * @param jout
+ * @param jarEntryName
+ * @param data
+ * @throws IOException
+ */
+ protected void writeJarEntry(JarOutputStream jout, String jarEntryName,
+ byte[] data) throws IOException
+ {
+ if (jout != null)
+ {
+ System.out.println("Writing jar entry " + jarEntryName);
+ jout.putNextEntry(new JarEntry(jarEntryName));
+ DataOutputStream dout = new DataOutputStream(jout);
+ dout.write(data, 0, data.length);
+ dout.flush();
+ jout.closeEntry();
+ }
+ }
+
+ /**
+ * Save the state of a structure viewer
+ *
+ * @param ap
+ * @param jds
+ * @param pdb
+ * the archive XML element under which to save the state
+ * @param entry
+ * @param viewIds
+ * @param matchedFile
+ * @param viewFrame
+ * @return
+ */
+ protected String saveStructureState(AlignmentPanel ap, SequenceI jds,
+ Pdbids pdb, PDBEntry entry, List viewIds,
+ String matchedFile, StructureViewerBase viewFrame)
+ {
+ final AAStructureBindingModel bindingModel = viewFrame.getBinding();
+
+ /*
+ * Look for any bindings for this viewer to the PDB file of interest
+ * (including part matches excluding chain id)
+ */
+ for (int peid = 0; peid < bindingModel.getPdbCount(); peid++)
+ {
+ final PDBEntry pdbentry = bindingModel.getPdbEntry(peid);
+ final String pdbId = pdbentry.getId();
+ if (!pdbId.equals(entry.getId())
+ && !(entry.getId().length() > 4 && entry.getId().toLowerCase()
+ .startsWith(pdbId.toLowerCase())))
+ {
+ /*
+ * not interested in a binding to a different PDB entry here
+ */
+ continue;
+ }
+ if (matchedFile == null)
+ {
+ matchedFile = pdbentry.getFile();
+ }
+ else if (!matchedFile.equals(pdbentry.getFile()))
+ {
+ Cache.log.warn(
+ "Probably lost some PDB-Sequence mappings for this structure file (which apparently has same PDB Entry code): "
+ + pdbentry.getFile());
+ }
+ // record the
+ // file so we
+ // can get at it if the ID
+ // match is ambiguous (e.g.
+ // 1QIP==1qipA)
+
+ for (int smap = 0; smap < viewFrame.getBinding()
+ .getSequence()[peid].length; smap++)
+ {
+ // if (jal.findIndex(jmol.jmb.sequence[peid][smap]) > -1)
+ if (jds == viewFrame.getBinding().getSequence()[peid][smap])
+ {
+ StructureState state = new StructureState();
+ state.setVisible(true);
+ state.setXpos(viewFrame.getX());
+ state.setYpos(viewFrame.getY());
+ state.setWidth(viewFrame.getWidth());
+ state.setHeight(viewFrame.getHeight());
+ final String viewId = viewFrame.getViewId();
+ state.setViewId(viewId);
+ state.setAlignwithAlignPanel(viewFrame.isUsedforaligment(ap));
+ state.setColourwithAlignPanel(viewFrame.isUsedforcolourby(ap));
+ state.setColourByJmol(viewFrame.isColouredByViewer());
+ state.setType(viewFrame.getViewerType().toString());
+ // pdb.addStructureState(state);
+ pdb.getStructureState().add(state);
+ }
+ }
+ }
+ return matchedFile;
+ }
+
+ /**
+ * Populates the AnnotationColourScheme xml for save. This captures the
+ * settings of the options in the 'Colour by Annotation' dialog.
+ *
+ * @param acg
+ * @param userColours
+ * @param jm
+ * @return
+ */
+ private AnnotationColourScheme constructAnnotationColours(
+ AnnotationColourGradient acg, List userColours,
+ JalviewModel jm)
+ {
+ AnnotationColourScheme ac = new AnnotationColourScheme();
+ ac.setAboveThreshold(acg.getAboveThreshold());
+ ac.setThreshold(acg.getAnnotationThreshold());
+ // 2.10.2 save annotationId (unique) not annotation label
+ ac.setAnnotation(acg.getAnnotation().annotationId);
+ if (acg.getBaseColour() instanceof UserColourScheme)
+ {
+ ac.setColourScheme(
+ setUserColourScheme(acg.getBaseColour(), userColours, jm));
+ }
+ else
+ {
+ ac.setColourScheme(
+ ColourSchemeProperty.getColourName(acg.getBaseColour()));
+ }
+
+ ac.setMaxColour(acg.getMaxColour().getRGB());
+ ac.setMinColour(acg.getMinColour().getRGB());
+ ac.setPerSequence(acg.isSeqAssociated());
+ ac.setPredefinedColours(acg.isPredefinedColours());
+ return ac;
+ }
+
+ private void storeAlignmentAnnotation(AlignmentAnnotation[] aa,
+ IdentityHashMap groupRefs,
+ AlignmentViewport av, Set calcIdSet, boolean storeDS,
+ SequenceSet vamsasSet)
+ {
+
+ for (int i = 0; i < aa.length; i++)
+ {
+ Annotation an = new Annotation();
+
+ AlignmentAnnotation annotation = aa[i];
+ if (annotation.annotationId != null)
+ {
+ annotationIds.put(annotation.annotationId, annotation);
+ }
+
+ an.setId(annotation.annotationId);
+
+ an.setVisible(annotation.visible);
+
+ an.setDescription(annotation.description);
+
+ if (annotation.sequenceRef != null)
+ {
+ // 2.9 JAL-1781 xref on sequence id rather than name
+ an.setSequenceRef(seqsToIds.get(annotation.sequenceRef));
+ }
+ if (annotation.groupRef != null)
+ {
+ String groupIdr = groupRefs.get(annotation.groupRef);
+ if (groupIdr == null)
+ {
+ // make a locally unique String
+ groupRefs.put(annotation.groupRef,
+ groupIdr = ("" + System.currentTimeMillis()
+ + annotation.groupRef.getName()
+ + groupRefs.size()));
+ }
+ an.setGroupRef(groupIdr.toString());
+ }
+
+ // store all visualization attributes for annotation
+ an.setGraphHeight(annotation.graphHeight);
+ an.setCentreColLabels(annotation.centreColLabels);
+ an.setScaleColLabels(annotation.scaleColLabel);
+ an.setShowAllColLabels(annotation.showAllColLabels);
+ an.setBelowAlignment(annotation.belowAlignment);
+
+ if (annotation.graph > 0)
+ {
+ an.setGraph(true);
+ an.setGraphType(annotation.graph);
+ an.setGraphGroup(annotation.graphGroup);
+ if (annotation.getThreshold() != null)
+ {
+ ThresholdLine line = new ThresholdLine();
+ line.setLabel(annotation.getThreshold().label);
+ line.setValue(annotation.getThreshold().value);
+ line.setColour(annotation.getThreshold().colour.getRGB());
+ an.setThresholdLine(line);
+ }
+ }
+ else
+ {
+ an.setGraph(false);
+ }
+
+ an.setLabel(annotation.label);
+
+ if (annotation == av.getAlignmentQualityAnnot()
+ || annotation == av.getAlignmentConservationAnnotation()
+ || annotation == av.getAlignmentConsensusAnnotation()
+ || annotation.autoCalculated)
+ {
+ // new way of indicating autocalculated annotation -
+ an.setAutoCalculated(annotation.autoCalculated);
+ }
+ if (annotation.hasScore())
+ {
+ an.setScore(annotation.getScore());
+ }
+
+ if (annotation.getCalcId() != null)
+ {
+ calcIdSet.add(annotation.getCalcId());
+ an.setCalcId(annotation.getCalcId());
+ }
+ if (annotation.hasProperties())
+ {
+ for (String pr : annotation.getProperties())
+ {
+ jalview.xml.binding.jalview.Annotation.Property prop = new jalview.xml.binding.jalview.Annotation.Property();
+ prop.setName(pr);
+ prop.setValue(annotation.getProperty(pr));
+ // an.addProperty(prop);
+ an.getProperty().add(prop);
+ }
+ }
+
+ AnnotationElement ae;
+ if (annotation.annotations != null)
+ {
+ an.setScoreOnly(false);
+ for (int a = 0; a < annotation.annotations.length; a++)
+ {
+ if ((annotation == null) || (annotation.annotations[a] == null))
+ {
+ continue;
+ }
+
+ ae = new AnnotationElement();
+ if (annotation.annotations[a].description != null)
+ {
+ ae.setDescription(annotation.annotations[a].description);
+ }
+ if (annotation.annotations[a].displayCharacter != null)
+ {
+ ae.setDisplayCharacter(
+ annotation.annotations[a].displayCharacter);
+ }
+
+ if (!Float.isNaN(annotation.annotations[a].value))
+ {
+ ae.setValue(annotation.annotations[a].value);
+ }
+
+ ae.setPosition(a);
+ if (annotation.annotations[a].secondaryStructure > ' ')
+ {
+ ae.setSecondaryStructure(
+ annotation.annotations[a].secondaryStructure + "");
+ }
+
+ if (annotation.annotations[a].colour != null
+ && annotation.annotations[a].colour != java.awt.Color.black)
+ {
+ ae.setColour(annotation.annotations[a].colour.getRGB());
+ }
+
+ // an.addAnnotationElement(ae);
+ an.getAnnotationElement().add(ae);
+ if (annotation.autoCalculated)
+ {
+ // only write one non-null entry into the annotation row -
+ // sufficient to get the visualization attributes necessary to
+ // display data
+ continue;
+ }
+ }
+ }
+ else
+ {
+ an.setScoreOnly(true);
+ }
+ if (!storeDS || (storeDS && !annotation.autoCalculated))
+ {
+ // skip autocalculated annotation - these are only provided for
+ // alignments
+ // vamsasSet.addAnnotation(an);
+ vamsasSet.getAnnotation().add(an);
+ }
+ }
+
+ }
+
+ private CalcIdParam createCalcIdParam(String calcId, AlignViewport av)
+ {
+ AutoCalcSetting settings = av.getCalcIdSettingsFor(calcId);
+ if (settings != null)
+ {
+ CalcIdParam vCalcIdParam = new CalcIdParam();
+ vCalcIdParam.setCalcId(calcId);
+ // vCalcIdParam.addServiceURL(settings.getServiceURI());
+ vCalcIdParam.getServiceURL().add(settings.getServiceURI());
+ // generic URI allowing a third party to resolve another instance of the
+ // service used for this calculation
+ for (String url : settings.getServiceURLs())
+ {
+ // vCalcIdParam.addServiceURL(urls);
+ vCalcIdParam.getServiceURL().add(url);
+ }
+ vCalcIdParam.setVersion("1.0");
+ if (settings.getPreset() != null)
+ {
+ WsParamSetI setting = settings.getPreset();
+ vCalcIdParam.setName(setting.getName());
+ vCalcIdParam.setDescription(setting.getDescription());
+ }
+ else
+ {
+ vCalcIdParam.setName("");
+ vCalcIdParam.setDescription("Last used parameters");
+ }
+ // need to be able to recover 1) settings 2) user-defined presets or
+ // recreate settings from preset 3) predefined settings provided by
+ // service - or settings that can be transferred (or discarded)
+ vCalcIdParam.setParameters(
+ settings.getWsParamFile().replace("\n", "|\\n|"));
+ vCalcIdParam.setAutoUpdate(settings.isAutoUpdate());
+ // todo - decide if updateImmediately is needed for any projects.
+
+ return vCalcIdParam;
+ }
+ return null;
+ }
+
+ private boolean recoverCalcIdParam(CalcIdParam calcIdParam,
+ AlignViewport av)
+ {
+ if (calcIdParam.getVersion().equals("1.0"))
+ {
+ final String[] calcIds = calcIdParam.getServiceURL().toArray(new String[0]);
+ Jws2Instance service = Jws2Discoverer.getDiscoverer()
+ .getPreferredServiceFor(calcIds);
+ if (service != null)
+ {
+ WsParamSetI parmSet = null;
+ try
+ {
+ parmSet = service.getParamStore().parseServiceParameterFile(
+ calcIdParam.getName(), calcIdParam.getDescription(),
+ calcIds,
+ calcIdParam.getParameters().replace("|\\n|", "\n"));
+ } catch (IOException x)
+ {
+ warn("Couldn't parse parameter data for "
+ + calcIdParam.getCalcId(), x);
+ return false;
+ }
+ List argList = null;
+ if (calcIdParam.getName().length() > 0)
+ {
+ parmSet = service.getParamStore()
+ .getPreset(calcIdParam.getName());
+ if (parmSet != null)
+ {
+ // TODO : check we have a good match with settings in AACon -
+ // otherwise we'll need to create a new preset
+ }
+ }
+ else
+ {
+ argList = parmSet.getArguments();
+ parmSet = null;
+ }
+ AAConSettings settings = new AAConSettings(
+ calcIdParam.isAutoUpdate(), service, parmSet, argList);
+ av.setCalcIdSettingsFor(calcIdParam.getCalcId(), settings,
+ calcIdParam.isNeedsUpdate());
+ return true;
+ }
+ else
+ {
+ warn("Cannot resolve a service for the parameters used in this project. Try configuring a JABAWS server.");
+ return false;
+ }
+ }
+ throw new Error(MessageManager.formatMessage(
+ "error.unsupported_version_calcIdparam", new Object[]
+ { calcIdParam.toString() }));
+ }
+
+ /**
+ * External mapping between jalview objects and objects yielding a valid and
+ * unique object ID string. This is null for normal Jalview project IO, but
+ * non-null when a jalview project is being read or written as part of a
+ * vamsas session.
+ */
+ IdentityHashMap jv2vobj = null;
+
+ /**
+ * Construct a unique ID for jvobj using either existing bindings or if none
+ * exist, the result of the hashcode call for the object.
+ *
+ * @param jvobj
+ * jalview data object
+ * @return unique ID for referring to jvobj
+ */
+ private String makeHashCode(Object jvobj, String altCode)
+ {
+ if (jv2vobj != null)
+ {
+ Object id = jv2vobj.get(jvobj);
+ if (id != null)
+ {
+ return id.toString();
+ }
+ // check string ID mappings
+ if (jvids2vobj != null && jvobj instanceof String)
+ {
+ id = jvids2vobj.get(jvobj);
+ }
+ if (id != null)
+ {
+ return id.toString();
+ }
+ // give up and warn that something has gone wrong
+ warn("Cannot find ID for object in external mapping : " + jvobj);
+ }
+ return altCode;
+ }
+
+ /**
+ * return local jalview object mapped to ID, if it exists
+ *
+ * @param idcode
+ * (may be null)
+ * @return null or object bound to idcode
+ */
+ private Object retrieveExistingObj(String idcode)
+ {
+ if (idcode != null && vobj2jv != null)
+ {
+ return vobj2jv.get(idcode);
+ }
+ return null;
+ }
+
+ /**
+ * binding from ID strings from external mapping table to jalview data model
+ * objects.
+ */
+ private Hashtable vobj2jv;
+
+ private Sequence createVamsasSequence(String id, SequenceI jds)
+ {
+ return createVamsasSequence(true, id, jds, null);
+ }
+
+ private Sequence createVamsasSequence(boolean recurse, String id,
+ SequenceI jds, SequenceI parentseq)
+ {
+ Sequence vamsasSeq = new Sequence();
+ vamsasSeq.setId(id);
+ vamsasSeq.setName(jds.getName());
+ vamsasSeq.setSequence(jds.getSequenceAsString());
+ vamsasSeq.setDescription(jds.getDescription());
+ jalview.datamodel.DBRefEntry[] dbrefs = null;
+ if (jds.getDatasetSequence() != null)
+ {
+ vamsasSeq.setDsseqid(seqHash(jds.getDatasetSequence()));
+ }
+ else
+ {
+ // seqId==dsseqid so we can tell which sequences really are
+ // dataset sequences only
+ vamsasSeq.setDsseqid(id);
+ dbrefs = jds.getDBRefs();
+ if (parentseq == null)
+ {
+ parentseq = jds;
+ }
+ }
+ if (dbrefs != null)
+ {
+ for (int d = 0; d < dbrefs.length; d++)
+ {
+ DBRef dbref = new DBRef();
+ dbref.setSource(dbrefs[d].getSource());
+ dbref.setVersion(dbrefs[d].getVersion());
+ dbref.setAccessionId(dbrefs[d].getAccessionId());
+ if (dbrefs[d].hasMap())
+ {
+ Mapping mp = createVamsasMapping(dbrefs[d].getMap(), parentseq,
+ jds, recurse);
+ dbref.setMapping(mp);
+ }
+ // vamsasSeq.addDBRef(dbref);
+ vamsasSeq.getDBRef().add(dbref);
+ }
+ }
+ return vamsasSeq;
+ }
+
+ private Mapping createVamsasMapping(jalview.datamodel.Mapping jmp,
+ SequenceI parentseq, SequenceI jds, boolean recurse)
+ {
+ Mapping mp = null;
+ if (jmp.getMap() != null)
+ {
+ mp = new Mapping();
+
+ jalview.util.MapList mlst = jmp.getMap();
+ List r = mlst.getFromRanges();
+ for (int[] range : r)
+ {
+ MapListFrom mfrom = new MapListFrom();
+ mfrom.setStart(range[0]);
+ mfrom.setEnd(range[1]);
+ // mp.addMapListFrom(mfrom);
+ mp.getMapListFrom().add(mfrom);
+ }
+ r = mlst.getToRanges();
+ for (int[] range : r)
+ {
+ MapListTo mto = new MapListTo();
+ mto.setStart(range[0]);
+ mto.setEnd(range[1]);
+ // mp.addMapListTo(mto);
+ mp.getMapListTo().add(mto);
+ }
+ mp.setMapFromUnit(BigInteger.valueOf(mlst.getFromRatio()));
+ mp.setMapToUnit(BigInteger.valueOf(mlst.getToRatio()));
+ if (jmp.getTo() != null)
+ {
+ // MappingChoice mpc = new MappingChoice();
+
+ // check/create ID for the sequence referenced by getTo()
+
+ String jmpid = "";
+ SequenceI ps = null;
+ if (parentseq != jmp.getTo()
+ && parentseq.getDatasetSequence() != jmp.getTo())
+ {
+ // chaining dbref rather than a handshaking one
+ jmpid = seqHash(ps = jmp.getTo());
+ }
+ else
+ {
+ jmpid = seqHash(ps = parentseq);
+ }
+ // mpc.setDseqFor(jmpid);
+ mp.setDseqFor(jmpid);
+ if (!seqRefIds.containsKey(jmpid))
+ {
+ jalview.bin.Cache.log.debug("creatign new DseqFor ID");
+ seqRefIds.put(jmpid, ps);
+ }
+ else
+ {
+ jalview.bin.Cache.log.debug("reusing DseqFor ID");
+ }
+
+ // mp.setMappingChoice(mpc);
+ }
+ }
+ return mp;
+ }
+
+ String setUserColourScheme(jalview.schemes.ColourSchemeI cs,
+ List userColours, JalviewModel jm)
+ {
+ String id = null;
+ jalview.schemes.UserColourScheme ucs = (jalview.schemes.UserColourScheme) cs;
+ boolean newucs = false;
+ if (!userColours.contains(ucs))
+ {
+ userColours.add(ucs);
+ newucs = true;
+ }
+ id = "ucs" + userColours.indexOf(ucs);
+ if (newucs)
+ {
+ // actually create the scheme's entry in the XML model
+ java.awt.Color[] colours = ucs.getColours();
+ UserColours uc = new UserColours();
+ // UserColourScheme jbucs = new UserColourScheme();
+ JalviewUserColours jbucs = new JalviewUserColours();
+
+ for (int i = 0; i < colours.length; i++)
+ {
+ Colour col = new Colour();
+ col.setName(ResidueProperties.aa[i]);
+ col.setRGB(jalview.util.Format.getHexString(colours[i]));
+ // jbucs.addColour(col);
+ jbucs.getColour().add(col);
+ }
+ if (ucs.getLowerCaseColours() != null)
+ {
+ colours = ucs.getLowerCaseColours();
+ for (int i = 0; i < colours.length; i++)
+ {
+ Colour col = new Colour();
+ col.setName(ResidueProperties.aa[i].toLowerCase());
+ col.setRGB(jalview.util.Format.getHexString(colours[i]));
+ // jbucs.addColour(col);
+ jbucs.getColour().add(col);
+ }
+ }
+
+ uc.setId(id);
+ uc.setUserColourScheme(jbucs);
+ // jm.addUserColours(uc);
+ jm.getUserColours().add(uc);
+ }
+
+ return id;
+ }
+
+ jalview.schemes.UserColourScheme getUserColourScheme(
+ JalviewModel jm, String id)
+ {
+ List uc = jm.getUserColours();
+ UserColours colours = null;
+/*
+ for (int i = 0; i < uc.length; i++)
+ {
+ if (uc[i].getId().equals(id))
+ {
+ colours = uc[i];
+ break;
+ }
+ }
+*/
+ for (UserColours c : uc)
+ {
+ if (c.getId().equals(id))
+ {
+ colours = c;
+ break;
+ }
+ }
+
+ java.awt.Color[] newColours = new java.awt.Color[24];
+
+ for (int i = 0; i < 24; i++)
+ {
+ newColours[i] = new java.awt.Color(Integer.parseInt(
+ // colours.getUserColourScheme().getColour(i).getRGB(), 16));
+ colours.getUserColourScheme().getColour().get(i).getRGB(),
+ 16));
+ }
+
+ jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme(
+ newColours);
+
+ if (colours.getUserColourScheme().getColour().size()/*Count()*/ > 24)
+ {
+ newColours = new java.awt.Color[23];
+ for (int i = 0; i < 23; i++)
+ {
+ newColours[i] = new java.awt.Color(Integer.parseInt(
+ colours.getUserColourScheme().getColour().get(i + 24)
+ .getRGB(),
+ 16));
+ }
+ ucs.setLowerCaseColours(newColours);
+ }
+
+ return ucs;
+ }
+
+ /**
+ * contains last error message (if any) encountered by XML loader.
+ */
+ String errorMessage = null;
+
+ /**
+ * flag to control whether the Jalview2XML_V1 parser should be deferred to if
+ * exceptions are raised during project XML parsing
+ */
+ public boolean attemptversion1parse = false;
+
+ /**
+ * Load a jalview project archive from a jar file
+ *
+ * @param file
+ * - HTTP URL or filename
+ */
+ public AlignFrame loadJalviewAlign(final String file)
+ {
+
+ jalview.gui.AlignFrame af = null;
+
+ try
+ {
+ // create list to store references for any new Jmol viewers created
+ newStructureViewers = new Vector<>();
+ // UNMARSHALLER SEEMS TO CLOSE JARINPUTSTREAM, MOST ANNOYING
+ // Workaround is to make sure caller implements the JarInputStreamProvider
+ // interface
+ // so we can re-open the jar input stream for each entry.
+
+ jarInputStreamProvider jprovider = createjarInputStreamProvider(file);
+ af = loadJalviewAlign(jprovider);
+ if (af != null)
+ {
+ af.setMenusForViewport();
+ }
+ } catch (MalformedURLException e)
+ {
+ errorMessage = "Invalid URL format for '" + file + "'";
+ reportErrors();
+ } finally
+ {
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ setLoadingFinishedForNewStructureViewers();
+ };
+ });
+ } catch (Exception x)
+ {
+ System.err.println("Error loading alignment: " + x.getMessage());
+ }
+ }
+ return af;
+ }
+
+ private jarInputStreamProvider createjarInputStreamProvider(
+ final String file) throws MalformedURLException
+ {
+ URL url = null;
+ errorMessage = null;
+ uniqueSetSuffix = null;
+ seqRefIds = null;
+ viewportsAdded.clear();
+ frefedSequence = null;
+
+ if (file.startsWith("http://"))
+ {
+ url = new URL(file);
+ }
+ final URL _url = url;
+ return new jarInputStreamProvider()
+ {
+
+ @Override
+ public JarInputStream getJarInputStream() throws IOException
+ {
+ if (_url != null)
+ {
+ return new JarInputStream(_url.openStream());
+ }
+ else
+ {
+ return new JarInputStream(new FileInputStream(file));
+ }
+ }
+
+ @Override
+ public String getFilename()
+ {
+ return file;
+ }
+ };
+ }
+
+ /**
+ * Recover jalview session from a jalview project archive. Caller may
+ * initialise uniqueSetSuffix, seqRefIds, viewportsAdded and frefedSequence
+ * themselves. Any null fields will be initialised with default values,
+ * non-null fields are left alone.
+ *
+ * @param jprovider
+ * @return
+ */
+ public AlignFrame loadJalviewAlign(final jarInputStreamProvider jprovider)
+ {
+ errorMessage = null;
+ if (uniqueSetSuffix == null)
+ {
+ uniqueSetSuffix = System.currentTimeMillis() % 100000 + "";
+ }
+ if (seqRefIds == null)
+ {
+ initSeqRefs();
+ }
+ AlignFrame af = null, _af = null;
+ IdentityHashMap importedDatasets = new IdentityHashMap<>();
+ Map gatherToThisFrame = new HashMap<>();
+ final String file = jprovider.getFilename();
+ try
+ {
+ JarInputStream jin = null;
+ JarEntry jarentry = null;
+ int entryCount = 1;
+
+ do
+ {
+ jin = jprovider.getJarInputStream();
+ for (int i = 0; i < entryCount; i++)
+ {
+ jarentry = jin.getNextJarEntry();
+ }
+
+ if (jarentry != null && jarentry.getName().endsWith(".xml"))
+ {
+ InputStreamReader in = new InputStreamReader(jin, UTF_8);
+ // JalviewModel object = new JalviewModel();
+
+ JAXBContext jc = JAXBContext
+ .newInstance("jalview.xml.binding.jalview");
+ XMLStreamReader streamReader = XMLInputFactory.newInstance()
+ .createXMLStreamReader(jin);
+ javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
+ JAXBElement jbe = um
+ .unmarshal(streamReader, JalviewModel.class);
+ JalviewModel object = jbe.getValue();
+
+ /*
+ Unmarshaller unmar = new Unmarshaller(object);
+ unmar.setValidation(false);
+ object = (JalviewModel) unmar.unmarshal(in);
+ */
+ if (true) // !skipViewport(object))
+ {
+ _af = loadFromObject(object, file, true, jprovider);
+ if (_af != null && object.getViewport().size() > 0)
+ // getJalviewModelSequence().getViewportCount() > 0)
+ {
+ if (af == null)
+ {
+ // store a reference to the first view
+ af = _af;
+ }
+ if (_af.getViewport().isGatherViewsHere())
+ {
+ // if this is a gathered view, keep its reference since
+ // after gathering views, only this frame will remain
+ af = _af;
+ gatherToThisFrame.put(_af.getViewport().getSequenceSetId(),
+ _af);
+ }
+ // Save dataset to register mappings once all resolved
+ importedDatasets.put(
+ af.getViewport().getAlignment().getDataset(),
+ af.getViewport().getAlignment().getDataset());
+ }
+ }
+ entryCount++;
+ }
+ else if (jarentry != null)
+ {
+ // Some other file here.
+ entryCount++;
+ }
+ } while (jarentry != null);
+ resolveFrefedSequences();
+ } catch (IOException ex)
+ {
+ ex.printStackTrace();
+ errorMessage = "Couldn't locate Jalview XML file : " + file;
+ System.err.println(
+ "Exception whilst loading jalview XML file : " + ex + "\n");
+ } catch (Exception ex)
+ {
+ System.err.println("Parsing as Jalview Version 2 file failed.");
+ ex.printStackTrace(System.err);
+ if (attemptversion1parse)
+ {
+ // Is Version 1 Jar file?
+ try
+ {
+ af = new Jalview2XML_V1(raiseGUI).LoadJalviewAlign(jprovider);
+ } catch (Exception ex2)
+ {
+ System.err.println("Exception whilst loading as jalviewXMLV1:");
+ ex2.printStackTrace();
+ af = null;
+ }
+ }
+ if (Desktop.instance != null)
+ {
+ Desktop.instance.stopLoading();
+ }
+ if (af != null)
+ {
+ System.out.println("Successfully loaded archive file");
+ return af;
+ }
+ ex.printStackTrace();
+
+ System.err.println(
+ "Exception whilst loading jalview XML file : " + ex + "\n");
+ } catch (OutOfMemoryError e)
+ {
+ // Don't use the OOM Window here
+ errorMessage = "Out of memory loading jalview XML file";
+ System.err.println("Out of memory whilst loading jalview XML file");
+ e.printStackTrace();
+ }
+
+ /*
+ * Regather multiple views (with the same sequence set id) to the frame (if
+ * any) that is flagged as the one to gather to, i.e. convert them to tabbed
+ * views instead of separate frames. Note this doesn't restore a state where
+ * some expanded views in turn have tabbed views - the last "first tab" read
+ * in will play the role of gatherer for all.
+ */
+ for (AlignFrame fr : gatherToThisFrame.values())
+ {
+ Desktop.instance.gatherViews(fr);
+ }
+
+ restoreSplitFrames();
+ for (AlignmentI ds : importedDatasets.keySet())
+ {
+ if (ds.getCodonFrames() != null)
+ {
+ StructureSelectionManager
+ .getStructureSelectionManager(Desktop.instance)
+ .registerMappings(ds.getCodonFrames());
+ }
+ }
+ if (errorMessage != null)
+ {
+ reportErrors();
+ }
+
+ if (Desktop.instance != null)
+ {
+ Desktop.instance.stopLoading();
+ }
+
+ return af;
+ }
+
+ /**
+ * Try to reconstruct and display SplitFrame windows, where each contains
+ * complementary dna and protein alignments. Done by pairing up AlignFrame
+ * objects (created earlier) which have complementary viewport ids associated.
+ */
+ protected void restoreSplitFrames()
+ {
+ List gatherTo = new ArrayList<>();
+ List addedToSplitFrames = new ArrayList<>();
+ Map dna = new HashMap<>();
+
+ /*
+ * Identify the DNA alignments
+ */
+ for (Entry candidate : splitFrameCandidates
+ .entrySet())
+ {
+ AlignFrame af = candidate.getValue();
+ if (af.getViewport().getAlignment().isNucleotide())
+ {
+ dna.put(candidate.getKey().getId(), af);
+ }
+ }
+
+ /*
+ * Try to match up the protein complements
+ */
+ for (Entry candidate : splitFrameCandidates
+ .entrySet())
+ {
+ AlignFrame af = candidate.getValue();
+ if (!af.getViewport().getAlignment().isNucleotide())
+ {
+ String complementId = candidate.getKey().getComplementId();
+ // only non-null complements should be in the Map
+ if (complementId != null && dna.containsKey(complementId))
+ {
+ final AlignFrame dnaFrame = dna.get(complementId);
+ SplitFrame sf = createSplitFrame(dnaFrame, af);
+ addedToSplitFrames.add(dnaFrame);
+ addedToSplitFrames.add(af);
+ dnaFrame.setMenusForViewport();
+ af.setMenusForViewport();
+ if (af.getViewport().isGatherViewsHere())
+ {
+ gatherTo.add(sf);
+ }
+ }
+ }
+ }
+
+ /*
+ * Open any that we failed to pair up (which shouldn't happen!) as
+ * standalone AlignFrame's.
+ */
+ for (Entry candidate : splitFrameCandidates
+ .entrySet())
+ {
+ AlignFrame af = candidate.getValue();
+ if (!addedToSplitFrames.contains(af))
+ {
+ Viewport view = candidate.getKey();
+ Desktop.addInternalFrame(af, view.getTitle(),
+ safeInt(view.getWidth()), safeInt(view.getHeight()));
+ af.setMenusForViewport();
+ System.err.println("Failed to restore view " + view.getTitle()
+ + " to split frame");
+ }
+ }
+
+ /*
+ * Gather back into tabbed views as flagged.
+ */
+ for (SplitFrame sf : gatherTo)
+ {
+ Desktop.instance.gatherViews(sf);
+ }
+
+ splitFrameCandidates.clear();
+ }
+
+ /**
+ * Construct and display one SplitFrame holding DNA and protein alignments.
+ *
+ * @param dnaFrame
+ * @param proteinFrame
+ * @return
+ */
+ protected SplitFrame createSplitFrame(AlignFrame dnaFrame,
+ AlignFrame proteinFrame)
+ {
+ SplitFrame splitFrame = new SplitFrame(dnaFrame, proteinFrame);
+ String title = MessageManager.getString("label.linked_view_title");
+ int width = (int) dnaFrame.getBounds().getWidth();
+ int height = (int) (dnaFrame.getBounds().getHeight()
+ + proteinFrame.getBounds().getHeight() + 50);
+
+ /*
+ * SplitFrame location is saved to both enclosed frames
+ */
+ splitFrame.setLocation(dnaFrame.getX(), dnaFrame.getY());
+ Desktop.addInternalFrame(splitFrame, title, width, height);
+
+ /*
+ * And compute cDNA consensus (couldn't do earlier with consensus as
+ * mappings were not yet present)
+ */
+ proteinFrame.getViewport().alignmentChanged(proteinFrame.alignPanel);
+
+ return splitFrame;
+ }
+
+ /**
+ * check errorMessage for a valid error message and raise an error box in the
+ * GUI or write the current errorMessage to stderr and then clear the error
+ * state.
+ */
+ protected void reportErrors()
+ {
+ reportErrors(false);
+ }
+
+ protected void reportErrors(final boolean saving)
+ {
+ if (errorMessage != null)
+ {
+ final String finalErrorMessage = errorMessage;
+ if (raiseGUI)
+ {
+ javax.swing.SwingUtilities.invokeLater(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+ finalErrorMessage,
+ "Error " + (saving ? "saving" : "loading")
+ + " Jalview file",
+ JvOptionPane.WARNING_MESSAGE);
+ }
+ });
+ }
+ else
+ {
+ System.err.println("Problem loading Jalview file: " + errorMessage);
+ }
+ }
+ errorMessage = null;
+ }
+
+ Map alreadyLoadedPDB = new HashMap<>();
+
+ /**
+ * when set, local views will be updated from view stored in JalviewXML
+ * Currently (28th Sep 2008) things will go horribly wrong in vamsas document
+ * sync if this is set to true.
+ */
+ private final boolean updateLocalViews = false;
+
+ /**
+ * Returns the path to a temporary file holding the PDB file for the given PDB
+ * id. The first time of asking, searches for a file of that name in the
+ * Jalview project jar, and copies it to a new temporary file. Any repeat
+ * requests just return the path to the file previously created.
+ *
+ * @param jprovider
+ * @param pdbId
+ * @return
+ */
+ String loadPDBFile(jarInputStreamProvider jprovider, String pdbId,
+ String origFile)
+ {
+ if (alreadyLoadedPDB.containsKey(pdbId))
+ {
+ return alreadyLoadedPDB.get(pdbId).toString();
+ }
+
+ String tempFile = copyJarEntry(jprovider, pdbId, "jalview_pdb",
+ origFile);
+ if (tempFile != null)
+ {
+ alreadyLoadedPDB.put(pdbId, tempFile);
+ }
+ return tempFile;
+ }
+
+ /**
+ * Copies the jar entry of given name to a new temporary file and returns the
+ * path to the file, or null if the entry is not found.
+ *
+ * @param jprovider
+ * @param jarEntryName
+ * @param prefix
+ * a prefix for the temporary file name, must be at least three
+ * characters long
+ * @param origFile
+ * null or original file - so new file can be given the same suffix
+ * as the old one
+ * @return
+ */
+ protected String copyJarEntry(jarInputStreamProvider jprovider,
+ String jarEntryName, String prefix, String origFile)
+ {
+ BufferedReader in = null;
+ PrintWriter out = null;
+ String suffix = ".tmp";
+ if (origFile == null)
+ {
+ origFile = jarEntryName;
+ }
+ int sfpos = origFile.lastIndexOf(".");
+ if (sfpos > -1 && sfpos < (origFile.length() - 3))
+ {
+ suffix = "." + origFile.substring(sfpos + 1);
+ }
+ try
+ {
+ JarInputStream jin = jprovider.getJarInputStream();
+ /*
+ * if (jprovider.startsWith("http://")) { jin = new JarInputStream(new
+ * URL(jprovider).openStream()); } else { jin = new JarInputStream(new
+ * FileInputStream(jprovider)); }
+ */
+
+ JarEntry entry = null;
+ do
+ {
+ entry = jin.getNextJarEntry();
+ } while (entry != null && !entry.getName().equals(jarEntryName));
+ if (entry != null)
+ {
+ in = new BufferedReader(new InputStreamReader(jin, UTF_8));
+ File outFile = File.createTempFile(prefix, suffix);
+ outFile.deleteOnExit();
+ out = new PrintWriter(new FileOutputStream(outFile));
+ String data;
+
+ while ((data = in.readLine()) != null)
+ {
+ out.println(data);
+ }
+ out.flush();
+ String t = outFile.getAbsolutePath();
+ return t;
+ }
+ else
+ {
+ warn("Couldn't find entry in Jalview Jar for " + jarEntryName);
+ }
+ } catch (Exception ex)
+ {
+ ex.printStackTrace();
+ } finally
+ {
+ if (in != null)
+ {
+ try
+ {
+ in.close();
+ } catch (IOException e)
+ {
+ // ignore
+ }
+ }
+ if (out != null)
+ {
+ out.close();
+ }
+ }
+
+ return null;
+ }
+
+ private class JvAnnotRow
+ {
+ public JvAnnotRow(int i, AlignmentAnnotation jaa)
+ {
+ order = i;
+ template = jaa;
+ }
+
+ /**
+ * persisted version of annotation row from which to take vis properties
+ */
+ public jalview.datamodel.AlignmentAnnotation template;
+
+ /**
+ * original position of the annotation row in the alignment
+ */
+ public int order;
+ }
+
+ /**
+ * Load alignment frame from jalview XML DOM object
+ *
+ * @param jalviewModel
+ * DOM
+ * @param file
+ * filename source string
+ * @param loadTreesAndStructures
+ * when false only create Viewport
+ * @param jprovider
+ * data source provider
+ * @return alignment frame created from view stored in DOM
+ */
+ AlignFrame loadFromObject(JalviewModel jalviewModel, String file,
+ boolean loadTreesAndStructures, jarInputStreamProvider jprovider)
+ {
+ SequenceSet vamsasSet = jalviewModel.getVamsasModel().getSequenceSet().get(0);
+ List vamsasSeqs = vamsasSet.getSequence();
+
+ // JalviewModelSequence jms = object.getJalviewModelSequence();
+
+ // Viewport view = (jms.getViewportCount() > 0) ? jms.getViewport(0)
+ // : null;
+ Viewport view = (jalviewModel.getViewport().size() > 0)
+ ? jalviewModel.getViewport().get(0)
+ : null;
+
+ // ////////////////////////////////
+ // INITIALISE ALIGNMENT SEQUENCESETID AND VIEWID
+ //
+ //
+ // If we just load in the same jar file again, the sequenceSetId
+ // will be the same, and we end up with multiple references
+ // to the same sequenceSet. We must modify this id on load
+ // so that each load of the file gives a unique id
+
+ /**
+ * used to resolve correct alignment dataset for alignments with multiple
+ * views
+ */
+ String uniqueSeqSetId = null;
+ String viewId = null;
+ if (view != null)
+ {
+ uniqueSeqSetId = view.getSequenceSetId() + uniqueSetSuffix;
+ viewId = (view.getId() == null ? null
+ : view.getId() + uniqueSetSuffix);
+ }
+
+ // ////////////////////////////////
+ // LOAD SEQUENCES
+
+ List hiddenSeqs = null;
+
+ List tmpseqs = new ArrayList<>();
+
+ boolean multipleView = false;
+ SequenceI referenceseqForView = null;
+ // JSeq[] jseqs = object.getJalviewModelSequence().getJSeq();
+ List jseqs = jalviewModel.getJSeq();
+ int vi = 0; // counter in vamsasSeq array
+ for (int i = 0; i < jseqs.size(); i++)
+ {
+ JSeq jseq = jseqs.get(i);
+ String seqId = jseq.getId();
+
+ SequenceI tmpSeq = seqRefIds.get(seqId);
+ if (tmpSeq != null)
+ {
+ if (!incompleteSeqs.containsKey(seqId))
+ {
+ // may not need this check, but keep it for at least 2.9,1 release
+ if (tmpSeq.getStart() != jseq.getStart()
+ || tmpSeq.getEnd() != jseq.getEnd())
+ {
+ System.err.println(
+ "Warning JAL-2154 regression: updating start/end for sequence "
+ + tmpSeq.toString() + " to " + jseq);
+ }
+ }
+ else
+ {
+ incompleteSeqs.remove(seqId);
+ }
+ if (vamsasSeqs.size() > vi
+ && vamsasSeqs.get(vi).getId().equals(seqId))
+ {
+ // most likely we are reading a dataset XML document so
+ // update from vamsasSeq section of XML for this sequence
+ tmpSeq.setName(vamsasSeqs.get(vi).getName());
+ tmpSeq.setDescription(vamsasSeqs.get(vi).getDescription());
+ tmpSeq.setSequence(vamsasSeqs.get(vi).getSequence());
+ vi++;
+ }
+ else
+ {
+ // reading multiple views, so vamsasSeq set is a subset of JSeq
+ multipleView = true;
+ }
+ tmpSeq.setStart(jseq.getStart());
+ tmpSeq.setEnd(jseq.getEnd());
+ tmpseqs.add(tmpSeq);
+ }
+ else
+ {
+ Sequence vamsasSeq = vamsasSeqs.get(vi);
+ tmpSeq = new jalview.datamodel.Sequence(vamsasSeq.getName(),
+ vamsasSeq.getSequence());
+ tmpSeq.setDescription(vamsasSeq.getDescription());
+ tmpSeq.setStart(jseq.getStart());
+ tmpSeq.setEnd(jseq.getEnd());
+ tmpSeq.setVamsasId(uniqueSetSuffix + seqId);
+ seqRefIds.put(vamsasSeq.getId(), tmpSeq);
+ tmpseqs.add(tmpSeq);
+ vi++;
+ }
+
+ if (safeBoolean(jseq.isViewreference()))
+ {
+ referenceseqForView = tmpseqs.get(tmpseqs.size() - 1);
+ }
+
+ if (jseq.isHidden() != null && jseq.isHidden().booleanValue())
+ {
+ if (hiddenSeqs == null)
+ {
+ hiddenSeqs = new ArrayList<>();
+ }
+
+ hiddenSeqs.add(tmpSeq);
+ }
+ }
+
+ // /
+ // Create the alignment object from the sequence set
+ // ///////////////////////////////
+ SequenceI[] orderedSeqs = tmpseqs
+ .toArray(new SequenceI[tmpseqs.size()]);
+
+ AlignmentI al = null;
+ // so we must create or recover the dataset alignment before going further
+ // ///////////////////////////////
+ if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "")
+ {
+ // older jalview projects do not have a dataset - so creat alignment and
+ // dataset
+ al = new Alignment(orderedSeqs);
+ al.setDataset(null);
+ }
+ else
+ {
+ boolean isdsal = jalviewModel.getViewport().isEmpty();
+ if (isdsal)
+ {
+ // we are importing a dataset record, so
+ // recover reference to an alignment already materialsed as dataset
+ al = getDatasetFor(vamsasSet.getDatasetId());
+ }
+ if (al == null)
+ {
+ // materialse the alignment
+ al = new Alignment(orderedSeqs);
+ }
+ if (isdsal)
+ {
+ addDatasetRef(vamsasSet.getDatasetId(), al);
+ }
+
+ // finally, verify all data in vamsasSet is actually present in al
+ // passing on flag indicating if it is actually a stored dataset
+ recoverDatasetFor(vamsasSet, al, isdsal, uniqueSeqSetId);
+ }
+
+ if (referenceseqForView != null)
+ {
+ al.setSeqrep(referenceseqForView);
+ }
+ // / Add the alignment properties
+ for (int i = 0; i < vamsasSet.getSequenceSetProperties().size(); i++)
+ {
+ SequenceSetProperties ssp = vamsasSet.getSequenceSetProperties()
+ .get(i);
+ al.setProperty(ssp.getKey(), ssp.getValue());
+ }
+
+ // ///////////////////////////////
+
+ Hashtable pdbloaded = new Hashtable(); // TODO nothing writes to this??
+ if (!multipleView)
+ {
+ // load sequence features, database references and any associated PDB
+ // structures for the alignment
+ //
+ // prior to 2.10, this part would only be executed the first time a
+ // sequence was encountered, but not afterwards.
+ // now, for 2.10 projects, this is also done if the xml doc includes
+ // dataset sequences not actually present in any particular view.
+ //
+ for (int i = 0; i < vamsasSeqs.size(); i++)
+ {
+ JSeq jseq = jseqs.get(i);
+ if (jseq.getFeatures().size() > 0)
+ {
+ List features = jseq.getFeatures();
+ for (int f = 0; f < features.size(); f++)
+ {
+ Feature feat = features.get(f);
+ SequenceFeature sf = new SequenceFeature(feat.getType(),
+ feat.getDescription(), feat.getBegin(), feat.getEnd(),
+ safeFloat(feat.getScore()), feat.getFeatureGroup());
+ sf.setStatus(feat.getStatus());
+
+ /*
+ * load any feature attributes - include map-valued attributes
+ */
+ Map> mapAttributes = new HashMap<>();
+ for (int od = 0; od < feat.getOtherData().size(); od++)
+ {
+ OtherData keyValue = feat.getOtherData().get(od);
+ String attributeName = keyValue.getKey();
+ String attributeValue = keyValue.getValue();
+ if (attributeName.startsWith("LINK"))
+ {
+ sf.addLink(attributeValue);
+ }
+ else
+ {
+ String subAttribute = keyValue.getKey2();
+ if (subAttribute == null)
+ {
+ // simple string-valued attribute
+ sf.setValue(attributeName, attributeValue);
+ }
+ else
+ {
+ // attribute 'key' has sub-attribute 'key2'
+ if (!mapAttributes.containsKey(attributeName))
+ {
+ mapAttributes.put(attributeName, new HashMap<>());
+ }
+ mapAttributes.get(attributeName).put(subAttribute,
+ attributeValue);
+ }
+ }
+ }
+ for (Entry> mapAttribute : mapAttributes
+ .entrySet())
+ {
+ sf.setValue(mapAttribute.getKey(), mapAttribute.getValue());
+ }
+
+ // adds feature to datasequence's feature set (since Jalview 2.10)
+ al.getSequenceAt(i).addSequenceFeature(sf);
+ }
+ }
+ if (vamsasSeqs.get(i).getDBRef().size() > 0)
+ {
+ // adds dbrefs to datasequence's set (since Jalview 2.10)
+ addDBRefs(
+ al.getSequenceAt(i).getDatasetSequence() == null
+ ? al.getSequenceAt(i)
+ : al.getSequenceAt(i).getDatasetSequence(),
+ vamsasSeqs.get(i));
+ }
+ if (jseq.getPdbids().size() > 0)
+ {
+ List ids = jseq.getPdbids();
+ for (int p = 0; p < ids.size(); p++)
+ {
+ Pdbids pdbid = ids.get(p);
+ jalview.datamodel.PDBEntry entry = new jalview.datamodel.PDBEntry();
+ entry.setId(pdbid.getId());
+ if (pdbid.getType() != null)
+ {
+ if (PDBEntry.Type.getType(pdbid.getType()) != null)
+ {
+ entry.setType(PDBEntry.Type.getType(pdbid.getType()));
+ }
+ else
+ {
+ entry.setType(PDBEntry.Type.FILE);
+ }
+ }
+ // jprovider is null when executing 'New View'
+ if (pdbid.getFile() != null && jprovider != null)
+ {
+ if (!pdbloaded.containsKey(pdbid.getFile()))
+ {
+ entry.setFile(loadPDBFile(jprovider, pdbid.getId(),
+ pdbid.getFile()));
+ }
+ else
+ {
+ entry.setFile(pdbloaded.get(pdbid.getId()).toString());
+ }
+ }
+ /*
+ if (pdbid.getPdbentryItem() != null)
+ {
+ for (PdbentryItem item : pdbid.getPdbentryItem())
+ {
+ for (Property pr : item.getProperty())
+ {
+ entry.setProperty(pr.getName(), pr.getValue());
+ }
+ }
+ }
+ */
+ for (Property prop : pdbid.getProperty())
+ {
+ entry.setProperty(prop.getName(), prop.getValue());
+ }
+ StructureSelectionManager
+ .getStructureSelectionManager(Desktop.instance)
+ .registerPDBEntry(entry);
+ // adds PDBEntry to datasequence's set (since Jalview 2.10)
+ if (al.getSequenceAt(i).getDatasetSequence() != null)
+ {
+ al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
+ }
+ else
+ {
+ al.getSequenceAt(i).addPDBId(entry);
+ }
+ }
+ }
+ }
+ } // end !multipleview
+
+ // ///////////////////////////////
+ // LOAD SEQUENCE MAPPINGS
+
+ if (vamsasSet.getAlcodonFrame().size() > 0)
+ {
+ // TODO Potentially this should only be done once for all views of an
+ // alignment
+ List alc = vamsasSet.getAlcodonFrame();
+ for (int i = 0; i < alc.size(); i++)
+ {
+ AlignedCodonFrame cf = new AlignedCodonFrame();
+ if (alc.get(i).getAlcodMap().size() > 0)
+ {
+ List maps = alc.get(i).getAlcodMap();
+ for (int m = 0; m < maps.size(); m++)
+ {
+ AlcodMap map = maps.get(m);
+ SequenceI dnaseq = seqRefIds.get(map.getDnasq());
+ // Load Mapping
+ jalview.datamodel.Mapping mapping = null;
+ // attach to dna sequence reference.
+ if (map.getMapping() != null)
+ {
+ mapping = addMapping(map.getMapping());
+ if (dnaseq != null && mapping.getTo() != null)
+ {
+ cf.addMap(dnaseq, mapping.getTo(), mapping.getMap());
+ }
+ else
+ {
+ // defer to later
+ frefedSequence.add(
+ newAlcodMapRef(map.getDnasq(), cf, mapping));
+ }
+ }
+ }
+ al.addCodonFrame(cf);
+ }
+ }
+ }
+
+ // ////////////////////////////////
+ // LOAD ANNOTATIONS
+ List |