Merge branch 'features/mchmmer' into features/mchmmer_merge_JAL-1950 features/mchmmer_merge_JAL-1950
authorJim Procter <jprocter@issues.jalview.org>
Wed, 18 Jul 2018 14:25:48 +0000 (15:25 +0100)
committerJim Procter <jprocter@issues.jalview.org>
Wed, 18 Jul 2018 14:25:48 +0000 (15:25 +0100)
12 files changed:
src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java
src/jalview/datamodel/features/FeatureAttributes.java
src/jalview/ext/ensembl/EnsemblRestClient.java
src/jalview/gui/CutAndPasteHtmlTransfer.java
src/jalview/gui/FeatureTypeSettings.java
src/jalview/gui/Jalview2XML.java
src/jalview/hmmer/HmmerCommand.java
src/jalview/io/SequenceAnnotationReport.java
src/jalview/util/MapList.java
test/jalview/analysis/AlignmentUtilsTests.java
test/jalview/datamodel/features/FeatureAttributesTest.java
test/jalview/util/MapListTest.java

index 85ae718..a910a5a 100644 (file)
@@ -40,6 +40,7 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -83,7 +84,7 @@ public class ChimeraManager
     this.structureManager = structureManager;
     chimera = null;
     chimeraListenerThread = null;
-    currentModelsMap = new HashMap<Integer, ChimeraModel>();
+    currentModelsMap = new HashMap<>();
 
   }
 
@@ -98,7 +99,7 @@ public class ChimeraManager
   public List<ChimeraModel> getChimeraModels(String modelName,
           ModelType modelType)
   {
-    List<ChimeraModel> models = new ArrayList<ChimeraModel>();
+    List<ChimeraModel> models = new ArrayList<>();
     for (ChimeraModel model : currentModelsMap.values())
     {
       if (modelName.equals(model.getModelName())
@@ -112,7 +113,7 @@ public class ChimeraManager
 
   public Map<String, List<ChimeraModel>> getChimeraModelsMap()
   {
-    Map<String, List<ChimeraModel>> models = new HashMap<String, List<ChimeraModel>>();
+    Map<String, List<ChimeraModel>> models = new HashMap<>();
     for (ChimeraModel model : currentModelsMap.values())
     {
       String modelName = model.getModelName();
@@ -393,7 +394,7 @@ public class ChimeraManager
 
   public Map<Integer, ChimeraModel> getSelectedModels()
   {
-    Map<Integer, ChimeraModel> selectedModelsMap = new HashMap<Integer, ChimeraModel>();
+    Map<Integer, ChimeraModel> selectedModelsMap = new HashMap<>();
     List<String> chimeraReply = sendChimeraCommand(
             "list selection level molecule", true);
     if (chimeraReply != null)
@@ -418,7 +419,7 @@ public class ChimeraManager
    */
   public List<String> getSelectedResidueSpecs()
   {
-    List<String> selectedResidues = new ArrayList<String>();
+    List<String> selectedResidues = new ArrayList<>();
     List<String> chimeraReply = sendChimeraCommand(
             "list selection level residue", true);
     if (chimeraReply != null)
@@ -471,7 +472,7 @@ public class ChimeraManager
   // TODO: [Optional] Handle smiles names in a better way in Chimera?
   public List<ChimeraModel> getModelList()
   {
-    List<ChimeraModel> modelList = new ArrayList<ChimeraModel>();
+    List<ChimeraModel> modelList = new ArrayList<>();
     List<String> list = sendChimeraCommand("list models type molecule",
             true);
     if (list != null)
@@ -494,7 +495,7 @@ public class ChimeraManager
    */
   public List<String> getPresets()
   {
-    ArrayList<String> presetList = new ArrayList<String>();
+    ArrayList<String> presetList = new ArrayList<>();
     List<String> output = sendChimeraCommand("preset list", true);
     if (output != null)
     {
@@ -550,17 +551,19 @@ public class ChimeraManager
     // iterate over possible paths for starting Chimera
     for (String chimeraPath : chimeraPaths)
     {
-      File path = new File(chimeraPath);
-      // uncomment the next line to simulate Chimera not installed
-      // path = new File(chimeraPath + "x");
-      if (!path.canExecute())
-      {
-        error += "File '" + path + "' does not exist.\n";
-        continue;
-      }
       try
       {
-        List<String> args = new ArrayList<String>();
+        // ensure symbolic links are resolved
+        chimeraPath = Paths.get(chimeraPath).toRealPath().toString();
+        File path = new File(chimeraPath);
+        // uncomment the next line to simulate Chimera not installed
+        // path = new File(chimeraPath + "x");
+        if (!path.canExecute())
+        {
+          error += "File '" + path + "' does not exist.\n";
+          continue;
+        }
+        List<String> args = new ArrayList<>();
         args.add(chimeraPath);
         // shows Chimera output window but suppresses REST responses:
         // args.add("--debug");
@@ -573,7 +576,7 @@ public class ChimeraManager
         break;
       } catch (Exception e)
       {
-        // Chimera could not be started
+        // Chimera could not be started using this path
         error += e.getMessage();
       }
     }
@@ -699,7 +702,7 @@ public class ChimeraManager
 
   public List<String> getAttrList()
   {
-    List<String> attributes = new ArrayList<String>();
+    List<String> attributes = new ArrayList<>();
     final List<String> reply = sendChimeraCommand("list resattr", true);
     if (reply != null)
     {
@@ -718,7 +721,7 @@ public class ChimeraManager
   public Map<ChimeraResidue, Object> getAttrValues(String aCommand,
           ChimeraModel model)
   {
-    Map<ChimeraResidue, Object> values = new HashMap<ChimeraResidue, Object>();
+    Map<ChimeraResidue, Object> values = new HashMap<>();
     final List<String> reply = sendChimeraCommand("list residue spec "
             + model.toSpec() + " attribute " + aCommand, true);
     if (reply != null)
@@ -818,10 +821,10 @@ public class ChimeraManager
   protected List<String> sendRestCommand(String command)
   {
     String restUrl = "http://127.0.0.1:" + this.chimeraRestPort + "/run";
-    List<NameValuePair> commands = new ArrayList<NameValuePair>(1);
+    List<NameValuePair> commands = new ArrayList<>(1);
     commands.add(new BasicNameValuePair("command", command));
 
-    List<String> reply = new ArrayList<String>();
+    List<String> reply = new ArrayList<>();
     BufferedReader response = null;
     try
     {
index e359b62..10249f3 100644 (file)
@@ -87,8 +87,8 @@ public class FeatureAttributes
     Datatype type;
 
     /**
-     * Note one instance of this attribute, recording unique, non-null names,
-     * and the min/max of any numerical values
+     * Note one instance of this attribute, recording unique, non-null
+     * descriptions, and the min/max of any numerical values
      * 
      * @param desc
      * @param value
@@ -99,20 +99,35 @@ public class FeatureAttributes
 
       if (value != null)
       {
-        try
-        {
-          float f = Float.valueOf(value);
-          min = hasValue ? Float.min(min, f) : f;
-          max = hasValue ? Float.max(max, f) : f;
-          hasValue = true;
-          type = (type == null || type == Datatype.Number) ? Datatype.Number
-                  : Datatype.Mixed;
-        } catch (NumberFormatException e)
+        value = value.trim();
+
+        /*
+         * Parse numeric value unless we have previously
+         * seen text data for this attribute type
+         */
+        if (type == null || type == Datatype.Number)
         {
-          // not a number, ignore for min-max purposes
-          type = (type == null || type == Datatype.Character)
-                  ? Datatype.Character
-                  : Datatype.Mixed;
+          try
+          {
+            float f = Float.valueOf(value);
+            min = hasValue ? Float.min(min, f) : f;
+            max = hasValue ? Float.max(max, f) : f;
+            hasValue = true;
+            type = (type == null || type == Datatype.Number)
+                    ? Datatype.Number
+                    : Datatype.Mixed;
+          } catch (NumberFormatException e)
+          {
+            /*
+             * non-numeric data: treat attribute as Character (or Mixed)
+             */
+            type = (type == null || type == Datatype.Character)
+                    ? Datatype.Character
+                    : Datatype.Mixed;
+            min = 0f;
+            max = 0f;
+            hasValue = false;
+          }
         }
       }
     }
@@ -284,9 +299,8 @@ public class FeatureAttributes
 
   /**
    * Answers the [min, max] value range of the given attribute for the given
-   * feature type, if known, else null. Attributes which only have text values
-   * would normally return null, however text values which happen to be numeric
-   * could result in a 'min-max' range.
+   * feature type, if known, else null. Attributes with a mixture of text and
+   * numeric values are considered text (do not return a min-max range).
    * 
    * @param featureType
    * @param attName
index 9dea886..92972eb 100644 (file)
@@ -66,17 +66,14 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
    * @see https://github.com/Ensembl/ensembl-rest/wiki/Change-log
    * @see http://rest.ensembl.org/info/rest?content-type=application/json
    */
-  private static final String LATEST_ENSEMBLGENOMES_REST_VERSION = "6.0";
+  private static final String LATEST_ENSEMBLGENOMES_REST_VERSION = "6.3";
 
-  private static final String LATEST_ENSEMBL_REST_VERSION = "6.1";
+  private static final String LATEST_ENSEMBL_REST_VERSION = "6.3";
 
   private static final String REST_CHANGE_LOG = "https://github.com/Ensembl/ensembl-rest/wiki/Change-log";
 
   private static Map<String, EnsemblData> domainData;
 
-  // @see https://github.com/Ensembl/ensembl-rest/wiki/Output-formats
-  private static final String PING_URL = "http://rest.ensembl.org/info/ping.json";
-
   private final static long AVAILABILITY_RETEST_INTERVAL = 10000L; // 10 seconds
 
   private final static long VERSION_RETEST_INTERVAL = 1000L * 3600; // 1 hr
index 2e51bce..1d7d1c9 100644 (file)
@@ -143,6 +143,7 @@ public class CutAndPasteHtmlTransfer extends GCutAndPasteHtmlTransfer
   {
     textarea.setDocument(textarea.getEditorKit().createDefaultDocument());
     textarea.setText(text);
+    textarea.setCaretPosition(0);
   }
 
   @Override
index 6eb583c..e13f6ee 100644 (file)
@@ -1414,7 +1414,7 @@ public class FeatureTypeSettings extends JalviewDialog
      * if a numeric condition is selected, show the value range
      * as a tooltip on the value input field
      */
-    setPatternTooltip(filterBy, selectedCondition, patternField);
+    setNumericHints(filterBy, selectedCondition, patternField);
 
     /*
      * add remove button if filter is populated (non-empty pattern)
@@ -1469,14 +1469,19 @@ public class FeatureTypeSettings extends JalviewDialog
   }
 
   /**
-   * If a numeric comparison condition is selected, retrieve the min-max range for
-   * the value (score or attribute), and set it as a tooltip on the value file
+   * If a numeric comparison condition is selected, retrieves the min-max range
+   * for the value (score or attribute), and sets it as a tooltip on the value
+   * field. If the field is currently empty, then pre-populates it with
+   * <ul>
+   * <li>the minimum value, if condition is > or >=</li>
+   * <li>the maximum value, if condition is < or <=</li>
+   * </ul>
    * 
    * @param attName
    * @param selectedCondition
    * @param patternField
    */
-  private void setPatternTooltip(String attName,
+  private void setNumericHints(String attName,
           Condition selectedCondition, JTextField patternField)
   {
     patternField.setToolTipText("");
@@ -1486,9 +1491,26 @@ public class FeatureTypeSettings extends JalviewDialog
       float[] minMax = getMinMax(attName);
       if (minMax != null)
       {
-        String tip = String.format("(%s - %s)",
-                DECFMT_2_2.format(minMax[0]), DECFMT_2_2.format(minMax[1]));
+        String minFormatted = DECFMT_2_2.format(minMax[0]);
+        String maxFormatted = DECFMT_2_2.format(minMax[1]);
+        String tip = String.format("(%s - %s)", minFormatted, maxFormatted);
         patternField.setToolTipText(tip);
+        if (patternField.getText().isEmpty())
+        {
+          if (selectedCondition == Condition.GE
+                  || selectedCondition == Condition.GT)
+          {
+            patternField.setText(minFormatted);
+          }
+          else
+          {
+            if (selectedCondition == Condition.LE
+                    || selectedCondition == Condition.LT)
+            {
+              patternField.setText(maxFormatted);
+            }
+          }
+        }
       }
     }
   }
@@ -1525,10 +1547,11 @@ public class FeatureTypeSettings extends JalviewDialog
     ItemListener listener = condCombo.getItemListeners()[0];
     condCombo.removeItemListener(listener);
     boolean condIsValid = false;
+
     condCombo.removeAllItems();
     for (Condition c : Condition.values())
     {
-      if ((c.isNumeric() && type != Datatype.Character)
+      if ((c.isNumeric() && type == Datatype.Number)
               || (!c.isNumeric() && type != Datatype.Number))
       {
         condCombo.addItem(c);
@@ -1551,8 +1574,6 @@ public class FeatureTypeSettings extends JalviewDialog
       condCombo.setSelectedIndex(0);
     }
 
-    condCombo.addItemListener(listener);
-
     /*
      * clear pattern if it is now invalid for condition
      */
@@ -1570,6 +1591,11 @@ public class FeatureTypeSettings extends JalviewDialog
         patternField.setText("");
       }
     }
+
+    /*
+     * restore the listener
+     */
+    condCombo.addItemListener(listener);
   }
 
   /**
@@ -1646,7 +1672,7 @@ public class FeatureTypeSettings extends JalviewDialog
     Condition cond = (Condition) condCombo.getSelectedItem();
     String pattern = valueField.getText().trim();
 
-    setPatternTooltip(attName, cond, valueField);
+    setNumericHints(attName, cond, valueField);
 
     if (pattern.length() == 0 && cond.needsAPattern())
     {
index aed73d6..9af840a 100644 (file)
@@ -3192,7 +3192,7 @@ public class Jalview2XML
          * load any HMMER profile
          */
         String hmmJarFile = jseqs[i].getHmmerProfile();
-        if (hmmJarFile != null)
+        if (hmmJarFile != null && jprovider != null)
         {
           loadHmmerProfile(jprovider, hmmJarFile, alignmentSeq);
         }
index f8f2cde..b5c1b25 100644 (file)
@@ -24,6 +24,7 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Hashtable;
 import java.util.List;
@@ -247,10 +248,13 @@ public abstract class HmmerCommand implements Runnable
    * @param cmd
    *          command short name e.g. hmmalign
    * @return
+   * @throws IOException
    */
-  protected String getCommandPath(String cmd)
+  protected String getCommandPath(String cmd) throws IOException
   {
     String binariesFolder = Cache.getProperty(Preferences.HMMER_PATH);
+    // ensure any symlink to the directory is resolved:
+    binariesFolder = Paths.get(binariesFolder).toRealPath().toString();
     File file = FileUtils.getExecutable(cmd, binariesFolder);
     if (file == null && af != null)
     {
index 6b82671..5ada355 100644 (file)
@@ -21,6 +21,7 @@
 package jalview.io;
 
 import jalview.api.FeatureColourI;
+import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.DBRefSource;
 import jalview.datamodel.SequenceFeature;
@@ -353,12 +354,27 @@ public class SequenceAnnotationReport
       sb.append("<br>").append(tmp);
       maxWidth = Math.max(maxWidth, tmp.length());
     }
+
     SequenceI ds = sequence;
     while (ds.getDatasetSequence() != null)
     {
       ds = ds.getDatasetSequence();
     }
 
+    /*
+     * add any annotation scores
+     */
+    AlignmentAnnotation[] anns = ds.getAnnotation();
+    for (int i = 0; anns != null && i < anns.length; i++)
+    {
+      AlignmentAnnotation aa = anns[i];
+      if (aa != null && aa.hasScore() && aa.sequenceRef != null)
+      {
+        sb.append("<br>").append(aa.label).append(": ")
+                .append(aa.getScore());
+      }
+    }
+
     if (showDbRefs)
     {
       maxWidth = Math.max(maxWidth, appendDbRefs(sb, ds, summary));
index 60054f1..39a7120 100644 (file)
@@ -327,6 +327,13 @@ public class MapList
     fromHighest = Integer.MIN_VALUE;
     for (int[] range : fromRange)
     {
+      if (range.length != 2)
+      {
+        // throw new IllegalArgumentException(range);
+        System.err.println(
+                "Invalid format for fromRange " + Arrays.toString(range)
+                + " may cause errors");
+      }
       fromLowest = Math.min(fromLowest, Math.min(range[0], range[1]));
       fromHighest = Math.max(fromHighest, Math.max(range[0], range[1]));
     }
@@ -335,6 +342,13 @@ public class MapList
     toHighest = Integer.MIN_VALUE;
     for (int[] range : toRange)
     {
+      if (range.length != 2)
+      {
+        // throw new IllegalArgumentException(range);
+        System.err.println("Invalid format for toRange "
+                + Arrays.toString(range)
+                + " may cause errors");
+      }
       toLowest = Math.min(toLowest, Math.min(range[0], range[1]));
       toHighest = Math.max(toHighest, Math.max(range[0], range[1]));
     }
@@ -1148,11 +1162,20 @@ public class MapList
     for (int[] range : getToRanges())
     {
       int[] transferred = map.locateInTo(range[0], range[1]);
-      if (transferred == null)
+      if (transferred == null || transferred.length % 2 != 0)
       {
         return null;
       }
-      toRanges.add(transferred);
+
+      /*
+       *  convert [start1, end1, start2, end2, ...] 
+       *  to [[start1, end1], [start2, end2], ...]
+       */
+      for (int i = 0; i < transferred.length;)
+      {
+        toRanges.add(new int[] { transferred[i], transferred[i + 1] });
+        i += 2;
+      }
     }
 
     return new MapList(getFromRanges(), toRanges, outFromRatio, outToRatio);
index 92bf0ce..a7a7d34 100644 (file)
@@ -2685,14 +2685,14 @@ public class AlignmentUtilsTests
     assertEquals(2, toMap.getFromRanges().get(0).length);
     assertEquals(1, toMap.getFromRanges().get(0)[0]);
     assertEquals(12, toMap.getFromRanges().get(0)[1]);
-    assertEquals(1, toMap.getToRanges().size());
-    assertEquals(4, toMap.getToRanges().get(0).length);
+    assertEquals(2, toMap.getToRanges().size());
+    assertEquals(2, toMap.getToRanges().get(0).length);
     assertEquals(158, toMap.getToRanges().get(0)[0]);
     assertEquals(164, toMap.getToRanges().get(0)[1]);
-    assertEquals(210, toMap.getToRanges().get(0)[2]);
-    assertEquals(214, toMap.getToRanges().get(0)[3]);
+    assertEquals(210, toMap.getToRanges().get(1)[0]);
+    assertEquals(214, toMap.getToRanges().get(1)[1]);
     // or summarised as (but toString might change in future):
-    assertEquals("[ [1, 12] ] 1:1 to [ [158, 164, 210, 214] ]",
+    assertEquals("[ [1, 12] ] 1:1 to [ [158, 164] [210, 214] ]",
             toMap.toString());
 
     /*
@@ -2704,7 +2704,7 @@ public class AlignmentUtilsTests
     assertEquals("GRCh38", toLoci.getAssemblyId());
     assertEquals("7", toLoci.getChromosomeId());
     toMap = toLoci.getMap();
-    assertEquals("[ [1, 12] ] 1:1 to [ [158, 164, 210, 214] ]",
+    assertEquals("[ [1, 12] ] 1:1 to [ [158, 164] [210, 214] ]",
             toMap.toString());
   }
 
index 686cd2f..0846ec2 100644 (file)
@@ -76,12 +76,14 @@ public class FeatureAttributesTest
     assertNull(fa.getMinMax("Pfam", "kd"));
     sf.setValue("domain", "xyz");
     assertNull(fa.getMinMax("Pfam", "kd"));
-    sf.setValue("kd", "some text");
-    assertNull(fa.getMinMax("Pfam", "kd"));
     sf.setValue("kd", "1.3");
     assertEquals(fa.getMinMax("Pfam", "kd"), new float[] { 1.3f, 1.3f });
     sf.setValue("kd", "-2.6");
     assertEquals(fa.getMinMax("Pfam", "kd"), new float[] { -2.6f, 1.3f });
+    // setting 'mixed' character and numeric values wipes the min/max value
+    sf.setValue("kd", "some text");
+    assertNull(fa.getMinMax("Pfam", "kd"));
+
     Map<String, String> csq = new HashMap<>();
     csq.put("AF", "-3");
     sf.setValue("CSQ", csq);
index cd3e124..493545a 100644 (file)
@@ -774,22 +774,31 @@ public class MapListTest
      */
     MapList ml1 = new MapList(new int[] { 3, 4, 8, 12 }, new int[] { 5, 8,
         11, 13 }, 1, 1);
+    assertEquals("{[3, 4], [8, 12]}", prettyPrint(ml1.getFromRanges()));
+    assertEquals("{[5, 8], [11, 13]}", prettyPrint(ml1.getToRanges()));
+
     MapList ml2 = new MapList(new int[] { 1, 50 }, new int[] { 40, 45, 70,
         75, 90, 127 }, 1, 1);
+    assertEquals("{[1, 50]}", prettyPrint(ml2.getFromRanges()));
+    assertEquals("{[40, 45], [70, 75], [90, 127]}",
+            prettyPrint(ml2.getToRanges()));
+
     MapList compound = ml1.traverse(ml2);
 
-    assertEquals(compound.getFromRatio(), 1);
-    assertEquals(compound.getToRatio(), 1);
+    assertEquals(1, compound.getFromRatio());
+    assertEquals(1, compound.getToRatio());
     List<int[]> fromRanges = compound.getFromRanges();
-    assertEquals(fromRanges.size(), 2);
+    assertEquals(2, fromRanges.size());
     assertArrayEquals(new int[] { 3, 4 }, fromRanges.get(0));
     assertArrayEquals(new int[] { 8, 12 }, fromRanges.get(1));
     List<int[]> toRanges = compound.getToRanges();
-    assertEquals(toRanges.size(), 2);
+    assertEquals(4, toRanges.size());
     // 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));
+    assertArrayEquals(new int[] { 44, 45 }, toRanges.get(0));
+    assertArrayEquals(new int[] { 70, 71 }, toRanges.get(1));
+    assertArrayEquals(new int[] { 74, 75 }, toRanges.get(2));
+    assertArrayEquals(new int[] { 90, 90 }, toRanges.get(3));
 
     /*
      * 1:1 over 1:1 backwards ('reverse strand')
@@ -799,14 +808,15 @@ public class MapListTest
             new int[] { 1000, 901, 600, 201 }, 1, 1);
     compound = ml1.traverse(ml2);
 
-    assertEquals(compound.getFromRatio(), 1);
-    assertEquals(compound.getToRatio(), 1);
+    assertEquals(1, compound.getFromRatio());
+    assertEquals(1, compound.getToRatio());
     fromRanges = compound.getFromRanges();
-    assertEquals(fromRanges.size(), 1);
+    assertEquals(1, fromRanges.size());
     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));
+    assertEquals(2, toRanges.size());
+    assertArrayEquals(new int[] { 931, 901 }, toRanges.get(0));
+    assertArrayEquals(new int[] { 600, 582 }, toRanges.get(1));
 
     /*
      * 1:1 plus 1:3 should result in 1:3
@@ -816,15 +826,16 @@ public class MapListTest
             1, 3);
     compound = ml1.traverse(ml2);
 
-    assertEquals(compound.getFromRatio(), 1);
-    assertEquals(compound.getToRatio(), 3);
+    assertEquals(1, compound.getFromRatio());
+    assertEquals(3, compound.getToRatio());
     fromRanges = compound.getFromRanges();
-    assertEquals(fromRanges.size(), 1);
+    assertEquals(1, fromRanges.size());
     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));
+    assertEquals(2, toRanges.size());
+    assertArrayEquals(new int[] { 31, 50 }, toRanges.get(0));
+    assertArrayEquals(new int[] { 91, 160 }, toRanges.get(1));
 
     /*
      * 3:1 plus 1:1 should result in 3:1
@@ -834,15 +845,16 @@ public class MapListTest
             1, 1);
     compound = ml1.traverse(ml2);
 
-    assertEquals(compound.getFromRatio(), 3);
-    assertEquals(compound.getToRatio(), 1);
+    assertEquals(3, compound.getFromRatio());
+    assertEquals(1, compound.getToRatio());
     fromRanges = compound.getFromRanges();
-    assertEquals(fromRanges.size(), 1);
+    assertEquals(1, fromRanges.size());
     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));
+    assertEquals(2, toRanges.size());
+    assertArrayEquals(new int[] { 11, 15 }, toRanges.get(0));
+    assertArrayEquals(new int[] { 91, 95 }, toRanges.get(1));
 
     /*
      * 1:3 plus 3:1 should result in 1:1
@@ -852,15 +864,16 @@ public class MapListTest
             3, 1);
     compound = ml1.traverse(ml2);
 
-    assertEquals(compound.getFromRatio(), 1);
-    assertEquals(compound.getToRatio(), 1);
+    assertEquals(1, compound.getFromRatio());
+    assertEquals(1, compound.getToRatio());
     fromRanges = compound.getFromRanges();
-    assertEquals(fromRanges.size(), 1);
+    assertEquals(1, fromRanges.size());
     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));
+    assertEquals(2, toRanges.size());
+    assertArrayEquals(new int[] { 55, 70 }, toRanges.get(0));
+    assertArrayEquals(new int[] { 121, 124 }, toRanges.get(1));
 
     /*
      * 3:1 plus 1:3 should result in 1:1
@@ -870,15 +883,16 @@ public class MapListTest
             1, 3);
     compound = ml1.traverse(ml2);
 
-    assertEquals(compound.getFromRatio(), 1);
-    assertEquals(compound.getToRatio(), 1);
+    assertEquals(1, compound.getFromRatio());
+    assertEquals(1, compound.getToRatio());
     fromRanges = compound.getFromRanges();
-    assertEquals(fromRanges.size(), 1);
+    assertEquals(1, fromRanges.size());
     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));
+    assertEquals(2, toRanges.size());
+    assertArrayEquals(new int[] { 47, 50 }, toRanges.get(0));
+    assertArrayEquals(new int[] { 71, 126 }, toRanges.get(1));
 
     /*
      * method returns null if not all regions are mapped through