From: gmungoc Date: Mon, 7 May 2018 13:59:27 +0000 (+0100) Subject: Merge branch 'releases/Release_2_10_4_Branch' into develop X-Git-Tag: Release_2_11_0~42 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=9f1f7839cfd50555efd3af1ff4102348f950c563;hp=-c;p=jalview.git Merge branch 'releases/Release_2_10_4_Branch' into develop Conflicts: .classpath --- 9f1f7839cfd50555efd3af1ff4102348f950c563 diff --combined .classpath index f4b8cf8,3a05b47..0cdc4b9 --- a/.classpath +++ b/.classpath @@@ -48,6 -48,8 +48,7 @@@ - + @@@ -66,7 -68,6 +67,7 @@@ + diff --combined resources/lang/Messages.properties index 3f5aa94,a80ac17..08a416c --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@@ -242,6 -242,7 +242,6 @@@ label.documentation = Documentatio label.about = About... label.show_sequence_limits = Show Sequence Limits action.feature_settings = Feature Settings... -label.feature_settings = Feature Settings label.all_columns = All Columns label.all_sequences = All Sequences label.selected_columns = Selected Columns @@@ -273,7 -274,6 +273,7 @@@ label.chimera_missing = Chimera structu label.chimera_failed = Error opening Chimera - is it installed?\nCheck path in Preferences, Structure label.min_colour = Minimum Colour label.max_colour = Maximum Colour +label.no_colour = No Colour label.use_original_colours = Use Original Colours label.threshold_minmax = Threshold is min/max label.represent_group_with = Represent Group with {0} @@@ -281,9 -281,9 +281,9 @@@ label.selection = Selectio label.group_colour = Group Colour label.sequence = Sequence label.view_pdb_structure = View PDB Structure -label.min = Min: -label.max = Max: -label.colour_by_label = Colour by label +label.min_value = Min value +label.max_value = Max value +label.no_value = No value label.new_feature = New Feature label.match_case = Match Case label.view_alignment_editor = View in alignment editor @@@ -368,8 -368,6 +368,8 @@@ label.optimise_order = Optimise Orde label.seq_sort_by_score = Sequence sort by Score label.load_colours = Load Colours label.save_colours = Save Colours +label.load_colours_tooltip = Load feature colours and filters from file +label.save_colours_tooltip = Save feature colours and filters to file label.fetch_das_features = Fetch DAS Features label.selected_database_to_fetch_from = Selected {0} database {1} to fetch from {2} label.database_param = Database: {0} @@@ -402,10 -400,6 +402,6 @@@ label.view_name_original = Origina label.enter_view_name = Enter View Name label.enter_label = Enter label label.enter_label_for_the_structure = Enter a label for the structure - label.pdb_entry_is_already_displayed = {0} is already displayed.\nDo you want to re-use this viewer ? - label.map_sequences_to_visible_window = Map Sequences to Visible Window: {0} - label.add_pdbentry_to_view = Do you want to add {0} to the view called\n{1}\n - label.align_to_existing_structure_view = Align to existing structure view label.pdb_entries_couldnt_be_retrieved = The following pdb entries could not be retrieved from the PDB\:\n{0}\nPlease retry, or try downloading them manually. label.couldnt_load_file = Couldn't load file label.couldnt_find_pdb_id_in_file = Couldn't find a PDB id in the file supplied. Please enter an Id to identify this structure. @@@ -492,10 -486,6 +488,10 @@@ label.settings_for_type = Settings for label.view_full_application = View in Full Application label.load_associated_tree = Load Associated Tree... label.load_features_annotations = Load Features/Annotations... +label.load_vcf = Load SNP variants from plain text or indexed VCF data +label.load_vcf_file = Load VCF File +label.searching_vcf = Loading VCF variants... +label.added_vcf = Added {0} VCF variants to {1} sequence(s) label.export_features = Export Features... label.export_annotations = Export Annotations... label.to_upper_case = To Upper Case @@@ -534,6 -524,7 +530,6 @@@ label.threshold_feature_above_threshol label.threshold_feature_below_threshold = Below Threshold label.adjust_threshold = Adjust threshold label.toggle_absolute_relative_display_threshold = Toggle between absolute and relative display threshold. -label.display_features_same_type_different_label_using_different_colour = Display features of the same type with a different label using a different colour. (e.g. domain features) label.select_colour_minimum_value = Select Colour for Minimum Value label.select_colour_maximum_value = Select Colour for Maximum Value label.open_url_param = Open URL {0} @@@ -785,7 -776,7 +781,7 @@@ label.pairwise_aligned_sequences = Pair label.original_data_for_params = Original Data for {0} label.points_for_params = Points for {0} label.transformed_points_for_params = Transformed points for {0} -label.graduated_color_for_params = Graduated Feature Colour for {0} +label.variable_color_for = Variable Feature Colour for {0} label.select_background_colour = Select Background Colour label.invalid_font = Invalid Font label.separate_multiple_accession_ids = Enter one or more accession IDs separated by a semi-colon ";" @@@ -872,7 -863,7 +868,7 @@@ label.msa_service_is_unknown = The Mult label.service_called_is_not_seq_search_service = The Service called \n{0}\nis not a \nSequence Search Service\! label.seq_search_service_is_unknown = The Sequence Search Service named {0} is unknown label.feature_type = Feature Type -label.display = Display +label.show = Show label.service_url = Service URL label.copied_sequences = Copied sequences label.cut_sequences = Cut Sequences @@@ -1218,7 -1209,6 +1214,6 @@@ label.pdb_sequence_fetcher = PDB Sequen label.result = result label.results = results label.structure_chooser = Structure Chooser - label.select = Select : label.invert = Invert label.select_pdb_file = Select PDB File info.select_filter_option = Select Filter Option/Manual Entry @@@ -1325,41 -1315,9 +1320,41 @@@ label.select_hidden_colour = Select hid label.overview = Overview label.reset_to_defaults = Reset to defaults label.oview_calc = Recalculating overview... +label.feature_details = Feature details +label.matchCondition_contains = Contains +label.matchCondition_notcontains = Does not contain +label.matchCondition_matches = Matches +label.matchCondition_notmatches = Does not match +label.matchCondition_present = Is present +label.matchCondition_notpresent = Is not present +label.matchCondition_eq = = +label.matchCondition_ne = not = +label.matchCondition_lt = < +label.matchCondition_le = <= +label.matchCondition_gt = > +label.matchCondition_ge = >= +label.numeric_required = The value should be numeric +label.filter = Filter +label.filters = Filters +label.join_conditions = Join conditions with +label.score = Score +label.colour_by_label = Colour by label +label.variable_colour = Variable colour... +label.select_colour = Select colour option.enable_disable_autosearch = When ticked, search is performed automatically option.autosearch = Autosearch label.retrieve_ids = Retrieve IDs +label.display_settings_for = Display settings for {0} features +label.simple = Simple +label.simple_colour = Simple Colour +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 label.best_quality = Best Quality label.best_resolution = Best Resolution label.most_protein_chain = Most Protein Chain diff --combined resources/lang/Messages_es.properties index e42d6b8,61bf42a..17c6112 --- a/resources/lang/Messages_es.properties +++ b/resources/lang/Messages_es.properties @@@ -226,6 -226,7 +226,6 @@@ label.automatic_scrolling = Desplazamie label.documentation = Documentación label.about = Acerca de... label.show_sequence_limits = Mostrar los límites de la secuencia -label.feature_settings = Ajustar funciones... label.all_columns = Todas las columnas label.all_sequences = Todas las secuencias label.selected_columns = Columnas seleccionadas @@@ -242,7 -243,6 +242,7 @@@ label.apply_all_groups = Aplicar a todo label.autocalculated_annotation = Anotación autocalculada label.min_colour = Color mínimo label.max_colour = Color máximo +label.no_colour = Sin color label.use_original_colours = Usar colores originales label.threshold_minmax = El umbral es mín/máx label.represent_group_with = Representar al grupo con @@@ -250,9 -250,8 +250,9 @@@ label.selection = Selecciona label.group_colour = Color del grupo label.sequence = Secuencia label.view_pdb_structure = Ver estructura PDB -label.min = Mín: -label.max = Máx: +label.max_value = Valor máximo +label.min_value = Valor mínimo +label.no_value = Sin valor label.colour_by_label = Color por etiquetas label.new_feature = Nueva función label.match_case = Hacer corresponder mayúsculas y minúsculas @@@ -337,8 -336,6 +337,8 @@@ label.optimise_order = Optimizar orde label.seq_sort_by_score = Ordenar las secuencias por puntuación label.load_colours = Cargar colores label.save_colours = Guardar colores +label.load_colours_tooltip = Cargar colores y filtros desde fichero +label.save_colours_tooltip = Guardar colores y filtros en fichero label.fetch_das_features = Recuperar funciones DAS label.selected_database_to_fetch_from = Seleccionada {0} Base de datos {1} para buscar de {2} label.database_param = Base de datos: {0} @@@ -370,10 -367,6 +370,6 @@@ label.ignore_unmatched_dropped_files = label.enter_view_name = Introduzca un nombre para la vista label.enter_label = Introducir etiqueta label.enter_label_for_the_structure = Introducir una etiqueta para la estructura - label.pdb_entry_is_already_displayed = {0} Ya est\u00E1 mostrado.\nQuieres volver a usar este visor? - label.map_sequences_to_visible_window = Mapa de secuencias en ventana visible: {0} - label.add_pdbentry_to_view = Quieres a\u00F1adir {0} a la vista llamada\n{1}\n - label.align_to_existing_structure_view = Alinear a una estructura ya existente label.pdb_entries_couldnt_be_retrieved = Las siguientes entradas pdb no pueden ser extra\u00EDdas del PDB\:\n{0}\nPor favor, prueba descarg\u00E1ndolas manualmente. label.couldnt_load_file = No se pudo cargar el fichero label.couldnt_find_pdb_id_in_file = No se pudo encontrar un Id PDB en el fichero suministrado. Por favor, introduzca un Id para identificar esta estructura. @@@ -459,10 -452,6 +455,10 @@@ label.settings_for_type = Ajustes para label.view_full_application = Ver en la aplicación completa label.load_associated_tree = Cargar árbol asociado ... label.load_features_annotations = Cargar características/anotaciones ... +label.load_vcf = Cargar variantes SNP desde fichero VCF texto o tab-indexado +label.load_vcf_file = Cargar fichero VCF +label.searching_vcf = Cargando variantes VCF... +label.added_vcf= {0} variantes VCF añadidas a {1} secuencia(s) label.export_features = Exportar características... label.export_annotations = Exportar anotaciones ... label.to_upper_case = Pasar a mayúsculas @@@ -496,6 -485,7 +492,6 @@@ label.threshold_feature_above_threshol label.threshold_feature_below_threshold = Por debajo del umbral label.adjust_threshold = Ajustar umbral label.toggle_absolute_relative_display_threshold = Cambiar entre mostrar el umbral absoluto y el relativo. -label.display_features_same_type_different_label_using_different_colour = Mostrar las características del mismo tipo con una etiqueta diferente y empleando un color distinto (p.e. características del dominio) label.select_colour_minimum_value = Seleccionar el color para el valor mínimo label.select_colour_maximum_value = Seleccionar el color para el valor máximo label.open_url_param = Abrir URL {0} @@@ -715,7 -705,7 +711,7 @@@ label.pairwise_aligned_sequences = Secu label.original_data_for_params = Datos originales de {0} label.points_for_params = Puntos de {0} label.transformed_points_for_params = Puntos transformados de {0} -label.graduated_color_for_params = Color graduado para la característica de {0} +label.variable_color_for = Color variable para la característica de {0} label.select_background_colour = Seleccionar color de fondo label.invalid_font = Fuente no válida label.separate_multiple_accession_ids = Separar los accession id con un punto y coma ";" @@@ -798,7 -788,7 +794,7 @@@ label.msa_service_is_unknown = El Servi label.service_called_is_not_seq_search_service = El Servicio llamando \n{0}\nno es un \nServicio de B\u00FAsqueda de Secuencias\! label.seq_search_service_is_unknown = El Servicio de Búsqueda de Sencuencias llamado {0} es desconocido label.feature_type = Tipo de característisca -label.display = Representación +label.show = Mostrar label.service_url = URL del servicio label.copied_sequences = Secuencias copiadas label.cut_sequences = Cortar secuencias @@@ -1178,7 -1168,6 +1174,6 @@@ label.structures_filter=Filtro de Estru label.scale_protein_to_cdna=Adaptar proteína a cDNA label.scale_protein_to_cdna_tip=Hacer a los residuos de proteínas de la misma anchura que los codones en ventanas divididas status.loading_cached_pdb_entries=Cargando Entradas PDB en Caché - label.select=Seleccionar : label.select_by_annotation=Seleccionar/Ocultar Columnas por Anotación action.select_by_annotation=Seleccionar/Ocultar Columnas por Anotación... action.export_features=Exportar Características @@@ -1326,41 -1315,9 +1321,41 @@@ label.select_hidden_colour = Selecciona label.overview = Resumen label.reset_to_defaults = Restablecen a los predeterminados label.oview_calc = Recalculando resumen +label.feature_details = Detalles de característica +label.matchCondition_contains = Contiene +label.matchCondition_notcontains = No contiene +label.matchCondition_matches = Es igual a +label.matchCondition_notmatches = No es igual a +label.matchCondition_present = Está presente +label.matchCondition_notpresent = No está presente +label.matchCondition_eq = = +label.matchCondition_ne = not = +label.matchCondition_lt = < +label.matchCondition_le = <= +label.matchCondition_gt = > +label.matchCondition_ge = >= +label.numeric_required = Valor numérico requerido +label.filter = Filtro +label.filters = Filtros +label.join_conditions = Combinar condiciones con +label.score = Puntuación +label.colour_by_label = Colorear por texto +label.variable_colour = Color variable... +label.select_colour = Seleccionar color option.enable_disable_autosearch = Marcar para buscar automáticamente option.autosearch = Auto búsqueda label.retrieve_ids = Recuperar IDs +label.display_settings_for = Visualización de características {0} +label.simple = Simple +label.simple_colour = Color simple +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 label.best_quality = Mejor Calidad label.best_resolution = Mejor Resolución label.most_protein_chain = Más Cadena de Proteína diff --combined src/jalview/gui/Desktop.java index 24ed1f7,9a696e9..75033f3 --- a/src/jalview/gui/Desktop.java +++ b/src/jalview/gui/Desktop.java @@@ -900,6 -900,8 +900,6 @@@ public class Desktop extends jalview.jb menuItem.removeActionListener(menuItem.getActionListeners()[0]); } windowMenu.remove(menuItem); - - System.gc(); }; }); @@@ -1387,6 -1389,7 +1387,6 @@@ { ssm.resetAll(); } - System.gc(); } @Override @@@ -3396,4 -3399,41 +3396,41 @@@ { Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected)); } + + /** + * Answers a (possibly empty) list of any structure viewer frames (currently + * for either Jmol or Chimera) which are currently open. This may optionally + * be restricted to viewers of a specified class, or viewers linked to a + * specified alignment panel. + * + * @param apanel + * if not null, only return viewers linked to this panel + * @param structureViewerClass + * if not null, only return viewers of this class + * @return + */ + public List getStructureViewers( + AlignmentPanel apanel, + Class structureViewerClass) + { + List result = new ArrayList<>(); + JInternalFrame[] frames = Desktop.instance.getAllFrames(); + + for (JInternalFrame frame : frames) + { + if (frame instanceof StructureViewerBase) + { + if (structureViewerClass == null + || structureViewerClass.isInstance(frame)) + { + if (apanel == null + || ((StructureViewerBase) frame).isLinkedWith(apanel)) + { + result.add((StructureViewerBase) frame); + } + } + } + } + return result; + } } diff --combined src/jalview/gui/SeqPanel.java index 12091a1,e36fa53..8b2e7bc --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@@ -59,6 -59,7 +59,6 @@@ 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; @@@ -75,11 -76,12 +75,11 @@@ import javax.swing.ToolTipManager public class SeqPanel extends JPanel implements MouseListener, MouseMotionListener, MouseWheelListener, SequenceListener, SelectionListener - { - /** DOCUMENT ME!! */ + private static final int MAX_TOOLTIP_LENGTH = 300; + public SeqCanvas seqCanvas; - /** DOCUMENT ME!! */ public AlignmentPanel ap; /* @@@ -146,33 -148,35 +146,33 @@@ SearchResultsI lastSearchResults; /** - * Creates a new SeqPanel object. + * Creates a new SeqPanel object * - * @param avp - * DOCUMENT ME! - * @param p - * DOCUMENT ME! + * @param viewport + * @param alignPanel */ - public SeqPanel(AlignViewport av, AlignmentPanel ap) + public SeqPanel(AlignViewport viewport, AlignmentPanel alignPanel) { linkImageURL = getClass().getResource("/images/link.gif"); seqARep = new SequenceAnnotationReport(linkImageURL.toString()); ToolTipManager.sharedInstance().registerComponent(this); ToolTipManager.sharedInstance().setInitialDelay(0); ToolTipManager.sharedInstance().setDismissDelay(10000); - this.av = av; + this.av = viewport; setBackground(Color.white); - seqCanvas = new SeqCanvas(ap); + seqCanvas = new SeqCanvas(alignPanel); setLayout(new BorderLayout()); add(seqCanvas, BorderLayout.CENTER); - this.ap = ap; + this.ap = alignPanel; - if (!av.isDataset()) + if (!viewport.isDataset()) { addMouseMotionListener(this); addMouseListener(this); addMouseWheelListener(this); - ssm = av.getStructureSelectionManager(); + ssm = viewport.getStructureSelectionManager(); ssm.addStructureViewerListener(this); ssm.addSelectionListener(this); } @@@ -846,7 -850,7 +846,7 @@@ List features = ap.getFeatureRenderer() .findFeaturesAtColumn(sequence, column + 1); seqARep.appendFeatures(tooltipText, pos, features, - this.ap.getSeqPanel().seqCanvas.fr.getMinMax()); + this.ap.getSeqPanel().seqCanvas.fr); } if (tooltipText.length() == 6) // { @@@ -855,11 -859,6 +855,11 @@@ } else { + if (tooltipText.length() > MAX_TOOLTIP_LENGTH) // constant + { + tooltipText.setLength(MAX_TOOLTIP_LENGTH); + tooltipText.append("..."); + } String textString = tooltipText.toString(); if (lastTooltip == null || !lastTooltip.equals(textString)) { @@@ -1840,10 -1839,21 +1840,10 @@@ final int column = findColumn(evt); final int seq = findSeq(evt); SequenceI sequence = av.getAlignment().getSequenceAt(seq); - List allFeatures = ap.getFeatureRenderer() + List features = ap.getFeatureRenderer() .findFeaturesAtColumn(sequence, column + 1); - List links = new ArrayList<>(); - for (SequenceFeature sf : allFeatures) - { - if (sf.links != null) - { - for (String link : sf.links) - { - links.add(link); - } - } - } - PopupMenu pop = new PopupMenu(ap, null, links); + PopupMenu pop = new PopupMenu(ap, null, features); pop.show(this, evt.getX(), evt.getY()); } @@@ -2292,4 -2302,13 +2292,13 @@@ return true; } + + /** + * + * @return null or last search results handled by this panel + */ + public SearchResultsI getLastSearchResults() + { + return lastSearchResults; + } } diff --combined src/jalview/util/MapList.java index c944345,ae530f9..7f1abc4 --- a/src/jalview/util/MapList.java +++ b/src/jalview/util/MapList.java @@@ -116,8 -116,17 +116,17 @@@ public class MapLis { int hashCode = 31 * fromRatio; hashCode = 31 * hashCode + toRatio; - hashCode = 31 * hashCode + fromShifts.toArray().hashCode(); - hashCode = 31 * hashCode + toShifts.toArray().hashCode(); + for (int[] shift : fromShifts) + { + hashCode = 31 * hashCode + shift[0]; + hashCode = 31 * hashCode + shift[1]; + } + for (int[] shift : toShifts) + { + hashCode = 31 * hashCode + shift[0]; + hashCode = 31 * hashCode + shift[1]; + } + return hashCode; } @@@ -1094,33 -1103,8 +1103,33 @@@ */ public boolean isFromForwardStrand() { + return isForwardStrand(getFromRanges()); + } + + /** + * Returns true if mapping is to forward strand, false if to reverse strand. + * Result is just based on the first 'to' range that is not a single position. + * Default is true unless proven to be false. Behaviour is not well defined if + * the mapping has a mixture of forward and reverse ranges. + * + * @return + */ + public boolean isToForwardStrand() + { + return isForwardStrand(getToRanges()); + } + + /** + * A helper method that returns true unless at least one range has start > end. + * Behaviour is undefined for a mixture of forward and reverse ranges. + * + * @param ranges + * @return + */ + private boolean isForwardStrand(List ranges) + { boolean forwardStrand = true; - for (int[] range : getFromRanges()) + for (int[] range : ranges) { if (range[1] > range[0]) { @@@ -1145,63 -1129,4 +1154,63 @@@ || (fromRatio == 3 && toRatio == 1); } + /** + * Returns a map which is the composite of this one and the input map. That + * is, the output map has the fromRanges of this map, and its toRanges are the + * toRanges of this map as transformed by the input map. + *

+ * Returns null if the mappings cannot be traversed (not all toRanges of this + * map correspond to fromRanges of the input), or if this.toRatio does not + * match map.fromRatio. + * + *

 +   * Example 1:
 +   *    this:   from [1-100] to [501-600]
 +   *    input:  from [10-40] to [60-90]
 +   *    output: from [10-40] to [560-590]
 +   * Example 2 ('reverse strand exons'):
 +   *    this:   from [1-100] to [2000-1951], [1000-951] // transcript to loci
 +   *    input:  from [1-50]  to [41-90] // CDS to transcript
 +   *    output: from [10-40] to [1960-1951], [1000-971] // CDS to gene loci
 +   * 
+ * + * @param map + * @return + */ + public MapList traverse(MapList map) + { + if (map == null) + { + return null; + } + + /* + * compound the ratios by this rule: + * A:B with M:N gives A*M:B*N + * reduced by greatest common divisor + * so 1:3 with 3:3 is 3:9 or 1:3 + * 1:3 with 3:1 is 3:3 or 1:1 + * 1:3 with 1:3 is 1:9 + * 2:5 with 3:7 is 6:35 + */ + int outFromRatio = getFromRatio() * map.getFromRatio(); + int outToRatio = getToRatio() * map.getToRatio(); + int gcd = MathUtils.gcd(outFromRatio, outToRatio); + outFromRatio /= gcd; + outToRatio /= gcd; + + List toRanges = new ArrayList<>(); + for (int[] range : getToRanges()) + { + int[] transferred = map.locateInTo(range[0], range[1]); + if (transferred == null) + { + return null; + } + toRanges.add(transferred); + } + + return new MapList(getFromRanges(), toRanges, outFromRatio, outToRatio); + } + } diff --combined test/jalview/io/Jalview2xmlTests.java index e9e0782,c0eb8c5..53bb0e7 --- a/test/jalview/io/Jalview2xmlTests.java +++ b/test/jalview/io/Jalview2xmlTests.java @@@ -23,13 -23,11 +23,13 @@@ package jalview.io import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; import static org.testng.Assert.assertSame; import static org.testng.Assert.assertTrue; import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; +import jalview.api.FeatureColourI; import jalview.api.ViewStyleI; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; @@@ -37,17 -35,12 +37,17 @@@ import jalview.datamodel.HiddenSequence import jalview.datamodel.PDBEntry; import jalview.datamodel.PDBEntry.Type; import jalview.datamodel.SequenceCollectionI; +import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; +import jalview.datamodel.features.FeatureMatcher; +import jalview.datamodel.features.FeatureMatcherSet; +import jalview.datamodel.features.FeatureMatcherSetI; import jalview.gui.AlignFrame; import jalview.gui.AlignViewport; import jalview.gui.AlignmentPanel; import jalview.gui.Desktop; +import jalview.gui.FeatureRenderer; import jalview.gui.Jalview2XML; import jalview.gui.JvOptionPane; import jalview.gui.PopupMenu; @@@ -57,16 -50,13 +57,16 @@@ import jalview.schemes.AnnotationColour import jalview.schemes.BuriedColourScheme; import jalview.schemes.ColourSchemeI; import jalview.schemes.ColourSchemeProperty; +import jalview.schemes.FeatureColour; import jalview.schemes.JalviewColourScheme; import jalview.schemes.RNAHelicesColour; import jalview.schemes.StrandColourScheme; import jalview.schemes.TCoffeeColourScheme; import jalview.structure.StructureImportSettings; +import jalview.util.matcher.Condition; import jalview.viewmodel.AlignmentViewport; +import java.awt.Color; import java.io.File; import java.io.IOException; import java.util.ArrayList; @@@ -257,6 -247,31 +257,31 @@@ public class Jalview2xmlTests extends J } + /** + * Test for JAL-2223 - multiple mappings in View Mapping report + * + * @throws Exception + */ + @Test(groups = { "Functional" }) + public void noDuplicatePdbMappingsMade() throws Exception + { + StructureImportSettings.setProcessSecondaryStructure(true); + StructureImportSettings.setVisibleChainAnnotation(true); + AlignFrame af = new FileLoader().LoadFileWaitTillLoaded( + "examples/exampleFile_2_7.jar", DataSourceType.FILE); + assertNotNull(af, "Didn't read in the example file correctly."); + + // locate Jmol viewer + // count number of PDB mappings the structure selection manager holds - + String pdbFile = af.getCurrentView().getStructureSelectionManager() + .findFileForPDBId("1A70"); + assertEquals( + af.getCurrentView().getStructureSelectionManager() + .getMapping(pdbFile).length, + 2, "Expected only two mappings for 1A70"); + + } + @Test(groups = { "Functional" }) public void viewRefPdbAnnotation() throws Exception { @@@ -851,163 -866,4 +876,163 @@@ assertTrue(rs.conservationApplied()); assertEquals(rs.getConservationInc(), 30); } + + /** + * Test save and reload of feature colour schemes and filter settings + * + * @throws IOException + */ + @Test(groups = { "Functional" }) + public void testSaveLoadFeatureColoursAndFilters() throws IOException + { + AlignFrame af = new FileLoader().LoadFileWaitTillLoaded( + ">Seq1\nACDEFGHIKLM", DataSourceType.PASTE); + SequenceI seq1 = af.getViewport().getAlignment().getSequenceAt(0); + + /* + * add some features to the sequence + */ + int score = 1; + addFeatures(seq1, "type1", score++); + addFeatures(seq1, "type2", score++); + addFeatures(seq1, "type3", score++); + addFeatures(seq1, "type4", score++); + addFeatures(seq1, "type5", score++); + + /* + * set colour schemes for features + */ + FeatureRenderer fr = af.getFeatureRenderer(); + fr.findAllFeatures(true); + + // type1: red + fr.setColour("type1", new FeatureColour(Color.red)); + + // type2: by label + FeatureColourI byLabel = new FeatureColour(); + byLabel.setColourByLabel(true); + fr.setColour("type2", byLabel); + + // type3: by score above threshold + FeatureColourI byScore = new FeatureColour(Color.BLACK, Color.BLUE, 1, + 10); + byScore.setAboveThreshold(true); + byScore.setThreshold(2f); + fr.setColour("type3", byScore); + + // type4: by attribute AF + FeatureColourI byAF = new FeatureColour(); + byAF.setColourByLabel(true); + byAF.setAttributeName("AF"); + fr.setColour("type4", byAF); + + // type5: by attribute CSQ:PolyPhen below threshold + FeatureColourI byPolyPhen = new FeatureColour(Color.BLACK, Color.BLUE, + 1, 10); + byPolyPhen.setBelowThreshold(true); + byPolyPhen.setThreshold(3f); + byPolyPhen.setAttributeName("CSQ", "PolyPhen"); + fr.setColour("type5", byPolyPhen); + + /* + * set filters for feature types + */ + + // filter type1 features by (label contains "x") + FeatureMatcherSetI filterByX = new FeatureMatcherSet(); + filterByX.and(FeatureMatcher.byLabel(Condition.Contains, "x")); + fr.setFeatureFilter("type1", filterByX); + + // filter type2 features by (score <= 2.4 and score > 1.1) + FeatureMatcherSetI filterByScore = new FeatureMatcherSet(); + filterByScore.and(FeatureMatcher.byScore(Condition.LE, "2.4")); + filterByScore.and(FeatureMatcher.byScore(Condition.GT, "1.1")); + fr.setFeatureFilter("type2", filterByScore); + + // filter type3 features by (AF contains X OR CSQ:PolyPhen != 0) + FeatureMatcherSetI filterByXY = new FeatureMatcherSet(); + filterByXY + .and(FeatureMatcher.byAttribute(Condition.Contains, "X", "AF")); + filterByXY.or(FeatureMatcher.byAttribute(Condition.NE, "0", "CSQ", + "PolyPhen")); + fr.setFeatureFilter("type3", filterByXY); + + /* + * save as Jalview project + */ + File tfile = File.createTempFile("JalviewTest", ".jvp"); + tfile.deleteOnExit(); + String filePath = tfile.getAbsolutePath(); + assertTrue(af.saveAlignment(filePath, FileFormat.Jalview), + "Failed to store as a project."); + + /* + * close current alignment and load the saved project + */ + af.closeMenuItem_actionPerformed(true); + af = null; + af = new FileLoader() + .LoadFileWaitTillLoaded(filePath, DataSourceType.FILE); + assertNotNull(af, "Failed to import new project"); + + /* + * verify restored feature colour schemes and filters + */ + fr = af.getFeatureRenderer(); + FeatureColourI fc = fr.getFeatureStyle("type1"); + assertTrue(fc.isSimpleColour()); + assertEquals(fc.getColour(), Color.red); + fc = fr.getFeatureStyle("type2"); + assertTrue(fc.isColourByLabel()); + fc = fr.getFeatureStyle("type3"); + assertTrue(fc.isGraduatedColour()); + assertNull(fc.getAttributeName()); + assertTrue(fc.isAboveThreshold()); + assertEquals(fc.getThreshold(), 2f); + fc = fr.getFeatureStyle("type4"); + assertTrue(fc.isColourByLabel()); + assertTrue(fc.isColourByAttribute()); + assertEquals(fc.getAttributeName(), new String[] { "AF" }); + fc = fr.getFeatureStyle("type5"); + assertTrue(fc.isGraduatedColour()); + assertTrue(fc.isColourByAttribute()); + assertEquals(fc.getAttributeName(), new String[] { "CSQ", "PolyPhen" }); + assertTrue(fc.isBelowThreshold()); + assertEquals(fc.getThreshold(), 3f); + + assertEquals(fr.getFeatureFilter("type1").toStableString(), + "Label Contains x"); + assertEquals(fr.getFeatureFilter("type2").toStableString(), + "(Score LE 2.4) AND (Score GT 1.1)"); + assertEquals(fr.getFeatureFilter("type3").toStableString(), + "(AF Contains X) OR (CSQ:PolyPhen NE 0.0)"); + } + + private void addFeature(SequenceI seq, String featureType, int score) + { + SequenceFeature sf = new SequenceFeature(featureType, "desc", 1, 2, + score, "grp"); + sf.setValue("AF", score); + sf.setValue("CSQ", new HashMap() + { + { + put("PolyPhen", Integer.toString(score)); + } + }); + seq.addSequenceFeature(sf); + } + + /** + * Adds two features of the given type to the given sequence, also setting the + * score as the value of attribute "AF" and sub-attribute "CSQ:PolyPhen" + * + * @param seq + * @param featureType + * @param score + */ + private void addFeatures(SequenceI seq, String featureType, int score) + { + addFeature(seq, featureType, score++); + addFeature(seq, featureType, score); + } } diff --combined test/jalview/util/MapListTest.java index 029b681,95d7efe..1acc9e1 --- a/test/jalview/util/MapListTest.java +++ b/test/jalview/util/MapListTest.java @@@ -391,7 -391,9 +391,9 @@@ public class MapListTes MapList ml7 = new MapList(codons, protein, 3, 1); // toShifts differ assertTrue(ml.equals(ml)); + assertEquals(ml.hashCode(), ml.hashCode()); assertTrue(ml.equals(ml1)); + assertEquals(ml.hashCode(), ml1.hashCode()); assertTrue(ml1.equals(ml)); assertFalse(ml.equals(null)); @@@ -814,155 -816,4 +816,155 @@@ assertEquals(1, merged.size()); assertArrayEquals(new int[] { 9, 0 }, merged.get(0)); } + + /** + * Test the method that compounds ('traverses') two mappings + */ + @Test(groups = "Functional") + public void testTraverse() + { + /* + * simple 1:1 plus 1:1 forwards + */ + MapList ml1 = new MapList(new int[] { 3, 4, 8, 12 }, new int[] { 5, 8, + 11, 13 }, 1, 1); + MapList ml2 = new MapList(new int[] { 1, 50 }, new int[] { 40, 45, 70, + 75, 90, 127 }, 1, 1); + MapList compound = ml1.traverse(ml2); + + assertEquals(compound.getFromRatio(), 1); + assertEquals(compound.getToRatio(), 1); + List fromRanges = compound.getFromRanges(); + assertEquals(fromRanges.size(), 2); + assertArrayEquals(new int[] { 3, 4 }, fromRanges.get(0)); + assertArrayEquals(new int[] { 8, 12 }, fromRanges.get(1)); + List toRanges = compound.getToRanges(); + assertEquals(toRanges.size(), 2); + // 5-8 maps to 44-45,70-71 + // 11-13 maps to 74-75,90 + assertArrayEquals(new int[] { 44, 45, 70, 71 }, toRanges.get(0)); + assertArrayEquals(new int[] { 74, 75, 90, 90 }, toRanges.get(1)); + + /* + * 1:1 over 1:1 backwards ('reverse strand') + */ + ml1 = new MapList(new int[] { 1, 50 }, new int[] { 70, 119 }, 1, 1); + ml2 = new MapList(new int[] { 1, 500 }, + new int[] { 1000, 901, 600, 201 }, 1, 1); + compound = ml1.traverse(ml2); + + assertEquals(compound.getFromRatio(), 1); + assertEquals(compound.getToRatio(), 1); + fromRanges = compound.getFromRanges(); + assertEquals(fromRanges.size(), 1); + assertArrayEquals(new int[] { 1, 50 }, fromRanges.get(0)); + toRanges = compound.getToRanges(); + assertEquals(toRanges.size(), 1); + assertArrayEquals(new int[] { 931, 901, 600, 582 }, toRanges.get(0)); + + /* + * 1:1 plus 1:3 should result in 1:3 + */ + ml1 = new MapList(new int[] { 1, 30 }, new int[] { 11, 40 }, 1, 1); + ml2 = new MapList(new int[] { 1, 100 }, new int[] { 1, 50, 91, 340 }, + 1, 3); + compound = ml1.traverse(ml2); + + assertEquals(compound.getFromRatio(), 1); + assertEquals(compound.getToRatio(), 3); + fromRanges = compound.getFromRanges(); + assertEquals(fromRanges.size(), 1); + assertArrayEquals(new int[] { 1, 30 }, fromRanges.get(0)); + // 11-40 maps to 31-50,91-160 + toRanges = compound.getToRanges(); + assertEquals(toRanges.size(), 1); + assertArrayEquals(new int[] { 31, 50, 91, 160 }, toRanges.get(0)); + + /* + * 3:1 plus 1:1 should result in 3:1 + */ + ml1 = new MapList(new int[] { 1, 30 }, new int[] { 11, 20 }, 3, 1); + ml2 = new MapList(new int[] { 1, 100 }, new int[] { 1, 15, 91, 175 }, + 1, 1); + compound = ml1.traverse(ml2); + + assertEquals(compound.getFromRatio(), 3); + assertEquals(compound.getToRatio(), 1); + fromRanges = compound.getFromRanges(); + assertEquals(fromRanges.size(), 1); + assertArrayEquals(new int[] { 1, 30 }, fromRanges.get(0)); + // 11-20 maps to 11-15, 91-95 + toRanges = compound.getToRanges(); + assertEquals(toRanges.size(), 1); + assertArrayEquals(new int[] { 11, 15, 91, 95 }, toRanges.get(0)); + + /* + * 1:3 plus 3:1 should result in 1:1 + */ + ml1 = new MapList(new int[] { 21, 40 }, new int[] { 13, 72 }, 1, 3); + ml2 = new MapList(new int[] { 1, 300 }, new int[] { 51, 70, 121, 200 }, + 3, 1); + compound = ml1.traverse(ml2); + + assertEquals(compound.getFromRatio(), 1); + assertEquals(compound.getToRatio(), 1); + fromRanges = compound.getFromRanges(); + assertEquals(fromRanges.size(), 1); + assertArrayEquals(new int[] { 21, 40 }, fromRanges.get(0)); + // 13-72 maps 3:1 to 55-70, 121-124 + toRanges = compound.getToRanges(); + assertEquals(toRanges.size(), 1); + assertArrayEquals(new int[] { 55, 70, 121, 124 }, toRanges.get(0)); + + /* + * 3:1 plus 1:3 should result in 1:1 + */ + ml1 = new MapList(new int[] { 31, 90 }, new int[] { 13, 32 }, 3, 1); + ml2 = new MapList(new int[] { 11, 40 }, new int[] { 41, 50, 71, 150 }, + 1, 3); + compound = ml1.traverse(ml2); + + assertEquals(compound.getFromRatio(), 1); + assertEquals(compound.getToRatio(), 1); + fromRanges = compound.getFromRanges(); + assertEquals(fromRanges.size(), 1); + assertArrayEquals(new int[] { 31, 90 }, fromRanges.get(0)); + // 13-32 maps to 47-50,71-126 + toRanges = compound.getToRanges(); + assertEquals(toRanges.size(), 1); + assertArrayEquals(new int[] { 47, 50, 71, 126 }, toRanges.get(0)); + + /* + * method returns null if not all regions are mapped through + */ + ml1 = new MapList(new int[] { 1, 50 }, new int[] { 101, 150 }, 1, 1); + ml2 = new MapList(new int[] { 131, 180 }, new int[] { 201, 250 }, 1, 3); + compound = ml1.traverse(ml2); + assertNull(compound); + } + + /** + * Test that method that inspects for the (first) forward or reverse 'to' range. + * Single position ranges are ignored. + */ + @Test(groups = { "Functional" }) + public void testIsToForwardsStrand() + { + // [3-9] declares forward strand + MapList ml = new MapList(new int[] { 20, 11 }, + new int[] + { 2, 2, 3, 9, 12, 11 }, 1, 1); + assertTrue(ml.isToForwardStrand()); + + // [11-5] declares reverse strand ([13-14] is ignored) + ml = new MapList(new int[] { 20, 11 }, + new int[] + { 2, 2, 11, 5, 13, 14 }, 1, 1); + assertFalse(ml.isToForwardStrand()); + + // all single position ranges - defaults to forward strand + ml = new MapList(new int[] { 3, 1 }, new int[] { 2, 2, 4, 4, 6, 6 }, 1, + 1); + assertTrue(ml.isToForwardStrand()); + } }