ensure successive matches to a regex have distinct annotation name (indice suffix...
[jalview.git] / src / jalview / io / AnnotationFile.java
index e54ae71..288ce2f 100755 (executable)
@@ -33,11 +33,42 @@ public class AnnotationFile
       "JALVIEW_ANNOTATION\n"\r
       + "# Created: "\r
       + new java.util.Date() + "\n\n");\r
-\r
+  /**\r
+   * convenience method for pre-2.4 feature files which have no view, hidden columns or hidden row keywords.\r
+   * @param annotations\r
+   * @param groups\r
+   * @param properties\r
+   * @return feature file as a string.\r
+   */\r
   public String printAnnotations(AlignmentAnnotation[] annotations,\r
                                  Vector groups,\r
                                  Hashtable properties)\r
   {\r
+    return printAnnotations(annotations, groups,\r
+            properties, null);\r
+\r
+  }\r
+  /**\r
+   * hold all the information about a particular view definition\r
+   * read from or written out in an annotations file.\r
+   */\r
+  public class ViewDef {\r
+    public String viewname;\r
+    public HiddenSequences hidseqs;\r
+    public ColumnSelection hiddencols;\r
+    public Vector visibleGroups;\r
+    public ViewDef(String viewname, HiddenSequences hidseqs,\r
+            ColumnSelection hiddencols)\r
+    {\r
+      this.viewname = viewname;\r
+      this.hidseqs = hidseqs;\r
+      this.hiddencols = hiddencols;\r
+    }\r
+  }\r
+  public String printAnnotations(AlignmentAnnotation[] annotations,\r
+          Vector groups,\r
+          Hashtable properties, ViewDef[] views)\r
+  {\r
     if (annotations != null)\r
     {\r
       boolean oneColour = true;\r
@@ -56,7 +87,7 @@ public class AnnotationFile
       {\r
         row = annotations[i];\r
 \r
-        if (!row.visible)\r
+        if (!row.visible && !row.hasScore())\r
         {\r
           continue;\r
         }\r
@@ -127,7 +158,7 @@ public class AnnotationFile
           text.append(row.description + "\t");\r
         }\r
 \r
-        for (int j = 0; j < row.annotations.length; j++)\r
+        for (int j = 0; row.annotations!=null && j < row.annotations.length; j++)\r
         {\r
           if (refSeq != null &&\r
               jalview.util.Comparison.isGap(refSeq.getCharAt(j)))\r
@@ -159,7 +190,7 @@ public class AnnotationFile
               }\r
 \r
               color = row.annotations[j].colour;\r
-              if (row.annotations[j].value != 0f)\r
+              if (row.annotations[j].value != 0f && row.annotations[j].value!=Float.NaN) \r
               {\r
                 text.append(comma + row.annotations[j].value);\r
               }\r
@@ -221,7 +252,7 @@ public class AnnotationFile
       }\r
 \r
     }\r
-\r
+    \r
     return text.toString();\r
   }\r
 \r
@@ -281,6 +312,10 @@ public class AnnotationFile
       {\r
         text.append("textColThreshold=" + sg.thresholdTextColour);\r
       }\r
+      if (sg.idColour!=null)\r
+      {\r
+        text.append("idColour="+jalview.util.Format.getHexString(sg.idColour)+"\t");\r
+      }\r
 \r
       text.append("\n\n");\r
 \r
@@ -288,6 +323,7 @@ public class AnnotationFile
   }\r
 \r
   SequenceI refSeq = null;\r
+  String refSeqId = null;\r
   public boolean readAnnotationFile(AlignmentI al,\r
                                     String file,\r
                                     String protocol)\r
@@ -385,7 +421,13 @@ public class AnnotationFile
 \r
         else if (token.equalsIgnoreCase("SEQUENCE_REF"))\r
         {\r
-          refSeq = al.findName(st.nextToken());\r
+          if (st.hasMoreTokens())\r
+          {\r
+            refSeq = al.findName(refSeqId=st.nextToken());\r
+          if (refSeq==null)\r
+          {\r
+            refSeqId=null;\r
+          }\r
           try\r
           {\r
             refSeqIndex = Integer.parseInt(st.nextToken());\r
@@ -400,7 +442,10 @@ public class AnnotationFile
           {\r
             refSeqIndex = 1;\r
           }\r
-\r
+          } else {\r
+            refSeq = null;\r
+            refSeqId = null;\r
+          }\r
           continue ;\r
         }\r
 \r
@@ -493,7 +538,7 @@ public class AnnotationFile
 \r
         annotation = new AlignmentAnnotation(label,\r
                                              description,\r
-                                             annotations,\r
+                                             (index==0) ? null : annotations,\r
                                              0,\r
                                              0,\r
                                              graphStyle);\r
@@ -503,16 +548,27 @@ public class AnnotationFile
         if (refSeq != null)\r
         {\r
           annotation.belowAlignment=false;\r
-          annotation.createSequenceMapping(refSeq, refSeqIndex, false);\r
-          annotation.adjustForAlignment();\r
-          refSeq.addAlignmentAnnotation(annotation);\r
-        }\r
-\r
-        al.addAnnotation(annotation);\r
-\r
-        al.setAnnotationIndex(annotation,\r
+          do {\r
+            // copy before we do any mapping business.\r
+            // TODO: verify that undo/redo with 1:many sequence associated annotations can be undone correctly\r
+            AlignmentAnnotation ann = new AlignmentAnnotation(annotation);\r
+            annotation.createSequenceMapping(refSeq, refSeqIndex, false);\r
+            annotation.adjustForAlignment();\r
+            refSeq.addAlignmentAnnotation(annotation);\r
+            al.addAnnotation(annotation);\r
+            al.setAnnotationIndex(annotation,\r
+                                al.getAlignmentAnnotation().length - existingAnnotations -\r
+                                1);\r
+            // and recover our virgin copy to use again if necessary.\r
+            annotation = ann;\r
+            \r
+          } while (refSeqId!=null && (refSeq=al.findName(refSeq, refSeqId, true))!=null);\r
+        } else {\r
+          al.addAnnotation(annotation);\r
+          al.setAnnotationIndex(annotation,\r
                               al.getAlignmentAnnotation().length - existingAnnotations -\r
                               1);\r
+        }\r
       }\r
 \r
     }\r
@@ -594,7 +650,14 @@ public class AnnotationFile
       displayChar = desc;\r
       desc = tmp;\r
     }\r
-\r
+    /*\r
+     * In principle, this code will ensure that the Annotation element generated is renderable by any of the applet or application rendering code\r
+     * but instead we check for null strings when the display character is rendered. \r
+    if (displayChar==null)\r
+    {\r
+      displayChar="";\r
+    }\r
+    */\r
     Annotation anot = new Annotation(displayChar, desc, ss, value);\r
 \r
     anot.colour = colour;\r
@@ -778,6 +841,8 @@ public class AnnotationFile
     if (sg != null)\r
     {\r
       String keyValue, key, value;\r
+      ColourSchemeI def = sg.cs;\r
+      sg.cs = null;\r
       while (st.hasMoreTokens())\r
       {\r
         keyValue = st.nextToken();\r
@@ -840,9 +905,17 @@ public class AnnotationFile
         {\r
           sg.thresholdTextColour = Integer.parseInt(value);\r
         }\r
-\r
+        else if (key.equalsIgnoreCase("idColour"))\r
+        {\r
+          // consider warning if colour doesn't resolve to a real colour\r
+          sg.setIdColour((def = new UserColourScheme(value)).findColour('A'));\r
+        }\r
         sg.recalcConservation();\r
       }\r
+      if (sg.cs==null)\r
+      {\r
+        sg.cs = def;\r
+      }\r
     }\r
   }\r
 \r
@@ -875,4 +948,33 @@ public class AnnotationFile
       al.setProperty(key,value);\r
     }\r
   }\r
+\r
+  /**\r
+   * Write annotations as a CSV file of the form 'label, value, value, ...' for each row.\r
+   * @param annotations\r
+   * @return CSV file as a string.\r
+   */\r
+  public String printCSVAnnotations(AlignmentAnnotation[] annotations)\r
+  {\r
+    StringBuffer sp = new StringBuffer();\r
+    for (int i=0; i<annotations.length; i++)\r
+    {\r
+      String atos = annotations[i].toString();\r
+      int p = 0;\r
+      do {\r
+        int cp = atos.indexOf("\n", p);\r
+        sp.append(annotations[i].label);\r
+        sp.append(",");\r
+        if (cp>p)\r
+        {\r
+          sp.append(atos.substring(p, cp+1));\r
+        } else {\r
+          sp.append(atos.substring(p));\r
+          sp.append("\n");\r
+        }\r
+        p = cp+1;\r
+      } while (p>0);\r
+    }\r
+    return sp.toString();\r
+  }\r
 }\r