Merge branch 'releases/Release_2_11_1_Branch' into merge/JAL-3628+JAL-3608+JAL-3609... bug/JAL-3702_Drag_and_drop_miscalculation_in_linux_HiDPI
authorBen Soares <b.soares@dundee.ac.uk>
Thu, 30 Jul 2020 13:08:06 +0000 (14:08 +0100)
committerBen Soares <b.soares@dundee.ac.uk>
Thu, 30 Jul 2020 13:08:06 +0000 (14:08 +0100)
33 files changed:
help/help/html/releases.html
j11lib/htsjdk-2.12.0.jar [deleted file]
j11lib/htsjdk-2.23.0.jar [new file with mode: 0644]
j11lib/intervalstore-v1.0.jar [deleted file]
j11lib/intervalstore-v1.1.jar [new file with mode: 0644]
j8lib/htsjdk-2.12.0.jar [deleted file]
j8lib/htsjdk-2.23.0.jar [new file with mode: 0644]
j8lib/intervalstore-v1.0.jar [deleted file]
j8lib/intervalstore-v1.1.jar [new file with mode: 0644]
src/jalview/datamodel/features/FeatureStore.java
src/jalview/datamodel/features/SequenceFeatures.java
src/jalview/gui/AnnotationColourChooser.java
src/jalview/gui/AnnotationColumnChooser.java
src/jalview/gui/AnnotationRowFilter.java
src/jalview/gui/FeatureSettings.java
src/jalview/gui/FeatureTypeSettings.java
src/jalview/gui/OptsAndParamsPage.java
src/jalview/gui/SeqPanel.java
src/jalview/gui/Slider.java [new file with mode: 0644]
src/jalview/io/FileParse.java
src/jalview/io/vcf/VCFLoader.java
src/jalview/ws/dbsources/Pfam.java
src/jalview/ws/dbsources/PfamFull.java
src/jalview/ws/dbsources/PfamSeed.java
src/jalview/ws/dbsources/Rfam.java
src/jalview/ws/dbsources/RfamFull.java
src/jalview/ws/dbsources/RfamSeed.java
src/jalview/ws/dbsources/Xfam.java
test/jalview/datamodel/features/SequenceFeaturesTest.java
test/jalview/ws/dbsources/PfamFullTest.java
test/jalview/ws/dbsources/PfamSeedTest.java
test/jalview/ws/dbsources/RfamFullTest.java
test/jalview/ws/dbsources/RfamSeedTest.java

index 8193433..8c4ba37 100755 (executable)
@@ -74,6 +74,21 @@ li:before {
             <!-- JAL-3633 -->Preferences -&gt; Connections, Proxy server settings now allow selecting "No proxy", "System proxy settings" or custom proxy settings
            which can be configured for HTTP, HTTPS and allows for an authenticated proxy server.
           </li>
+          <li>
+            <!-- JAL-3695 -->Support import of VCF 4.3 by updating
+            HTSJDK from 2.12 to 2.23
+          </li>
+          <li>
+            <!-- JAL-3621 -->IntervalStore library updated to v.1.1:
+            optimisations and improvements suggested by Bob Hanson and
+            improved compatibility with JalviewJS
+          </li>
+          <li>
+          <!-- JAL-3615 -->Retrieve GZipped stockholm formatted alignments from Pfam and Rfam
+          </li>
+          <li>
+          <!-- JAL-2656 -->Recognise GZipped content for URLs and File import (no longer based on .gz extension)
+          </li>
           <li></li>
         </ul>
       </td>
@@ -102,13 +117,14 @@ li:before {
             <!-- JAL-3632 JAL-3633 -->Proxy properties don't set HTTPS proxy for launcher or application.
           </li>
           <li>
-            <!-- JAL- -->
-          </li>
-          <li>
-            <!-- JAL- -->
+            <!-- JAL-3696 -->Errors encountered when processing variants
+            from VCF files yield "Error processing VCF: Format specifier
+            '%s'" on the console
           </li>
           <li>
-            <!-- JAL- -->
+            <!-- JAL-3697 -->Count of features not shown can be wrong
+            when there are both local and complementary features mapped
+            to the position under the cursor
           </li>
         </ul>
       </td>
diff --git a/j11lib/htsjdk-2.12.0.jar b/j11lib/htsjdk-2.12.0.jar
deleted file mode 100644 (file)
index 1df12b2..0000000
Binary files a/j11lib/htsjdk-2.12.0.jar and /dev/null differ
diff --git a/j11lib/htsjdk-2.23.0.jar b/j11lib/htsjdk-2.23.0.jar
new file mode 100644 (file)
index 0000000..bcf201f
Binary files /dev/null and b/j11lib/htsjdk-2.23.0.jar differ
diff --git a/j11lib/intervalstore-v1.0.jar b/j11lib/intervalstore-v1.0.jar
deleted file mode 100644 (file)
index 4f6101c..0000000
Binary files a/j11lib/intervalstore-v1.0.jar and /dev/null differ
diff --git a/j11lib/intervalstore-v1.1.jar b/j11lib/intervalstore-v1.1.jar
new file mode 100644 (file)
index 0000000..668b543
Binary files /dev/null and b/j11lib/intervalstore-v1.1.jar differ
diff --git a/j8lib/htsjdk-2.12.0.jar b/j8lib/htsjdk-2.12.0.jar
deleted file mode 100644 (file)
index 1df12b2..0000000
Binary files a/j8lib/htsjdk-2.12.0.jar and /dev/null differ
diff --git a/j8lib/htsjdk-2.23.0.jar b/j8lib/htsjdk-2.23.0.jar
new file mode 100644 (file)
index 0000000..bcf201f
Binary files /dev/null and b/j8lib/htsjdk-2.23.0.jar differ
diff --git a/j8lib/intervalstore-v1.0.jar b/j8lib/intervalstore-v1.0.jar
deleted file mode 100644 (file)
index 4f6101c..0000000
Binary files a/j8lib/intervalstore-v1.0.jar and /dev/null differ
diff --git a/j8lib/intervalstore-v1.1.jar b/j8lib/intervalstore-v1.1.jar
new file mode 100644 (file)
index 0000000..668b543
Binary files /dev/null and b/j8lib/intervalstore-v1.1.jar differ
index 54c0d59..7651016 100644 (file)
@@ -20,8 +20,6 @@
  */
 package jalview.datamodel.features;
 
-import jalview.datamodel.SequenceFeature;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
@@ -30,7 +28,9 @@ import java.util.Set;
 
 import intervalstore.api.IntervalStoreI;
 import intervalstore.impl.BinarySearcher;
+import intervalstore.impl.BinarySearcher.Compare;
 import intervalstore.impl.IntervalStore;
+import jalview.datamodel.SequenceFeature;
 
 /**
  * A data store for a set of sequence features that supports efficient lookup of
@@ -277,7 +277,7 @@ public class FeatureStore
      * binary search the sorted list to find the insertion point
      */
     int insertPosition = BinarySearcher.findFirst(contactFeatureStarts,
-            f -> f.getBegin() >= feature.getBegin());
+            true, Compare.GE, feature.getBegin());
     contactFeatureStarts.add(insertPosition, feature);
 
 
@@ -286,7 +286,7 @@ public class FeatureStore
      * binary search the sorted list to find the insertion point
      */
     insertPosition = BinarySearcher.findFirst(contactFeatureEnds,
-            f -> f.getEnd() >= feature.getEnd());
+            false, Compare.GE, feature.getEnd());
     contactFeatureEnds.add(insertPosition, feature);
 
     return true;
@@ -314,8 +314,8 @@ public class FeatureStore
      */
     // int pos = binarySearch(features,
     // SearchCriterion.byFeature(feature, RangeComparator.BY_START_POSITION));
-    int pos = BinarySearcher.findFirst(features,
-            val -> val.getBegin() >= feature.getBegin());
+    int pos = BinarySearcher.findFirst(features, true, Compare.GE,
+            feature.getBegin());
     int len = features.size();
     while (pos < len)
     {
@@ -396,7 +396,7 @@ public class FeatureStore
      * whose end point is not before the target range
      */
     int index = BinarySearcher.findFirst(contactFeatureEnds,
-            f -> f.getEnd() >= from);
+            false, Compare.GE, (int) from);
 
     while (index < contactFeatureEnds.size())
     {
@@ -449,7 +449,7 @@ public class FeatureStore
           List<SequenceFeature> result)
   {
     int index = BinarySearcher.findFirst(contactFeatureStarts,
-            f -> f.getBegin() >= from);
+            true, Compare.GE, (int) from);
 
     while (index < contactFeatureStarts.size())
     {
index db2f0e1..8ac4991 100644 (file)
  */
 package jalview.datamodel.features;
 
-import jalview.datamodel.SequenceFeature;
-import jalview.io.gff.SequenceOntologyFactory;
-import jalview.io.gff.SequenceOntologyI;
-
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -33,6 +30,9 @@ import java.util.Set;
 import java.util.TreeMap;
 
 import intervalstore.api.IntervalI;
+import jalview.datamodel.SequenceFeature;
+import jalview.io.gff.SequenceOntologyFactory;
+import jalview.io.gff.SequenceOntologyI;
 
 /**
  * A class that stores sequence features in a way that supports efficient
@@ -44,7 +44,6 @@ import intervalstore.api.IntervalI;
  */
 public class SequenceFeatures implements SequenceFeaturesI
 {
-
   /*
    * map from feature type to structured store of features for that type
    * null types are permitted (but not a good idea!)
@@ -414,7 +413,10 @@ public class SequenceFeatures implements SequenceFeaturesI
   public static void sortFeatures(List<? extends IntervalI> features,
           final boolean forwardStrand)
   {
-    IntervalI.sortIntervals(features, forwardStrand);
+    Collections.sort(features,
+            forwardStrand
+                    ? IntervalI.COMPARE_BEGIN_ASC_END_DESC
+                    : IntervalI.COMPARE_END_DESC);
   }
 
   /**
index 60ad75d..7ed178b 100644 (file)
@@ -53,8 +53,6 @@ import net.miginfocom.swing.MigLayout;
 @SuppressWarnings("serial")
 public class AnnotationColourChooser extends AnnotationRowFilter
 {
-  private static final int ONETHOUSAND = 1000;
-
   private ColourSchemeI oldcs;
 
   private JButton defColours;
@@ -147,7 +145,8 @@ public class AnnotationColourChooser extends AnnotationRowFilter
                 "error.implementation_error_dont_know_about_threshold_setting"));
       }
       thresholdIsMin.setSelected(acg.isThresholdIsMinMax());
-      thresholdValue.setText("" + acg.getAnnotationThreshold());
+      thresholdValue
+              .setText(String.valueOf(acg.getAnnotationThreshold()));
     }
 
     jbInit();
@@ -338,7 +337,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
       {
         updateView();
       }
-      getCurrentAnnotation().threshold.value = slider.getValue() / 1000f;
+      getCurrentAnnotation().threshold.value = getSliderValue();
       propagateSeqAssociatedThreshold(updateAllAnnotation,
               getCurrentAnnotation());
       ap.paintAlignment(false, false);
@@ -378,6 +377,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
     thresholdValue.setEnabled(true);
     thresholdIsMin.setEnabled(!useOriginalColours.isSelected());
 
+    final AlignmentAnnotation currentAnnotation = getCurrentAnnotation();
     if (selectedThresholdItem == AnnotationColourGradient.NO_THRESHOLD)
     {
       slider.setEnabled(false);
@@ -386,33 +386,26 @@ public class AnnotationColourChooser extends AnnotationRowFilter
       thresholdIsMin.setEnabled(false);
     }
     else if (selectedThresholdItem != AnnotationColourGradient.NO_THRESHOLD
-            && getCurrentAnnotation().threshold == null)
+            && currentAnnotation.threshold == null)
     {
-      getCurrentAnnotation().setThreshold(new GraphLine(
-              (getCurrentAnnotation().graphMax
-                      - getCurrentAnnotation().graphMin) / 2f,
+      currentAnnotation.setThreshold(new GraphLine(
+              (currentAnnotation.graphMax - currentAnnotation.graphMin)
+                      / 2f,
               "Threshold", Color.black));
     }
 
     if (selectedThresholdItem != AnnotationColourGradient.NO_THRESHOLD)
     {
       adjusting = true;
-      float range = getCurrentAnnotation().graphMax * ONETHOUSAND
-              - getCurrentAnnotation().graphMin * ONETHOUSAND;
-
-      slider.setMinimum(
-              (int) (getCurrentAnnotation().graphMin * ONETHOUSAND));
-      slider.setMaximum(
-              (int) (getCurrentAnnotation().graphMax * ONETHOUSAND));
-      slider.setValue(
-              (int) (getCurrentAnnotation().threshold.value * ONETHOUSAND));
-      thresholdValue.setText(getCurrentAnnotation().threshold.value + "");
-      slider.setMajorTickSpacing((int) (range / 10f));
+      setSliderModel(currentAnnotation.graphMin, currentAnnotation.graphMax,
+              currentAnnotation.threshold.value);
       slider.setEnabled(true);
+
+      setThresholdValueText();
       thresholdValue.setEnabled(true);
       adjusting = false;
     }
-    colorAlignmentContaining(getCurrentAnnotation(), selectedThresholdItem);
+    colorAlignmentContaining(currentAnnotation, selectedThresholdItem);
 
     ap.alignmentChanged();
   }
index 5adecad..82ec0e7 100644 (file)
@@ -21,6 +21,7 @@
 
 package jalview.gui;
 
+import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.HiddenColumns;
 import jalview.io.cache.JvCacheableInputBox;
 import jalview.schemes.AnnotationColourGradient;
@@ -254,7 +255,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter
   {
     if (slider.isEnabled())
     {
-      getCurrentAnnotation().threshold.value = slider.getValue() / 1000f;
+      getCurrentAnnotation().threshold.value = getSliderValue();
       updateView();
       propagateSeqAssociatedThreshold(updateAllAnnotation,
               getCurrentAnnotation());
@@ -284,6 +285,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter
     thresholdValue.setEnabled(true);
     percentThreshold.setEnabled(true);
 
+    final AlignmentAnnotation currentAnnotation = getCurrentAnnotation();
     if (selectedThresholdItem == AnnotationColourGradient.NO_THRESHOLD)
     {
       slider.setEnabled(false);
@@ -294,26 +296,22 @@ public class AnnotationColumnChooser extends AnnotationRowFilter
     }
     else if (selectedThresholdItem != AnnotationColourGradient.NO_THRESHOLD)
     {
-      if (getCurrentAnnotation().threshold == null)
+      if (currentAnnotation.threshold == null)
       {
-        getCurrentAnnotation().setThreshold(new jalview.datamodel.GraphLine(
-                (getCurrentAnnotation().graphMax
-                        - getCurrentAnnotation().graphMin) / 2f,
+        currentAnnotation.setThreshold(new jalview.datamodel.GraphLine(
+                (currentAnnotation.graphMax
+                        - currentAnnotation.graphMin) / 2f,
                 "Threshold", Color.black));
       }
 
       adjusting = true;
-      float range = getCurrentAnnotation().graphMax * 1000
-              - getCurrentAnnotation().graphMin * 1000;
 
-      slider.setMinimum((int) (getCurrentAnnotation().graphMin * 1000));
-      slider.setMaximum((int) (getCurrentAnnotation().graphMax * 1000));
-      slider.setValue(
-              (int) (getCurrentAnnotation().threshold.value * 1000));
+      setSliderModel(currentAnnotation.graphMin,
+              currentAnnotation.graphMax,
+              currentAnnotation.threshold.value);
 
       setThresholdValueText();
 
-      slider.setMajorTickSpacing((int) (range / 10f));
       slider.setEnabled(true);
       thresholdValue.setEnabled(true);
       adjusting = false;
@@ -321,10 +319,10 @@ public class AnnotationColumnChooser extends AnnotationRowFilter
       // build filter params
       filterParams.setThresholdType(
               AnnotationFilterParameter.ThresholdType.NO_THRESHOLD);
-      if (getCurrentAnnotation().isQuantitative())
+      if (currentAnnotation.isQuantitative())
       {
         filterParams
-                .setThresholdValue(getCurrentAnnotation().threshold.value);
+                .setThresholdValue(currentAnnotation.threshold.value);
 
         if (selectedThresholdItem == AnnotationColourGradient.ABOVE_THRESHOLD)
         {
@@ -380,7 +378,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter
     // adding them to the selection
     av.showAllHiddenColumns();
     av.getColumnSelection().filterAnnotations(
-            getCurrentAnnotation().annotations, filterParams);
+            currentAnnotation.annotations, filterParams);
 
     boolean hideCols = getActionOption() == ACTION_OPTION_HIDE;
     if (hideCols)
index f13cb10..6f72e10 100644 (file)
@@ -35,6 +35,8 @@ import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
+import java.math.BigDecimal;
+import java.math.MathContext;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Vector;
@@ -44,7 +46,6 @@ import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
 import javax.swing.JInternalFrame;
 import javax.swing.JPanel;
-import javax.swing.JSlider;
 import javax.swing.JTextField;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
@@ -52,6 +53,10 @@ import javax.swing.event.ChangeListener;
 @SuppressWarnings("serial")
 public abstract class AnnotationRowFilter extends JPanel
 {
+  private static final String TWO_DP = "%.2f";
+
+  private final static MathContext FOUR_SIG_FIG = new MathContext(4);
+
   protected AlignViewport av;
 
   protected AlignmentPanel ap;
@@ -64,7 +69,7 @@ public abstract class AnnotationRowFilter extends JPanel
 
   protected JCheckBox percentThreshold = new JCheckBox();
 
-  protected JSlider slider = new JSlider();
+  protected Slider slider;
 
   protected JTextField thresholdValue = new JTextField(20);
 
@@ -103,6 +108,8 @@ public abstract class AnnotationRowFilter extends JPanel
   {
     this.av = viewport;
     this.ap = alignPanel;
+    this.slider = new Slider(0f, 100f, 50f);
+
     thresholdValue.addFocusListener(new FocusAdapter()
     {
       @Override
@@ -140,16 +147,62 @@ public abstract class AnnotationRowFilter extends JPanel
     adjusting = true;
     if (percentThreshold.isSelected())
     {
-      thresholdValue.setText("" + (slider.getValue() - slider.getMinimum())
-              * 100f / (slider.getMaximum() - slider.getMinimum()));
+      thresholdValue
+              .setText(String.format(TWO_DP, getSliderPercentageValue()));
     }
     else
     {
-      thresholdValue.setText((slider.getValue() / 1000f) + "");
+      /*
+       * round to 4 significant digits without trailing zeroes
+       */
+      float f = getSliderValue();
+      BigDecimal formatted = new BigDecimal(f).round(FOUR_SIG_FIG)
+              .stripTrailingZeros();
+      thresholdValue.setText(formatted.toPlainString());
     }
     adjusting = oldadj;
   }
 
+  /**
+   * Answers the value of the slider position (descaled to 'true' value)
+   * 
+   * @return
+   */
+  protected float getSliderValue()
+  {
+    return slider.getSliderValue();
+  }
+
+  /**
+   * Sets the slider value (scaled from the true value to the slider range)
+   * 
+   * @param value
+   */
+  protected void setSliderValue(float value)
+  {
+    slider.setSliderValue(value);
+  }
+  /**
+   * Answers the value of the slider position as a percentage between minimum and
+   * maximum of its range
+   * 
+   * @return
+   */
+  protected float getSliderPercentageValue()
+  {
+    return slider.getSliderPercentageValue();
+  }
+
+  /**
+   * Sets the slider position for a given percentage value of its min-max range
+   * 
+   * @param pct
+   */
+  protected void setSliderPercentageValue(float pct)
+  {
+    slider.setSliderPercentageValue(pct);
+  }
+
   protected void addSliderMouseListeners()
   {
 
@@ -290,6 +343,10 @@ public abstract class AnnotationRowFilter extends JPanel
     updateView();
   }
 
+  /**
+   * Updates the slider position, and the display, for an update in the slider's
+   * text input field
+   */
   protected void thresholdValue_actionPerformed()
   {
     try
@@ -297,12 +354,11 @@ public abstract class AnnotationRowFilter extends JPanel
       float f = Float.parseFloat(thresholdValue.getText());
       if (percentThreshold.isSelected())
       {
-        slider.setValue(slider.getMinimum() + ((int) ((f / 100f)
-                * (slider.getMaximum() - slider.getMinimum()))));
+        setSliderPercentageValue(f);
       }
       else
       {
-        slider.setValue((int) (f * 1000));
+        setSliderValue(f);
       }
       updateView();
     } catch (NumberFormatException ex)
@@ -528,4 +584,23 @@ public abstract class AnnotationRowFilter extends JPanel
       valueChanged(true);
     }
   }
+
+  /**
+   * Sets the min-max range and current value of the slider, with rescaling from
+   * true values to slider range as required
+   * 
+   * @param min
+   * @param max
+   * @param value
+   */
+  protected void setSliderModel(float min, float max, float value)
+  {
+    slider.setSliderModel(min, max, value);
+
+    /*
+     * tick mark every 10th position
+     */
+    slider.setMajorTickSpacing(
+            (slider.getMaximum() - slider.getMinimum()) / 10);
+  }
 }
index b87e4bf..aeac959 100644 (file)
@@ -162,7 +162,7 @@ public class FeatureSettings extends JPanel
 
   JPanel groupPanel;
 
-  JSlider transparency = new JSlider();
+  JSlider transparency= new JSlider();
 
   private JCheckBox showComplementOnTop;
 
index 5b77dfc..512cf06 100644 (file)
@@ -49,6 +49,8 @@ import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
+import java.math.BigDecimal;
+import java.math.MathContext;
 import java.text.DecimalFormat;
 import java.util.ArrayList;
 import java.util.List;
@@ -63,7 +65,6 @@ import javax.swing.JComboBox;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
 import javax.swing.JRadioButton;
-import javax.swing.JSlider;
 import javax.swing.JTextField;
 import javax.swing.border.EmptyBorder;
 import javax.swing.border.LineBorder;
@@ -79,6 +80,8 @@ import javax.swing.event.ChangeListener;
  */
 public class FeatureTypeSettings extends JalviewDialog
 {
+  private final static MathContext FOUR_SIG_FIG = new MathContext(4);
+
   private final static String LABEL_18N = MessageManager
           .getString("label.label");
 
@@ -142,11 +145,6 @@ public class FeatureTypeSettings extends JalviewDialog
   private float max;
 
   /*
-   * scale factor for conversion between absolute min-max and slider
-   */
-  private float scaleFactor;
-
-  /*
    * radio button group, to select what to colour by:
    * simple colour, by category (text), or graduated
    */
@@ -169,7 +167,7 @@ public class FeatureTypeSettings extends JalviewDialog
 
   private JComboBox<Object> threshold = new JComboBox<>();
 
-  private JSlider slider = new JSlider();
+  private Slider slider;
 
   private JTextField thresholdValue = new JTextField(20);
 
@@ -348,12 +346,11 @@ public class FeatureTypeSettings extends JalviewDialog
        * update min-max scaling if there is a range to work with,
        * else disable the widgets (this shouldn't happen if only 
        * valid options are offered in the combo box)
+       * offset slider to have only non-negative values if necessary (JAL-2983)
        */
-      scaleFactor = (max == min) ? 1f : 100f / (max - min);
-      float range = (max - min) * scaleFactor;
-      slider.setMinimum((int) (min * scaleFactor));
-      slider.setMaximum((int) (max * scaleFactor));
-      slider.setMajorTickSpacing((int) (range / 10f));
+      slider.setSliderModel(min, max, min);
+      slider.setMajorTickSpacing(
+              (int) ((slider.getMaximum() - slider.getMinimum()) / 10f));
 
       threshline = new GraphLine((max - min) / 2f, "Threshold",
               Color.black);
@@ -365,8 +362,8 @@ public class FeatureTypeSettings extends JalviewDialog
                 fc.isAboveThreshold() ? ABOVE_THRESHOLD_OPTION
                         : BELOW_THRESHOLD_OPTION);
         slider.setEnabled(true);
-        slider.setValue((int) (fc.getThreshold() * scaleFactor));
-        thresholdValue.setText(String.valueOf(fc.getThreshold()));
+        slider.setSliderValue(fc.getThreshold());
+        setThresholdValueText(fc.getThreshold());
         thresholdValue.setEnabled(true);
         thresholdIsMin.setEnabled(true);
       }
@@ -641,6 +638,7 @@ public class FeatureTypeSettings extends JalviewDialog
         thresholdValue_actionPerformed();
       }
     });
+    slider = new Slider(0f, 100f, 50f);
     slider.setPaintLabels(false);
     slider.setPaintTicks(true);
     slider.setBackground(Color.white);
@@ -657,8 +655,7 @@ public class FeatureTypeSettings extends JalviewDialog
       {
         if (!adjusting)
         {
-          thresholdValue
-                  .setText(String.valueOf(slider.getValue() / scaleFactor));
+          setThresholdValueText(slider.getSliderValue());
           thresholdValue.setBackground(Color.white); // to reset red for invalid
           sliderValueChanged();
         }
@@ -1033,8 +1030,8 @@ public class FeatureTypeSettings extends JalviewDialog
       float f = Float.parseFloat(thresholdValue.getText());
       f = Float.max(f,  this.min);
       f = Float.min(f, this.max);
-      thresholdValue.setText(String.valueOf(f));
-      slider.setValue((int) (f * scaleFactor));
+      setThresholdValueText(f);
+      slider.setSliderValue(f);
       threshline.value = f;
       thresholdValue.setBackground(Color.white); // ok
       adjusting = false;
@@ -1047,13 +1044,25 @@ public class FeatureTypeSettings extends JalviewDialog
   }
 
   /**
+   * Sets the text field for threshold value, rounded to four significant figures
+   * 
+   * @param f
+   */
+  void setThresholdValueText(float f)
+  {
+    BigDecimal formatted = new BigDecimal(f).round(FOUR_SIG_FIG)
+            .stripTrailingZeros();
+    thresholdValue.setText(formatted.toPlainString());
+  }
+
+  /**
    * Action on change of threshold slider value. This may be done interactively
    * (by moving the slider), or programmatically (to update the slider after
    * manual input of a threshold value).
    */
   protected void sliderValueChanged()
   {
-    threshline.value = getRoundedSliderValue();
+    threshline.value = slider.getSliderValue();
 
     /*
      * repaint alignment, but not Overview or structure,
@@ -1062,21 +1071,6 @@ public class FeatureTypeSettings extends JalviewDialog
     colourChanged(false);
   }
 
-  /**
-   * Converts the slider value to its absolute value by dividing by the
-   * scaleFactor. Rounding errors are squashed by forcing min/max of slider
-   * range to the actual min/max of feature score range
-   * 
-   * @return
-   */
-  private float getRoundedSliderValue()
-  {
-    int value = slider.getValue();
-    float f = value == slider.getMaximum() ? max
-            : (value == slider.getMinimum() ? min : value / scaleFactor);
-    return f;
-  }
-
   void addActionListener(ActionListener listener)
   {
     if (featureSettings != null)
index 5342c90..0f4d0e7 100644 (file)
@@ -53,7 +53,6 @@ import javax.swing.JMenuItem;
 import javax.swing.JPanel;
 import javax.swing.JPopupMenu;
 import javax.swing.JScrollPane;
-import javax.swing.JSlider;
 import javax.swing.JTextArea;
 import javax.swing.JTextField;
 import javax.swing.border.TitledBorder;
@@ -277,7 +276,7 @@ public class OptsAndParamsPage
 
     boolean choice = false;
 
-    JComboBox choicebox;
+    JComboBox<String> choicebox;
 
     JPanel controlPanel = new JPanel();
 
@@ -289,7 +288,7 @@ public class OptsAndParamsPage
 
     boolean integ = false;
 
-    Object lastVal;
+    String lastVal;
 
     ParameterI parameter;
 
@@ -299,7 +298,7 @@ public class OptsAndParamsPage
 
     JButton showDesc = new JButton();
 
-    JSlider slider = null;
+    Slider slider = null;
 
     JTextArea string = new JTextArea();
 
@@ -363,7 +362,7 @@ public class OptsAndParamsPage
       validate();
     }
 
-    private void makeExpanderParam(ParameterI parm)
+    private void makeExpanderParam(final ParameterI parm)
     {
       setPreferredSize(new Dimension(PARAM_WIDTH, PARAM_CLOSEDHEIGHT));
       setBorder(new TitledBorder(parm.getName()));
@@ -451,6 +450,9 @@ public class OptsAndParamsPage
       validate();
     }
 
+    /**
+     * Action on input in text field
+     */
     @Override
     public void actionPerformed(ActionEvent e)
     {
@@ -467,24 +469,20 @@ public class OptsAndParamsPage
 
     private void checkIfModified()
     {
-      Object cstate = updateSliderFromValueField();
-      boolean notmod = false;
-      if (cstate.getClass() == lastVal.getClass())
-      {
-        if (cstate instanceof int[])
-        {
-          notmod = (((int[]) cstate)[0] == ((int[]) lastVal)[0]);
-        }
-        else if (cstate instanceof float[])
-        {
-          notmod = (((float[]) cstate)[0] == ((float[]) lastVal)[0]);
-        }
-        else if (cstate instanceof String[])
-        {
-          notmod = (((String[]) cstate)[0].equals(((String[]) lastVal)[0]));
-        }
-      }
-      pmdialogbox.argSetModified(this, !notmod);
+      Object cstate = getCurrentValue();
+      boolean modified = !cstate.equals(lastVal);
+      pmdialogbox.argSetModified(this, modified);
+    }
+
+    /**
+     * Answers the current value of the parameter, as text
+     * 
+     * @return
+     */
+    private String getCurrentValue()
+    {
+      return choice ? (String) choicebox.getSelectedItem()
+              : valueField.getText();
     }
 
     @Override
@@ -566,16 +564,20 @@ public class OptsAndParamsPage
 
     }
 
+    /**
+     * Action on change of slider value
+     */
     @Override
     public void stateChanged(ChangeEvent e)
     {
       if (!adjusting)
       {
-        valueField.setText("" + ((integ) ? ("" + slider.getValue())
-                : ("" + slider.getValue() / 1000f)));
+        float value = slider.getSliderValue();
+        valueField.setText(
+                integ ? Integer.toString((int) value)
+                        : Float.toString(value));
         checkIfModified();
       }
-
     }
 
     public void updateControls(ParameterI parm)
@@ -592,8 +594,6 @@ public class OptsAndParamsPage
         }
         else
         {
-          slider = new JSlider();
-          slider.addChangeListener(this);
           valueField = new JTextField();
           valueField.addActionListener(this);
           valueField.addKeyListener(new KeyListener()
@@ -622,9 +622,11 @@ public class OptsAndParamsPage
             }
           });
           valueField.setPreferredSize(new Dimension(60, 25));
+          slider = makeSlider(parm.getValidValue());
+          slider.addChangeListener(this);
+
           controlPanel.add(slider, BorderLayout.WEST);
           controlPanel.add(valueField, BorderLayout.EAST);
-
         }
       }
 
@@ -634,8 +636,8 @@ public class OptsAndParamsPage
         {
           if (init)
           {
-            List vals = parm.getPossibleValues();
-            for (Object val : vals)
+            List<String> vals = parm.getPossibleValues();
+            for (String val : vals)
             {
               choicebox.addItem(val);
             }
@@ -651,96 +653,105 @@ public class OptsAndParamsPage
           valueField.setText(parm.getValue());
         }
       }
-      lastVal = updateSliderFromValueField();
+      lastVal = getCurrentValue();
       adjusting = false;
     }
 
-    public Object updateSliderFromValueField()
+    private Slider makeSlider(ValueConstrainI validValue)
+    {
+      if (validValue != null)
+      {
+        final Number minValue = validValue.getMin();
+        final Number maxValue = validValue.getMax();
+        if (minValue != null && maxValue != null)
+        {
+          return new Slider(minValue.floatValue(), maxValue.floatValue(),
+                  minValue.floatValue());
+        }
+      }
+
+      /*
+       * otherwise, a nominal slider which will not be visible
+       */
+      return new Slider(0, 100, 50);
+    }
+
+    public void updateSliderFromValueField()
     {
-      int iVal;
-      float fVal;
       if (validator != null)
       {
+        final Number minValue = validator.getMin();
+        final Number maxValue = validator.getMax();
         if (integ)
         {
-          iVal = 0;
+          int iVal = 0;
           try
           {
             valueField.setText(valueField.getText().trim());
             iVal = Integer.valueOf(valueField.getText());
-            if (validator.getMin() != null
-                    && validator.getMin().intValue() > iVal)
+            if (minValue != null
+                    && minValue.intValue() > iVal)
             {
-              iVal = validator.getMin().intValue();
+              iVal = minValue.intValue();
               // TODO: provide visual indication that hard limit was reached for
               // this parameter
             }
-            if (validator.getMax() != null
-                    && validator.getMax().intValue() < iVal)
+            if (maxValue != null && maxValue.intValue() < iVal)
             {
-              iVal = validator.getMax().intValue();
-              // TODO: provide visual indication that hard limit was reached for
-              // this parameter
+              iVal = maxValue.intValue();
             }
-          } catch (Exception e)
+          } catch (NumberFormatException e)
           {
+            System.err.println(e.toString());
           }
-          ;
-          // update value field to reflect any bound checking we performed.
-          valueField.setText("" + iVal);
-          if (validator.getMin() != null && validator.getMax() != null)
+          if (minValue != null || maxValue != null)
           {
-            slider.getModel().setRangeProperties(iVal, 1,
-                    validator.getMin().intValue(),
-                    validator.getMax().intValue() + 1, true);
+            valueField.setText(String.valueOf(iVal));
+            slider.setSliderValue(iVal);
           }
           else
           {
             slider.setVisible(false);
           }
-          return new int[] { iVal };
         }
         else
         {
-          fVal = 0f;
+          float fVal = 0f;
           try
           {
             valueField.setText(valueField.getText().trim());
             fVal = Float.valueOf(valueField.getText());
-            if (validator.getMin() != null
-                    && validator.getMin().floatValue() > fVal)
+            if (minValue != null
+                    && minValue.floatValue() > fVal)
             {
-              fVal = validator.getMin().floatValue();
+              fVal = minValue.floatValue();
               // TODO: provide visual indication that hard limit was reached for
               // this parameter
               // update value field to reflect any bound checking we performed.
               valueField.setText("" + fVal);
             }
-            if (validator.getMax() != null
-                    && validator.getMax().floatValue() < fVal)
+            if (maxValue != null
+                    && maxValue.floatValue() < fVal)
             {
-              fVal = validator.getMax().floatValue();
+              fVal = maxValue.floatValue();
               // TODO: provide visual indication that hard limit was reached for
               // this parameter
               // update value field to reflect any bound checking we performed.
               valueField.setText("" + fVal);
             }
-          } catch (Exception e)
+          } catch (NumberFormatException e)
           {
+            System.err.println(e.toString());
           }
-          ;
-          if (validator.getMin() != null && validator.getMax() != null)
+          if (minValue != null && maxValue != null)
           {
-            slider.getModel().setRangeProperties((int) (fVal * 1000f), 1,
-                    (int) (validator.getMin().floatValue() * 1000f),
-                    1 + (int) (validator.getMax().floatValue() * 1000f),
-                    true);
+            slider.setSliderModel(minValue.floatValue(),
+                    maxValue.floatValue(), fVal);
           }
           else
           {
             slider.setVisible(false);
           }
-          return new float[] { fVal };
         }
       }
       else
@@ -748,14 +759,8 @@ public class OptsAndParamsPage
         if (!choice)
         {
           slider.setVisible(false);
-          return new String[] { valueField.getText().trim() };
-        }
-        else
-        {
-          return new String[] { (String) choicebox.getSelectedItem() };
         }
       }
-
     }
   }
 
@@ -801,9 +806,9 @@ public class OptsAndParamsPage
 
   URL linkImageURL = getClass().getResource("/images/link.gif");
 
-  Map<String, OptionBox> optSet = new java.util.LinkedHashMap<String, OptionBox>();
+  Map<String, OptionBox> optSet = new java.util.LinkedHashMap<>();
 
-  Map<String, ParamBox> paramSet = new java.util.LinkedHashMap<String, ParamBox>();
+  Map<String, ParamBox> paramSet = new java.util.LinkedHashMap<>();
 
   public Map<String, OptionBox> getOptSet()
   {
@@ -904,7 +909,7 @@ public class OptsAndParamsPage
    */
   public List<ArgumentI> getCurrentSettings()
   {
-    List<ArgumentI> argSet = new ArrayList<ArgumentI>();
+    List<ArgumentI> argSet = new ArrayList<>();
     for (OptionBox opts : getOptSet().values())
     {
       OptionI opt = opts.getOptionIfEnabled();
index 407acd6..92f221c 100644 (file)
@@ -1098,7 +1098,7 @@ public class SeqPanel extends JPanel
                   pos);
           if (mf != null)
           {
-            unshownFeatures = seqARep.appendFeatures(tooltipText,
+            unshownFeatures += seqARep.appendFeatures(tooltipText,
                     pos, mf, fr2, MAX_TOOLTIP_LENGTH);
           }
         }
diff --git a/src/jalview/gui/Slider.java b/src/jalview/gui/Slider.java
new file mode 100644 (file)
index 0000000..c8f673d
--- /dev/null
@@ -0,0 +1,115 @@
+package jalview.gui;
+
+import javax.swing.JSlider;
+
+/**
+ * A modified {@code javax.swing.JSlider} that
+ * <ul>
+ * <li>supports float valued numbers (by scaling up integer values)</li>
+ * <li>rescales 'true' value range to avoid negative values, as these are not
+ * rendered correctly by some look and feel libraries</li>
+ * </ul>
+ * 
+ * @author gmcarstairs
+ */
+@SuppressWarnings("serial")
+public class Slider extends JSlider
+{
+  /*
+   * 'true' value corresponding to zero on the slider
+   */
+  private float trueMin;
+
+  /*
+   * 'true' value corresponding to slider maximum
+   */
+  private float trueMax;
+
+  /*
+   * scaleFactor applied to true value range to give a
+   * slider range of 0 - 100
+   */
+  private float sliderScaleFactor;
+
+  /**
+   * Constructor that rescales min - max to 0 - 100 for the slider
+   * 
+   * @param min
+   * @param max
+   * @param value
+   */
+  public Slider(float min, float max, float value)
+  {
+    super();
+    setSliderModel(min, max, value);
+  }
+
+  /**
+   * Sets the min-max range and current value of the slider, with rescaling from
+   * true values to slider range as required
+   * 
+   * @param min
+   * @param max
+   * @param value
+   */
+  public void setSliderModel(float min, float max, float value)
+  {
+    trueMin = min;
+    trueMax = max;
+    setMinimum(0);
+    sliderScaleFactor = 100f / (max - min);
+    int sliderMax = (int) ((max - min) * sliderScaleFactor);
+    int extent = 1;
+    setExtent(extent);
+    setMaximum(sliderMax + extent);
+    setSliderValue(value);
+  }
+
+  /**
+   * Answers the value of the slider position (descaled to 'true' value)
+   * 
+   * @return
+   */
+  public float getSliderValue()
+  {
+    /*
+     * convert slider max to 'true max' in case of rounding errors
+     */
+    int value = getValue();
+    return value == getMaximum() ? trueMax
+            : value / sliderScaleFactor + trueMin;
+  }
+
+  /**
+   * Sets the slider value (scaled from the true value to the slider range)
+   * 
+   * @param value
+   */
+  public void setSliderValue(float value)
+  {
+    setValue(Math.round((value - trueMin) * sliderScaleFactor));
+  }
+
+  /**
+   * Answers the value of the slider position as a percentage between minimum and
+   * maximum of its range
+   * 
+   * @return
+   */
+  public float getSliderPercentageValue()
+  {
+    return (getValue() - getMinimum()) * 100f
+            / (getMaximum() - getMinimum());
+  }
+
+  /**
+   * Sets the slider position for a given percentage value of its min-max range
+   * 
+   * @param pct
+   */
+  public void setSliderPercentageValue(float pct)
+  {
+    float pc = pct / 100f * getMaximum();
+    setValue((int) pc);
+  }
+}
index 7117d0f..f1d79fe 100755 (executable)
@@ -26,19 +26,22 @@ import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureSettingsModelI;
 import jalview.util.MessageManager;
 
+import java.io.BufferedInputStream;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.io.StringReader;
+import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.net.URLConnection;
 import java.util.zip.GZIPInputStream;
 
+
 /**
  * implements a random access wrapper around a particular datasource, for
  * passing to identifyFile and AlignFile objects.
@@ -56,6 +59,7 @@ public class FileParse
 
   public File inFile = null;
 
+
   /**
    * a viewport associated with the current file operation. May be null. May
    * move to different object.
@@ -182,29 +186,73 @@ public class FileParse
     }
     if (!error)
     {
-      if (fileStr.toLowerCase().endsWith(".gz"))
+      try
       {
-        try
-        {
-          dataIn = tryAsGzipSource(new FileInputStream(fileStr));
-          dataName = fileStr;
-          return error;
-        } catch (Exception x)
-        {
-          warningMessage = "Failed  to resolve as a GZ stream ("
-                  + x.getMessage() + ")";
-          // x.printStackTrace();
-        }
-        ;
+        dataIn = checkForGzipStream(new FileInputStream(fileStr));
+        dataName = fileStr;
+      } catch (Exception x)
+      {
+        warningMessage = "Failed to resolve " + fileStr
+                + " as a data source. (" + x.getMessage() + ")";
+        // x.printStackTrace();
+        error = true;
       }
-
-      dataIn = new BufferedReader(new FileReader(fileStr));
-      dataName = fileStr;
+      ;
     }
     return error;
   }
+  
+  /**
+   * Recognise the 2-byte magic header for gzip streams
+   * 
+   * https://recalll.co/ask/v/topic/java-How-to-check-if-InputStream-is-Gzipped/555aadd62bd27354438b90f6
+   * 
+   * @param bytes - at least two bytes 
+   * @return 
+   */
+  private static boolean isGzipStream(byte[] bytes) {
+    int head = ((int) bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
+    return (GZIPInputStream.GZIP_MAGIC == head);
+  }
 
-  private BufferedReader tryAsGzipSource(InputStream inputStream)
+  /**
+   * Returns a Reader for the given input after wrapping it in a buffered input
+   * stream, and then checking if it needs to be wrapped by a GZipInputStream
+   * 
+   * @param input
+   * @return
+   */
+  private BufferedReader checkForGzipStream(InputStream input) throws Exception {
+
+    // NB: stackoverflow https://stackoverflow.com/questions/4818468/how-to-check-if-inputstream-is-gzipped
+    // could use a PushBackInputStream rather than a BufferedInputStream
+    
+    BufferedInputStream bufinput;
+    if (!input.markSupported()) {
+       bufinput= new BufferedInputStream(input,16);
+       input = bufinput;
+    }
+    input.mark(4);
+    byte[] bytes=input.readNBytes(2);
+    input.reset();
+    if (bytes.length==2 && isGzipStream(bytes)) {
+      return getGzipReader(input);
+    }
+    // return a buffered reader for the stream.
+    InputStreamReader isReader= new InputStreamReader(input);
+    BufferedReader toReadFrom=new BufferedReader(isReader);
+    return toReadFrom;
+  }
+  /**
+   * Returns a {@code BufferedReader} which wraps the input stream with a
+   * GZIPInputStream. Throws a {@code ZipException} if a GZIP format error
+   * occurs or the compression method used is unsupported.
+   * 
+   * @param inputStream
+   * @return
+   * @throws Exception
+   */
+  private BufferedReader getGzipReader(InputStream inputStream)
           throws Exception
   {
     BufferedReader inData = new BufferedReader(
@@ -215,44 +263,74 @@ public class FileParse
     return inData;
   }
 
-  private boolean checkURLSource(String fileStr)
+  /**
+   * Tries to read from the given URL. If successful, saves a reader to the
+   * response in field {@code dataIn}, otherwise (on exception, or HTTP response
+   * status not 200), throws an exception.
+   * <p>
+   * If the response status includes
+   * 
+   * <pre>
+   * Content-Type : application/x-gzip
+   * </pre>
+   * 
+   * then tries to read as gzipped content.
+   * 
+   * @param urlStr
+   * @throws IOException
+   * @throws MalformedURLException
+   */
+  private void checkURLSource(String urlStr)
           throws IOException, MalformedURLException
   {
     errormessage = "URL NOT FOUND";
-    URL url = new URL(fileStr);
-    //
-    // GZIPInputStream code borrowed from Aquaria (soon to be open sourced) via
-    // Kenny Sabir
-    Exception e = null;
-    if (fileStr.toLowerCase().endsWith(".gz"))
+    URL url = new URL(urlStr);
+    URLConnection _conn = url.openConnection();
+    if (_conn instanceof HttpURLConnection)
     {
-      try
+      HttpURLConnection conn = (HttpURLConnection) _conn;
+      int rc = conn.getResponseCode();
+      if (rc != HttpURLConnection.HTTP_OK)
       {
-        InputStream inputStream = url.openStream();
-        dataIn = tryAsGzipSource(inputStream);
-        dataName = fileStr;
-        return false;
+        throw new IOException(
+                "Response status from " + urlStr + " was " + rc);
+      }
+    } else {
+      try {
+      dataIn = checkForGzipStream(_conn.getInputStream());
+      dataName=urlStr;
+      } catch (IOException ex)
+      {
+        throw new IOException("Failed to handle non-HTTP URI stream",ex);
       } catch (Exception ex)
       {
-        e = ex;
+        throw new IOException("Failed to determine type of input stream for given URI",ex);
       }
+      return;
     }
-
-    try
-    {
-      dataIn = new BufferedReader(new InputStreamReader(url.openStream()));
-    } catch (IOException q)
+    String encoding = _conn.getContentEncoding();
+    String contentType = _conn.getContentType();
+    boolean isgzipped = "application/x-gzip".equalsIgnoreCase(contentType)
+            || "gzip".equals(encoding);
+    Exception e = null;
+    InputStream inputStream = _conn.getInputStream();
+    if (isgzipped)
     {
-      if (e != null)
+      try
+      {
+        dataIn = getGzipReader(inputStream);
+        dataName = urlStr;
+      } catch (Exception e1)
       {
         throw new IOException(MessageManager
                 .getString("exception.failed_to_resolve_gzip_stream"), e);
       }
-      throw q;
+      return;
     }
-    // record URL as name of datasource.
-    dataName = fileStr;
-    return false;
+
+    dataIn = new BufferedReader(new InputStreamReader(inputStream));
+    dataName = urlStr;
+    return;
   }
 
   /**
index a44ee26..13d7ae8 100644 (file)
@@ -912,7 +912,7 @@ public class VCFLoader
          * RuntimeException throwable by htsjdk
          */
         String msg = String.format("Error reading VCF for %s:%d-%d: %s ",
-                map.chromosome, vcfStart, vcfEnd);
+                map.chromosome, vcfStart, vcfEnd,e.getLocalizedMessage());
         Cache.log.error(msg);
       }
     }
index 8877c34..47e66ac 100644 (file)
@@ -35,6 +35,11 @@ import com.stevesoft.pat.Regex;
  */
 abstract public class Pfam extends Xfam
 {
+  /*
+   * append to URLs to retrieve as a gzipped file
+   */
+  protected static final String GZIPPED = "/gzipped";
+
   static final String PFAM_BASEURL_KEY = "PFAM_BASEURL";
 
   private static final String DEFAULT_PFAM_BASEURL = "https://pfam.xfam.org";
index 0600427..d71892b 100644 (file)
@@ -34,7 +34,7 @@ public class PfamFull extends Pfam
   @Override
   public String getURLSuffix()
   {
-    return "/alignment/full";
+    return "/alignment/full" + GZIPPED;
   }
 
   /*
index dff8a17..f64d07f 100644 (file)
@@ -36,7 +36,7 @@ public class PfamSeed extends Pfam
   @Override
   public String getURLSuffix()
   {
-    return "/alignment/seed";
+    return "/alignment/seed" + GZIPPED;
   }
 
   /*
index 1d9d99a..c9ee7fc 100644 (file)
@@ -36,6 +36,11 @@ abstract public class Rfam extends Xfam
 
   private static final String DEFAULT_RFAM_BASEURL = "https://rfam.xfam.org";
 
+  /*
+   * append to URLs to retrieve as a gzipped file
+   */
+  protected static final String GZIPPED = "?gzip=1&download=1";
+
   @Override
   protected String getURLPrefix()
   {
index d815336..396511c 100644 (file)
@@ -36,7 +36,7 @@ public class RfamFull extends Rfam
   @Override
   public String getURLSuffix()
   {
-    return "/alignment/full";
+    return "/alignment/full" + GZIPPED;
   }
 
   /*
index a74e829..eaa574b 100644 (file)
@@ -36,8 +36,7 @@ public class RfamSeed extends Rfam
   @Override
   public String getURLSuffix()
   {
-    // to download gzipped file add '?gzip=1'
-    return "/alignment/stockholm";
+    return "/alignment/stockholm" + GZIPPED;
   }
 
   /*
index b83f558..f0cb14b 100644 (file)
@@ -36,7 +36,6 @@ import jalview.ws.seqfetcher.DbSourceProxyImpl;
  */
 public abstract class Xfam extends DbSourceProxyImpl
 {
-
   public Xfam()
   {
     super();
index 4198a37..56512cd 100644 (file)
@@ -5,8 +5,6 @@ import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertSame;
 import static org.testng.Assert.assertTrue;
 
-import jalview.datamodel.SequenceFeature;
-
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
@@ -15,6 +13,7 @@ import java.util.Set;
 
 import org.testng.annotations.Test;
 
+import jalview.datamodel.SequenceFeature;
 import junit.extensions.PA;
 
 public class SequenceFeaturesTest
@@ -1052,7 +1051,8 @@ public class SequenceFeaturesTest
   public void testSortFeatures()
   {
     List<SequenceFeature> sfs = new ArrayList<>();
-    SequenceFeature sf1 = new SequenceFeature("Pfam", "desc", 30, 80,
+    SequenceFeature sf1 = new SequenceFeature("Pfam", "desc", 30,
+            60,
             Float.NaN, null);
     sfs.add(sf1);
     SequenceFeature sf2 = new SequenceFeature("Rfam", "desc", 40, 50,
@@ -1061,18 +1061,34 @@ public class SequenceFeaturesTest
     SequenceFeature sf3 = new SequenceFeature("Rfam", "desc", 50, 60,
             Float.NaN, null);
     sfs.add(sf3);
+    SequenceFeature sf4 = new SequenceFeature("Xfam", "desc", 30,
+            80,
+            Float.NaN, null);
+    sfs.add(sf4);
+    SequenceFeature sf5 = new SequenceFeature("Xfam", "desc", 30,
+            90,
+            Float.NaN, null);
+    sfs.add(sf5);
 
-    // sort by end position descending
+    /*
+     * sort by end position descending, order unchanged if matched
+     */
     SequenceFeatures.sortFeatures(sfs, false);
-    assertSame(sfs.get(0), sf1);
-    assertSame(sfs.get(1), sf3);
-    assertSame(sfs.get(2), sf2);
+    assertSame(sfs.get(0), sf5); // end 90
+    assertSame(sfs.get(1), sf4); // end 80
+    assertSame(sfs.get(2), sf1); // end 60, start 50
+    assertSame(sfs.get(3), sf3); // end 60, start 30
+    assertSame(sfs.get(4), sf2); // end 50
 
-    // sort by start position ascending
+    /*
+     * resort {5, 4, 1, 3, 2} by start position ascending, end descending
+     */
     SequenceFeatures.sortFeatures(sfs, true);
-    assertSame(sfs.get(0), sf1);
-    assertSame(sfs.get(1), sf2);
-    assertSame(sfs.get(2), sf3);
+    assertSame(sfs.get(0), sf5); // start 30, end 90
+    assertSame(sfs.get(1), sf4); // start 30, end 80
+    assertSame(sfs.get(2), sf1); // start 30, end 60
+    assertSame(sfs.get(3), sf2); // start 40
+    assertSame(sfs.get(4), sf3); // start 50
   }
 
   @Test(groups = "Functional")
index f5cc640..23cceb2 100644 (file)
@@ -38,7 +38,7 @@ public class PfamFullTest
   @Test(groups = "Functional")
   public void testGetURL()
   {
-    String path = "pfam.xfam.org/family/ABC/alignment/full";
+    String path = "pfam.xfam.org/family/ABC/alignment/full/gzipped";
 
     // with default value for domain
     String url = new PfamFull().getURL(" abc ");
index 355ef0c..451810b 100644 (file)
@@ -38,7 +38,7 @@ public class PfamSeedTest
   @Test(groups = "Functional")
   public void testGetURL()
   {
-    String path = "pfam.xfam.org/family/ABC/alignment/seed";
+    String path = "pfam.xfam.org/family/ABC/alignment/seed/gzipped";
 
     // with default value for domain
     String url = new PfamSeed().getURL(" abc ");
index 2d1497f..87b963f 100644 (file)
@@ -38,7 +38,7 @@ public class RfamFullTest
   @Test(groups = "Functional")
   public void testGetURL()
   {
-    String path = "rfam.xfam.org/family/ABC/alignment/full";
+    String path = "rfam.xfam.org/family/ABC/alignment/full?gzip=1&download=1";
 
     // with default value for domain
     String url = new RfamFull().getURL(" abc ");
index 745ba2e..1165d1f 100644 (file)
@@ -38,7 +38,7 @@ public class RfamSeedTest
   @Test(groups = "Functional")
   public void testGetURL()
   {
-    String path = "rfam.xfam.org/family/ABC/alignment/stockholm";
+    String path = "rfam.xfam.org/family/ABC/alignment/stockholm?gzip=1&download=1";
 
     // with default value for domain
     String url = new RfamSeed().getURL(" abc ");