Merge branch 'releases/Release_2_10_4_Branch' into develop
authorJim Procter <jprocter@issues.jalview.org>
Wed, 7 Mar 2018 14:40:59 +0000 (14:40 +0000)
committerJim Procter <jprocter@issues.jalview.org>
Wed, 7 Mar 2018 14:40:59 +0000 (14:40 +0000)
check carefully that JAL-2885 (configurable Ensembl/Genomes Endpoints) functionality is still present

22 files changed:
1  2 
build.xml
src/jalview/analysis/AlignmentUtils.java
src/jalview/analysis/Dna.java
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/FeatureSettings.java
src/jalview/datamodel/Sequence.java
src/jalview/datamodel/SequenceI.java
src/jalview/ext/ensembl/EnsemblInfo.java
src/jalview/ext/ensembl/EnsemblLookup.java
src/jalview/ext/ensembl/EnsemblRestClient.java
src/jalview/ext/ensembl/EnsemblSequenceFetcher.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AnnotationLabels.java
src/jalview/gui/FeatureSettings.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/PopupMenu.java
src/jalview/gui/SeqPanel.java
src/jalview/util/MappingUtils.java
test/jalview/analysis/AlignmentUtilsTests.java
test/jalview/gui/AlignFrameTest.java
test/jalview/gui/PopupMenuTest.java
test/jalview/util/MappingUtilsTest.java

diff --cc build.xml
Simple merge
@@@ -2049,16 -2004,16 +2059,18 @@@ public class AlignmentUtil
     * 
     * @param cdsSeq
     * @param contig
 +   * @param proteinProduct
     * @param mapping
 -   * @return list of DBRefEntrys added.
 +   * @return list of DBRefEntrys added
     */
 -  public static List<DBRefEntry> propagateDBRefsToCDS(SequenceI cdsSeq,
 +  protected static List<DBRefEntry> propagateDBRefsToCDS(SequenceI cdsSeq,
            SequenceI contig, SequenceI proteinProduct, Mapping mapping)
    {
 -    // gather direct refs from contig congrent with mapping
 +    // gather direct refs from contig congruent with mapping
      List<DBRefEntry> direct = new ArrayList<>();
      HashSet<String> directSources = new HashSet<>();
++
      if (contig.getDBRefs() != null)
      {
        for (DBRefEntry dbr : contig.getDBRefs())
@@@ -392,26 -436,12 +436,13 @@@ public class Dn
            List<SequenceI> proteinSeqs)
    {
      List<int[]> skip = new ArrayList<>();
-     int skipint[] = null;
-     ShiftList vismapping = new ShiftList(); // map from viscontigs to seqstring
-     // intervals
-     int vc;
-     int[] scontigs = new int[contigs.length];
+     int[] skipint = null;
++
      int npos = 0;
-     for (vc = 0; vc < contigs.length; vc += 2)
-     {
-       if (vc == 0)
-       {
-         vismapping.addShift(npos, contigs[vc]);
-       }
-       else
-       {
-         // hidden region
-         vismapping.addShift(npos, contigs[vc] - contigs[vc - 1] + 1);
-       }
-       scontigs[vc] = contigs[vc];
-       scontigs[vc + 1] = contigs[vc + 1];
-     }
+     int vc = 0;
+     int[] scontigs = new int[startcontigs.length];
+     System.arraycopy(startcontigs, 0, scontigs, 0, startcontigs.length);
  
      // allocate a roughly sized buffer for the protein sequence
      StringBuilder protein = new StringBuilder(seqstring.length() / 2);
Simple merge
Simple merge
  package jalview.datamodel;
  
  import jalview.datamodel.features.SequenceFeaturesI;
 +import jalview.util.MapList;
  
  import java.util.BitSet;
+ import java.util.Iterator;
  import java.util.List;
  import java.util.Vector;
  
@@@ -527,20 -534,23 +535,42 @@@ public interface SequenceI extends ASeq
    public int replace(char c1, char c2);
  
    /**
 +   * Answers the GeneLociI, or null if not known
 +   * 
 +   * @return
 +   */
 +  GeneLociI getGeneLoci();
 +
 +  /**
 +   * Sets the mapping to gene loci for the sequence
 +   * 
 +   * @param speciesId
 +   * @param assemblyId
 +   * @param chromosomeId
 +   * @param map
 +   */
 +  void setGeneLoci(String speciesId, String assemblyId,
 +          String chromosomeId, MapList map);
++
++
++  /**
+    * Returns the sequence string constructed from the substrings of a sequence
+    * defined by the int[] ranges provided by an iterator. E.g. the iterator
+    * could iterate over all visible regions of the alignment
+    * 
+    * @param it
+    *          the iterator to use
+    * @return a String corresponding to the sequence
+    */
+   public String getSequenceStringFromIterator(Iterator<int[]> it);
+   /**
+    * Locate the first position in this sequence which is not contained in an
+    * iterator region. If no such position exists, return 0
+    * 
+    * @param it
+    *          iterator over regions
+    * @return first residue not contained in regions
+    */
+   public int firstResidueOutsideIterator(Iterator<int[]> it);
  }
 -/*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 - * 
 - * This file is part of Jalview.
 - * 
 - * Jalview is free software: you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License 
 - * as published by the Free Software Foundation, either version 3
 - * of the License, or (at your option) any later version.
 - *  
 - * Jalview is distributed in the hope that it will be useful, but 
 - * WITHOUT ANY WARRANTY; without even the implied warranty 
 - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
 - * PURPOSE.  See the GNU General Public License for more details.
 - * 
 - * You should have received a copy of the GNU General Public License
 - * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
 - * The Jalview Authors are detailed in the 'AUTHORS' file.
 - */
  package jalview.ext.ensembl;
  
 -/**
 - * A data class to model the data and rest version of one Ensembl domain,
 - * currently for rest.ensembl.org and rest.ensemblgenomes.org
 - * 
 - * @author gmcarstairs
 - */
 -class EnsemblInfo
 +import jalview.datamodel.AlignmentI;
 +import jalview.datamodel.DBRefSource;
 +
 +import java.io.BufferedReader;
 +import java.io.IOException;
 +import java.net.MalformedURLException;
 +import java.net.URL;
 +import java.util.HashMap;
 +import java.util.Iterator;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Set;
 +
 +import org.json.simple.JSONArray;
 +import org.json.simple.parser.JSONParser;
 +import org.json.simple.parser.ParseException;
 +
 +public class EnsemblInfo extends EnsemblRestClient
  {
 -  /*
 -   * The http domain this object is holding data values for
 -   */
 -  String domain;
  
    /*
 -   * The latest version Jalview has tested for, e.g. "4.5"; a minor version change should be
 -   * ok, a major version change may break stuff 
 +   * cached results of REST /info/divisions service, currently
 +   * <pre>
 +   * { 
 +   *  { "ENSEMBLFUNGI", "http://rest.ensemblgenomes.org"},
 +   *    "ENSEMBLBACTERIA", "http://rest.ensemblgenomes.org"},
 +   *    "ENSEMBLPROTISTS", "http://rest.ensemblgenomes.org"},
 +   *    "ENSEMBLMETAZOA", "http://rest.ensemblgenomes.org"},
 +   *    "ENSEMBLPLANTS",  "http://rest.ensemblgenomes.org"},
 +   *    "ENSEMBL", "http://rest.ensembl.org" }
 +   *  }
 +   * </pre>
 +   * The values for EnsemblGenomes are retrieved by a REST call, that for
 +   * Ensembl is added programmatically for convenience of lookup
     */
 -  String expectedRestVersion;
 +  private static Map<String, String> divisions;
  
 -  /*
 -   * Major / minor / point version e.g. "4.5.1"
 -   * @see http://rest.ensembl.org/info/rest/?content-type=application/json
 -   */
 -  String restVersion;
 +  @Override
 +  public String getDbName()
 +  {
 +    return "ENSEMBL";
 +  }
  
 -  /*
 -   * data version
 -   * @see http://rest.ensembl.org/info/data/?content-type=application/json
 -   */
 -  String dataVersion;
 +  @Override
 +  public AlignmentI getSequenceRecords(String queries) throws Exception
 +  {
 +    return null;
 +  }
  
 -  /*
 -   * true when http://rest.ensembl.org/info/ping/?content-type=application/json
 -   * returns response code 200 and not {"error":"Database is unavailable"}
 +  @Override
 +  protected URL getUrl(List<String> ids) throws MalformedURLException
 +  {
 +    return null;
 +  }
 +
 +  @Override
 +  protected boolean useGetRequest()
 +  {
 +    return true;
 +  }
 +
 +  @Override
 +  protected String getRequestMimeType(boolean multipleIds)
 +  {
 +    return "application/json";
 +  }
 +
 +  @Override
 +  protected String getResponseMimeType()
 +  {
 +    return "application/json";
 +  }
 +
 +  /**
 +   * Answers the domain (http://rest.ensembl.org or
 +   * http://rest.ensemblgenomes.org) for the given division, or null if not
 +   * recognised by Ensembl.
 +   * 
 +   * @param division
 +   * @return
     */
 -  boolean restAvailable;
 +  public String getDomain(String division)
 +  {
 +    if (divisions == null)
 +    {
 +      fetchDivisions();
 +    }
 +    return divisions.get(division.toUpperCase());
 +  }
  
 -  /*
 -   * absolute time when availability was last checked
 +  /**
 +   * On first request only, populate the lookup map by fetching the list of
 +   * divisions known to EnsemblGenomes.
     */
 -  long lastAvailableCheckTime;
 +  void fetchDivisions()
 +  {
 +    divisions = new HashMap<>();
  
 -  /*
 -   * absolute time when version numbers were last checked
 +    /*
 +     * for convenience, pre-fill ensembl.org as the domain for "ENSEMBL"
 +     */
-     divisions.put(DBRefSource.ENSEMBL.toUpperCase(), ENSEMBL_REST);
++    divisions.put(DBRefSource.ENSEMBL.toUpperCase(), ensemblDomain);
 +
 +    BufferedReader br = null;
 +    try
 +    {
-       URL url = getDivisionsUrl(ENSEMBL_GENOMES_REST);
++      URL url = getDivisionsUrl(ensemblGenomesDomain);
 +      if (url != null)
 +      {
 +        br = getHttpResponse(url, null);
 +      }
-       parseResponse(br, ENSEMBL_GENOMES_REST);
++      parseResponse(br, ensemblGenomesDomain);
 +    } catch (IOException e)
 +    {
 +      // ignore
 +    } finally
 +    {
 +      if (br != null)
 +      {
 +        try
 +        {
 +          br.close();
 +        } catch (IOException e)
 +        {
 +          // ignore
 +        }
 +      }
 +    }
 +  }
 +
 +  /**
 +   * Parses the JSON response to /info/divisions, and add each to the lookup map
 +   * 
 +   * @param br
 +   * @param domain
     */
 -  long lastVersionCheckTime;
 +  void parseResponse(BufferedReader br, String domain)
 +  {
 +    JSONParser jp = new JSONParser();
 +
 +    try
 +    {
 +      JSONArray parsed = (JSONArray) jp.parse(br);
  
 -  // flag set to true if REST major version is not the one expected
 -  boolean restMajorVersionMismatch;
 +      Iterator rvals = parsed.iterator();
 +      while (rvals.hasNext())
 +      {
 +        String division = rvals.next().toString();
 +        divisions.put(division.toUpperCase(), domain);
 +      }
 +    } catch (IOException | ParseException | NumberFormatException e)
 +    {
 +      // ignore
 +    }
 +  }
  
    /**
 -   * Constructor given expected REST version number e.g 4.5 or 3.4.3
 +   * Constructs the URL for the EnsemblGenomes /info/divisions REST service
 +   * @param domain TODO
     * 
 -   * @param restExpected
 +   * @return
 +   * @throws MalformedURLException
     */
 -  EnsemblInfo(String theDomain, String restExpected)
 +  URL getDivisionsUrl(String domain) throws MalformedURLException
    {
 -    domain = theDomain;
 -    expectedRestVersion = restExpected;
 -    lastAvailableCheckTime = -1;
 -    lastVersionCheckTime = -1;
 +    return new URL(domain
 +            + "/info/divisions?content-type=application/json");
    }
  
 +  /**
 +   * Returns the set of 'divisions' recognised by Ensembl or EnsemblGenomes
 +   * 
 +   * @return
 +   */
 +  public Set<String> getDivisions() {
 +    if (divisions == null)
 +    {
 +      fetchDivisions();
 +    }
 +
 +    return divisions.keySet();
 +  }
  }
@@@ -46,7 -41,18 +46,18 @@@ import org.json.simple.parser.ParseExce
   */
  public class EnsemblLookup extends EnsemblRestClient
  {
 -
 +  private static final String SPECIES = "species";
+   private static final String OBJECT_TYPE_TRANSLATION = "Translation";
+   private static final String PARENT = "Parent";
+   private static final String OBJECT_TYPE_TRANSCRIPT = "Transcript";
+   private static final String ID = "id";
+   private static final String OBJECT_TYPE_GENE = "Gene";
+   private static final String OBJECT_TYPE = "object_type";
+   /**
+    * keep track of last identifier retrieved to break loops
+    */
+   private String lastId;
  
    /**
     * Default constructor (to use rest.ensembl.org)
@@@ -85,11 -82,11 +85,11 @@@ abstract class EnsemblRestClient extend
  
    static
    {
 +    domainData = new HashMap<>();
-     domainData.put(ENSEMBL_REST,
-             new EnsemblData(ENSEMBL_REST, LATEST_ENSEMBL_REST_VERSION));
-     domainData.put(ENSEMBL_GENOMES_REST, new EnsemblData(
-             ENSEMBL_GENOMES_REST, LATEST_ENSEMBLGENOMES_REST_VERSION));
+     domainData.put(DEFAULT_ENSEMBL_BASEURL,
 -            new EnsemblInfo(DEFAULT_ENSEMBL_BASEURL, LATEST_ENSEMBL_REST_VERSION));
 -    domainData.put(DEFAULT_ENSEMBL_GENOMES_BASEURL,
 -            new EnsemblInfo(
++            new EnsemblData(DEFAULT_ENSEMBL_BASEURL, LATEST_ENSEMBL_REST_VERSION));
++    domainData.put(DEFAULT_ENSEMBL_GENOMES_BASEURL, new EnsemblData(
+             DEFAULT_ENSEMBL_GENOMES_BASEURL, LATEST_ENSEMBLGENOMES_REST_VERSION));
    }
  
    protected volatile boolean inProgress = false;
     */
    public EnsemblRestClient()
    {
-     this(ENSEMBL_REST);
+     super();
+     /*
+      * initialise domain info lazily
+      */
+     if (!domainData.containsKey(ensemblDomain))
+     {
+       domainData.put(ensemblDomain,
 -              new EnsemblInfo(ensemblDomain, LATEST_ENSEMBL_REST_VERSION));
++              new EnsemblData(ensemblDomain, LATEST_ENSEMBL_REST_VERSION));
+     }
+     if (!domainData.containsKey(ensemblGenomesDomain))
+     {
 -      domainData.put(ensemblGenomesDomain, new EnsemblInfo(
++      domainData.put(ensemblGenomesDomain, new EnsemblData(
+               ensemblGenomesDomain, LATEST_ENSEMBLGENOMES_REST_VERSION));
+     }
    }
  
    /**
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -2082,6 -2034,6 +2083,7 @@@ public class AlignmentUtilsTest
      List<DnaVariant> codon1Variants = new ArrayList<>();
      List<DnaVariant> codon2Variants = new ArrayList<>();
      List<DnaVariant> codon3Variants = new ArrayList<>();
++
      List<DnaVariant> codonVariants[] = new ArrayList[3];
      codonVariants[0] = codon1Variants;
      codonVariants[1] = codon2Variants;
@@@ -31,6 -30,6 +31,7 @@@ import jalview.bin.Cache
  import jalview.bin.Jalview;
  import jalview.datamodel.Alignment;
  import jalview.datamodel.AlignmentI;
++import jalview.datamodel.HiddenColumns;
  import jalview.datamodel.Sequence;
  import jalview.datamodel.SequenceFeature;
  import jalview.datamodel.SequenceGroup;
@@@ -92,52 -85,37 +93,60 @@@ public class AlignFrameTes
       */
      assertFalse(alignFrame.hideFeatureColumns("exon", true));
      assertTrue(alignFrame.getViewport().getColumnSelection().isEmpty());
-     assertTrue(alignFrame.getViewport().getAlignment().getHiddenColumns()
-             .getHiddenColumnsCopy().isEmpty());
++
+     assertEquals(alignFrame.getViewport().getAlignment().getHiddenColumns()
+             .getNumberOfRegions(), 0);
++
      assertFalse(alignFrame.hideFeatureColumns("exon", false));
      assertTrue(alignFrame.getViewport().getColumnSelection().isEmpty());
-     assertTrue(alignFrame.getViewport().getAlignment().getHiddenColumns()
-             .getHiddenColumnsCopy().isEmpty());
++
+     assertEquals(alignFrame.getViewport().getAlignment().getHiddenColumns()
+             .getNumberOfRegions(), 0);
  
      /*
       * hiding a feature in all columns does nothing
       */
      assertFalse(alignFrame.hideFeatureColumns("Metal", true));
      assertTrue(alignFrame.getViewport().getColumnSelection().isEmpty());
-     List<int[]> hidden = alignFrame.getViewport().getAlignment()
-             .getHiddenColumns().getHiddenColumnsCopy();
-     assertTrue(hidden.isEmpty());
++
+     assertEquals(alignFrame.getViewport().getAlignment().getHiddenColumns()
+             .getNumberOfRegions(), 0);
 +
 +    /*
 +     * threshold Metal to hide features where score < 5
 +     * seq1 feature in columns 1-5 is hidden
 +     * seq2 feature in columns 6-10 is shown
 +     */
 +    FeatureColourI fc = new FeatureColour(Color.red, Color.blue, 0f, 10f);
 +    fc.setAboveThreshold(true);
 +    fc.setThreshold(5f);
 +    alignFrame.getFeatureRenderer().setColour("Metal", fc);
 +    assertTrue(alignFrame.hideFeatureColumns("Metal", true));
-     hidden = alignFrame.getViewport().getAlignment().getHiddenColumns()
-             .getHiddenColumnsCopy();
-     assertEquals(hidden.size(), 1);
-     assertEquals(hidden.get(0)[0], 5);
-     assertEquals(hidden.get(0)[1], 9);
++    HiddenColumns hidden = alignFrame.getViewport().getAlignment().getHiddenColumns();
++    assertEquals(hidden.getNumberOfRegions(), 1);
++    Iterator<int[]> regions = hidden.iterator();
++    int[] next = regions.next();
++    assertEquals(next[0], 5);
++    assertEquals(next[1], 9);
 +
      /*
       * hide a feature present in some columns
       * sequence positions [2-4], [7-9] are column positions
       * [1-3], [6-8] base zero
       */
 +    alignFrame.getViewport().showAllHiddenColumns();
      assertTrue(alignFrame.hideFeatureColumns("Turn", true));
-     hidden = alignFrame.getViewport().getAlignment().getHiddenColumns()
-             .getHiddenColumnsCopy();
-     assertEquals(hidden.size(), 2);
-     assertEquals(hidden.get(0)[0], 1);
-     assertEquals(hidden.get(0)[1], 3);
-     assertEquals(hidden.get(1)[0], 6);
-     assertEquals(hidden.get(1)[1], 8);
 -    Iterator<int[]> regions = alignFrame.getViewport().getAlignment()
++    regions = alignFrame.getViewport().getAlignment()
+             .getHiddenColumns().iterator();
+     assertEquals(alignFrame.getViewport().getAlignment().getHiddenColumns()
+             .getNumberOfRegions(), 2);
 -    int[] next = regions.next();
++    next = regions.next();
+     assertEquals(next[0], 1);
+     assertEquals(next[1], 3);
+     next = regions.next();
+     assertEquals(next[0], 6);
+     assertEquals(next[1], 8);
    }
  
    @BeforeClass(alwaysRun = true)
@@@ -30,9 -29,12 +30,13 @@@ import jalview.bin.Cache
  import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.Annotation;
+ import jalview.datamodel.ColumnSelection;
  import jalview.datamodel.DBRefEntry;
  import jalview.datamodel.DBRefSource;
+ import jalview.datamodel.HiddenColumns;
+ import jalview.datamodel.Sequence;
 +import jalview.datamodel.SequenceFeature;
+ import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
  import jalview.io.DataSourceType;
  import jalview.io.FileFormat;
@@@ -45,7 -44,7 +49,8 @@@ import jalview.util.UrlConstants
  import java.awt.Component;
  import java.io.IOException;
  import java.util.ArrayList;
 +import java.util.Collections;
+ import java.util.Iterator;
  import java.util.List;
  
  import javax.swing.JMenu;
@@@ -589,4 -563,117 +594,117 @@@ public class PopupMenuTes
      assertFalse(linkMenu.isEnabled());
  
    }
+   /**
+    * Test for adding feature links
+    */
+   @Test(groups = { "Functional" })
+   public void testHideInsertions()
+   {
+     // get sequences from the alignment
+     List<SequenceI> seqs = parentPanel.getAlignment().getSequences();
+     
+     // add our own seqs to avoid problems with changes to existing sequences
+     // (gap at end of sequences varies depending on how tests are run!)
+     Sequence seqGap1 = new Sequence("GappySeq",
+             "AAAA----AA-AAAAAAA---AAA-----------AAAAAAAAAA--");
+     seqGap1.createDatasetSequence();
+     seqs.add(seqGap1);
+     Sequence seqGap2 = new Sequence("LessGappySeq",
+             "AAAAAA-AAAAA---AAA--AAAAA--AAAAAAA-AAAAAA");
+     seqGap2.createDatasetSequence();
+     seqs.add(seqGap2);
+     Sequence seqGap3 = new Sequence("AnotherGapSeq",
+             "AAAAAA-AAAAAA--AAAAAA-AAAAAAAAAAA---AAAAAAAA");
+     seqGap3.createDatasetSequence();
+     seqs.add(seqGap3);
+     Sequence seqGap4 = new Sequence("NoGaps",
+             "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+     seqGap4.createDatasetSequence();
+     seqs.add(seqGap4);
+     ColumnSelection sel = new ColumnSelection();
+     parentPanel.av.getAlignment().getHiddenColumns()
+             .revealAllHiddenColumns(sel);
+     // get the Popup Menu for 7th sequence - no insertions
 -    testee = new PopupMenu(parentPanel, (Sequence) seqs.get(7), null);
++    testee = new PopupMenu(parentPanel, seqs.get(7), null);
+     testee.hideInsertions_actionPerformed(null);
+     
+     HiddenColumns hidden = parentPanel.av.getAlignment().getHiddenColumns();
+     Iterator<int[]> it = hidden.iterator();
+     assertFalse(it.hasNext());
+     // get the Popup Menu for GappySeq - this time we have insertions
 -    testee = new PopupMenu(parentPanel, (Sequence) seqs.get(4), null);
++    testee = new PopupMenu(parentPanel, seqs.get(4), null);
+     testee.hideInsertions_actionPerformed(null);
+     hidden = parentPanel.av.getAlignment().getHiddenColumns();
+     it = hidden.iterator();
+     assertTrue(it.hasNext());
+     int[] region = it.next();
+     assertEquals(region[0], 4);
+     assertEquals(region[1], 7);
+     assertTrue(it.hasNext());
+     region = it.next();
+     assertEquals(region[0], 10);
+     assertEquals(region[1], 10);
+     assertTrue(it.hasNext());
+     region = it.next();
+     assertEquals(region[0], 18);
+     assertEquals(region[1], 20);
+     assertTrue(it.hasNext());
+     region = it.next();
+     assertEquals(region[0], 24);
+     assertEquals(region[1], 34);
+     assertTrue(it.hasNext());
+     region = it.next();
+     assertEquals(region[0], 45);
+     assertEquals(region[1], 46);
+     assertFalse(it.hasNext());
+     sel = new ColumnSelection();
+     hidden.revealAllHiddenColumns(sel);
+     // make a sequence group and hide insertions within the group
+     SequenceGroup sg = new SequenceGroup();
+     sg.setStartRes(8);
+     sg.setEndRes(42);
+     sg.addSequence(seqGap2, false);
+     sg.addSequence(seqGap3, false);
+     parentPanel.av.setSelectionGroup(sg);
+     // hide columns outside and within selection
+     // only hidden columns outside the collection will be retained (unless also
+     // gaps in the selection)
+     hidden.hideColumns(1, 10);
+     hidden.hideColumns(31, 40);
+     // get the Popup Menu for LessGappySeq in the sequence group
 -    testee = new PopupMenu(parentPanel, (Sequence) seqs.get(5), null);
++    testee = new PopupMenu(parentPanel, seqs.get(5), null);
+     testee.hideInsertions_actionPerformed(null);
+     hidden = parentPanel.av.getAlignment().getHiddenColumns();
+     it = hidden.iterator();
+     assertTrue(it.hasNext());
+     region = it.next();
+     assertEquals(region[0], 1);
+     assertEquals(region[1], 7);
+     assertTrue(it.hasNext());
+     region = it.next();
+     assertEquals(region[0], 13);
+     assertEquals(region[1], 14);
+     assertTrue(it.hasNext());
+     region = it.next();
+     assertEquals(region[0], 34);
+     assertEquals(region[1], 34);
+   }
  }