Merge branch 'develop' into features/JAL-1793VCF
[jalview.git] / src / jalview / datamodel / SequenceFeature.java
index 9c4087e..420ade1 100755 (executable)
 package jalview.datamodel;
 
 import jalview.datamodel.features.FeatureLocationI;
+import jalview.util.StringUtils;
 
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.TreeMap;
 import java.util.Vector;
 
 /**
- * DOCUMENT ME!
- * 
- * @author $author$
- * @version $Revision$
+ * A class that models a single contiguous feature on a sequence. If flag
+ * 'contactFeature' is true, the start and end positions are interpreted instead
+ * as two contact points.
  */
 public class SequenceFeature implements FeatureLocationI
 {
@@ -51,6 +52,20 @@ public class SequenceFeature implements FeatureLocationI
   // private key for ENA location designed not to conflict with real GFF data
   private static final String LOCATION = "!Location";
 
+  private static final String ROW_DATA = "<tr><td>%s</td><td>%s</td></tr>";
+
+  /*
+   * map of otherDetails special keys, and their value fields' delimiter
+   */
+  private static final Map<String, String> INFO_KEYS = new HashMap<>();
+
+  static
+  {
+    INFO_KEYS.put("CSQ", ",");
+    // todo capture second level metadata (CSQ FORMAT)
+    // and delimiter "|" so as to report in a table within a table?
+  }
+
   /*
    * ATTRIBUTES is reserved for the GFF 'column 9' data, formatted as
    * name1=value1;name2=value2,value3;...etc
@@ -535,4 +550,68 @@ public class SequenceFeature implements FeatureLocationI
   {
     return begin == 0 && end == 0;
   }
+
+  /**
+   * Answers an html-formatted report of feature details
+   * 
+   * @return
+   */
+  public String getDetailsReport()
+  {
+    StringBuilder sb = new StringBuilder(128);
+    sb.append("<br>");
+    sb.append("<table>");
+    sb.append(String.format(ROW_DATA, "Type", type));
+    sb.append(String.format(ROW_DATA, "Start/end", begin == end ? begin
+            : begin + (isContactFeature() ? ":" : "-") + end));
+    String desc = StringUtils.stripHtmlTags(description);
+    sb.append(String.format(ROW_DATA, "Description", desc));
+    if (!Float.isNaN(score) && score != 0f)
+    {
+      sb.append(String.format(ROW_DATA, "Score", score));
+    }
+    if (featureGroup != null)
+    {
+      sb.append(String.format(ROW_DATA, "Group", featureGroup));
+    }
+
+    if (otherDetails != null)
+    {
+      TreeMap<String, Object> ordered = new TreeMap<>(
+              String.CASE_INSENSITIVE_ORDER);
+      ordered.putAll(otherDetails);
+
+      for (Entry<String, Object> entry : ordered.entrySet())
+      {
+        String key = entry.getKey();
+        if (ATTRIBUTES.equals(key))
+        {
+          continue; // to avoid double reporting
+        }
+        if (INFO_KEYS.containsKey(key))
+        {
+          /*
+           * split selected INFO data by delimiter over multiple lines
+           */
+          String delimiter = INFO_KEYS.get(key);
+          String[] values = entry.getValue().toString().split(delimiter);
+          for (String value : values)
+          {
+            sb.append("<tr><td>").append(key).append("</td><td>")
+                    .append(value)
+                    .append("</td></tr>");
+          }
+        }
+        else
+        { // tried <td title="key"> but it failed to provide a tooltip :-(
+          sb.append("<tr><td>").append(key).append("</td><td>");
+          sb.append(entry.getValue().toString()).append("</td></tr>");
+        }
+      }
+    }
+    sb.append("</table>");
+
+    String text = sb.toString();
+    return text;
+  }
 }