Annotation/feature exporter
authoramwaterhouse <Andrew Waterhouse>
Fri, 9 Jun 2006 16:14:17 +0000 (16:14 +0000)
committeramwaterhouse <Andrew Waterhouse>
Fri, 9 Jun 2006 16:14:17 +0000 (16:14 +0000)
src/jalview/gui/AnnotationExporter.java [new file with mode: 0755]
src/jalview/io/AnnotationFile.java [new file with mode: 0755]
src/jalview/io/FeaturesFile.java [new file with mode: 0755]

diff --git a/src/jalview/gui/AnnotationExporter.java b/src/jalview/gui/AnnotationExporter.java
new file mode 100755 (executable)
index 0000000..d4975a6
--- /dev/null
@@ -0,0 +1,227 @@
+/*\r
+ * Jalview - A Sequence Alignment Editor and Viewer\r
+ * Copyright (C) 2006 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
+ */\r
+\r
+package jalview.gui;\r
+\r
+import javax.swing.*;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.ActionEvent;\r
+\r
+import jalview.io.*;\r
+import jalview.datamodel.AlignmentAnnotation;\r
+\r
+import java.awt.Color;\r
+import java.awt.FlowLayout;\r
+import javax.swing.BorderFactory;\r
+\r
+\r
+public class AnnotationExporter\r
+    extends JPanel\r
+{\r
+  JInternalFrame frame;\r
+  AlignmentPanel ap;\r
+  boolean features = true;\r
+  AlignmentAnnotation [] annotations;\r
+\r
+  public AnnotationExporter()\r
+  {\r
+    try\r
+    {\r
+      jbInit();\r
+    }\r
+    catch (Exception ex)\r
+    {\r
+      ex.printStackTrace();\r
+    }\r
+\r
+    frame = new JInternalFrame();\r
+    frame.setContentPane(this);\r
+    frame.setLayer(JLayeredPane.PALETTE_LAYER);\r
+    Desktop.addInternalFrame(frame,\r
+                             "",\r
+                             260, 125);\r
+  }\r
+\r
+  public void exportFeatures(AlignmentPanel ap)\r
+  {\r
+    this.ap = ap;\r
+    features = true;\r
+    frame.setTitle("Export Features");\r
+  }\r
+\r
+  public void exportAnnotations(AlignmentPanel ap,\r
+                                AlignmentAnnotation [] annotations)\r
+  {\r
+    this.ap = ap;\r
+    features = false;\r
+    GFFFormat.setVisible(false);\r
+    this.annotations = annotations;\r
+    frame.setTitle("Export Annotations");\r
+  }\r
+\r
+  public void toFile_actionPerformed(ActionEvent e)\r
+  {\r
+      JalviewFileChooser chooser = new JalviewFileChooser(\r
+      jalview.bin.Cache.getProperty( "LAST_DIRECTORY"));\r
+\r
+      chooser.setFileView(new JalviewFileView());\r
+      chooser.setDialogTitle(\r
+          features ? "Save Features to File" : "Save Annotation to File");\r
+      chooser.setToolTipText("Save");\r
+\r
+      int value = chooser.showSaveDialog(this);\r
+\r
+      if (value == JalviewFileChooser.APPROVE_OPTION)\r
+      {\r
+        String text = "No features found on alignment";\r
+        if (features)\r
+        {\r
+          if (GFFFormat.isSelected())\r
+            text = new FeaturesFile().printGFFFormat(\r
+                ap.av.alignment.getDataset().getSequencesArray(),\r
+                ap.av.featuresDisplayed);\r
+          else\r
+            text = new FeaturesFile().printJalviewFormat(\r
+                ap.av.alignment.getDataset().getSequencesArray(),\r
+                ap.av.featuresDisplayed);\r
+        }\r
+        else\r
+        {\r
+           text = new AnnotationFile().printAnnotations( annotations );\r
+        }\r
+\r
+        try\r
+        {\r
+          java.io.PrintWriter out = new java.io.PrintWriter(\r
+              new java.io.FileWriter(chooser.getSelectedFile()));\r
+\r
+          out.print(text);\r
+          out.close();\r
+        }\r
+        catch (Exception ex)\r
+        {\r
+          ex.printStackTrace();\r
+        }\r
+      }\r
+  }\r
+\r
+  public void toTextbox_actionPerformed(ActionEvent e)\r
+  {\r
+    String text = "No features found on alignment";\r
+    if(features)\r
+    {\r
+      if (GFFFormat.isSelected())\r
+        text = new FeaturesFile().printGFFFormat(\r
+            ap.av.alignment.getDataset().getSequencesArray(),\r
+            ap.av.featuresDisplayed);\r
+      else\r
+        text = new FeaturesFile().printJalviewFormat(\r
+            ap.av.alignment.getDataset().getSequencesArray(),\r
+                ap.av.featuresDisplayed);\r
+    }\r
+    else if(!features)\r
+    {\r
+      text = new AnnotationFile().printAnnotations( annotations );\r
+    }\r
+\r
+\r
+    CutAndPasteTransfer cap = new CutAndPasteTransfer();\r
+    cap.setText(text);\r
+    Desktop.addInternalFrame(cap,\r
+                             (features ?  "Features for - " : "Annotations for - ")\r
+                             + ap.alignFrame.getTitle(),\r
+                             600,\r
+                             500);\r
+\r
+\r
+  }\r
+\r
+  public void close_actionPerformed(ActionEvent e)\r
+  {\r
+    try{\r
+      frame.setClosed(true);\r
+    }catch(java.beans.PropertyVetoException ex)\r
+    {}\r
+  }\r
+\r
+\r
+\r
+  private void jbInit()\r
+      throws Exception\r
+  {\r
+    this.setLayout(flowLayout1);\r
+    toFile.setText("to File");\r
+    toFile.addActionListener(new ActionListener()\r
+    {\r
+      public void actionPerformed(ActionEvent e)\r
+      {\r
+        toFile_actionPerformed(e);\r
+      }\r
+    });\r
+    toTextbox.setText("to Textbox");\r
+    toTextbox.addActionListener(new ActionListener()\r
+    {\r
+      public void actionPerformed(ActionEvent e)\r
+      {\r
+        toTextbox_actionPerformed(e);\r
+      }\r
+    });\r
+    close.setText("Close");\r
+    close.addActionListener(new ActionListener()\r
+    {\r
+      public void actionPerformed(ActionEvent e)\r
+      {\r
+        close_actionPerformed(e);\r
+      }\r
+    });\r
+    jalviewFormat.setOpaque(false);\r
+    jalviewFormat.setSelected(true);\r
+    jalviewFormat.setText("Jalview");\r
+    GFFFormat.setOpaque(false);\r
+    GFFFormat.setText("GFF");\r
+    jLabel1.setHorizontalAlignment(SwingConstants.TRAILING);\r
+    jLabel1.setText("Format: ");\r
+    this.setBackground(Color.white);\r
+    jPanel3.setBorder(BorderFactory.createEtchedBorder());\r
+    jPanel3.setOpaque(false);\r
+    jPanel1.setOpaque(false);\r
+    jPanel1.add(toFile);\r
+    jPanel1.add(toTextbox);\r
+    jPanel1.add(close);\r
+    jPanel3.add(jLabel1);\r
+    jPanel3.add(jalviewFormat);\r
+    jPanel3.add(GFFFormat);\r
+    buttonGroup.add(jalviewFormat);\r
+    buttonGroup.add(GFFFormat);\r
+    this.add(jPanel3, null);\r
+    this.add(jPanel1, null);\r
+  }\r
+\r
+  JPanel jPanel1 = new JPanel();\r
+  JButton toFile = new JButton();\r
+  JButton toTextbox = new JButton();\r
+  JButton close = new JButton();\r
+  ButtonGroup buttonGroup = new ButtonGroup();\r
+  JRadioButton jalviewFormat = new JRadioButton();\r
+  JRadioButton GFFFormat = new JRadioButton();\r
+  JLabel jLabel1 = new JLabel();\r
+  JPanel jPanel3 = new JPanel();\r
+  FlowLayout flowLayout1 = new FlowLayout();\r
+\r
+}\r
diff --git a/src/jalview/io/AnnotationFile.java b/src/jalview/io/AnnotationFile.java
new file mode 100755 (executable)
index 0000000..e427605
--- /dev/null
@@ -0,0 +1,420 @@
+/*\r
+ * Jalview - A Sequence Alignment Editor and Viewer\r
+ * Copyright (C) 2006 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
+ */\r
+\r
+package jalview.io;\r
+\r
+import java.io.*;\r
+import jalview.datamodel.*;\r
+import java.util.*;\r
+import jalview.schemes.UserColourScheme;\r
+import java.net.URL;\r
+\r
+\r
+public class AnnotationFile\r
+{\r
+\r
+  public String printAnnotations(AlignmentAnnotation [] annotations)\r
+  {\r
+    StringBuffer text = new StringBuffer(\r
+      "JALVIEW_ANNOTATION\n"\r
+      +"# Created: "\r
+      +new java.util.Date()+"\n\n");\r
+\r
+    AlignmentAnnotation row;\r
+    String comma;\r
+    String seqref = null;\r
+\r
+    StringBuffer colours = new StringBuffer();\r
+    StringBuffer graphLine = new StringBuffer();\r
+\r
+    Hashtable graphGroup = new Hashtable();\r
+\r
+    java.awt.Color color;\r
+\r
+    for(int i=0; i<annotations.length; i++)\r
+    {\r
+      row = annotations[i];\r
+      color = null;\r
+\r
+      if( row.sequenceRef == null)\r
+        seqref = null;\r
+\r
+      else if (seqref == null\r
+           || !seqref.equals(row.sequenceRef.getName()))\r
+      {\r
+        seqref = row.sequenceRef.getName();\r
+        text.append("\nSEQUENCE_REF\t" + seqref + "\n");\r
+      }\r
+\r
+\r
+      switch( row.graph )\r
+      {\r
+        case AlignmentAnnotation.NO_GRAPH:\r
+          text.append("NO_GRAPH\t");\r
+          break;\r
+        case AlignmentAnnotation.BAR_GRAPH:\r
+          text.append("BAR_GRAPH\t");\r
+        case AlignmentAnnotation.LINE_GRAPH:\r
+          text.append("LINE_GRAPH\t");\r
+\r
+          if(row.getThreshold()!=null)\r
+            graphLine.append("GRAPHLINE\t"\r
+                             + row.label + "\t"\r
+                             + row.getThreshold().value + "\t"\r
+                             + row.getThreshold().label + "\t"\r
+                             + jalview.util.Format.getHexString(\r
+                                 row.getThreshold().colour)+"\n"\r
+                );\r
+\r
+          if(row.graphGroup>-1)\r
+          {\r
+            String key = String.valueOf(row.graphGroup);\r
+            if(graphGroup.containsKey(key))\r
+              graphGroup.put(key, graphGroup.get(key)\r
+                             +"\t"+row.label);\r
+            else\r
+              graphGroup.put(key, row.label);\r
+          }\r
+      }\r
+\r
+      text.append(row.label+"\t");\r
+\r
+      for(int j=0; j<row.annotations.length; j++)\r
+      {\r
+        if(row.annotations[j]!=null)\r
+        {\r
+          comma = "";\r
+          if (row.annotations[j].displayCharacter.length() > 0\r
+              && !row.annotations[j].displayCharacter.equals(" "))\r
+          {\r
+            text.append(row.annotations[j].displayCharacter);\r
+            comma = ",";\r
+          }\r
+          if (row.annotations[j].secondaryStructure!=' ')\r
+          {\r
+            text.append(comma + row.annotations[j].secondaryStructure);\r
+            comma = ",";\r
+          }\r
+          if (row.annotations[j].value!=0f)\r
+          {\r
+            color = row.annotations[j].colour;\r
+            text.append(comma + row.annotations[j].value);\r
+          }\r
+        }\r
+        text.append("|");\r
+      }\r
+\r
+      text.append("\n");\r
+\r
+      if(color!=null && color!=java.awt.Color.black)\r
+      {\r
+        colours.append("COLOUR\t"\r
+                       +row.label+"\t"\r
+                       +jalview.util.Format.getHexString(color)+"\n");\r
+      }\r
+\r
+    }\r
+\r
+    text.append("\n");\r
+\r
+    text.append(colours.toString());\r
+    text.append(graphLine.toString());\r
+    if(graphGroup.size()>0)\r
+    {\r
+      text.append("COMBINE\t");\r
+      Enumeration en = graphGroup.elements();\r
+      while(en.hasMoreElements())\r
+      {\r
+        text.append(en.nextElement()+"\n");\r
+      }\r
+    }\r
+\r
+    return text.toString();\r
+  }\r
+\r
+  public boolean readAnnotationFile(AlignmentI al, String file)\r
+  {\r
+    try\r
+    {\r
+      BufferedReader in = null;\r
+      java.io.InputStream is = getClass().getResourceAsStream("/" + file);\r
+      if (is != null)\r
+      {\r
+        in = new BufferedReader(new java.io.InputStreamReader(is));\r
+      }\r
+      else\r
+      {\r
+        try\r
+        {\r
+          URL url = new URL(file);\r
+          in = new BufferedReader(new InputStreamReader(url.openStream()));\r
+        }\r
+        catch (java.net.MalformedURLException ex)\r
+        {\r
+          in = new BufferedReader(new FileReader(file));\r
+        }\r
+      }\r
+\r
+      String line, label, description, token;\r
+      int graphStyle, index;\r
+      SequenceI refSeq = null;\r
+      int refSeqIndex = 1;\r
+      int existingAnnotations = 0;\r
+      if(al.getAlignmentAnnotation()!=null)\r
+       existingAnnotations = al.getAlignmentAnnotation().length;\r
+\r
+      int alWidth = al.getWidth();\r
+\r
+      StringTokenizer st;\r
+      Annotation[] annotations;\r
+      AlignmentAnnotation annotation = null;\r
+\r
+      // First confirm this is an Annotation file\r
+      boolean jvAnnotationFile = false;\r
+      while ( (line = in.readLine()) != null)\r
+      {\r
+        if (line.indexOf("#") == 0 )\r
+          continue;\r
+\r
+        if (line.indexOf("JALVIEW_ANNOTATION") > -1)\r
+        {\r
+          jvAnnotationFile = true;\r
+          break;\r
+        }\r
+      }\r
+\r
+      if(!jvAnnotationFile)\r
+      {\r
+        in.close();\r
+        return false;\r
+      }\r
+\r
+      while ( (line = in.readLine()) != null)\r
+      {\r
+        if(line.indexOf("#")==0\r
+           || line.indexOf("JALVIEW_ANNOTATION")>-1\r
+           || line.length()==0)\r
+          continue;\r
+\r
+        st = new StringTokenizer(line, "\t");\r
+        token = st.nextToken();\r
+        if(token.equalsIgnoreCase("COLOUR"))\r
+        {\r
+          colourAnnotations(al, st.nextToken(), st.nextToken());\r
+          continue;\r
+        }\r
+\r
+        if(token.equalsIgnoreCase("COMBINE") )\r
+        {\r
+          combineAnnotations(al, st);\r
+          continue;\r
+        }\r
+\r
+        if (token.equalsIgnoreCase("GRAPHLINE"))\r
+        {\r
+          addLine(al, st);\r
+          continue;\r
+        }\r
+\r
+\r
+        if(token.equalsIgnoreCase("SEQUENCE_REF") )\r
+        {\r
+          refSeq = al.findName(st.nextToken());\r
+          try{\r
+            refSeqIndex = Integer.parseInt(st.nextToken());\r
+          }\r
+          catch(Exception ex)\r
+          {\r
+            refSeqIndex = 1;\r
+          }\r
+\r
+          continue;\r
+        }\r
+\r
+\r
+        graphStyle = AlignmentAnnotation.getGraphValueFromString(token);\r
+        label = description = st.nextToken();\r
+\r
+        line = st.nextToken();\r
+\r
+        st = new StringTokenizer(line, "|", true);\r
+        annotations = new Annotation[alWidth];\r
+\r
+        index = 0;\r
+        boolean emptyColumn = true;\r
+\r
+\r
+        while (st.hasMoreElements() && index<alWidth)\r
+        {\r
+          token = st.nextToken().trim();\r
+          if(token.equals("|"))\r
+          {\r
+            if(emptyColumn)\r
+              index++;\r
+\r
+            emptyColumn = true;\r
+          }\r
+          else\r
+          {\r
+            annotations[index++] = parseAnnotation(token);\r
+            emptyColumn = false;\r
+          }\r
+        }\r
+\r
+       annotation = new AlignmentAnnotation(label,\r
+                                          description,\r
+                                          annotations,\r
+                                          0,\r
+                                          0,\r
+                                          graphStyle);\r
+\r
+       if(refSeq!=null)\r
+       {\r
+         annotation.createSequenceMapping(refSeq, refSeqIndex);\r
+         refSeq.addAlignmentAnnotation(annotation);\r
+       }\r
+\r
+       al.addAnnotation(annotation);\r
+\r
+       al.setAnnotationIndex(annotation,  al.getAlignmentAnnotation().length - existingAnnotations-1);\r
+      }\r
+\r
+    }catch(Exception ex)\r
+    {\r
+      ex.printStackTrace();\r
+      System.out.println("Problem reading annotation file: "+ex);\r
+      return false;\r
+    }\r
+    return true;\r
+  }\r
+\r
+  Annotation parseAnnotation(String string)\r
+  {\r
+    String desc = "", displayChar="";\r
+    char ss = ' '; // secondaryStructure\r
+    float value = 0;\r
+    boolean parsedValue = false;\r
+    StringTokenizer st = new StringTokenizer(string, ",");\r
+    String token;\r
+    while(st.hasMoreTokens())\r
+    {\r
+      token = st.nextToken().trim();\r
+      if(token.length()==0)\r
+        continue;\r
+\r
+      if(!parsedValue)\r
+      {\r
+        try{\r
+          value = new Float(token).floatValue();\r
+          displayChar = token;\r
+          parsedValue = true;\r
+        }catch(NumberFormatException ex){}\r
+      }\r
+\r
+      if(token.equals("H") || token.equals("E"))\r
+      {\r
+        // Either this character represents a helix or sheet\r
+        // or an integer which can be displayed\r
+        ss = token.charAt(0);\r
+      }\r
+      else if(desc.length()<1)\r
+        desc = token;\r
+\r
+    }\r
+\r
+    return new Annotation(displayChar, desc, ss, value);\r
+  }\r
+\r
+  void colourAnnotations(AlignmentI al, String label, String colour)\r
+  {\r
+    UserColourScheme ucs = new UserColourScheme(colour);\r
+    Annotation[] annotations;\r
+    for(int i=0; i<al.getAlignmentAnnotation().length; i++)\r
+    {\r
+      if(al.getAlignmentAnnotation()[i].label.equalsIgnoreCase(label))\r
+      {\r
+        annotations = al.getAlignmentAnnotation()[i].annotations;\r
+        for(int j=0; j<annotations.length; j++)\r
+        {\r
+          if(annotations[j]!=null)\r
+            annotations[j].colour = ucs.findColour("A");\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+  void combineAnnotations(AlignmentI al, StringTokenizer st)\r
+  {\r
+    int graphGroup = -1;\r
+    String group = st.nextToken();\r
+    //First make sure we are not overwriting the graphIndex\r
+    for(int i=0; i<al.getAlignmentAnnotation().length; i++)\r
+    {\r
+      if(al.getAlignmentAnnotation()[i].label.equalsIgnoreCase(group))\r
+      {\r
+        graphGroup = al.getAlignmentAnnotation()[i].graphGroup +1;\r
+        al.getAlignmentAnnotation()[i].graphGroup = graphGroup;\r
+        break;\r
+      }\r
+    }\r
+\r
+    //Now update groups\r
+    while(st.hasMoreTokens())\r
+    {\r
+      group = st.nextToken();\r
+      for(int i=0; i<al.getAlignmentAnnotation().length; i++)\r
+      {\r
+        if (al.getAlignmentAnnotation()[i].label.equalsIgnoreCase(group))\r
+        {\r
+          al.getAlignmentAnnotation()[i].graphGroup = graphGroup;\r
+          break;\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+  void addLine(AlignmentI al, StringTokenizer st)\r
+  {\r
+    String group = st.nextToken();\r
+    AlignmentAnnotation annotation = null;\r
+\r
+    for (int i = 0; i < al.getAlignmentAnnotation().length; i++)\r
+    {\r
+      if (al.getAlignmentAnnotation()[i].label.equalsIgnoreCase(group))\r
+      {\r
+        annotation = al.getAlignmentAnnotation()[i];\r
+        break;\r
+      }\r
+    }\r
+\r
+    if(annotation==null)\r
+      return;\r
+    float value = new Float(st.nextToken()).floatValue();\r
+    String label = st.hasMoreTokens() ?  st.nextToken() : null;\r
+    java.awt.Color colour = null;\r
+    if(st.hasMoreTokens())\r
+    {\r
+      UserColourScheme ucs = new UserColourScheme(st.nextToken());\r
+      colour = ucs.findColour("A");\r
+    }\r
+\r
+    annotation.setThreshold(new GraphLine(value, label, colour));\r
+\r
+  }\r
+}\r
diff --git a/src/jalview/io/FeaturesFile.java b/src/jalview/io/FeaturesFile.java
new file mode 100755 (executable)
index 0000000..a83dff8
--- /dev/null
@@ -0,0 +1,381 @@
+/*\r
+* Jalview - A Sequence Alignment Editor and Viewer\r
+* Copyright (C) 2005 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
+*\r
+* This program is free software; you can redistribute it and/or\r
+* modify it under the terms of the GNU General Public License\r
+* as published by the Free Software Foundation; either version 2\r
+* of the License, or (at your option) any later version.\r
+*\r
+* This program is distributed in the hope that it will be useful,\r
+* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+* GNU General Public License for more details.\r
+*\r
+* You should have received a copy of the GNU General Public License\r
+* along with this program; if not, write to the Free Software\r
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
+*/\r
+package jalview.io;\r
+\r
+import jalview.datamodel.*;\r
+\r
+import java.io.*;\r
+import java.util.*;\r
+import jalview.schemes.UserColourScheme;\r
+\r
+\r
+/**\r
+ * DOCUMENT ME!\r
+ *\r
+ * @author $author$\r
+ * @version $Revision$\r
+ */\r
+public class FeaturesFile extends AlignFile\r
+{\r
+    /**\r
+     * Creates a new FeaturesFile object.\r
+     */\r
+    public FeaturesFile()\r
+    {\r
+    }\r
+\r
+    /**\r
+     * Creates a new FeaturesFile object.\r
+     *\r
+     * @param inStr DOCUMENT ME!\r
+     */\r
+    public FeaturesFile(String inStr)\r
+    {\r
+        super(inStr);\r
+    }\r
+\r
+    /**\r
+     * Creates a new FeaturesFile object.\r
+     *\r
+     * @param inFile DOCUMENT ME!\r
+     * @param type DOCUMENT ME!\r
+     *\r
+     * @throws IOException DOCUMENT ME!\r
+     */\r
+    public FeaturesFile(String inFile, String type)\r
+       throws IOException\r
+    {\r
+        super(inFile, type);\r
+    }\r
+\r
+    /**\r
+     * DOCUMENT ME!\r
+     *\r
+     * @throws IOException DOCUMENT ME!\r
+     */\r
+    public boolean parse(AlignmentI align,  Hashtable colours)\r
+        throws IOException\r
+    {\r
+      String line = null;\r
+      try\r
+      {\r
+        SequenceI seq = null;\r
+        String type, desc, token=null;\r
+\r
+        int index, start, end;\r
+        float score;\r
+        StringTokenizer st;\r
+        SequenceFeature sf;\r
+        String featureGroup = null;\r
+\r
+        boolean GFFFile = true;\r
+\r
+        while ( (line = nextLine()) != null)\r
+        {\r
+          if(line.startsWith("#"))\r
+            continue;\r
+\r
+          st = new StringTokenizer(line, "\t");\r
+          if (st.countTokens()>1 && st.countTokens() < 4 )\r
+          {\r
+            GFFFile = false;\r
+            type = st.nextToken();\r
+            if (type.equalsIgnoreCase("startgroup"))\r
+            {\r
+              featureGroup = st.nextToken();\r
+            }\r
+            else if (type.equalsIgnoreCase("endgroup"))\r
+            {\r
+              //We should check whether this is the current group,\r
+              //but at present theres no way of showing more than 1 group\r
+              st.nextToken();\r
+              featureGroup = null;\r
+            }\r
+            else\r
+            {\r
+              UserColourScheme ucs = new UserColourScheme(st.nextToken());\r
+              colours.put(type, ucs.findColour("A"));\r
+            }\r
+            continue;\r
+          }\r
+\r
+          while (st.hasMoreElements())\r
+          {\r
+\r
+            if(GFFFile)\r
+            {\r
+              // Still possible this is an old Jalview file,\r
+              // which does not have type colours at the beginning\r
+              token = st.nextToken();\r
+              seq = align.findName(token);\r
+              if(seq != null)\r
+              {\r
+                desc = st.nextToken();\r
+                type = st.nextToken();\r
+                start = Integer.parseInt(st.nextToken());\r
+                end = Integer.parseInt(st.nextToken());\r
+                try\r
+                {\r
+                  score = Float.parseFloat(st.nextToken());\r
+                }\r
+                catch (NumberFormatException ex)\r
+                {\r
+                  score = 0;\r
+                }\r
+\r
+                sf = new SequenceFeature(type, desc, start, end, score, null);\r
+\r
+                try\r
+                {\r
+                  sf.setValue("STRAND", st.nextToken());\r
+                  sf.setValue("FRAME", st.nextToken());\r
+                }\r
+                catch (Exception ex)\r
+                {}\r
+\r
+                seq.getDatasetSequence().addSequenceFeature(sf);\r
+\r
+                break;\r
+              }\r
+            }\r
+\r
+            if(GFFFile && seq==null)\r
+            {\r
+              desc = token;\r
+            }\r
+            else\r
+              desc = st.nextToken();\r
+\r
+\r
+            token = st.nextToken();\r
+            if (!token.equals("ID_NOT_SPECIFIED"))\r
+            {\r
+              seq = align.findName(token);\r
+              st.nextToken();\r
+            }\r
+            else\r
+            {\r
+              try{\r
+                index = Integer.parseInt(st.nextToken());\r
+                seq = align.getSequenceAt(index);\r
+              }\r
+              catch(NumberFormatException ex)\r
+              {\r
+                seq = null;\r
+              }\r
+            }\r
+\r
+            if(seq==null)\r
+            {\r
+              System.out.println("Sequence not found: "+line);\r
+              break;\r
+            }\r
+\r
+            start = Integer.parseInt(st.nextToken());\r
+            end = Integer.parseInt(st.nextToken());\r
+\r
+            type = st.nextToken();\r
+\r
+            if (!colours.containsKey(type))\r
+            {\r
+              // Probably the old style groups file\r
+              UserColourScheme ucs = new UserColourScheme(type);\r
+              colours.put(type, ucs.findColour("A"));\r
+            }\r
+\r
+            sf = new SequenceFeature(type, desc, "", start, end, featureGroup);\r
+\r
+            seq.getDatasetSequence().addSequenceFeature(sf);\r
+\r
+            //If we got here, its not a GFFFile\r
+            GFFFile = false;\r
+          }\r
+        }\r
+      }\r
+      catch (Exception ex)\r
+      {\r
+        System.out.println(line);\r
+        ex.printStackTrace();\r
+        System.out.println("Error parsing groups file: " + ex +"\n"+line);\r
+        return false;\r
+      }\r
+\r
+      return true;\r
+\r
+    }\r
+\r
+\r
+    /**\r
+     * DOCUMENT ME!\r
+     *\r
+     * @param s DOCUMENT ME!\r
+     * @param len DOCUMENT ME!\r
+     * @param gaps DOCUMENT ME!\r
+     * @param displayId DOCUMENT ME!\r
+     *\r
+     * @return DOCUMENT ME!\r
+     */\r
+    public String printJalviewFormat(SequenceI [] seqs,\r
+                                     Hashtable visible)\r
+    {\r
+        StringBuffer out = new StringBuffer();\r
+        SequenceFeature [] next;\r
+\r
+        if(visible==null || visible.size()<1)\r
+          return "No Features Visible";\r
+\r
+        Enumeration en = visible.keys();\r
+        String type;\r
+        int color;\r
+        while( en.hasMoreElements() )\r
+        {\r
+          type = en.nextElement().toString();\r
+          color = Integer.parseInt( visible.get(type).toString() );\r
+          out.append(type + "\t"\r
+                     + jalview.util.Format.getHexString(\r
+                         new java.awt.Color(color)  )\r
+                     +"\n");\r
+        }\r
+\r
+        //Work out which groups are both present and visible\r
+        Vector groups = new Vector();\r
+        int groupIndex = 0;\r
+\r
+        for(int i=0; i<seqs.length; i++)\r
+        {\r
+          next = seqs[i].getSequenceFeatures();\r
+          if(next!=null)\r
+          {\r
+            for(int j=0; j<next.length; j++)\r
+            {\r
+              if (!visible.containsKey(next[j].type))\r
+                continue;\r
+\r
+              if (    next[j].featureGroup != null\r
+                  && !groups.contains(next[j].featureGroup))\r
+                groups.addElement(next[j].featureGroup);\r
+            }\r
+          }\r
+        }\r
+\r
+        String group = null;\r
+\r
+        do\r
+        {\r
+          if (groups.size() > 0)\r
+          {\r
+            group = groups.elementAt(groupIndex).toString();\r
+            out.append("\nSTARTGROUP\t" + group + "\n");\r
+          }\r
+\r
+\r
+          for (int i = 0; i < seqs.length; i++)\r
+          {\r
+            next = seqs[i].getSequenceFeatures();\r
+            if (next != null)\r
+            {\r
+              for (int j = 0; j < next.length; j++)\r
+              {\r
+                if (!visible.containsKey(next[j].type))\r
+                  continue;\r
+\r
+                if (group != null && !next[j].featureGroup.equals(group))\r
+                  continue;\r
+\r
+                if(next[j].description==null || next[j].description.equals(""))\r
+                  out.append(next[j].type+"\t");\r
+                else\r
+                  out.append(next[j].description + "\t");\r
+\r
+                out.append(  seqs[i].getName() + "\t-1\t"\r
+                           + next[j].begin + "\t"\r
+                           + next[j].end + "\t"\r
+                           + next[j].type + "\n"\r
+                    );\r
+              }\r
+            }\r
+          }\r
+\r
+          if(groups.size()>0)\r
+          {\r
+            out.append("ENDGROUP\t"+group+"\n");\r
+          }\r
+\r
+          groupIndex++;\r
+        }\r
+        while(groupIndex < groups.size());\r
+\r
+\r
+      return out.toString();\r
+    }\r
+\r
+    public String printGFFFormat(SequenceI [] seqs, Hashtable visible)\r
+    {\r
+      StringBuffer out = new StringBuffer();\r
+      SequenceFeature [] next;\r
+\r
+      for(int i=0; i<seqs.length; i++)\r
+      {\r
+        if(seqs[i].getSequenceFeatures()!=null)\r
+        {\r
+          next = seqs[i].getSequenceFeatures();\r
+          for(int j=0; j<next.length; j++)\r
+          {\r
+            if(!visible.containsKey(next[j].type))\r
+              continue;\r
+\r
+            out.append(seqs[i].getName() + "\t"\r
+                       + next[j].description + "\t"\r
+                       + next[j].type  + "\t"\r
+                       + next[j].begin + "\t"\r
+                       + next[j].end   + "\t"\r
+                       + next[j].score + "\t"\r
+                      );\r
+\r
+            if(next[j].getValue("STRAND")!=null)\r
+              out.append(next[j].getValue("STRAND")+"\t");\r
+            else\r
+              out.append(".\t");\r
+            if(next[j].getValue("FRAME")!=null)\r
+              out.append(next[j].getValue("FRAME")+"\n");\r
+            else\r
+              out.append(".\n");\r
+\r
+          }\r
+        }\r
+      }\r
+\r
+      return out.toString();\r
+    }\r
+\r
+    public void parse()\r
+    {\r
+      //IGNORED\r
+    }\r
+\r
+    /**\r
+     * DOCUMENT ME!\r
+     *\r
+     * @return DOCUMENT ME!\r
+     */\r
+    public String print()\r
+    {\r
+        return "USE printGFFFormat() or printJalviewFormat()";\r
+    }\r
+}\r