Merge branch 'develop' into features/mchmmer
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 8 May 2018 09:33:48 +0000 (10:33 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 8 May 2018 09:33:48 +0000 (10:33 +0100)
1  2 
help/help.jhm
help/helpTOC.xml
help/html/features/preferences.html
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/jalview/bin/Jalview.java
src/jalview/gui/Desktop.java
src/jalview/gui/SeqPanel.java
src/jalview/util/MapList.java
test/jalview/io/Jalview2xmlTests.java
test/jalview/util/MapListTest.java

diff --combined help/help.jhm
@@@ -54,6 -54,7 +54,7 @@@
     <mapID target="pdbmcviewer" url="html/features/pdbviewer.html"/>
     <mapID target="pdbjmol" url="html/features/jmol.html"/>
     <mapID target="chimera" url="html/features/chimera.html"/>
+    <mapID target="chimera.annotxfer" url="html/features/chimera.html#annotxfer"/>
     <mapID target="varna" url="html/features/varna.html"/>
     <mapID target="xsspannotation" url="html/features/xsspannotation.html"/>
     <mapID target="preferences" url="html/features/preferences.html"/>     
     <mapID target="alwCalc" url="html/menus/alwcalculate.html"/>
     
     <mapID target="wsMenu" url="html/menus/wsmenu.html"/>
 +   <mapID target="alwHmmer" url="html/menus/alwhmmer.html"/>
     <mapID target="popMenu" url="html/menus/popupMenu.html"/>
     <mapID target="popMenuAddref" url="html/menus/popupMenu.html#addrefannot"/>
     <mapID target="annotPanelMenu" url="html/menus/alwannotationpanel.html"/>
diff --combined help/helpTOC.xml
@@@ -24,6 -24,8 +24,8 @@@
        <tocitem text="Jalview Documentation" target="home" expand="true">
                        <tocitem text="What's new" target="new" expand="true">
                                <tocitem text="Latest Release Notes" target="release"/>
+                               <tocitem text="Structure Chooser" target="pdbchooser"/>
+                               <tocitem text="Chimera Annotation Exchange" target="chimera.annotxfer"/>
                </tocitem>
                
                <tocitem text="Editing Alignments" target="edit" />
                                <tocitem text="Colour Menu" target="alwColour" />
                                <tocitem text="Calculate Menu" target="alwCalc" />
                                <tocitem text="Web Service Menu" target="wsMenu" />
 +                              <tocitem text="HMMER Menu" target="alwHmmer" />
                                <tocitem text="Annotation Panel Menu" target="annotPanelMenu" />
                                <tocitem text="Popup Menu" target="popMenu" />
                        </tocitem>
        sequence alignments and EPS files.
      </li>
      <li>The <a href="#editing"><strong>&quot;Editing&quot;</strong>
 -        Preferences</a> tab contains settings affecting the export of
 -      sequence alignments and EPS files.
 +        Preferences</a> tab contains settings affecting the behaviour of alignments as you edit them.
 +    </li>
 +    <li>The <a href="#hmmer"><strong>&quot;HMMER&quot;</strong>
 +        Preferences</a> tab allows you to configure locally installed HMMER tools.
      </li>
      <li>The <a href="dassettings.html"><strong>&quot;DAS
            Settings&quot;</strong> Preferences</a> tab allows you to select which DAS
      and PDB file association (if available). The Jalview id/start-end
      option is ignored if Modeller output is selected.
    <p>
-     <a name="editing"><strong>e&quot;Editinge&quot; Preferences tab</strong></a>
+     <a name="editing"><strong>&quot;Editing&quot; Preferences tab</strong></a>
    </p>
    <p>There are currently three options available which can be
      selected / deselected.</p>
      <em>Sort with New Tree</em> - When selected, any trees calculated or
      loaded onto the alignment will automatically sort the alignment.
    </p>
 -  <p>&nbsp;</p>
 -  <p>&nbsp;</p>
 +  <p>
 +    <a name="hmmer"><strong>&quot;HMMER&quot; Preferences tab</strong></a>
 +  </p>
 +  <p>If you have installed HMMER tools (available from <a href="http://hmmerorg">hmmer.org</a>),
 +  then you should specify on this screen the location of the installation (the path to the folder 
 +  containing binary executable programs). Double-click in the input field to open a file browser.</p>
 +  <p>When this path is configured, the <a href="../menus/alwhmmer.html">HMMER menu</a> will be
 +  enabled in the Alignment window.</p>
  </body>
  </html>
@@@ -11,7 -11,6 +11,7 @@@ action.paste = Past
  action.show_html_source = Show HTML Source
  action.print = Print...
  action.web_service = Web Service
 +action.hmmer = HMMER
  action.cancel_job = Cancel Job
  action.start_job = Start Job
  action.revert = Revert
@@@ -204,8 -203,6 +204,8 @@@ label.colourScheme_purine/pyrimidine = 
  label.colourScheme_nucleotide = Nucleotide
  label.colourScheme_t-coffee_scores = T-Coffee Scores
  label.colourScheme_rna_helices = By RNA Helices
 +label.colourScheme_hmmer-uniprot = HMMER profile v global background
 +label.colourScheme_hmmer-alignment = HMMER profile v alignment background
  label.blc = BLC
  label.fasta = Fasta
  label.msf = MSF
@@@ -405,10 -402,6 +405,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.
@@@ -822,8 -815,8 +818,8 @@@ label.fetch_retrieve_from_all_sources 
  label.feature_settings_click_drag = Drag up or down to change render order.<br/>Double click to select columns containing feature.
  label.transparency_tip = Adjust transparency to 'see through' feature colours.
  label.opt_and_params_further_details = see further details by right-clicking
 -label.opt_and_params_show_brief_desc_image_link = <html>Click to show brief description<br><img src="{0}"/> Right click for further information.</html> 
 -label.opt_and_params_show_brief_desc = <html>Click to show brief description<br></html>
 +label.opt_and_params_show_brief_desc_image_link = Click to show brief description<br><img src="{0}"/> Right click for further information. 
 +label.opt_and_params_show_brief_desc = Click to show brief description<br>
  label.adjusts_width_generated_eps_png = <html>Adjusts the width of the generated EPS or PNG file to ensure even the longest sequence ID or annotation label is displayed</html>
  label.manually_specify_width_left_column = <html>Manually specify the width of the left hand column where sequence IDs and annotation labels will be rendered in exported alignment figures. This setting will be ignored if 'Automatically set ID width' is set</html>
  label.job_created_when_checked = <html>When checked, a job is created for every sequence in the current selection.</html>
@@@ -885,6 -878,7 +881,6 @@@ label.error_unsupported_owwner_user_col
  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
@@@ -963,6 -957,7 +959,6 @@@ label.groovy_support_failed = Jalview G
  label.couldnt_create_groovy_shell = Couldn't create the groovy Shell. Check the error log for the details of what went wrong.
  error.unsupported_version_calcIdparam = Unsupported Version for calcIdparam {0}
  error.implementation_error_cant_reorder_tree = Implementation Error: Can't reorder this tree. Not DefaultMutableTreeNode.
 -error.invalid_value_for_option = Invalid value {0} for option {1}
  error.implementation_error_cannot_import_vamsas_doc = Implementation Error - cannot import existing vamsas document into an existing session, Yet!
  label.vamsas_doc_couldnt_be_opened_as_new_session = VAMSAS Document could not be opened as a new session - please choose another
  error.implementation_error_vamsas_operation_not_init = Impementation error! Vamsas Operations when client not initialised and connected
@@@ -1148,9 -1143,6 +1144,9 @@@ status.loading_cached_pdb_entries = Loa
  status.searching_for_pdb_structures = Searching for PDB Structures
  status.opening_file_for = opening file for
  status.colouring_chimera = Colouring Chimera
 +status.running_hmmbuild = Building Hidden Markov Model
 +status.running_hmmalign = Creating alignment with Hidden Markov Model
 +status.running_hmmsearch = Searching for matching sequences
  label.font_doesnt_have_letters_defined = Font doesn't have letters defined\nso cannot be used\nwith alignment data
  label.font_too_small = Font size is too small
  label.error_loading_file_params = Error loading file {0}
@@@ -1222,7 -1214,6 +1218,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
@@@ -1371,56 -1362,3 +1366,56 @@@ label.most_bound_molecules = Most Boun
  label.most_polymer_residues = Most Polymer Residues
  label.cached_structures = Cached Structures
  label.free_text_search = Free Text Search
 +label.hmmalign = hmmalign
 +label.use_hmm = HMM profile to use
 +label.hmmbuild = hmmbuild
 +label.hmmsearch = hmmsearch
 +label.installation = Installation
 +label.hmmer_location = HMMER Binaries Installation Location
 +label.cygwin_location = Cygwin Binaries Installation Location (Windows)
 +label.information_annotation = Information Annotation
 +label.ignore_below_background_frequency = Ignore Below Background Frequency
 +label.information_description = Information content, measured in bits
 +warn.no_hmm = No Hidden Markov model found.\nRun hmmbuild or load an HMM file first.
 +label.no_sequences_found = No matching sequences, or an error occurred.
 +label.hmmer = HMMER
 +label.trim_termini = Trim Non-Matching Termini
 +label.trim_termini_desc = If true, non-matching regions on either end of the resulting alignment are removed.
 +label.no_of_sequences = Number of sequences returned
 +label.freq_alignment = Use alignment background frequencies
 +label.freq_uniprot = Use Uniprot background frequencies
 +label.hmmalign_options = hmmalign options
 +label.hmmsearch_options = hmmsearch options
 +label.executable_not_found = The ''{0}'' executable file was not found
 +warn.command_failed = {0} failed
 +label.invalid_folder = Invalid Folder
 +label.number_of_results = Number of Results to Return
 +label.auto_align_seqs = Automatically Align Fetched Sequences
 +label.use_accessions = Return Accessions
 +label.seq_e_value = Sequence E-value Cutoff
 +label.seq_score = Sequence Score Threshold
 +label.dom_e_value = Domain E-value Cutoff
 +label.dom_score = Domain Score Threshold
 +label.number_of_results_desc = The maximum number of hmmsearch results to display
 +label.auto_align_seqs_desc = If true, all fetched sequences will be aligned to the hidden Markov model with which the search was performed
 +label.use_accessions_desc = If true, the accession number of each sequence is returned, rather than that sequence's name
 +label.seq_e_value_desc = The E-value cutoff for returned sequences (hmmsearch -E)
 +label.seq_score_desc = The score threshold for returned sequences
 +label.dom_e_value_desc = The E-value cutoff for returned domains (hmmsearch -domE)
 +label.dom_score_desc = The score threshold for returned domains
 +label.add_database = Add Database
 +label.this_alignment = This alignment
 +warn.invalid_format = This is not a valid database file format. The current supported formats are Fasta, Stockholm and Pfam.
 +label.database_for_hmmsearch = The database hmmsearch will search through
 +label.use_reference = Use Reference Annotation
 +label.use_reference_desc = If true, hmmbuild will keep all columns defined as a reference position by the reference annotation
 +label.hmm_name = Alignment HMM Name
 +label.hmm_name_desc = The name given to the HMM for the alignment
 +warn.no_reference_annotation = No reference annotation found
 +label.hmmbuild_for = Build HMM for
 +label.hmmbuild_for_desc = Build an HMM for the selected sets of sequences
 +label.alignment = Alignment
 +label.groups_and_alignment = All groups and alignment
 +label.groups = All groups
 +label.selected_group = Selected group
 +label.use_info_for_height = Use Information Content as Letter Height
@@@ -370,10 -370,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.
@@@ -808,6 -804,7 +804,6 @@@ label.error_unsupported_owwner_user_col
  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
@@@ -886,6 -883,7 +882,6 @@@ label.groovy_support_failed = El soport
  label.couldnt_create_groovy_shell = No es posible crear el shell de Groovy. Compruebe el fichero de log para conocer los detalles.
  error.unsupported_version_calcIdparam = Versión no soportada de {0}
  error.implementation_error_cant_reorder_tree = Error de implementación: no es posible reordenar este Ã¡rbol. No DefaultMutableTreeNode.
 -error.invalid_value_for_option = Valor no válido de {0} para la opción {1}
  error.implementation_error_cannot_import_vamsas_doc = Error de implementación - todavía no es posible importar el documento VAMSAS existente en una sesión existente.
  label.vamsas_doc_couldnt_be_opened_as_new_session = El documento VAMSAS no ha podido abrirse como una nueva sesión. Por favor, escoja otra.
  error.implementation_error_vamsas_operation_not_init = Â¡Error de implementación! Operaciones VAMSAS cuando el cliente no estaba inicializado ni conectado
@@@ -1176,7 -1174,6 +1172,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
@@@ -175,8 -175,6 +175,8 @@@ public class Jalvie
     * 
     * @param args
     *          open <em>filename</em>
 +   * @throws InterruptedException
 +   * @throws IOException
     */
    public static void main(String[] args)
    {
  
    /**
     * @param args
 +   * @throws InterruptedException
 +   * @throws IOException
     */
    void doMain(String[] args)
    {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
      } catch (Exception ex)
      {
+       System.err.println("Unexpected Look and Feel Exception");
+       ex.printStackTrace();
      }
      if (Platform.isAMac())
      {
          System.err.println(
                  "Failed to set QuaQua look and feel: " + e.toString());
        }
+       if (!ch.randelshofer.quaqua.QuaquaManager.getLookAndFeel()
+               .equals(UIManager.getLookAndFeel()))
+       {
+         try
+         {
+           System.err.println(
+                   "Quaqua LaF not available. Using VAqua(4).");
+           UIManager.setLookAndFeel("org.violetlib.aqua.AquaLookAndFeel");
+         } catch (Throwable e)
+         {
+           System.err.println(
+                   "Failed to reset look and feel: " + e.toString());
+         }
+       }
      }
  
      /*
@@@ -1471,8 -1471,7 +1471,8 @@@ public class Desktop extends jalview.jb
        return;
      }
  
 -    AlignmentViewport source = null, target = null;
 +    AlignViewportI source = null;
 +    AlignViewportI target = null;
      if (frames[0] instanceof AlignFrame)
      {
        source = ((AlignFrame) frames[0]).getCurrentView();
    {
      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<StructureViewerBase> getStructureViewers(
+           AlignmentPanel apanel,
+           Class<? extends StructureViewerBase> structureViewerClass)
+   {
+     List<StructureViewerBase> 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;
+   }
  }
@@@ -1867,7 -1867,7 +1867,7 @@@ public class SeqPanel extends JPane
  
      // always do this - annotation has own state
      // but defer colourscheme update until hidden sequences are passed in
 -    boolean vischange = stretchGroup.recalcConservation(true);
 +    boolean vischange = stretchGroup.recalcAnnotations(true);
      updateOverviewAndStructs |= vischange && av.isSelectionDefinedGroup()
              && afterDrag;
      if (stretchGroup.cs != null)
  
      return true;
    }
+   /**
+    * 
+    * @return null or last search results handled by this panel
+    */
+   public SearchResultsI getLastSearchResults()
+   {
+     return lastSearchResults;
+   }
  }
@@@ -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;
    }
  
  
      for (int[] range : map.getFromRanges())
      {
 -      addRange(range, fromShifts);
 +      MappingUtils.addRange(range, fromShifts);
      }
      for (int[] range : map.getToRanges())
      {
 -      addRange(range, toShifts);
 +      MappingUtils.addRange(range, toShifts);
      }
    }
  
    /**
 -   * Adds the given range to a list of ranges. If the new range just extends
 -   * existing ranges, the current endpoint is updated instead.
 -   * 
 -   * @param range
 -   * @param addTo
 -   */
 -  static void addRange(int[] range, List<int[]> addTo)
 -  {
 -    /*
 -     * list is empty - add to it!
 -     */
 -    if (addTo.size() == 0)
 -    {
 -      addTo.add(range);
 -      return;
 -    }
 -
 -    int[] last = addTo.get(addTo.size() - 1);
 -    boolean lastForward = last[1] >= last[0];
 -    boolean newForward = range[1] >= range[0];
 -
 -    /*
 -     * contiguous range in the same direction - just update endpoint
 -     */
 -    if (lastForward == newForward && last[1] == range[0])
 -    {
 -      last[1] = range[1];
 -      return;
 -    }
 -
 -    /*
 -     * next range starts at +1 in forward sense - update endpoint
 -     */
 -    if (lastForward && newForward && range[0] == last[1] + 1)
 -    {
 -      last[1] = range[1];
 -      return;
 -    }
 -
 -    /*
 -     * next range starts at -1 in reverse sense - update endpoint
 -     */
 -    if (!lastForward && !newForward && range[0] == last[1] - 1)
 -    {
 -      last[1] = range[1];
 -      return;
 -    }
 -
 -    /*
 -     * just add the new range
 -     */
 -    addTo.add(range);
 -  }
 -
 -  /**
     * Returns true if mapping is from forward strand, false if from reverse
     * strand. Result is just based on the first 'from' range that is not a single
     * position. Default is true unless proven to be false. Behaviour is not well
@@@ -33,9 -33,7 +33,9 @@@ import jalview.api.FeatureColourI
  import jalview.api.ViewStyleI;
  import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.AlignmentI;
 +import jalview.datamodel.HiddenMarkovModel;
  import jalview.datamodel.HiddenSequences;
 +import jalview.datamodel.Mapping;
  import jalview.datamodel.PDBEntry;
  import jalview.datamodel.PDBEntry.Type;
  import jalview.datamodel.SequenceCollectionI;
@@@ -46,6 -44,7 +46,6 @@@ import jalview.datamodel.features.Featu
  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;
@@@ -80,8 -79,6 +80,8 @@@ import org.testng.AssertJUnit
  import org.testng.annotations.BeforeClass;
  import org.testng.annotations.Test;
  
 +import junit.extensions.PA;
 +
  @Test(singleThreaded = true)
  public class Jalview2xmlTests extends Jalview2xmlBase
  {
  
    }
  
+   /**
+    * 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
    {
      AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
              "examples/uniref50.fa", DataSourceType.FILE);
  
 -    AlignViewport av = af.getViewport();
 +    AlignViewportI av = af.getViewport();
      AlignmentI al = av.getAlignment();
  
      /*
     * @throws IOException
     */
    @Test(groups = { "Functional" })
 -  public void testSaveLoadFeatureColoursAndFilters() throws IOException
 +  public void testStoreAndRecoverFeatureColoursAndFilters()
 +          throws IOException
    {
      AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
              ">Seq1\nACDEFGHIKLM", DataSourceType.PASTE);
      addFeature(seq, featureType, score++);
      addFeature(seq, featureType, score);
    }
 +
 +  /**
 +   * Load an HMM profile to an alignment, and confirm it is correctly restored
 +   * when reloaded from project
 +   * 
 +   * @throws IOException
 +   */
 +  @Test(groups = { "Functional" })
 +  public void testStoreAndRecoverHmmProfile() throws IOException
 +  {
 +    Desktop.instance.closeAll_actionPerformed(null);
 +    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
 +            "examples/uniref50.fa", DataSourceType.FILE);
 +  
 +    AlignViewportI av = af.getViewport();
 +    AlignmentI al = av.getAlignment();
 +
 +    /*
 +     * mimic drag and drop of hmm file on to alignment
 +     */
 +    AlignFrame af2 = new FileLoader().LoadFileWaitTillLoaded(
 +            "examples/uniref50.hmm", DataSourceType.FILE);
 +    al.insertSequenceAt(0,
 +            af2.getViewport().getAlignment().getSequenceAt(0));
 +
 +    /*
 +     * check it loaded in
 +     */
 +    SequenceI hmmSeq = al.getSequenceAt(0);
 +    assertTrue(hmmSeq.hasHMMProfile());
 +    HiddenMarkovModel hmm = hmmSeq.getHMM();
 +    assertSame(hmm.getConsensusSequence(), hmmSeq);
 +
 +    /*
 +     * save project, close windows, reload project, verify
 +     */
 +    File tfile = File.createTempFile("testStoreAndRecoverHmmProfile",
 +            ".jvp");
 +    tfile.deleteOnExit();
 +    new Jalview2XML(false).saveState(tfile);
 +    Desktop.instance.closeAll_actionPerformed(null);
 +    af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
 +            DataSourceType.FILE);
 +    Assert.assertNotNull(af, "Failed to reload project");
 +
 +    hmmSeq = al.getSequenceAt(0);
 +    assertTrue(hmmSeq.hasHMMProfile());
 +    assertSame(hmm.getConsensusSequence(), hmmSeq);
 +    Mapping mapToHmmConsensus = (Mapping) PA.getValue(hmm,
 +            "mapToHmmConsensus");
 +    assertNotNull(mapToHmmConsensus);
 +    assertSame(mapToHmmConsensus.getTo(), hmmSeq.getDatasetSequence());
 +  }
  }
@@@ -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));
      assertEquals("[ [11, 16] ] 1:3 to [ [72, 53] ]", ml.toString());
    }
  
 -  @Test(groups = "Functional")
 -  public void testAddRange()
 -  {
 -    int[] range = { 1, 5 };
 -    List<int[]> ranges = new ArrayList<>();
 -
 -    // add to empty list:
 -    MapList.addRange(range, ranges);
 -    assertEquals(1, ranges.size());
 -    assertSame(range, ranges.get(0));
 -
 -    // extend contiguous (same position):
 -    MapList.addRange(new int[] { 5, 10 }, ranges);
 -    assertEquals(1, ranges.size());
 -    assertEquals(1, ranges.get(0)[0]);
 -    assertEquals(10, ranges.get(0)[1]);
 -
 -    // extend contiguous (next position):
 -    MapList.addRange(new int[] { 11, 15 }, ranges);
 -    assertEquals(1, ranges.size());
 -    assertEquals(1, ranges.get(0)[0]);
 -    assertEquals(15, ranges.get(0)[1]);
 -
 -    // change direction: range is not merged:
 -    MapList.addRange(new int[] { 16, 10 }, ranges);
 -    assertEquals(2, ranges.size());
 -    assertEquals(16, ranges.get(1)[0]);
 -    assertEquals(10, ranges.get(1)[1]);
 -
 -    // extend reverse contiguous (same position):
 -    MapList.addRange(new int[] { 10, 8 }, ranges);
 -    assertEquals(2, ranges.size());
 -    assertEquals(16, ranges.get(1)[0]);
 -    assertEquals(8, ranges.get(1)[1]);
 -
 -    // extend reverse contiguous (next position):
 -    MapList.addRange(new int[] { 7, 6 }, ranges);
 -    assertEquals(2, ranges.size());
 -    assertEquals(16, ranges.get(1)[0]);
 -    assertEquals(6, ranges.get(1)[1]);
 -
 -    // change direction: range is not merged:
 -    MapList.addRange(new int[] { 6, 9 }, ranges);
 -    assertEquals(3, ranges.size());
 -    assertEquals(6, ranges.get(2)[0]);
 -    assertEquals(9, ranges.get(2)[1]);
 -
 -    // not contiguous: not merged
 -    MapList.addRange(new int[] { 11, 12 }, ranges);
 -    assertEquals(4, ranges.size());
 -    assertEquals(11, ranges.get(3)[0]);
 -    assertEquals(12, ranges.get(3)[1]);
 -  }
 -
    /**
     * Check state after construction
     */