Merge branch 'releases/Release_2_10_4_Branch'
[jalview.git] / src / jalview / io / FeaturesFile.java
index afc00ee..e0722c0 100755 (executable)
@@ -45,7 +45,6 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -73,23 +72,8 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI
 
   private static final String NOTE = "Note";
 
-  protected static final String TAB = "\t";
-
   protected static final String GFF_VERSION = "##gff-version";
 
-  private static final Comparator<String> SORT_NULL_LAST = new Comparator<String>()
-  {
-    @Override
-    public int compare(String o1, String o2)
-    {
-      if (o1 == null)
-      {
-        return o2 == null ? 0 : 1;
-      }
-      return (o2 == null ? -1 : o1.compareTo(o2));
-    }
-  };
-
   private AlignmentI lastmatchedAl = null;
 
   private SequenceIdMatcher matcher = null;
@@ -108,14 +92,14 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI
   /**
    * Constructor which does not parse the file immediately
    * 
-   * @param inFile
+   * @param file
    * @param paste
    * @throws IOException
    */
-  public FeaturesFile(String inFile, DataSourceType paste)
+  public FeaturesFile(String file, DataSourceType paste)
           throws IOException
   {
-    super(false, inFile, paste);
+    super(false, file, paste);
   }
 
   /**
@@ -131,15 +115,14 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI
    * Constructor that optionally parses the file immediately
    * 
    * @param parseImmediately
-   * @param inFile
+   * @param file
    * @param type
    * @throws IOException
    */
-  public FeaturesFile(boolean parseImmediately, String inFile,
-          DataSourceType type)
-          throws IOException
+  public FeaturesFile(boolean parseImmediately, String file,
+          DataSourceType type) throws IOException
   {
-    super(parseImmediately, inFile, type);
+    super(parseImmediately, file, type);
   }
 
   /**
@@ -195,11 +178,11 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI
           Map<String, FeatureColourI> colours, boolean removeHTML,
           boolean relaxedIdmatching)
   {
-    Map<String, String> gffProps = new HashMap<String, String>();
+    Map<String, String> gffProps = new HashMap<>();
     /*
      * keep track of any sequences we try to create from the data
      */
-    List<SequenceI> newseqs = new ArrayList<SequenceI>();
+    List<SequenceI> newseqs = new ArrayList<>();
 
     String line = null;
     try
@@ -319,7 +302,8 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI
    */
   protected boolean parseJalviewFeature(String line, String[] gffColumns,
           AlignmentI alignment, Map<String, FeatureColourI> featureColours,
-          boolean removeHTML, boolean relaxedIdMatching, String featureGroup)
+          boolean removeHTML, boolean relaxedIdMatching,
+          String featureGroup)
   {
     /*
      * tokens: description seqid seqIndex start end type [score]
@@ -373,20 +357,23 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI
       Color colour = ColorUtils.createColourFromName(ft);
       featureColours.put(ft, new FeatureColour(colour));
     }
-    SequenceFeature sf = new SequenceFeature(ft, desc, "", startPos,
-            endPos, featureGroup);
+    SequenceFeature sf = null;
     if (gffColumns.length > 6)
     {
       float score = Float.NaN;
       try
       {
         score = new Float(gffColumns[6]).floatValue();
-        // update colourgradient bounds if allowed to
       } catch (NumberFormatException ex)
       {
-        // leave as NaN
+        sf = new SequenceFeature(ft, desc, startPos, endPos, featureGroup);
       }
-      sf.setScore(score);
+      sf = new SequenceFeature(ft, desc, startPos, endPos, score,
+              featureGroup);
+    }
+    else
+    {
+      sf = new SequenceFeature(ft, desc, startPos, endPos, featureGroup);
     }
 
     parseDescriptionHTML(sf, removeHTML);
@@ -542,12 +529,15 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI
             .toArray(new String[visible.keySet().size()]);
 
     /*
-     * sort groups alphabetically, and ensure that null group is output last
+     * sort groups alphabetically, and ensure that features with a
+     * null or empty group are output after those in named groups
      */
-    List<String> sortedGroups = new ArrayList<String>(visibleFeatureGroups);
+    List<String> sortedGroups = new ArrayList<>(visibleFeatureGroups);
     sortedGroups.remove(null);
+    sortedGroups.remove("");
     Collections.sort(sortedGroups);
     sortedGroups.add(null);
+    sortedGroups.add("");
 
     boolean foundSome = false;
 
@@ -570,7 +560,8 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI
 
     for (String group : sortedGroups)
     {
-      if (group != null)
+      boolean isNamedGroup = (group != null && !"".equals(group));
+      if (isNamedGroup)
       {
         out.append(newline);
         out.append("STARTGROUP").append(TAB);
@@ -584,7 +575,7 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI
       for (int i = 0; i < sequences.length; i++)
       {
         String sequenceName = sequences[i].getName();
-        List<SequenceFeature> features = new ArrayList<SequenceFeature>();
+        List<SequenceFeature> features = new ArrayList<>();
         if (types.length > 0)
         {
           features.addAll(sequences[i].getFeatures().getFeaturesForGroup(
@@ -598,7 +589,7 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI
         }
       }
 
-      if (group != null)
+      if (isNamedGroup)
       {
         out.append("ENDGROUP").append(TAB);
         out.append(group);
@@ -695,7 +686,7 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI
       dataset = new Alignment(new SequenceI[] {});
     }
 
-    Map<String, FeatureColourI> featureColours = new HashMap<String, FeatureColourI>();
+    Map<String, FeatureColourI> featureColours = new HashMap<>();
     boolean parseResult = parse(dataset, featureColours, false, true);
     if (!parseResult)
     {
@@ -755,7 +746,7 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI
 
     for (SequenceI seq : sequences)
     {
-      List<SequenceFeature> features = new ArrayList<SequenceFeature>();
+      List<SequenceFeature> features = new ArrayList<>();
       if (includeNonPositionalFeatures)
       {
         features.addAll(seq.getFeatures().getNonPositionalFeatures());
@@ -869,8 +860,8 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI
         fromCount = Integer.parseInt(tokens[2]);
       } catch (NumberFormatException nfe)
       {
-        throw new IOException("Invalid number in Align field: "
-                + nfe.getMessage());
+        throw new IOException(
+                "Invalid number in Align field: " + nfe.getMessage());
       }
 
       /*
@@ -1091,9 +1082,8 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI
    * @param newseqs
    * @throws IOException
    */
-  protected void processGffPragma(String line,
-          Map<String, String> gffProps, AlignmentI align,
-          List<SequenceI> newseqs) throws IOException
+  protected void processGffPragma(String line, Map<String, String> gffProps,
+          AlignmentI align, List<SequenceI> newseqs) throws IOException
   {
     line = line.trim();
     if ("###".equals(line))