JAL-2015 JAL-1956 rollout of FeatureColourI in place of
[jalview.git] / src / jalview / io / FeaturesFile.java
index d6c3c21..6bc0374 100755 (executable)
 package jalview.io;
 
 import jalview.analysis.SequenceIdMatcher;
+import jalview.api.FeatureColourI;
 import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceDummy;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.schemes.AnnotationColourGradient;
-import jalview.schemes.GraduatedColor;
+import jalview.schemes.FeatureColour;
 import jalview.schemes.UserColourScheme;
 import jalview.util.Format;
 import jalview.util.MapList;
@@ -70,27 +71,48 @@ public class FeaturesFile extends AlignFile
   }
 
   /**
-   * Creates a new FeaturesFile object.
-   * 
    * @param inFile
-   *          DOCUMENT ME!
    * @param type
-   *          DOCUMENT ME!
-   * 
    * @throws IOException
-   *           DOCUMENT ME!
    */
   public FeaturesFile(String inFile, String type) throws IOException
   {
     super(inFile, type);
   }
 
+  /**
+   * @param source
+   * @throws IOException
+   */
   public FeaturesFile(FileParse source) throws IOException
   {
     super(source);
   }
 
   /**
+   * @param parseImmediately
+   * @param source
+   * @throws IOException
+   */
+  public FeaturesFile(boolean parseImmediately, FileParse source)
+          throws IOException
+  {
+    super(parseImmediately, source);
+  }
+
+  /**
+   * @param parseImmediately
+   * @param inFile
+   * @param type
+   * @throws IOException
+   */
+  public FeaturesFile(boolean parseImmediately, String inFile, String type)
+          throws IOException
+  {
+    super(parseImmediately, inFile, type);
+  }
+
+  /**
    * Parse GFF or sequence features file using case-independent matching,
    * discarding URLs
    * 
@@ -102,8 +124,7 @@ public class FeaturesFile extends AlignFile
    *          - process html strings into plain text
    * @return true if features were added
    */
-  public boolean parse(AlignmentI align, Hashtable colours,
-          boolean removeHTML)
+  public boolean parse(AlignmentI align, Map colours, boolean removeHTML)
   {
     return parse(align, colours, null, removeHTML, false);
   }
@@ -148,6 +169,27 @@ public class FeaturesFile extends AlignFile
     return parse(align, colours, featureLink, removeHTML, false);
   }
 
+  @Override
+  public void addAnnotations(AlignmentI al)
+  {
+    // TODO Auto-generated method stub
+    super.addAnnotations(al);
+  }
+
+  @Override
+  public void addProperties(AlignmentI al)
+  {
+    // TODO Auto-generated method stub
+    super.addProperties(al);
+  }
+
+  @Override
+  public void addSeqGroups(AlignmentI al)
+  {
+    // TODO Auto-generated method stub
+    super.addSeqGroups(al);
+  }
+
   /**
    * Parse GFF or sequence features file
    * 
@@ -172,7 +214,8 @@ public class FeaturesFile extends AlignFile
     {
       SequenceI seq = null;
       /**
-       * keep track of any sequences we try to create from the data if it is a GFF3 file
+       * keep track of any sequences we try to create from the data if it is a
+       * GFF3 file
        */
       ArrayList<SequenceI> newseqs = new ArrayList<SequenceI>();
       String type, desc, token = null;
@@ -235,7 +278,7 @@ public class FeaturesFile extends AlignFile
           }
           else
           {
-            Object colour = null;
+            FeatureColourI colour = null;
             String colscheme = st.nextToken();
             if (colscheme.indexOf("|") > -1
                     || colscheme.trim().equalsIgnoreCase("label"))
@@ -351,7 +394,7 @@ public class FeaturesFile extends AlignFile
               }
               try
               {
-                colour = new jalview.schemes.GraduatedColor(
+                colour = new FeatureColour(
                         new UserColourScheme(mincol).findColour('A'),
                         new UserColourScheme(maxcol).findColour('A'), min,
                         max);
@@ -364,10 +407,8 @@ public class FeaturesFile extends AlignFile
               }
               if (colour != null)
               {
-                ((jalview.schemes.GraduatedColor) colour)
-                        .setColourByLabel(labelCol);
-                ((jalview.schemes.GraduatedColor) colour)
-                        .setAutoScaled(abso == null);
+                colour.setColourByLabel(labelCol);
+                colour.setAutoScaled(abso == null);
                 // add in any additional parameters
                 String ttype = null, tval = null;
                 if (gcol.hasMoreTokens())
@@ -376,18 +417,14 @@ public class FeaturesFile extends AlignFile
                   ttype = gcol.nextToken();
                   if (ttype.toLowerCase().startsWith("below"))
                   {
-                    ((jalview.schemes.GraduatedColor) colour)
-                            .setThreshType(AnnotationColourGradient.BELOW_THRESHOLD);
+                    colour.setBelowThreshold(true);
                   }
                   else if (ttype.toLowerCase().startsWith("above"))
                   {
-                    ((jalview.schemes.GraduatedColor) colour)
-                            .setThreshType(AnnotationColourGradient.ABOVE_THRESHOLD);
+                    colour.setAboveThreshold(true);
                   }
                   else
                   {
-                    ((jalview.schemes.GraduatedColor) colour)
-                            .setThreshType(AnnotationColourGradient.NO_THRESHOLD);
                     if (!ttype.toLowerCase().startsWith("no"))
                     {
                       System.err
@@ -396,14 +433,13 @@ public class FeaturesFile extends AlignFile
                     }
                   }
                 }
-                if (((GraduatedColor) colour).getThreshType() != AnnotationColourGradient.NO_THRESHOLD)
+                if (colour.hasThreshold())
                 {
                   try
                   {
                     gcol.nextToken();
                     tval = gcol.nextToken();
-                    ((jalview.schemes.GraduatedColor) colour)
-                            .setThresh(new Float(tval).floatValue());
+                    colour.setThreshold(new Float(tval).floatValue());
                   } catch (Exception e)
                   {
                     System.err
@@ -428,7 +464,7 @@ public class FeaturesFile extends AlignFile
             else
             {
               UserColourScheme ucs = new UserColourScheme(colscheme);
-              colour = ucs.findColour('A');
+              colour = new FeatureColour(ucs.findColour('A'));
             }
             if (colour != null)
             {
@@ -791,18 +827,19 @@ public class FeaturesFile extends AlignFile
    * @return true if sf was actually added to the sequence, false if it was
    *         processed in another way
    */
-  public boolean processOrAddSeqFeature(AlignmentI align, List<SequenceI> newseqs, SequenceI seq, SequenceFeature sf,
+  public boolean processOrAddSeqFeature(AlignmentI align,
+          List<SequenceI> newseqs, SequenceI seq, SequenceFeature sf,
           boolean gFFFile, boolean relaxedIdMatching)
   {
     String attr = (String) sf.getValue("ATTRIBUTES");
     boolean add = true;
     if (gFFFile && attr != null)
     {
-      int nattr=8;
+      int nattr = 8;
 
       for (String attset : attr.split("\t"))
       {
-        if (attset==null || attset.trim().length()==0)
+        if (attset == null || attset.trim().length() == 0)
         {
           continue;
         }
@@ -818,22 +855,25 @@ public class FeaturesFile extends AlignFile
             continue;
           }
 
-          // expect either space seperated (gff2) or '=' separated (gff3) 
+          // expect either space seperated (gff2) or '=' separated (gff3)
           // key/value pairs here
 
-          int eqpos = pair.indexOf('='),sppos = pair.indexOf(' ');
+          int eqpos = pair.indexOf('='), sppos = pair.indexOf(' ');
           String key = null, value = null;
 
           if (sppos > -1 && (eqpos == -1 || sppos < eqpos))
           {
             key = pair.substring(0, sppos);
             value = pair.substring(sppos + 1);
-          } else {
+          }
+          else
+          {
             if (eqpos > -1 && (sppos == -1 || eqpos < sppos))
             {
               key = pair.substring(0, eqpos);
               value = pair.substring(eqpos + 1);
-            } else
+            }
+            else
             {
               key = pair;
             }
@@ -856,8 +896,8 @@ public class FeaturesFile extends AlignFile
         {
           add &= processGffKey(set, nattr, seq, sf, align, newseqs,
                   relaxedIdMatching); // process decides if
-                                                     // feature is actually
-                                                     // added
+                                      // feature is actually
+                                      // added
         } catch (InvalidGFF3FieldException ivfe)
         {
           System.err.println(ivfe);
@@ -906,20 +946,20 @@ public class FeaturesFile extends AlignFile
     {
       int strand = sf.getStrand();
       // exonerate cdna/protein map
-      // look for fields 
+      // look for fields
       List<SequenceI> querySeq = findNames(align, newseqs,
-              relaxedIdMatching, set.get(attr="Query"));
-      if (querySeq==null || querySeq.size()!=1)
+              relaxedIdMatching, set.get(attr = "Query"));
+      if (querySeq == null || querySeq.size() != 1)
       {
-        throw new InvalidGFF3FieldException( attr, set,
+        throw new InvalidGFF3FieldException(attr, set,
                 "Expecting exactly one sequence in Query field (got "
                         + set.get(attr) + ")");
       }
-      if (set.containsKey(attr="Align"))
+      if (set.containsKey(attr = "Align"))
       {
         // process the align maps and create cdna/protein maps
         // ideally, the query sequences are in the alignment, but maybe not...
-        
+
         AlignedCodonFrame alco = new AlignedCodonFrame();
         MapList codonmapping = constructCodonMappingFromAlign(set, attr,
                 strand);
@@ -937,8 +977,8 @@ public class FeaturesFile extends AlignFile
   }
 
   private MapList constructCodonMappingFromAlign(
-          Map<String, List<String>> set,
-          String attr, int strand) throws InvalidGFF3FieldException
+          Map<String, List<String>> set, String attr, int strand)
+          throws InvalidGFF3FieldException
   {
     if (strand == 0)
     {
@@ -1092,21 +1132,21 @@ public class FeaturesFile extends AlignFile
           }
         }
       }
-      
+
     }
-    if (match==null && newseqs!=null)
+    if (match == null && newseqs != null)
     {
       match = new SequenceDummy(seqId);
       if (relaxedIdMatching)
       {
-        matcher.addAll(Arrays.asList(new SequenceI[]
-        { match }));
+        matcher.addAll(Arrays.asList(new SequenceI[] { match }));
       }
       // add dummy sequence to the newseqs list
       newseqs.add(match);
     }
     return match;
   }
+
   public void parseDescriptionHTML(SequenceFeature sf, boolean removeHTML)
   {
     if (sf.getDescription() == null)
@@ -1130,19 +1170,20 @@ public class FeaturesFile extends AlignFile
    * 
    * @param seqs
    *          source of sequence features
-   * @param visible
+   * @param map
    *          hash of feature types and colours
    * @return features file contents
    */
-  public String printJalviewFormat(SequenceI[] seqs, Map<String,Object> visible)
+  public String printJalviewFormat(SequenceI[] seqs,
+          Map<String, FeatureColourI> map)
   {
-    return printJalviewFormat(seqs, visible, true, true);
+    return printJalviewFormat(seqs, map, true, true);
   }
 
   /**
    * generate a features file for seqs with colours from visible (if any)
    * 
-   * @param seqs
+   * @param sequences
    *          source of features
    * @param visible
    *          hash of Colours for each feature type
@@ -1153,7 +1194,8 @@ public class FeaturesFile extends AlignFile
    *          of group or type)
    * @return features file contents
    */
-  public String printJalviewFormat(SequenceI[] seqs, Map visible,
+  public String printJalviewFormat(SequenceI[] sequences,
+          Map<String, FeatureColourI> visible,
           boolean visOnly, boolean nonpos)
   {
     StringBuffer out = new StringBuffer();
@@ -1170,54 +1212,46 @@ public class FeaturesFile extends AlignFile
       // write feature colours only if we're given them and we are generating
       // viewed features
       // TODO: decide if feature links should also be written here ?
-      Iterator en = visible.keySet().iterator();
-      String type, color;
+      Iterator<String> en = visible.keySet().iterator();
+      String feature, color;
       while (en.hasNext())
       {
-        type = en.next().toString();
+        feature = en.next();
 
-        if (visible.get(type) instanceof GraduatedColor)
+        FeatureColourI gc = visible.get(feature);
+        if (!gc.isSimpleColour())
         {
-          GraduatedColor gc = (GraduatedColor) visible.get(type);
           color = (gc.isColourByLabel() ? "label|" : "")
-                  + Format.getHexString(gc.getMinColor()) + "|"
-                  + Format.getHexString(gc.getMaxColor())
-                  + (gc.isAutoScale() ? "|" : "|abso|") + gc.getMin() + "|"
+                  + Format.getHexString(gc.getMinColour()) + "|"
+                  + Format.getHexString(gc.getMaxColour())
+                  + (gc.isAutoScaled() ? "|" : "|abso|") + gc.getMin()
+                  + "|"
                   + gc.getMax() + "|";
-          if (gc.getThreshType() != AnnotationColourGradient.NO_THRESHOLD)
+          if (gc.isBelowThreshold())
           {
-            if (gc.getThreshType() == AnnotationColourGradient.BELOW_THRESHOLD)
-            {
-              color += "below";
-            }
-            else
-            {
-              if (gc.getThreshType() != AnnotationColourGradient.ABOVE_THRESHOLD)
-              {
-                System.err.println("WARNING: Unsupported threshold type ("
-                        + gc.getThreshType() + ") : Assuming 'above'");
-              }
-              color += "above";
-            }
-            // add the value
-            color += "|" + gc.getThresh();
+            color += "below|" + gc.getThreshold();
+          }
+          else if (gc.isAboveThreshold())
+          {
+            color += "above|" + gc.getThreshold();
           }
           else
           {
             color += "none";
           }
         }
-        else if (visible.get(type) instanceof java.awt.Color)
-        {
-          color = Format.getHexString((java.awt.Color) visible.get(type));
-        }
         else
         {
-          // legacy support for integer objects containing colour triplet values
-          color = Format.getHexString(new java.awt.Color(Integer
-                  .parseInt(visible.get(type).toString())));
+          color = Format.getHexString(gc.getColour());
         }
-        out.append(type);
+        // else
+        // {
+        // // legacy support for integer objects containing colour triplet
+        // values
+        // color = Format.getHexString(new java.awt.Color(Integer
+        // .parseInt(visible.get(type).toString())));
+        // }
+        out.append(feature);
         out.append("\t");
         out.append(color);
         out.append(newline);
@@ -1228,9 +1262,9 @@ public class FeaturesFile extends AlignFile
     int groupIndex = 0;
     boolean isnonpos = false;
 
-    for (int i = 0; i < seqs.length; i++)
+    for (int i = 0; i < sequences.length; i++)
     {
-      next = seqs[i].getSequenceFeatures();
+      next = sequences[i].getSequenceFeatures();
       if (next != null)
       {
         for (int j = 0; j < next.length; j++)
@@ -1269,9 +1303,9 @@ public class FeaturesFile extends AlignFile
         group = null;
       }
 
-      for (int i = 0; i < seqs.length; i++)
+      for (int i = 0; i < sequences.length; i++)
       {
-        next = seqs[i].getSequenceFeatures();
+        next = sequences[i].getSequenceFeatures();
         if (next != null)
         {
           for (int j = 0; j < next.length; j++)
@@ -1335,14 +1369,14 @@ public class FeaturesFile extends AlignFile
 
               out.append("\t");
             }
-            out.append(seqs[i].getName());
+            out.append(sequences[i].getName());
             out.append("\t-1\t");
             out.append(next[j].begin);
             out.append("\t");
             out.append(next[j].end);
             out.append("\t");
             out.append(next[j].type);
-            if (next[j].score != Float.NaN)
+            if (!Float.isNaN(next[j].score))
             {
               out.append("\t");
               out.append(next[j].score);
@@ -1379,16 +1413,17 @@ public class FeaturesFile extends AlignFile
    * default.
    * 
    * @param seqs
-   * @param visible
+   * @param map
    * @return
    */
-  public String printGFFFormat(SequenceI[] seqs, Map<String,Object> visible)
+  public String printGFFFormat(SequenceI[] seqs,
+          Map<String, FeatureColourI> map)
   {
-    return printGFFFormat(seqs, visible, true, true);
+    return printGFFFormat(seqs, map, true, true);
   }
 
-  public String printGFFFormat(SequenceI[] seqs, Map<String,Object> visible,
-          boolean visOnly, boolean nonpos)
+  public String printGFFFormat(SequenceI[] seqs,
+          Map<String, FeatureColourI> map, boolean visOnly, boolean nonpos)
   {
     StringBuffer out = new StringBuffer();
     SequenceFeature[] next;
@@ -1403,7 +1438,7 @@ public class FeaturesFile extends AlignFile
         {
           isnonpos = next[j].begin == 0 && next[j].end == 0;
           if ((!nonpos && isnonpos)
-                  || (!isnonpos && visOnly && !visible
+                  || (!isnonpos && visOnly && !map
                           .containsKey(next[j].type)))
           {
             continue;
@@ -1466,6 +1501,7 @@ public class FeaturesFile extends AlignFile
   /**
    * this is only for the benefit of object polymorphism - method does nothing.
    */
+  @Override
   public void parse()
   {
     // IGNORED
@@ -1476,6 +1512,7 @@ public class FeaturesFile extends AlignFile
    * 
    * @return error message
    */
+  @Override
   public String print()
   {
     return "USE printGFFFormat() or printJalviewFormat()";