Merge branch 'features/JAL-2360colourSchemeApplicability' into features/JAL-2371colle...
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 17 Jan 2017 13:32:33 +0000 (13:32 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 17 Jan 2017 13:32:33 +0000 (13:32 +0000)
38 files changed:
examples/groovy/fileFormat.groovy [new file with mode: 0644]
src/jalview/appletgui/APopupMenu.java
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/CutAndPasteTransfer.java
src/jalview/bin/JalviewLite.java
src/jalview/bin/JalviewLiteURLRetrieve.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/CutAndPasteTransfer.java
src/jalview/gui/Desktop.java
src/jalview/gui/PCAPanel.java
src/jalview/gui/PopupMenu.java
src/jalview/gui/Preferences.java
src/jalview/io/AlignFile.java
src/jalview/io/AlignmentFileReaderI.java [moved from src/jalview/io/AlignmentFileI.java with 69% similarity]
src/jalview/io/AlignmentFileWriterI.java [new file with mode: 0644]
src/jalview/io/AppletFormatAdapter.java
src/jalview/io/FileFormat.java
src/jalview/io/FileFormatI.java
src/jalview/io/FileFormats.java [new file with mode: 0644]
src/jalview/io/FileLoader.java
src/jalview/io/FormatAdapter.java
src/jalview/io/IdentifyFile.java
src/jalview/io/JalviewFileChooser.java
src/jalview/io/JalviewFileView.java
src/jalview/jbgui/GAlignFrame.java
src/jalview/math/Matrix.java
src/jalview/util/Format.java
src/jalview/ws/jws1/JPredThread.java
src/jalview/ws/rest/params/Alignment.java
test/jalview/analysis/AlignmentGenerator.java [moved from test/jalview/analysis/DnaAlignmentGenerator.java with 82% similarity]
test/jalview/analysis/DnaTest.java
test/jalview/io/FileFormatsTest.java [new file with mode: 0644]
test/jalview/io/FormatAdapterTest.java
test/jalview/math/MatrixTest.java [new file with mode: 0644]
test/jalview/util/FormatTest.java
test/jalview/ws/jabaws/DisorderAnnotExportImport.java
test/jalview/ws/jabaws/JpredJabaStructExportImport.java
test/jalview/ws/jabaws/RNAStructExportImport.java

diff --git a/examples/groovy/fileFormat.groovy b/examples/groovy/fileFormat.groovy
new file mode 100644 (file)
index 0000000..c314a6c
--- /dev/null
@@ -0,0 +1,108 @@
+import jalview.datamodel.AlignmentI
+import jalview.datamodel.SequenceI
+import jalview.datamodel.Sequence
+import jalview.io.FileFormatI
+import jalview.io.FileFormats
+import jalview.io.FileParse
+import jalview.io.AlignFile
+
+/*
+ * Example script that registers a new alignment file format 
+ * consisting of lines like
+ * !I=<sequence id>
+ * !S=<sequence string>
+ */
+
+/*
+ * A parser class to read or write the format
+ */
+class MyParser extends AlignFile 
+{
+  /*
+   * Constructor for reading a file; the superclass
+   * constructor will call the parse() method
+   */
+  MyParser(FileParse src) 
+  {
+    super(src)
+  }
+
+  /*
+   * Constructor for writing out an alignment
+   */
+  MyParser(AlignmentI al) 
+  {
+  }
+  
+  /*
+   * Parse a formatted data file (with no error checking!)
+   */
+  void parse() 
+  {
+    String id
+    String line
+    while ((line = nextLine()) != null) 
+    {
+      if (line.startsWith('!I='))
+      {
+        int pos = line.indexOf('/')
+        id = line.substring(3, pos == -1 ? line.length() : pos)
+      } else if (line.startsWith('!S='))
+      {
+        String seq = line.substring(3)
+        addSequence(new Sequence(id, seq))
+      }
+    }
+  }
+  
+  /*
+   * Print the formatted sequences
+   * (addSuffix always defaults to true as no user preference for it)
+   */
+  String print(SequenceI[] seqs, boolean addSuffix) 
+  {
+      StringBuilder sb = new StringBuilder()
+      for (SequenceI seq : seqs) 
+      {
+          sb.append('!I=').append(seq.getDisplayId(addSuffix)).append('\n')
+          sb.append('!S=').append(seq.getSequenceAsString()).append('\n')
+      }
+      sb.toString()
+  }
+}
+
+/*
+ * A closure that defines the 'Groovy example' file format,
+ * delegating to MyParser for reading and writing
+ */
+def myFormat = { ->
+  [
+    getName: { -> 'Groovy example' },
+    
+    toString: { -> getName() },
+    
+    getExtensions: { -> 'grv' },
+    
+    getReader: { FileParse source -> new MyParser(source) },
+    
+    getWriter: { AlignmentI al -> new MyParser(al) },
+    
+    isReadable: { -> true },
+    
+    isWritable: { -> true },
+    
+    isTextFormat: { -> true },
+    
+    isStructureFile: { -> false },
+    
+    isComplexAlignFile: { -> false },
+
+   ] as FileFormatI
+}
+
+/*
+ * Register the file format. After running this script in Jalview's
+ * Groovy console, the new format should be shown in open file,
+ * save file, and output to textbox menu options.
+ */
+FileFormats.instance.registerFileFormat(myFormat())
index 941052d..6882c6c 100644 (file)
@@ -36,7 +36,8 @@ import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.io.AppletFormatAdapter;
 import jalview.io.DataSourceType;
-import jalview.io.FileFormat;
+import jalview.io.FileFormatI;
+import jalview.io.FileFormats;
 import jalview.io.SequenceAnnotationReport;
 import jalview.schemes.Blosum62ColourScheme;
 import jalview.schemes.BuriedColourScheme;
@@ -223,7 +224,7 @@ public class APopupMenu extends java.awt.PopupMenu implements
       e.printStackTrace();
     }
 
-    for (String ff : FileFormat.getWritableFormats(true))
+    for (String ff : FileFormats.getInstance().getWritableFormats(true))
     {
       MenuItem item = new MenuItem(ff);
 
@@ -836,7 +837,8 @@ public class APopupMenu extends java.awt.PopupMenu implements
     // now returns a full copy of sequence data
     // TODO consider using getSequenceSelection instead here
 
-    FileFormat fileFormat = FileFormat.valueOf(e.getActionCommand());
+    FileFormatI fileFormat = FileFormats.getInstance().forName(
+            e.getActionCommand());
     cap.setText(new AppletFormatAdapter().formatSequences(fileFormat,
             ap.av.getShowJVSuffix(), ap, true));
 
index 3d961b1..31d049a 100644 (file)
@@ -52,6 +52,8 @@ import jalview.io.AppletFormatAdapter;
 import jalview.io.DataSourceType;
 import jalview.io.FeaturesFile;
 import jalview.io.FileFormat;
+import jalview.io.FileFormatI;
+import jalview.io.FileFormats;
 import jalview.io.TCoffeeScoreFile;
 import jalview.schemes.Blosum62ColourScheme;
 import jalview.schemes.BuriedColourScheme;
@@ -1364,7 +1366,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
             "label.alignment_output_command",
             new Object[] { e.getActionCommand() }), 600, 500);
 
-    FileFormat fileFormat = FileFormat.valueOf(e.getActionCommand());
+    FileFormatI fileFormat = FileFormats.getInstance().forName(
+            e.getActionCommand());
     cap.setText(new AppletFormatAdapter(alignPanel).formatSequences(
             fileFormat, viewport.getAlignment(),
             viewport.getShowJVSuffix()));
@@ -3244,7 +3247,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     inputText.addActionListener(this);
     Menu outputTextboxMenu = new Menu(
             MessageManager.getString("label.out_to_textbox"));
-    for (String ff : FileFormat.getWritableFormats(true))
+    for (String ff : FileFormats.getInstance().getWritableFormats(true))
     {
       MenuItem item = new MenuItem(ff);
 
index d50fcef..c658734 100644 (file)
@@ -28,7 +28,7 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
-import jalview.io.AlignmentFileI;
+import jalview.io.AlignmentFileReaderI;
 import jalview.io.AnnotationFile;
 import jalview.io.AppletFormatAdapter;
 import jalview.io.DataSourceType;
@@ -68,7 +68,7 @@ public class CutAndPasteTransfer extends Panel implements ActionListener,
 
   AlignFrame alignFrame;
 
-  AlignmentFileI source = null;
+  AlignmentFileReaderI source = null;
 
   public CutAndPasteTransfer(boolean forImport, AlignFrame alignFrame)
   {
index e01ab57..7fa5147 100644 (file)
@@ -38,8 +38,8 @@ import jalview.datamodel.SequenceI;
 import jalview.io.AnnotationFile;
 import jalview.io.AppletFormatAdapter;
 import jalview.io.DataSourceType;
-import jalview.io.FileFormat;
 import jalview.io.FileFormatI;
+import jalview.io.FileFormats;
 import jalview.io.FileParse;
 import jalview.io.IdentifyFile;
 import jalview.io.JPredFile;
@@ -518,7 +518,7 @@ public class JalviewLite extends Applet implements
   {
     try
     {
-      FileFormatI theFormat = FileFormat.valueOf(format);
+      FileFormatI theFormat = FileFormats.getInstance().forName(format);
       boolean seqlimits = suffix.equalsIgnoreCase(TRUE);
       if (alf.viewport.getSelectionGroup() != null)
       {
@@ -716,7 +716,7 @@ public class JalviewLite extends Applet implements
     {
       boolean seqlimits = suffix.equalsIgnoreCase(TRUE);
 
-      FileFormatI theFormat = FileFormat.valueOf(format);
+      FileFormatI theFormat = FileFormats.getInstance().forName(format);
       String reply = new AppletFormatAdapter().formatSequences(theFormat,
               alf.viewport.getAlignment(), seqlimits);
       return reply;
index 156e146..d6fb090 100644 (file)
@@ -25,6 +25,7 @@ import jalview.io.AppletFormatAdapter;
 import jalview.io.DataSourceType;
 import jalview.io.FileFormat;
 import jalview.io.FileFormatI;
+import jalview.io.FileFormats;
 import jalview.io.FileParse;
 import jalview.io.IdentifyFile;
 
@@ -115,7 +116,8 @@ public class JalviewLiteURLRetrieve extends Applet
                 + " cannot be read with protocol==" + protocol);
         return;
       }
-      FileFormatI format = FileFormat.valueOf(getParameter("format"));
+      FileFormatI format = FileFormats.getInstance().forName(
+              getParameter("format"));
       if (format == null)
       {
         format = new IdentifyFile().identify(file, protocol);
index 2405bbd..2af53ea 100644 (file)
@@ -67,6 +67,7 @@ import jalview.io.BioJsHTMLOutput;
 import jalview.io.DataSourceType;
 import jalview.io.FileFormat;
 import jalview.io.FileFormatI;
+import jalview.io.FileFormats;
 import jalview.io.FileLoader;
 import jalview.io.FormatAdapter;
 import jalview.io.HtmlSvgOutput;
@@ -118,6 +119,8 @@ import java.awt.print.PageFormat;
 import java.awt.print.PrinterJob;
 import java.beans.PropertyChangeEvent;
 import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -1094,7 +1097,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void saveAs_actionPerformed(ActionEvent e)
   {
     String format = currentFileFormat == null ? null : currentFileFormat
-            .toString();
+            .getName();
     JalviewFileChooser chooser = JalviewFileChooser.forWrite(
             Cache.getProperty("LAST_DIRECTORY"), format);
 
@@ -1128,8 +1131,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
       fileName = chooser.getSelectedFile().getPath();
 
-      Cache.setProperty("DEFAULT_FILE_FORMAT",
-              currentFileFormat.toString());
+      Cache.setProperty("DEFAULT_FILE_FORMAT", currentFileFormat.getName());
 
       Cache.setProperty("LAST_DIRECTORY", fileName);
       saveAlignment(fileName, currentFileFormat);
@@ -1159,17 +1161,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
     else
     {
-      // if (!jalview.io.AppletFormatAdapter.isValidFormat(format, true))
-      // {
-      // warningMessage("Cannot save file " + fileName + " using format "
-      // + format, "Alignment output format not supported");
-      // if (!Jalview.isHeadlessMode())
-      // {
-      // saveAs_actionPerformed(null);
-      // }
-      // return false;
-      // }
-
       AlignmentExportData exportData = getAlignmentForExport(format,
               viewport, null);
       if (exportData.getSettings().isCancelled())
@@ -1194,15 +1185,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         try
         {
-          java.io.PrintWriter out = new java.io.PrintWriter(
-                  new java.io.FileWriter(file));
+          PrintWriter out = new PrintWriter(new FileWriter(file));
 
           out.print(output);
           out.close();
           this.setTitle(file);
           statusBar.setText(MessageManager.formatMessage(
                   "label.successfully_saved_to_file_in_format",
-                  new Object[] { fileName, format }));
+                  new Object[] { fileName, format.getName() }));
         } catch (Exception ex)
         {
           success = false;
@@ -1247,8 +1237,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void outputText_actionPerformed(ActionEvent e)
   {
-
-    FileFormatI fileFormat = FileFormat.forName(e.getActionCommand());
+    FileFormatI fileFormat = FileFormats.getInstance().forName(
+            e.getActionCommand());
     AlignmentExportData exportData = getAlignmentForExport(fileFormat,
             viewport, null);
     if (exportData.getSettings().isCancelled())
index df0142c..7a0b0af 100644 (file)
@@ -30,7 +30,7 @@ import jalview.bin.Jalview;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.SequenceI;
-import jalview.io.AlignmentFileI;
+import jalview.io.AlignmentFileReaderI;
 import jalview.io.AppletFormatAdapter;
 import jalview.io.DataSourceType;
 import jalview.io.FileFormatException;
@@ -73,7 +73,7 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
 
   AlignViewportI viewport;
 
-  AlignmentFileI source = null;
+  AlignmentFileReaderI source = null;
 
   public CutAndPasteTransfer()
   {
@@ -233,7 +233,7 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
       if (!Jalview.isHeadlessMode())
       {
         JvOptionPane.showInternalMessageDialog(Desktop.desktop,
-                AppletFormatAdapter.SUPPORTED_FORMATS,
+                AppletFormatAdapter.getSupportedFormats(),
                 MessageManager.getString("label.couldnt_read_data"),
                 JvOptionPane.WARNING_MESSAGE);
       }
@@ -262,7 +262,7 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
     {
       String title = MessageManager.formatMessage(
               "label.input_cut_paste_params",
-              new String[] { format.toString() });
+              new String[] { format.getName() });
       FeatureSettingsModelI proxyColourScheme = source
               .getFeatureColourScheme();
 
@@ -340,7 +340,7 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
       if (!Jalview.isHeadlessMode())
       {
         JvOptionPane.showInternalMessageDialog(Desktop.desktop,
-                AppletFormatAdapter.SUPPORTED_FORMATS,
+                AppletFormatAdapter.getSupportedFormats(),
                 MessageManager.getString("label.couldnt_read_data"),
                 JvOptionPane.WARNING_MESSAGE);
       }
index ac957d8..4ce42dc 100644 (file)
@@ -31,6 +31,7 @@ import jalview.io.DataSourceType;
 import jalview.io.FileFormat;
 import jalview.io.FileFormatException;
 import jalview.io.FileFormatI;
+import jalview.io.FileFormats;
 import jalview.io.FileLoader;
 import jalview.io.IdentifyFile;
 import jalview.io.JalviewFileChooser;
@@ -1025,20 +1026,21 @@ public class Desktop extends jalview.jbgui.GDesktop implements
       Cache.setProperty("LAST_DIRECTORY", chooser
               .getSelectedFile().getParent());
 
-      FileFormatI format = null;
-      FileFormatI selectedFormat = chooser.getSelectedFormat();
-      if (FileFormat.Jalview.equals(selectedFormat))
-      {
-        format = FileFormat.Jalview;
-      }
-      else
+      FileFormatI format = chooser.getSelectedFormat();
+
+      /*
+       * Call IdentifyFile to verify the file contains what its extension implies.
+       * Skip this step for dynamically added file formats, because
+       * IdentifyFile does not know how to recognise them.
+       */
+      if (FileFormats.getInstance().isIdentifiable(format))
       {
         try
         {
           format = new IdentifyFile().identify(choice, DataSourceType.FILE);
         } catch (FileFormatException e)
         {
-          // format is null
+          // format = null; //??
         }
       }
 
index 288ef1f..58ed008 100644 (file)
@@ -47,6 +47,8 @@ import javax.swing.JCheckBoxMenuItem;
 import javax.swing.JColorChooser;
 import javax.swing.JMenuItem;
 import javax.swing.JRadioButtonMenuItem;
+import javax.swing.event.InternalFrameAdapter;
+import javax.swing.event.InternalFrameEvent;
 
 /**
  * DOCUMENT ME!
@@ -80,6 +82,7 @@ public class PCAPanel extends GPCAPanel implements Runnable,
    */
   public PCAPanel(AlignmentPanel ap)
   {
+    super();
     this.av = ap.av;
     this.ap = ap;
 
@@ -120,6 +123,16 @@ public class PCAPanel extends GPCAPanel implements Runnable,
 
       return;
     }
+
+    addInternalFrameListener(new InternalFrameAdapter()
+    {
+      @Override
+      public void internalFrameClosed(InternalFrameEvent e)
+      {
+        close_actionPerformed();
+      }
+    });
+
     pcaModel = new PCAModel(seqstrings, seqs, nucleotide);
     PaintRefresher.Register(this, av.getSequenceSetId());
 
@@ -129,6 +142,15 @@ public class PCAPanel extends GPCAPanel implements Runnable,
     worker.start();
   }
 
+  /**
+   * Ensure references to potentially very large objects (the PCA matrices) are
+   * nulled when the frame is closed
+   */
+  protected void close_actionPerformed()
+  {
+    pcaModel = null;
+  }
+
   @Override
   protected void scoreMatrix_menuSelected()
   {
index fdac7f8..7841d3b 100644 (file)
@@ -39,8 +39,8 @@ import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.gui.ColourMenuHelper.ColourChangeListener;
-import jalview.io.FileFormat;
 import jalview.io.FileFormatI;
+import jalview.io.FileFormats;
 import jalview.io.FormatAdapter;
 import jalview.io.SequenceAnnotationReport;
 import jalview.schemes.AnnotationColourGradient;
@@ -200,7 +200,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
     this.ap = ap;
     sequence = seq;
 
-    for (String ff : FileFormat.getWritableFormats(true))
+    for (String ff : FileFormats.getInstance().getWritableFormats(true))
     {
       JMenuItem item = new JMenuItem(ff);
 
@@ -1831,7 +1831,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
     // or we simply trust the user wants
     // wysiwig behaviour
 
-    FileFormatI fileFormat = FileFormat.forName(e.getActionCommand());
+    FileFormatI fileFormat = FileFormats.getInstance().forName(e.getActionCommand());
     cap.setText(new FormatAdapter(ap).formatSequences(fileFormat, ap, true));
   }
 
index d88c861..d32ff46 100755 (executable)
@@ -707,7 +707,7 @@ public class Preferences extends GPreferences
       if (format != null)
       {
         Cache.applicationProperties.setProperty("DEFAULT_FILE_FORMAT",
-                format.toString());
+                format.getName());
       }
       startupFileTextfield.setText(chooser.getSelectedFile()
               .getAbsolutePath());
index 4de510a..7333075 100755 (executable)
@@ -40,7 +40,8 @@ import java.util.Vector;
  * @author $author$
  * @version $Revision$
  */
-public abstract class AlignFile extends FileParse implements AlignmentFileI
+public abstract class AlignFile extends FileParse implements
+        AlignmentFileReaderI, AlignmentFileWriterI
 {
   int noSeqs = 0;
 
@@ -416,4 +417,8 @@ public abstract class AlignFile extends FileParse implements AlignmentFileI
     }
   }
 
+  protected void addSequence(SequenceI seq)
+  {
+    seqs.add(seq);
+  }
 }
similarity index 69%
rename from src/jalview/io/AlignmentFileI.java
rename to src/jalview/io/AlignmentFileReaderI.java
index 1a000a3..77a7643 100644 (file)
@@ -6,7 +6,7 @@ import jalview.api.FeatureSettingsModelI;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
 
-public interface AlignmentFileI
+public interface AlignmentFileReaderI
 {
 
   SequenceI[] getSeqsAsArray();
@@ -15,16 +15,8 @@ public interface AlignmentFileI
 
   void addGroups(AlignmentI al);
 
-  void setNewlineString(String newline);
-
-  void setExportSettings(AlignExportSettingI exportSettings);
-
-  void configureForView(AlignmentViewPanel viewpanel);
-
   void setSeqs(SequenceI[] sequencesArray);
 
-  String print(SequenceI[] seqs, boolean jvsuffix);
-
   boolean hasWarningMessage();
 
   String getWarningMessage();
diff --git a/src/jalview/io/AlignmentFileWriterI.java b/src/jalview/io/AlignmentFileWriterI.java
new file mode 100644 (file)
index 0000000..d8e2dd4
--- /dev/null
@@ -0,0 +1,24 @@
+package jalview.io;
+
+import jalview.api.AlignExportSettingI;
+import jalview.api.AlignmentViewPanel;
+import jalview.api.FeatureSettingsModelI;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
+
+public interface AlignmentFileWriterI
+{
+
+  void setNewlineString(String newline);
+
+  void setExportSettings(AlignExportSettingI exportSettings);
+
+  void configureForView(AlignmentViewPanel viewpanel);
+
+  String print(SequenceI[] seqs, boolean jvsuffix);
+
+  boolean hasWarningMessage();
+
+  String getWarningMessage();
+
+}
index 45d65d6..c5a80e3 100755 (executable)
@@ -64,7 +64,7 @@ public class AppletFormatAdapter
    */
   boolean serviceSecondaryStruct = false;
 
-  private AlignmentFileI alignFile = null;
+  private AlignmentFileReaderI alignFile = null;
 
   String inFile;
 
@@ -77,9 +77,16 @@ public class AppletFormatAdapter
 
   public static String INVALID_CHARACTERS = "Contains invalid characters";
 
-  public static String SUPPORTED_FORMATS = "Formats currently supported are\n"
-          + prettyPrint(FileFormat.getReadableFormats());
-
+  /**
+   * Returns an error message with a list of supported readable file formats
+   * 
+   * @return
+   */
+  public static String getSupportedFormats()
+  {
+    return "Formats currently supported are\n"
+          + prettyPrint(FileFormats.getInstance().getReadableFormats());
+  }
   public AppletFormatAdapter()
   {
   }
@@ -157,7 +164,7 @@ public class AppletFormatAdapter
         }
         else
         {
-          // todo is MCview parsing obsolete yet?
+          // todo is MCview parsing obsolete yet? JAL-2120
           StructureImportSettings.setShowSeqFeatures(true);
           alignFile = new MCview.PDBfile(annotFromStructure,
                   localSecondaryStruct, serviceSecondaryStruct, inFile,
@@ -168,7 +175,9 @@ public class AppletFormatAdapter
       }
       else
       {
-        alignFile = fileFormat.getAlignmentFile(inFile, sourceType);
+        // alignFile = fileFormat.getAlignmentFile(inFile, sourceType);
+        alignFile = fileFormat.getReader(new FileParse(inFile,
+                sourceType));
       }
       return buildAlignmentFromFile();
     } catch (Exception e)
@@ -208,7 +217,7 @@ public class AppletFormatAdapter
         throw new IOException(e.getMessage());
       }
     }
-    throw new FileFormatException(SUPPORTED_FORMATS);
+    throw new FileFormatException(getSupportedFormats());
   }
 
   /**
@@ -248,7 +257,7 @@ public class AppletFormatAdapter
       }
       else
       {
-        alignFile = format.getAlignmentFile(source);
+        alignFile = format.getReader(source);
       }
 
       return buildAlignmentFromFile();
@@ -287,7 +296,7 @@ public class AppletFormatAdapter
       }
 
       // If we get to this stage, the format was not supported
-      throw new FileFormatException(SUPPORTED_FORMATS);
+      throw new FileFormatException(getSupportedFormats());
     }
   }
 
@@ -361,7 +370,7 @@ public class AppletFormatAdapter
   {
     try
     {
-      AlignmentFileI afile = format.getAlignmentFile(alignment);
+      AlignmentFileWriterI afile = format.getWriter(alignment);
 
       afile.setNewlineString(newline);
       afile.setExportSettings(exportSettings);
@@ -389,7 +398,8 @@ public class AppletFormatAdapter
       return afileresp;
     } catch (Exception e)
     {
-      System.err.println("Failed to write alignment as a '" + format
+      System.err.println("Failed to write alignment as a '"
+              + format.getName()
               + "' file\n");
       e.printStackTrace();
     }
@@ -638,7 +648,7 @@ public class AppletFormatAdapter
     return null;
   }
 
-  public AlignmentFileI getAlignFile()
+  public AlignmentFileReaderI getAlignFile()
   {
     return alignFile;
   }
index 5f441d2..a11147c 100644 (file)
@@ -6,31 +6,20 @@ import jalview.ext.jmol.JmolParser;
 import jalview.structure.StructureImportSettings;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
 
 public enum FileFormat implements FileFormatI
 {
   Fasta("Fasta", "fa, fasta, mfa, fastq", true, true)
   {
     @Override
-    public AlignmentFileI getAlignmentFile(String inFile,
-            DataSourceType sourceType) throws IOException
-    {
-      return new FastaFile(inFile, sourceType);
-    }
-
-    @Override
-    public AlignmentFileI getAlignmentFile(FileParse source)
+    public AlignmentFileReaderI getReader(FileParse source)
             throws IOException
     {
       return new FastaFile(source);
     }
 
     @Override
-    public AlignmentFileI getAlignmentFile(AlignmentI al)
+    public AlignmentFileWriterI getWriter(AlignmentI al)
     {
       return new FastaFile();
     }
@@ -38,21 +27,14 @@ public enum FileFormat implements FileFormatI
   Pfam("PFAM", "pfam", true, true)
   {
     @Override
-    public AlignmentFileI getAlignmentFile(String inFile,
-            DataSourceType sourceType) throws IOException
-    {
-      return new PfamFile(inFile, sourceType);
-    }
-
-    @Override
-    public AlignmentFileI getAlignmentFile(FileParse source)
+    public AlignmentFileReaderI getReader(FileParse source)
             throws IOException
     {
       return new PfamFile(source);
     }
 
     @Override
-    public AlignmentFileI getAlignmentFile(AlignmentI al)
+    public AlignmentFileWriterI getWriter(AlignmentI al)
     {
       return new PfamFile();
     }
@@ -60,20 +42,14 @@ public enum FileFormat implements FileFormatI
   Stockholm("Stockholm", "sto,stk", true, true)
   {
     @Override
-    public AlignmentFileI getAlignmentFile(String inFile,
-            DataSourceType sourceType) throws IOException
-    {
-      return new StockholmFile(inFile, sourceType);
-    }
-    @Override
-    public AlignmentFileI getAlignmentFile(FileParse source)
+    public AlignmentFileReaderI getReader(FileParse source)
             throws IOException
     {
       return new StockholmFile(source);
     }
 
     @Override
-    public AlignmentFileI getAlignmentFile(AlignmentI al)
+    public AlignmentFileWriterI getWriter(AlignmentI al)
     {
       return new StockholmFile(al);
     }
@@ -83,20 +59,14 @@ public enum FileFormat implements FileFormatI
   PIR("PIR", "pir", true, true)
   {
     @Override
-    public AlignmentFileI getAlignmentFile(String inFile,
-            DataSourceType sourceType) throws IOException
-    {
-      return new PIRFile(inFile, sourceType);
-    }
-    @Override
-    public AlignmentFileI getAlignmentFile(FileParse source)
+    public AlignmentFileReaderI getReader(FileParse source)
             throws IOException
     {
       return new PIRFile(source);
     }
 
     @Override
-    public AlignmentFileI getAlignmentFile(AlignmentI al)
+    public AlignmentFileWriterI getWriter(AlignmentI al)
     {
       return new PIRFile();
     }
@@ -104,19 +74,14 @@ public enum FileFormat implements FileFormatI
   BLC("BLC", "BLC", true, true)
   {
     @Override
-    public AlignmentFileI getAlignmentFile(String inFile,
-            DataSourceType sourceType) throws IOException
-    {
-      return new BLCFile(inFile, sourceType);
-    }    @Override
-    public AlignmentFileI getAlignmentFile(FileParse source)
+    public AlignmentFileReaderI getReader(FileParse source)
             throws IOException
     {
       return new BLCFile(source);
     }
 
     @Override
-    public AlignmentFileI getAlignmentFile(AlignmentI al)
+    public AlignmentFileWriterI getWriter(AlignmentI al)
     {
       return new BLCFile();
     }
@@ -124,21 +89,14 @@ public enum FileFormat implements FileFormatI
   AMSA("AMSA", "amsa", true, true)
   {
     @Override
-    public AlignmentFileI getAlignmentFile(String inFile,
-            DataSourceType sourceType) throws IOException
-    {
-      return new AMSAFile(inFile, sourceType);
-    }
-
-    @Override
-    public AlignmentFileI getAlignmentFile(FileParse source)
+    public AlignmentFileReaderI getReader(FileParse source)
             throws IOException
     {
       return new AMSAFile(source);
     }
 
     @Override
-    public AlignmentFileI getAlignmentFile(AlignmentI al)
+    public AlignmentFileWriterI getWriter(AlignmentI al)
     {
       return new AMSAFile(al);
     }
@@ -146,19 +104,14 @@ public enum FileFormat implements FileFormatI
   Html("HTML", "html", true, false)
   {
     @Override
-    public AlignmentFileI getAlignmentFile(String inFile,
-            DataSourceType sourceType) throws IOException
-    {
-      return new HtmlFile(inFile, sourceType);
-    }    @Override
-    public AlignmentFileI getAlignmentFile(FileParse source)
+    public AlignmentFileReaderI getReader(FileParse source)
             throws IOException
     {
       return new HtmlFile(source);
     }
 
     @Override
-    public AlignmentFileI getAlignmentFile(AlignmentI al)
+    public AlignmentFileWriterI getWriter(AlignmentI al)
     {
       return new HtmlFile();
     }
@@ -173,40 +126,30 @@ public enum FileFormat implements FileFormatI
   Rnaml("RNAML", "xml,rnaml", true, false)
   {
     @Override
-    public AlignmentFileI getAlignmentFile(String inFile,
-            DataSourceType sourceType) throws IOException
-    {
-      return new RnamlFile(inFile, sourceType);
-    }    @Override
-    public AlignmentFileI getAlignmentFile(FileParse source)
+    public AlignmentFileReaderI getReader(FileParse source)
             throws IOException
     {
       return new RnamlFile(source);
     }
 
     @Override
-    public AlignmentFileI getAlignmentFile(AlignmentI al)
+    public AlignmentFileWriterI getWriter(AlignmentI al)
     {
       return new RnamlFile();
     }
 
   },
-  Json("JSON","json", true, true)
+  Json("JSON", "json", true, true)
   {
     @Override
-    public AlignmentFileI getAlignmentFile(String inFile,
-            DataSourceType sourceType) throws IOException
-    {
-      return new JSONFile(inFile, sourceType);
-    }    @Override
-    public AlignmentFileI getAlignmentFile(FileParse source)
+    public AlignmentFileReaderI getReader(FileParse source)
             throws IOException
     {
       return new JSONFile(source);
     }
 
     @Override
-    public AlignmentFileI getAlignmentFile(AlignmentI al)
+    public AlignmentFileWriterI getWriter(AlignmentI al)
     {
       return new JSONFile();
     }
@@ -221,19 +164,14 @@ public enum FileFormat implements FileFormatI
   Pileup("PileUp", "pileup", true, true)
   {
     @Override
-    public AlignmentFileI getAlignmentFile(String inFile,
-            DataSourceType sourceType) throws IOException
-    {
-      return new PileUpfile(inFile, sourceType);
-    }    @Override
-    public AlignmentFileI getAlignmentFile(FileParse source)
+    public AlignmentFileReaderI getReader(FileParse source)
             throws IOException
     {
       return new PileUpfile(source);
     }
 
     @Override
-    public AlignmentFileI getAlignmentFile(AlignmentI al)
+    public AlignmentFileWriterI getWriter(AlignmentI al)
     {
       return new PileUpfile();
     }
@@ -242,19 +180,14 @@ public enum FileFormat implements FileFormatI
   MSF("MSF", "msf", true, true)
   {
     @Override
-    public AlignmentFileI getAlignmentFile(String inFile,
-            DataSourceType sourceType) throws IOException
-    {
-      return new MSFfile(inFile, sourceType);
-    }    @Override
-    public AlignmentFileI getAlignmentFile(FileParse source)
+    public AlignmentFileReaderI getReader(FileParse source)
             throws IOException
     {
       return new MSFfile(source);
     }
 
     @Override
-    public AlignmentFileI getAlignmentFile(AlignmentI al)
+    public AlignmentFileWriterI getWriter(AlignmentI al)
     {
       return new MSFfile();
     }
@@ -263,19 +196,14 @@ public enum FileFormat implements FileFormatI
   Clustal("Clustal", "aln", true, true)
   {
     @Override
-    public AlignmentFileI getAlignmentFile(String inFile,
-            DataSourceType sourceType) throws IOException
-    {
-      return new ClustalFile(inFile, sourceType);
-    }    @Override
-    public AlignmentFileI getAlignmentFile(FileParse source)
+    public AlignmentFileReaderI getReader(FileParse source)
             throws IOException
     {
       return new ClustalFile(source);
     }
 
     @Override
-    public AlignmentFileI getAlignmentFile(AlignmentI al)
+    public AlignmentFileWriterI getWriter(AlignmentI al)
     {
       return new ClustalFile();
     }
@@ -283,21 +211,14 @@ public enum FileFormat implements FileFormatI
   Phylip("PHYLIP", "phy", true, true)
   {
     @Override
-    public AlignmentFileI getAlignmentFile(String inFile,
-            DataSourceType sourceType) throws IOException
-    {
-      return new PhylipFile(inFile, sourceType);
-    }
-
-    @Override
-    public AlignmentFileI getAlignmentFile(FileParse source)
+    public AlignmentFileReaderI getReader(FileParse source)
             throws IOException
     {
       return new PhylipFile(source);
     }
 
     @Override
-    public AlignmentFileI getAlignmentFile(AlignmentI al)
+    public AlignmentFileWriterI getWriter(AlignmentI al)
     {
       return new PhylipFile();
     }
@@ -305,16 +226,7 @@ public enum FileFormat implements FileFormatI
   Jnet("JnetFile", "", false, false)
   {
     @Override
-    public AlignmentFileI getAlignmentFile(String inFile,
-            DataSourceType sourceType) throws IOException
-    {
-      JPredFile af = new JPredFile(inFile, sourceType);
-      af.removeNonSequences();
-      return af;
-    }
-
-    @Override
-    public AlignmentFileI getAlignmentFile(FileParse source)
+    public AlignmentFileReaderI getReader(FileParse source)
             throws IOException
     {
       JPredFile af = new JPredFile(source);
@@ -323,7 +235,7 @@ public enum FileFormat implements FileFormatI
     }
 
     @Override
-    public AlignmentFileI getAlignmentFile(AlignmentI al)
+    public AlignmentFileWriterI getWriter(AlignmentI al)
     {
       return null; // todo is this called?
     }
@@ -332,54 +244,22 @@ public enum FileFormat implements FileFormatI
   Features("GFF or Jalview features", "gff2,gff3", true, false)
   {
     @Override
-    public AlignmentFileI getAlignmentFile(String inFile,
-            DataSourceType sourceType) throws IOException
-    {
-      return new FeaturesFile(true, inFile, sourceType);
-    }
-
-    @Override
-    public AlignmentFileI getAlignmentFile(FileParse source)
+    public AlignmentFileReaderI getReader(FileParse source)
             throws IOException
     {
       return new FeaturesFile(source);
     }
 
     @Override
-    public AlignmentFileI getAlignmentFile(AlignmentI al)
+    public AlignmentFileWriterI getWriter(AlignmentI al)
     {
       return new FeaturesFile();
     }
   },
   PDB("PDB", "pdb,ent", true, false)
   {
-
-    @Override
-    public AlignmentFileI getAlignmentFile(String inFile,
-            DataSourceType sourceType) throws IOException
-    {
-      // TODO obtain config value from preference settings.
-      // Set value to 'true' to test PDB processing with Jmol: JAL-1213
-      boolean isParseWithJMOL = StructureImportSettings
-              .getDefaultStructureFileFormat() != PDBEntry.Type.PDB;
-      if (isParseWithJMOL)
-      {
-        return new JmolParser(inFile, sourceType);
-      }
-      else
-      {
-        StructureImportSettings.setShowSeqFeatures(true);
-        return new MCview.PDBfile(
-                StructureImportSettings.isVisibleChainAnnotation(),
-                StructureImportSettings.isProcessSecondaryStructure(),
-                StructureImportSettings.isExternalSecondaryStructure(),
-                inFile,
-                sourceType);
-      }
-    }
-
     @Override
-    public AlignmentFileI getAlignmentFile(FileParse source)
+    public AlignmentFileReaderI getReader(FileParse source)
             throws IOException
     {
       boolean isParseWithJMOL = StructureImportSettings
@@ -400,7 +280,7 @@ public enum FileFormat implements FileFormatI
     }
 
     @Override
-    public AlignmentFileI getAlignmentFile(AlignmentI al)
+    public AlignmentFileWriterI getWriter(AlignmentI al)
     {
       return new JmolParser(); // todo or null?
     }
@@ -413,23 +293,15 @@ public enum FileFormat implements FileFormatI
   },
   MMCif("mmCIF", "cif", true, false)
   {
-
-    @Override
-    public AlignmentFileI getAlignmentFile(String inFile,
-            DataSourceType sourceType) throws IOException
-    {
-      return new JmolParser(inFile, sourceType);
-    }
-
     @Override
-    public AlignmentFileI getAlignmentFile(FileParse source)
+    public AlignmentFileReaderI getReader(FileParse source)
             throws IOException
     {
       return new JmolParser(source);
     }
 
     @Override
-    public AlignmentFileI getAlignmentFile(AlignmentI al)
+    public AlignmentFileWriterI getWriter(AlignmentI al)
     {
       return new JmolParser(); // todo or null?
     }
@@ -442,23 +314,15 @@ public enum FileFormat implements FileFormatI
   },
   Jalview("Jalview", "jar,jvp", true, true)
   {
-
     @Override
-    public AlignmentFileI getAlignmentFile(String inFile,
-            DataSourceType sourceType) throws IOException
-    {
-      return null;
-    }
-
-    @Override
-    public AlignmentFileI getAlignmentFile(FileParse source)
+    public AlignmentFileReaderI getReader(FileParse source)
             throws IOException
     {
       return null;
     }
 
     @Override
-    public AlignmentFileI getAlignmentFile(AlignmentI al)
+    public AlignmentFileWriterI getWriter(AlignmentI al)
     {
       return null;
     }
@@ -468,20 +332,13 @@ public enum FileFormat implements FileFormatI
     {
       return false;
     }
-  };
 
-  /**
-   * A lookup map of enums by upper-cased name
-   */
-  private static Map<String, FileFormat> names;
-  static
-  {
-    names = new HashMap<String, FileFormat>();
-    for (FileFormat format : FileFormat.values())
+    @Override
+    public boolean isIdentifiable()
     {
-      names.put(format.toString().toUpperCase(), format);
+      return false;
     }
-  }
+  };
 
   private boolean writable;
 
@@ -491,65 +348,12 @@ public enum FileFormat implements FileFormatI
 
   private String name;
 
-  /**
-   * Answers a list of writeable file formats (as string, corresponding to the
-   * toString() and forName() methods)
-   * 
-   * @return
-   */
-  public static List<String> getWritableFormats(boolean textOnly)
-  {
-    List<String> l = new ArrayList<String>();
-    for (FileFormatI ff : values())
-    {
-      if (ff.isWritable() && (!textOnly || ff.isTextFormat()))
-      {
-        l.add(ff.toString());
-      }
-    }
-    return l;
-  }
-
-  /**
-   * Answers a list of readable file formats (as string, corresponding to the
-   * toString() and forName() methods)
-   * 
-   * @return
-   */
-  public static List<String> getReadableFormats()
-  {
-    List<String> l = new ArrayList<String>();
-    for (FileFormatI ff : values())
-    {
-      if (ff.isReadable())
-      {
-        l.add(ff.toString());
-      }
-    }
-    return l;
-  }
-
   @Override
   public boolean isComplexAlignFile()
   {
     return false;
   }
 
-  /**
-   * Returns the file format with the given name, or null if format is null or
-   * invalid. Unlike valueOf(), this is not case-sensitive, to be kind to
-   * writers of javascript.
-   * 
-   * @param format
-   * @return
-   */
-  public static FileFormatI forName(String format)
-  {
-    // or could store format.getShortDescription().toUpperCase()
-    // in order to decouple 'given name' from enum name
-    return format == null ? null : names.get(format.toUpperCase());
-  }
-
   @Override
   public boolean isReadable()
   {
@@ -586,19 +390,17 @@ public enum FileFormat implements FileFormatI
     return extensions;
   }
 
+  /**
+   * Answers the display name of the file format (as for example shown in menu
+   * options). This name should not be locale (language) dependent.
+   */
   @Override
-  public String toString()
+  public String getName()
   {
     return name;
   }
 
   @Override
-  public AlignmentFileI getAlignmentFile()
-  {
-    return getAlignmentFile((AlignmentI) null);
-  }
-
-  @Override
   public boolean isTextFormat()
   {
     return true;
@@ -609,4 +411,14 @@ public enum FileFormat implements FileFormatI
   {
     return false;
   }
+
+  /**
+   * By default, answers true, indicating the format is one that can be
+   * identified by IdentifyFile. Formats that cannot be identified should
+   * override this method to return false.
+   */
+  public boolean isIdentifiable()
+  {
+    return true;
+  }
 }
index 0593d1e..e6f390d 100644 (file)
@@ -6,20 +6,19 @@ import java.io.IOException;
 
 public interface FileFormatI
 {
+  AlignmentFileReaderI getReader(FileParse source) throws IOException;
 
-  AlignmentFileI getAlignmentFile(String inFile, DataSourceType sourceType)
-          throws IOException;
-
-  // TODO can we get rid of one of these methods?
-  AlignmentFileI getAlignmentFile(FileParse source) throws IOException;
-
-  AlignmentFileI getAlignmentFile(AlignmentI al);
-
-  AlignmentFileI getAlignmentFile();
+  AlignmentFileWriterI getWriter(AlignmentI al);
 
   boolean isComplexAlignFile();
 
   /**
+   * Answers the display name of the file format (as for example shown in menu
+   * options). This name should not be locale (language) dependent.
+   */
+  String getName();
+
+  /**
    * Returns a comma-separated list of file extensions associated with the
    * format
    * 
@@ -29,9 +28,9 @@ public interface FileFormatI
 
   /**
    * Answers true if the format is one that Jalview can read. This implies that
-   * the format provides implementations for getAlignmentFile(FileParse) and
-   * getAlignmentFile(String, DataSourceType) which parse the data source for
-   * sequence data.
+   * the format provides an implementation for getReader which can parse a data
+   * source for sequence data. Readable formats are included in the options in
+   * the open file dialogue.
    * 
    * @return
    */
@@ -39,8 +38,9 @@ public interface FileFormatI
 
   /**
    * Answers true if the format is one that Jalview can write. This implies that
-   * the object returned by getAlignmentFile provides an implementation of the
-   * print() method.
+   * the object returned by getWriter provides an implementation of the print()
+   * method. Writable formats are included in the options in the Save As file
+   * dialogue, and the 'output to Textbox' option (if text format).
    * 
    * @return
    */
@@ -55,7 +55,7 @@ public interface FileFormatI
   boolean isTextFormat();
 
   /**
-   * Answers true if the file format is one that provides a 3D structure
+   * Answers true if the file format is one that provides 3D structure data
    * 
    * @return
    */
diff --git a/src/jalview/io/FileFormats.java b/src/jalview/io/FileFormats.java
new file mode 100644 (file)
index 0000000..158489e
--- /dev/null
@@ -0,0 +1,168 @@
+package jalview.io;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A singleton registry of alignment file formats known to Jalview. On startup,
+ * the 'built-in' formats are added (from the FileFormat enum). Additional
+ * formats can be registered (or formats deregistered) programmatically, for
+ * example with a Groovy script.
+ * 
+ * @author gmcarstairs
+ *
+ */
+public class FileFormats
+{
+  private static FileFormats instance = new FileFormats();
+
+  /*
+   * A lookup map of file formats by upper-cased name
+   */
+  private static Map<String, FileFormatI> formats;
+
+  /*
+   * Formats in this set are capable of being identified by IdentifyFile 
+   */
+  private static Set<FileFormatI> identifiable;
+
+  public static FileFormats getInstance()
+  {
+    return instance;
+  }
+
+  /**
+   * Private constructor registers Jalview's built-in file formats
+   */
+  private FileFormats()
+  {
+    reset();
+  }
+
+  /**
+   * Reset to just the built-in file formats packaged with Jalview. These are
+   * added (and will be shown in menus) in the order of their declaration in the
+   * FileFormat enum.
+   */
+  public synchronized void reset()
+  {
+    formats = new LinkedHashMap<String, FileFormatI>();
+    identifiable = new HashSet<FileFormatI>();
+    for (FileFormat format : FileFormat.values())
+    {
+      registerFileFormat(format, format.isIdentifiable());
+    }
+  }
+
+  /**
+   * Answers true if the format is one that can be identified by IdentifyFile.
+   * Answers false for a null value.
+   */
+  public boolean isIdentifiable(FileFormatI f)
+  {
+    return identifiable.contains(f);
+  }
+
+  /**
+   * Registers a file format for case-insensitive lookup by name
+   * 
+   * @param format
+   */
+  public void registerFileFormat(FileFormatI format)
+  {
+    registerFileFormat(format, false);
+  }
+
+  protected void registerFileFormat(FileFormatI format,
+          boolean isIdentifiable)
+  {
+    String name = format.getName().toUpperCase();
+    if (formats.containsKey(name))
+    {
+      System.err.println("Overwriting file format: " + format.getName());
+    }
+    formats.put(name, format);
+    if (isIdentifiable)
+    {
+      identifiable.add(format);
+    }
+  }
+
+  /**
+   * Deregisters a file format so it is no longer shown in menus
+   * 
+   * @param name
+   */
+  public void deregisterFileFormat(String name)
+  {
+    FileFormatI ff = formats.remove(name.toUpperCase());
+    identifiable.remove(ff);
+  }
+
+  /**
+   * Answers a list of writeable file formats (as strings, corresponding to the
+   * getName() and forName() methods)
+   * 
+   * @param textOnly
+   *          if true, only text (not binary) formats are included
+   * @return
+   */
+  public List<String> getWritableFormats(boolean textOnly)
+  {
+    List<String> l = new ArrayList<String>();
+    for (FileFormatI ff : formats.values())
+    {
+      if (ff.isWritable() && (!textOnly || ff.isTextFormat()))
+      {
+        l.add(ff.getName());
+      }
+    }
+    return l;
+  }
+
+  /**
+   * Answers a list of readable file formats (as strings, corresponding to the
+   * getName() and forName() methods)
+   * 
+   * @return
+   */
+  public List<String> getReadableFormats()
+  {
+    List<String> l = new ArrayList<String>();
+    for (FileFormatI ff : formats.values())
+    {
+      if (ff.isReadable())
+      {
+        l.add(ff.getName());
+      }
+    }
+    return l;
+  }
+
+  /**
+   * Returns the file format with the given name, or null if format is null or
+   * invalid. This is not case-sensitive.
+   * 
+   * @param format
+   * @return
+   */
+  public FileFormatI forName(String format)
+  {
+    return format == null ? null : formats.get(format.toUpperCase());
+  }
+
+  /**
+   * Returns an iterable collection of registered file formats (in the order in
+   * which they were registered)
+   * 
+   * @return
+   */
+  public Iterable<FileFormatI> getFormats()
+  {
+    return formats.values();
+  }
+}
index 3ad74c7..4f83ab1 100755 (executable)
@@ -53,7 +53,8 @@ public class FileLoader implements Runnable
 
   FileFormatI format;
 
-  AlignmentFileI source = null; // alternative specification of where data comes
+  AlignmentFileReaderI source = null; // alternative specification of where data
+                                      // comes
 
   // from
 
@@ -160,7 +161,7 @@ public class FileLoader implements Runnable
    * @param format
    * @return alignFrame constructed from file contents
    */
-  public AlignFrame LoadFileWaitTillLoaded(AlignmentFileI source,
+  public AlignFrame LoadFileWaitTillLoaded(AlignmentFileReaderI source,
           FileFormatI format)
   {
     this.source = source;
@@ -237,7 +238,7 @@ public class FileLoader implements Runnable
 
     if (protocol == DataSourceType.FILE)
     {
-      Cache.setProperty("DEFAULT_FILE_FORMAT", format.toString());
+      Cache.setProperty("DEFAULT_FILE_FORMAT", format.getName());
     }
   }
 
@@ -279,7 +280,7 @@ public class FileLoader implements Runnable
                   Desktop.desktop,
                   MessageManager.getString("label.couldnt_read_data")
                           + " in " + file + "\n"
-                          + AppletFormatAdapter.SUPPORTED_FORMATS,
+                          + AppletFormatAdapter.getSupportedFormats(),
                   MessageManager.getString("label.couldnt_read_data"),
                   JvOptionPane.WARNING_MESSAGE);
         }
@@ -311,27 +312,27 @@ public class FileLoader implements Runnable
       }
       else
       {
-        String error = AppletFormatAdapter.SUPPORTED_FORMATS;
-          try
+        String error = AppletFormatAdapter.getSupportedFormats();
+        try
+        {
+          if (source != null)
           {
-            if (source != null)
-            {
-              // read from the provided source
+            // read from the provided source
             al = new FormatAdapter().readFromFile(source, format);
-            }
-            else
-            {
-
-              // open a new source and read from it
-              FormatAdapter fa = new FormatAdapter();
-              al = fa.readFile(file, protocol, format);
-              source = fa.getAlignFile(); // keep reference for later if
-                                          // necessary.
-            }
-          } catch (java.io.IOException ex)
+          }
+          else
           {
-            error = ex.getMessage();
+
+            // open a new source and read from it
+            FormatAdapter fa = new FormatAdapter();
+            al = fa.readFile(file, protocol, format);
+            source = fa.getAlignFile(); // keep reference for later if
+                                        // necessary.
           }
+        } catch (java.io.IOException ex)
+        {
+          error = ex.getMessage();
+        }
 
         if ((al != null) && (al.getHeight() > 0) && al.hasValidSequence())
         {
index 1dbfdef..d9dd79d 100755 (executable)
@@ -155,84 +155,13 @@ public class FormatAdapter extends AppletFormatAdapter
    */
   public String formatSequences(FileFormatI format, SequenceI[] seqs)
   {
-    //
-    // try
-    // {
     boolean withSuffix = getCacheSuffixDefault(format);
-    return format.getAlignmentFile().print(seqs, withSuffix);
-      // null;
-      //
-      // if (format.equalsIgnoreCase("FASTA"))
-      // {
-      // afile = new FastaFile();
-      // afile.addJVSuffix(jalview.bin.Cache.getDefault("FASTA_JVSUFFIX",
-      // true));
-      // }
-      // else if (format.equalsIgnoreCase("MSF"))
-      // {
-      // afile = new MSFfile();
-      // afile.addJVSuffix(jalview.bin.Cache
-      // .getDefault("MSF_JVSUFFIX", true));
-      // }
-      // else if (format.equalsIgnoreCase("PileUp"))
-      // {
-      // afile = new PileUpfile();
-      // afile.addJVSuffix(jalview.bin.Cache.getDefault("PILEUP_JVSUFFIX",
-      // true));
-      // }
-      // else if (format.equalsIgnoreCase("CLUSTAL"))
-      // {
-      // afile = new ClustalFile();
-      // afile.addJVSuffix(jalview.bin.Cache.getDefault("CLUSTAL_JVSUFFIX",
-      // true));
-      // }
-      // else if (format.equalsIgnoreCase("BLC"))
-      // {
-      // afile = new BLCFile();
-      // afile.addJVSuffix(jalview.bin.Cache
-      // .getDefault("BLC_JVSUFFIX", true));
-      // }
-      // else if (format.equalsIgnoreCase("PIR"))
-      // {
-      // afile = new PIRFile();
-      // afile.addJVSuffix(jalview.bin.Cache
-      // .getDefault("PIR_JVSUFFIX", true));
-      // }
-      // else if (format.equalsIgnoreCase("PFAM"))
-      // {
-      // afile = new PfamFile();
-      // afile.addJVSuffix(jalview.bin.Cache.getDefault("PFAM_JVSUFFIX",
-      // true));
-      // }
-      // /*
-      // * amsa is not supported by this function - it requires an alignment
-      // * rather than a sequence vector else if
-      // (format.equalsIgnoreCase("AMSA"))
-      // * { afile = new AMSAFile(); afile.addJVSuffix(
-      // * jalview.bin.Cache.getDefault("AMSA_JVSUFFIX", true)); }
-      // */
-
-//      afile.setSeqs(seqs);
-//      String afileresp = afile.print();
-//      if (afile.hasWarningMessage())
-//      {
-//        System.err.println("Warning raised when writing as " + format
-//                + " : " + afile.getWarningMessage());
-//      }
-//      return afileresp;
-//    } catch (Exception e)
-//    {
-//      System.err.println("Failed to write alignment as a '" + format
-//              + "' file\n");
-//      e.printStackTrace();
-//    }
-//
-//    return null;
+    return format.getWriter(null).print(seqs, withSuffix);
   }
 
   public boolean getCacheSuffixDefault(FileFormatI format)
   {
-    return Cache.getDefault(format.toString() + "_JVSUFFIX", true);
+    return Cache.getDefault(format.getName() + "_JVSUFFIX", true);
   }
 
   public String formatSequences(FileFormatI format, AlignmentI alignment,
@@ -329,8 +258,8 @@ public class FormatAdapter extends AppletFormatAdapter
             selectedOnly);
   }
 
-  public AlignmentI readFromFile(AlignmentFileI source, FileFormatI format)
-          throws IOException
+  public AlignmentI readFromFile(AlignmentFileReaderI source,
+          FileFormatI format) throws IOException
   {
     FileParse fp = new FileParse(source.getInFile(),
             source.getDataSourceType());
index 72a1155..0556e76 100755 (executable)
@@ -72,7 +72,7 @@ public class IdentifyFile
     // preserves original behaviour prior to version 2.3
   }
 
-  public FileFormatI identify(AlignmentFileI file, boolean closeSource)
+  public FileFormatI identify(AlignmentFileReaderI file, boolean closeSource)
           throws IOException
   {
     FileParse fp = new FileParse(file.getInFile(), file.getDataSourceType());
index 7ccdaa9..2a0f8b1 100755 (executable)
@@ -68,12 +68,12 @@ public class JalviewFileChooser extends JFileChooser
   {
     List<String> extensions = new ArrayList<String>();
     List<String> descs = new ArrayList<String>();
-    for (FileFormatI format : FileFormat.values())
+    for (FileFormatI format : FileFormats.getInstance().getFormats())
     {
       if (format.isReadable())
       {
         extensions.add(format.getExtensions());
-        descs.add(format.toString());
+        descs.add(format.getName());
       }
     }
     return new JalviewFileChooser(directory,
@@ -96,12 +96,12 @@ public class JalviewFileChooser extends JFileChooser
     // with a lambda expression parameter for isReadable/isWritable
     List<String> extensions = new ArrayList<String>();
     List<String> descs = new ArrayList<String>();
-    for (FileFormatI format : FileFormat.values())
+    for (FileFormatI format : FileFormats.getInstance().getFormats())
     {
       if (format.isWritable())
       {
         extensions.add(format.getExtensions());
-        descs.add(format.toString());
+        descs.add(format.getName());
       }
     }
     return new JalviewFileChooser(directory,
@@ -269,7 +269,7 @@ public class JalviewFileChooser extends JFileChooser
       format = format.substring(0, parenPos).trim();
       try
       {
-        return FileFormat.valueOf(format);
+        return FileFormats.getInstance().forName(format);
       } catch (IllegalArgumentException e)
       {
         System.err.println("Unexpected format: " + format);
index 3461110..6bfb3b9 100755 (executable)
@@ -38,9 +38,9 @@ public class JalviewFileView extends FileView
   private void loadExtensions()
   {
     extensions = new HashMap<String, String>();
-    for (FileFormatI ff : FileFormat.values())
+    for (FileFormatI ff : FileFormats.getInstance().getFormats())
     {
-      String desc = ff.toString() + " file";
+      String desc = ff.getName() + " file";
       String exts = ff.getExtensions();
       for (String ext : exts.split(","))
       {
index dff20d3..a320a9c 100755 (executable)
@@ -25,7 +25,7 @@ import jalview.api.SplitContainerI;
 import jalview.bin.Cache;
 import jalview.gui.JvSwingUtils;
 import jalview.gui.Preferences;
-import jalview.io.FileFormat;
+import jalview.io.FileFormats;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
 
@@ -211,7 +211,7 @@ public class GAlignFrame extends JInternalFrame
       setJMenuBar(alignFrameMenuBar);
 
       // dynamically fill save as menu with available formats
-      for (String ff : FileFormat.getWritableFormats(true))
+      for (String ff : FileFormats.getInstance().getWritableFormats(true))
       {
         JMenuItem item = new JMenuItem(ff);
 
index bb7ed37..28b9d67 100755 (executable)
@@ -57,24 +57,33 @@ public class Matrix
   int maxIter = 45; // fudge - add 15 iterations, just in case
 
   /**
-   * Creates a new Matrix object.
+   * Creates a new Matrix object. For example
    * 
-   * @param value
-   *          DOCUMENT ME!
+   * <pre>
+   *   new Matrix(new double[][] {{2, 3}, {4, 5}, 2, 2)
+   * constructs
+   *   (2 3)
+   *   (4 5)
+   * </pre>
+   * 
+   * Note that ragged arrays (with not all rows, or columns, of the same
+   * length), are not supported by this class. They can be constructed, but
+   * results of operations on them are undefined and may throw exceptions.
+   * 
+   * @param values
+   *          the matrix values in row-major order
    * @param rows
-   *          DOCUMENT ME!
    * @param cols
-   *          DOCUMENT ME!
    */
-  public Matrix(double[][] value, int rows, int cols)
+  public Matrix(double[][] values, int rows, int cols)
   {
     this.rows = rows;
     this.cols = cols;
-    this.value = value;
+    this.value = values;
   }
 
   /**
-   * DOCUMENT ME!
+   * Returns a new matrix which is the transposes of this one
    * 
    * @return DOCUMENT ME!
    */
@@ -113,15 +122,24 @@ public class Matrix
   }
 
   /**
-   * DOCUMENT ME!
+   * Returns a new matrix which is the result of premultiplying this matrix by
+   * the supplied argument. If this of size AxB (A rows and B columns), and the
+   * argument is CxA (C rows and A columns), the result is of size CxB.
    * 
    * @param in
-   *          DOCUMENT ME!
    * 
-   * @return DOCUMENT ME!
+   * @return
+   * @throws IllegalArgumentException
+   *           if the number of columns in the pre-multiplier is not equal to
+   *           the number of rows in the multiplicand (this)
    */
   public Matrix preMultiply(Matrix in)
   {
+    if (in.cols != this.rows)
+    {
+      throw new IllegalArgumentException("Can't pre-multiply " + this.rows
+              + " rows by " + in.cols + " columns");
+    }
     double[][] tmp = new double[in.rows][this.cols];
 
     for (int i = 0; i < in.rows; i++)
@@ -141,12 +159,10 @@ public class Matrix
   }
 
   /**
-   * DOCUMENT ME!
    * 
    * @param in
-   *          DOCUMENT ME!
    * 
-   * @return DOCUMENT ME!
+   * @return
    */
   public double[] vectorPostMultiply(double[] in)
   {
@@ -166,37 +182,34 @@ public class Matrix
   }
 
   /**
-   * DOCUMENT ME!
+   * Returns a new matrix which is the result of postmultiplying this matrix by
+   * the supplied argument. If this of size AxB (A rows and B columns), and the
+   * argument is BxC (B rows and C columns), the result is of size AxC.
+   * <p>
+   * This method simply returns the result of in.preMultiply(this)
    * 
    * @param in
-   *          DOCUMENT ME!
    * 
-   * @return DOCUMENT ME!
+   * @return
+   * @throws IllegalArgumentException
+   *           if the number of rows in the post-multiplier is not equal to the
+   *           number of columns in the multiplicand (this)
+   * @see #preMultiply(Matrix)
    */
   public Matrix postMultiply(Matrix in)
   {
-    double[][] out = new double[this.rows][in.cols];
-
-    for (int i = 0; i < this.rows; i++)
+    if (in.rows != this.cols)
     {
-      for (int j = 0; j < in.cols; j++)
-      {
-        out[i][j] = 0.0;
-
-        for (int k = 0; k < rows; k++)
-        {
-          out[i][j] = out[i][j] + (value[i][k] * in.value[k][j]);
-        }
-      }
+      throw new IllegalArgumentException("Can't post-multiply " + this.cols
+              + " columns by " + in.rows + " rows");
     }
-
-    return new Matrix(out, this.cols, in.rows);
+    return in.preMultiply(this);
   }
 
   /**
-   * DOCUMENT ME!
+   * Answers a new matrix with a copy of the values in this one
    * 
-   * @return DOCUMENT ME!
+   * @return
    */
   public Matrix copy()
   {
@@ -204,10 +217,11 @@ public class Matrix
 
     for (int i = 0; i < rows; i++)
     {
-      for (int j = 0; j < cols; j++)
-      {
-        newmat[i][j] = value[i][j];
-      }
+      System.arraycopy(value[i], 0, newmat[i], 0, value[i].length);
+      // for (int j = 0; j < cols; j++)
+      // {
+      // newmat[i][j] = value[i][j];
+      // }
     }
 
     return new Matrix(newmat, rows, cols);
@@ -738,20 +752,19 @@ public class Matrix
   }
 
   /**
-   * DOCUMENT ME!
+   * Returns an array containing the values in the specified column
    * 
-   * @param n
-   *          DOCUMENT ME!
+   * @param col
    * 
-   * @return DOCUMENT ME!
+   * @return
    */
-  public double[] getColumn(int n)
+  public double[] getColumn(int col)
   {
     double[] out = new double[rows];
 
     for (int i = 0; i < rows; i++)
     {
-      out[i] = value[i][n];
+      out[i] = value[i][col];
     }
 
     return out;
@@ -784,85 +797,4 @@ public class Matrix
       Format.print(ps, "%15.4e", e[j]);
     }
   }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param args
-   *          DOCUMENT ME!
-   */
-  public static void main(String[] args) throws Exception
-  {
-    int n = Integer.parseInt(args[0]);
-    double[][] in = new double[n][n];
-
-    for (int i = 0; i < n; i++)
-    {
-      for (int j = 0; j < n; j++)
-      {
-        in[i][j] = (double) Math.random();
-      }
-    }
-
-    Matrix origmat = new Matrix(in, n, n);
-
-    // System.out.println(" --- Original matrix ---- ");
-    // / origmat.print(System.out);
-    // System.out.println();
-    // System.out.println(" --- transpose matrix ---- ");
-    Matrix trans = origmat.transpose();
-
-    // trans.print(System.out);
-    // System.out.println();
-    // System.out.println(" --- OrigT * Orig ---- ");
-    Matrix symm = trans.postMultiply(origmat);
-
-    // symm.print(System.out);
-    // System.out.println();
-    // Copy the symmetric matrix for later
-    // Matrix origsymm = symm.copy();
-
-    // This produces the tridiagonal transformation matrix
-    // long tstart = System.currentTimeMillis();
-    symm.tred();
-
-    // long tend = System.currentTimeMillis();
-
-    // System.out.println("Time take for tred = " + (tend-tstart) + "ms");
-    // System.out.println(" ---Tridiag transform matrix ---");
-    // symm.print(System.out);
-    // System.out.println();
-    // System.out.println(" --- D vector ---");
-    // symm.printD(System.out);
-    // System.out.println();
-    // System.out.println(" --- E vector ---");
-    // symm.printE(System.out);
-    // System.out.println();
-    // Now produce the diagonalization matrix
-    // tstart = System.currentTimeMillis();
-    symm.tqli();
-    // tend = System.currentTimeMillis();
-
-    // System.out.println("Time take for tqli = " + (tend-tstart) + " ms");
-    // System.out.println(" --- New diagonalization matrix ---");
-    // symm.print(System.out);
-    // System.out.println();
-    // System.out.println(" --- D vector ---");
-    // symm.printD(System.out);
-    // System.out.println();
-    // System.out.println(" --- E vector ---");
-    // symm.printE(System.out);
-    // System.out.println();
-    // System.out.println(" --- First eigenvector --- ");
-    // double[] eigenv = symm.getColumn(0);
-    // for (int i=0; i < eigenv.length;i++) {
-    // Format.print(System.out,"%15.4f",eigenv[i]);
-    // }
-    // System.out.println();
-    // double[] neigenv = origsymm.vectorPostMultiply(eigenv);
-    // for (int i=0; i < neigenv.length;i++) {
-    // Format.print(System.out,"%15.4f",neigenv[i]/symm.d[0]);
-    // }
-    // System.out.println();
-  }
 }
index 389afcd..bf52027 100755 (executable)
@@ -884,21 +884,18 @@ public class Format
     String f = "";
     int e = 0;
     double dd = d;
-    double factor = 1;
 
     if (d != 0)
     {
       while (dd > 10)
       {
         e++;
-        factor /= 10;
         dd = dd / 10;
       }
 
       while (dd < 1)
       {
         e--;
-        factor *= 10;
         dd = dd * 10;
       }
     }
@@ -908,8 +905,7 @@ public class Format
       return fixed_format(d);
     }
 
-    d = d * factor;
-    f = f + fixed_format(d);
+    f = f + fixed_format(dd);
 
     if ((fmt == 'e') || (fmt == 'g'))
     {
index 4e5cadc..e685d00 100644 (file)
@@ -180,8 +180,8 @@ class JPredThread extends JWS1Thread implements WSClientI
         else
         {
           throw (new Exception(MessageManager.formatMessage(
-                  "exception.unknown_format_for_file", new String[] {
-                      format.toString(), result.getAligfile() })));
+                  "exception.unknown_format_for_file", new String[] { "",
+                      result.getAligfile() })));
         }
       }
       else
index 08a242d..a8258e2 100644 (file)
@@ -23,6 +23,7 @@ package jalview.ws.rest.params;
 import jalview.datamodel.AlignmentI;
 import jalview.io.FileFormat;
 import jalview.io.FileFormatI;
+import jalview.io.FileFormats;
 import jalview.io.FormatAdapter;
 import jalview.ws.params.OptionI;
 import jalview.ws.params.simple.BooleanOption;
@@ -147,9 +148,9 @@ public class Alignment extends InputType
 
     if (tok.startsWith("format"))
     {
-      for (FileFormatI fmt : FileFormat.values())
+      for (FileFormatI fmt : FileFormats.getInstance().getFormats())
       {
-        if (fmt.isWritable() && val.equalsIgnoreCase(fmt.toString()))
+        if (fmt.isWritable() && val.equalsIgnoreCase(fmt.getName()))
         {
           format = fmt;
           return true;
@@ -157,12 +158,9 @@ public class Alignment extends InputType
       }
       warnings.append("Invalid alignment format '" + val
               + "'. Must be one of (");
-      for (FileFormatI fmt : FileFormat.values())
+      for (String fmt : FileFormats.getInstance().getWritableFormats(true))
       {
-        if (fmt.isWritable())
-        {
-          warnings.append(" " + fmt).toString();
-        }
+        warnings.append(" ").append(fmt);
       }
       warnings.append(")\n");
     }
@@ -197,28 +195,14 @@ public class Alignment extends InputType
             "Append jalview style /start-end suffix to ID", false, false,
             writeAsFile, null));
 
+    List<String> writable = FileFormats
+            .getInstance().getWritableFormats(true);
     lst.add(new Option("format", "Alignment upload format", true,
-            FileFormat.Fasta.toString(), format.toString(), getWritableFormats(),
+            FileFormat.Fasta.toString(), format.getName(), writable,
             null));
     lst.add(createMolTypeOption("type", "Sequence type", false, type, null));
 
     return lst;
   }
 
-  /**
-   * @return
-   */
-  protected List<String> getWritableFormats()
-  {
-    List<String> formats = new ArrayList<String>();
-    for (FileFormatI ff : FileFormat.values())
-    {
-      if (ff.isWritable())
-      {
-        formats.add(ff.toString());
-      }
-    }
-    return formats;
-  }
-
 }
@@ -51,9 +51,8 @@ import org.testng.annotations.BeforeClass;
  * @author gmcarstairs
  *
  */
-public class DnaAlignmentGenerator
+public class AlignmentGenerator
 {
-
   @BeforeClass(alwaysRun = true)
   public void setUpJvOptionPane()
   {
@@ -65,7 +64,11 @@ public class DnaAlignmentGenerator
 
   private static final char ZERO = '0';
 
-  private static final char[] BASES = new char[] { 'G', 'T', 'C', 'A' };
+  private static final char[] NUCS = "GTCA".toCharArray();
+
+  private static final char[] PEPS = "MILVFYWHKRDEQNTCGASNP".toCharArray();
+
+  private static char[] BASES;
 
   private Random random;
 
@@ -78,17 +81,19 @@ public class DnaAlignmentGenerator
    */
   public static void main(String[] args)
   {
-    if (args.length != 5)
+    if (args.length != 6)
     {
       usage();
       return;
     }
-    int width = Integer.parseInt(args[0]);
-    int height = Integer.parseInt(args[1]);
-    long randomSeed = Long.valueOf(args[2]);
-    int gapPercentage = Integer.valueOf(args[3]);
-    int changePercentage = Integer.valueOf(args[4]);
-    AlignmentI al = new DnaAlignmentGenerator().generate(width, height,
+    boolean nucleotide = args[0].toLowerCase().startsWith("n");
+    int width = Integer.parseInt(args[1]);
+    int height = Integer.parseInt(args[2]);
+    long randomSeed = Long.valueOf(args[3]);
+    int gapPercentage = Integer.valueOf(args[4]);
+    int changePercentage = Integer.valueOf(args[5]);
+    AlignmentI al = new AlignmentGenerator(nucleotide).generate(width,
+            height,
             randomSeed, gapPercentage, changePercentage);
 
     System.out.println("; " + height + " sequences of " + width
@@ -104,25 +109,26 @@ public class DnaAlignmentGenerator
   private static void usage()
   {
     System.out.println("Usage:");
-    System.out.println("arg0: number of (non-gap) bases per sequence");
-    System.out.println("arg1: number sequences");
+    System.out.println("arg0: n (for nucleotide) or p (for peptide)");
+    System.out.println("arg1: number of (non-gap) bases per sequence");
+    System.out.println("arg2: number sequences");
     System.out
-            .println("arg2: an integer as random seed (same seed = same results)");
-    System.out.println("arg3: percentage of gaps to (randomly) generate");
+            .println("arg3: an integer as random seed (same seed = same results)");
+    System.out.println("arg4: percentage of gaps to (randomly) generate");
     System.out
-            .println("arg4: percentage of 'mutations' to (randomly) generate");
-    System.out.println("Example: DnaAlignmentGenerator 12 15 387 10 5");
+            .println("arg5: percentage of 'mutations' to (randomly) generate");
+    System.out.println("Example: AlignmentGenerator n 12 15 387 10 5");
     System.out
-            .println("- 15 sequences of 12 bases each, approx 10% gaps and 5% mutations, random seed = 387");
+            .println("- 15 nucleotide sequences of 12 bases each, approx 10% gaps and 5% mutations, random seed = 387");
 
   }
 
   /**
-   * Default constructor
+   * Constructor that sets nucleotide or peptide symbol set
    */
-  public DnaAlignmentGenerator()
+  public AlignmentGenerator(boolean nuc)
   {
-
+    BASES = nuc ? NUCS : PEPS;
   }
 
   /**
@@ -168,7 +174,8 @@ public class DnaAlignmentGenerator
     for (int count = 0; count < length;)
     {
       boolean addGap = random.nextInt(100) < gapPercentage;
-      char c = addGap ? GAP : BASES[random.nextInt(Integer.MAX_VALUE) % 4];
+      char c = addGap ? GAP : BASES[random.nextInt(Integer.MAX_VALUE)
+              % BASES.length];
       seq.append(c);
       if (!addGap)
       {
@@ -277,7 +284,7 @@ public class DnaAlignmentGenerator
     char newchar = c;
     while (newchar == c)
     {
-      newchar = BASES[random.nextInt(Integer.MAX_VALUE) % 4];
+      newchar = BASES[random.nextInt(Integer.MAX_VALUE) % BASES.length];
     }
     return newchar;
   }
index cd5d3ca..2e21d9c 100644 (file)
@@ -294,7 +294,8 @@ public class DnaTest
     /*
      * Generate cDNA - 8 sequences of 12 bases each.
      */
-    AlignmentI cdna = new DnaAlignmentGenerator().generate(12, 8, 97, 5, 5);
+    AlignmentI cdna = new AlignmentGenerator(true)
+            .generate(12, 8, 97, 5, 5);
     ColumnSelection cs = new ColumnSelection();
     AlignViewportI av = new AlignViewport(cdna, cs);
     Dna dna = new Dna(av, new int[] { 0, cdna.getWidth() - 1 });
diff --git a/test/jalview/io/FileFormatsTest.java b/test/jalview/io/FileFormatsTest.java
new file mode 100644 (file)
index 0000000..8df228f
--- /dev/null
@@ -0,0 +1,156 @@
+package jalview.io;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertSame;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Iterator;
+
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+public class FileFormatsTest
+{
+  @AfterMethod()
+  public void tearDown()
+  {
+    FileFormats.getInstance().reset();
+  }
+
+  @Test(groups = "Functional")
+  public void testIsDynamic()
+  {
+    FileFormats formats = FileFormats.getInstance();
+    for (FileFormatI ff : FileFormat.values())
+    {
+      assertFalse(formats.isIdentifiable(ff));
+    }
+    assertTrue(formats.isIdentifiable(null));
+
+    /*
+     * remove and re-add a format: it is now considered 'dynamically added'
+     */
+    formats.deregisterFileFormat(FileFormat.Fasta.getName());
+    assertNull(formats.forName(FileFormat.Fasta.getName()));
+    formats.registerFileFormat(FileFormat.Fasta);
+    assertSame(FileFormat.Fasta,
+            formats.forName(FileFormat.Fasta.getName()));
+    assertTrue(formats.isIdentifiable(FileFormat.Fasta));
+  }
+
+  @Test(groups = "Functional")
+  public void testGetReadableFormats()
+  {
+    String expected = "[Fasta, PFAM, Stockholm, PIR, BLC, AMSA, HTML, RNAML, JSON, PileUp, MSF, Clustal, PHYLIP, GFF or Jalview features, PDB, mmCIF, Jalview]";
+    FileFormats formats = FileFormats.getInstance();
+    assertEquals(formats.getReadableFormats().toString(), expected);
+  }
+
+  @Test(groups = "Functional")
+  public void testGetWritableFormats()
+  {
+    String expected = "[Fasta, PFAM, Stockholm, PIR, BLC, AMSA, JSON, PileUp, MSF, Clustal, PHYLIP]";
+    FileFormats formats = FileFormats.getInstance();
+    assertEquals(formats.getWritableFormats(true).toString(), expected);
+    expected = "[Fasta, PFAM, Stockholm, PIR, BLC, AMSA, JSON, PileUp, MSF, Clustal, PHYLIP, Jalview]";
+    assertEquals(formats.getWritableFormats(false).toString(), expected);
+  }
+
+  @Test(groups = "Functional")
+  public void testDeregisterFileFormat()
+  {
+    String writable = "[Fasta, PFAM, Stockholm, PIR, BLC, AMSA, JSON, PileUp, MSF, Clustal, PHYLIP]";
+    String readable = "[Fasta, PFAM, Stockholm, PIR, BLC, AMSA, HTML, RNAML, JSON, PileUp, MSF, Clustal, PHYLIP, GFF or Jalview features, PDB, mmCIF, Jalview]";
+    FileFormats formats = FileFormats.getInstance();
+    assertEquals(formats.getWritableFormats(true).toString(), writable);
+    assertEquals(formats.getReadableFormats().toString(), readable);
+
+    formats.deregisterFileFormat(FileFormat.Fasta.getName());
+    writable = "[PFAM, Stockholm, PIR, BLC, AMSA, JSON, PileUp, MSF, Clustal, PHYLIP]";
+    readable = "[PFAM, Stockholm, PIR, BLC, AMSA, HTML, RNAML, JSON, PileUp, MSF, Clustal, PHYLIP, GFF or Jalview features, PDB, mmCIF, Jalview]";
+    assertEquals(formats.getWritableFormats(true).toString(), writable);
+    assertEquals(formats.getReadableFormats().toString(), readable);
+
+    /*
+     * re-register the format: it gets added to the end of the list
+     */
+    formats.registerFileFormat(FileFormat.Fasta);
+    writable = "[PFAM, Stockholm, PIR, BLC, AMSA, JSON, PileUp, MSF, Clustal, PHYLIP, Fasta]";
+    readable = "[PFAM, Stockholm, PIR, BLC, AMSA, HTML, RNAML, JSON, PileUp, MSF, Clustal, PHYLIP, GFF or Jalview features, PDB, mmCIF, Jalview, Fasta]";
+    assertEquals(formats.getWritableFormats(true).toString(), writable);
+    assertEquals(formats.getReadableFormats().toString(), readable);
+  }
+
+  @Test(groups = "Functional")
+  public void testForName()
+  {
+    FileFormats formats = FileFormats.getInstance();
+    for (FileFormatI ff : FileFormat.values())
+    {
+      assertSame(ff, formats.forName(ff.getName()));
+      assertSame(ff, formats.forName(ff.getName().toUpperCase()));
+      assertSame(ff, formats.forName(ff.getName().toLowerCase()));
+    }
+    assertNull(formats.forName(null));
+    assertNull(formats.forName("rubbish"));
+  }
+
+  @Test(groups = "Functional")
+  public void testRegisterFileFormat()
+  {
+    FileFormats formats = FileFormats.getInstance();
+    assertSame(FileFormat.MMCif,
+            formats.forName(FileFormat.MMCif.getName()));
+    assertFalse(formats.isIdentifiable(FileFormat.MMCif));
+
+    /*
+     * deregister mmCIF format
+     */
+    formats.deregisterFileFormat(FileFormat.MMCif.getName());
+    assertNull(formats.forName(FileFormat.MMCif.getName()));
+
+    /*
+     * re-register mmCIF format
+     * it is reinstated (but now classed as 'dynamic')
+     */
+    formats.registerFileFormat(FileFormat.MMCif);
+    assertSame(FileFormat.MMCif,
+            formats.forName(FileFormat.MMCif.getName()));
+    assertTrue(formats.isIdentifiable(FileFormat.MMCif));
+    // repeating does nothing
+    formats.registerFileFormat(FileFormat.MMCif);
+    assertSame(FileFormat.MMCif,
+            formats.forName(FileFormat.MMCif.getName()));
+  }
+
+  @Test(groups = "Functional")
+  public void testGetFormats()
+  {
+    /*
+     * verify the list of file formats registered matches the enum values
+     */
+    FileFormats instance = FileFormats.getInstance();
+    Iterator<FileFormatI> formats = instance.getFormats()
+            .iterator();
+    FileFormatI[] builtIn = FileFormat.values();
+
+    for (FileFormatI ff : builtIn)
+    {
+      assertSame(ff, formats.next());
+    }
+    assertFalse(formats.hasNext());
+
+    /*
+     * remove the first format, check it is no longer in 
+     * the list of formats
+     */
+    String firstFormatName = instance.getFormats().iterator().next()
+            .getName();
+    instance.deregisterFileFormat(firstFormatName);
+    assertNotEquals(instance.getFormats().iterator().next().getName(),
+            firstFormatName);
+  }
+}
index 7aa5769..b500266 100644 (file)
@@ -132,7 +132,7 @@ public class FormatAdapterTest
   static Object[][] getFormats()
   {
     List<FileFormatI> both = new ArrayList<FileFormatI>();
-    for (FileFormat format : FileFormat.values())
+    for (FileFormatI format : FileFormats.getInstance().getFormats())
     {
       if (format.isReadable() && format.isWritable()
               && format.isTextFormat())
diff --git a/test/jalview/math/MatrixTest.java b/test/jalview/math/MatrixTest.java
new file mode 100644 (file)
index 0000000..795b2fa
--- /dev/null
@@ -0,0 +1,239 @@
+package jalview.math;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+
+import java.util.Arrays;
+
+import org.testng.annotations.Test;
+
+public class MatrixTest
+{
+  @Test(groups = "Timing")
+  public void testPreMultiply_timing()
+  {
+    int rows = 500;
+    int cols = 1000;
+    double[][] d1 = new double[rows][cols];
+    double[][] d2 = new double[cols][rows];
+    Matrix m1 = new Matrix(d1, rows, cols);
+    Matrix m2 = new Matrix(d2, cols, rows);
+    long start = System.currentTimeMillis();
+    m1.preMultiply(m2);
+    long elapsed = System.currentTimeMillis() - start;
+    System.out.println(rows + "x" + cols
+            + " multiplications of double took " + elapsed + "ms");
+  }
+
+  @Test(groups = "Functional")
+  public void testPreMultiply()
+  {
+    Matrix m1 = new Matrix(new double[][] { { 2, 3, 4 } }, 1, 3); // 1x3
+    Matrix m2 = new Matrix(new double[][] { { 5 }, { 6 }, { 7 } }, 3, 1); // 3x1
+
+    /*
+     * 1x3 times 3x1 is 1x1
+     * 2x5 + 3x6 + 4*7 =  56
+     */
+    Matrix m3 = m2.preMultiply(m1);
+    assertEquals(m3.rows, 1);
+    assertEquals(m3.cols, 1);
+    assertEquals(m3.value[0][0], 56d);
+
+    /*
+     * 3x1 times 1x3 is 3x3
+     */
+    m3 = m1.preMultiply(m2);
+    assertEquals(m3.rows, 3);
+    assertEquals(m3.cols, 3);
+    assertEquals(Arrays.toString(m3.value[0]), "[10.0, 15.0, 20.0]");
+    assertEquals(Arrays.toString(m3.value[1]), "[12.0, 18.0, 24.0]");
+    assertEquals(Arrays.toString(m3.value[2]), "[14.0, 21.0, 28.0]");
+  }
+
+  @Test(
+    groups = "Functional",
+    expectedExceptions = { IllegalArgumentException.class })
+  public void testPreMultiply_tooManyColumns()
+  {
+    Matrix m1 = new Matrix(new double[][] { { 2, 3, 4 }, { 3, 4, 5 } }, 2,
+            3); // 2x3
+
+    /*
+     * 2x3 times 2x3 invalid operation - 
+     * multiplier has more columns than multiplicand has rows
+     */
+    m1.preMultiply(m1);
+    fail("Expected exception");
+  }
+
+  @Test(
+    groups = "Functional",
+    expectedExceptions = { IllegalArgumentException.class })
+  public void testPreMultiply_tooFewColumns()
+  {
+    Matrix m1 = new Matrix(new double[][] { { 2, 3, 4 }, { 3, 4, 5 } }, 2,
+            3); // 2x3
+
+    /*
+     * 3x2 times 3x2 invalid operation - 
+     * multiplier has more columns than multiplicand has row
+     */
+    m1.preMultiply(m1);
+    fail("Expected exception");
+  }
+  
+  
+  private boolean matrixEquals(Matrix m1, Matrix m2) {
+    return Arrays.deepEquals(m1.value, m2.value);
+  }
+
+  @Test(groups = "Functional")
+  public void testPostMultiply()
+  {
+    /*
+     * Square matrices
+     * (2 3) . (10   100)
+     * (4 5)   (1000 10000)
+     * =
+     * (3020 30200)
+     * (5040 50400)
+     */
+    Matrix m1 = new Matrix(new double[][] { { 2, 3 }, { 4, 5 } }, 2, 2);
+    Matrix m2 = new Matrix(new double[][] { { 10, 100 }, { 1000, 10000 } },
+            2, 2);
+    Matrix m3 = m1.postMultiply(m2);
+    assertEquals(Arrays.toString(m3.value[0]), "[3020.0, 30200.0]");
+    assertEquals(Arrays.toString(m3.value[1]), "[5040.0, 50400.0]");
+
+    /*
+     * also check m2.preMultiply(m1) - should be same as m1.postMultiply(m2) 
+     */
+    m3 = m2.preMultiply(m1);
+    assertEquals(Arrays.toString(m3.value[0]), "[3020.0, 30200.0]");
+    assertEquals(Arrays.toString(m3.value[1]), "[5040.0, 50400.0]");
+
+    /*
+     * m1 has more rows than columns
+     * (2).(10 100 1000) = (20 200 2000)
+     * (3)                 (30 300 3000)
+     */
+    m1 = new Matrix(new double[][] { { 2 }, { 3 } }, 2, 1);
+    m2 = new Matrix(new double[][] { { 10, 100, 1000 } }, 1, 3);
+    m3 = m1.postMultiply(m2);
+    assertEquals(m3.rows, 2);
+    assertEquals(m3.cols, 3);
+    assertEquals(Arrays.toString(m3.value[0]), "[20.0, 200.0, 2000.0]");
+    assertEquals(Arrays.toString(m3.value[1]), "[30.0, 300.0, 3000.0]");
+    m3 = m2.preMultiply(m1);
+    assertEquals(m3.rows, 2);
+    assertEquals(m3.cols, 3);
+    assertEquals(Arrays.toString(m3.value[0]), "[20.0, 200.0, 2000.0]");
+    assertEquals(Arrays.toString(m3.value[1]), "[30.0, 300.0, 3000.0]");
+
+    /*
+     * m1 has more columns than rows
+     * (2 3 4) . (5 4) = (56 25)
+     *           (6 3) 
+     *           (7 2)
+     * [0, 0] = 2*5 + 3*6 + 4*7 = 56
+     * [0, 1] = 2*4 + 3*3 + 4*2 = 25  
+     */
+    m1 = new Matrix(new double[][] { { 2, 3, 4 } }, 1, 3);
+    m2 = new Matrix(new double[][] { { 5, 4 }, { 6, 3 }, { 7, 2 } }, 3, 2);
+    m3 = m1.postMultiply(m2);
+    assertEquals(m3.rows, 1);
+    assertEquals(m3.cols, 2);
+    assertEquals(m3.value[0][0], 56d);
+    assertEquals(m3.value[0][1], 25d);
+
+    /*
+     * and check premultiply equivalent
+     */
+    m3 = m2.preMultiply(m1);
+    assertEquals(m3.rows, 1);
+    assertEquals(m3.cols, 2);
+    assertEquals(m3.value[0][0], 56d);
+    assertEquals(m3.value[0][1], 25d);
+  }
+
+  /**
+   * main method extracted from Matrix
+   * 
+   * @param args
+   */
+  public static void main(String[] args) throws Exception
+  {
+    int n = Integer.parseInt(args[0]);
+    double[][] in = new double[n][n];
+  
+    for (int i = 0; i < n; i++)
+    {
+      for (int j = 0; j < n; j++)
+      {
+        in[i][j] = Math.random();
+      }
+    }
+  
+    Matrix origmat = new Matrix(in, n, n);
+  
+    // System.out.println(" --- Original matrix ---- ");
+    // / origmat.print(System.out);
+    // System.out.println();
+    // System.out.println(" --- transpose matrix ---- ");
+    Matrix trans = origmat.transpose();
+  
+    // trans.print(System.out);
+    // System.out.println();
+    // System.out.println(" --- OrigT * Orig ---- ");
+    Matrix symm = trans.postMultiply(origmat);
+  
+    // symm.print(System.out);
+    // System.out.println();
+    // Copy the symmetric matrix for later
+    // Matrix origsymm = symm.copy();
+  
+    // This produces the tridiagonal transformation matrix
+    // long tstart = System.currentTimeMillis();
+    symm.tred();
+  
+    // long tend = System.currentTimeMillis();
+  
+    // System.out.println("Time take for tred = " + (tend-tstart) + "ms");
+    // System.out.println(" ---Tridiag transform matrix ---");
+    // symm.print(System.out);
+    // System.out.println();
+    // System.out.println(" --- D vector ---");
+    // symm.printD(System.out);
+    // System.out.println();
+    // System.out.println(" --- E vector ---");
+    // symm.printE(System.out);
+    // System.out.println();
+    // Now produce the diagonalization matrix
+    // tstart = System.currentTimeMillis();
+    symm.tqli();
+    // tend = System.currentTimeMillis();
+  
+    // System.out.println("Time take for tqli = " + (tend-tstart) + " ms");
+    // System.out.println(" --- New diagonalization matrix ---");
+    // symm.print(System.out);
+    // System.out.println();
+    // System.out.println(" --- D vector ---");
+    // symm.printD(System.out);
+    // System.out.println();
+    // System.out.println(" --- E vector ---");
+    // symm.printE(System.out);
+    // System.out.println();
+    // System.out.println(" --- First eigenvector --- ");
+    // double[] eigenv = symm.getColumn(0);
+    // for (int i=0; i < eigenv.length;i++) {
+    // Format.print(System.out,"%15.4f",eigenv[i]);
+    // }
+    // System.out.println();
+    // double[] neigenv = origsymm.vectorPostMultiply(eigenv);
+    // for (int i=0; i < neigenv.length;i++) {
+    // Format.print(System.out,"%15.4f",neigenv[i]/symm.d[0]);
+    // }
+    // System.out.println();
+  }
+}
index 1404f0b..999e456 100644 (file)
@@ -21,6 +21,7 @@
 package jalview.util;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
 
 import jalview.gui.JvOptionPane;
 
@@ -96,4 +97,69 @@ public class FormatTest
     assertEquals(Format.repeat('b', 0), "");
     assertEquals(Format.repeat('c', -1), "");
   }
+
+  @Test(groups = "Functional")
+  public void testFormat_scientific()
+  {
+    Format f = new Format("%3.4e");
+    double d = 1d;
+    assertEquals(f.form(d), "1.0000e+000");
+    assertEquals(String.format("%3.4e", d), "1.0000e+00");
+
+    d = 12345678.12345678d;
+    assertEquals(f.form(d), "1.2346e+007");
+    assertEquals(String.format("%3.4e", d), "1.2346e+07");
+  }
+
+  /**
+   * Test that fails (in 2.10.1) with timeout as there is an infinite loop in
+   * Format.exp_format()
+   */
+  @Test(groups = "Functional", timeOut = 500)
+  public void testFormat_scientific_overflow()
+  {
+    Format f = new Format("%3.4e");
+    double d = 1.12E-310;
+    /*
+     * problem: exp_format() scales up 'd' to the range 1-10
+     * while computing a scaling factor, but this factor acquires
+     * the value Double.POSITIVE_INFINITY instead of 1.0E+309
+     * the value to be formatted is multipled by factor and becomes Infinity
+     * resulting in an infinite loop in the recursive call to exp_format()
+     */
+    assertEquals(f.form(d), "1.1200e-310");
+  }
+
+  /**
+   * This test shows that Format.form() is faster for this case than
+   * String.format()
+   */
+  @Test(groups = "Timing")
+  public void testFormat_scientificTiming()
+  {
+    Format f = new Format("%3.4e");
+    double d = 12345678.12345678d;
+
+    int iterations = 1000;
+    long start = System.currentTimeMillis();
+    for (int i = 0; i < iterations; i++)
+    {
+      f.form(d);
+    }
+    long stop = System.currentTimeMillis();
+    long elapsed1 = stop - start;
+    System.out.println(iterations + " x Format.form took " + elapsed1
+            + "ms");
+
+    start = System.currentTimeMillis();
+    for (int i = 0; i < iterations; i++)
+    {
+      String.format("%3.4e", d);
+    }
+    stop = System.currentTimeMillis();
+    long elapsed2 = stop - start;
+    System.out.println(iterations + " x String.format took " + elapsed2
+            + "ms");
+    assertTrue(elapsed2 > elapsed1);
+  }
 }
index 3573f50..2d317e4 100644 (file)
@@ -138,7 +138,7 @@ public class DisorderAnnotExportImport
   {
     try
     {
-      String aligfileout = FileFormat.Pfam.getAlignmentFile().print(
+      String aligfileout = FileFormat.Pfam.getWriter(al).print(
               al.getSequencesArray(), true);
       String anfileout = new AnnotationFile()
               .printAnnotationsForAlignment(al);
index b8fe0a3..d5b6ed1 100644 (file)
@@ -188,7 +188,7 @@ public class JpredJabaStructExportImport
     try
     {
       // what format would be appropriate for RNAalifold annotations?
-      String aligfileout = FileFormat.Pfam.getAlignmentFile().print(
+      String aligfileout = FileFormat.Pfam.getWriter(null).print(
               al.getSequencesArray(), true);
 
       String anfileout = new AnnotationFile()
index 06c803f..f1430f6 100644 (file)
@@ -201,7 +201,7 @@ public class RNAStructExportImport
     try
     {
       // what format would be appropriate for RNAalifold annotations?
-      String aligfileout = FileFormat.Pfam.getAlignmentFile().print(
+      String aligfileout = FileFormat.Pfam.getWriter(null).print(
               al.getSequencesArray(), true);
 
       String anfileout = new AnnotationFile()