Merge branch 'develop' into features/filetypeEnum
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 30 Aug 2016 11:56:34 +0000 (12:56 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 30 Aug 2016 11:56:34 +0000 (12:56 +0100)
Conflicts:
src/jalview/io/AppletFormatAdapter.java
src/jalview/io/FormatAdapter.java
src/jalview/io/MSFfile.java
src/jalview/io/RnamlFile.java
src/jalview/io/StockholmFile.java
src/jalview/ws/dbsources/Pdb.java
test/jalview/ext/jmol/JmolParserTest.java
test/jalview/gui/AlignViewportTest.java
test/jalview/ws/jabaws/RNAStructExportImport.java

60 files changed:
1  2 
src/MCview/PDBfile.java
src/jalview/appletgui/APopupMenu.java
src/jalview/appletgui/AlignFrame.java
src/jalview/bin/Jalview.java
src/jalview/bin/JalviewLite.java
src/jalview/controller/AlignViewController.java
src/jalview/datamodel/Alignment.java
src/jalview/datamodel/PDBEntry.java
src/jalview/ext/ensembl/EnsemblRestClient.java
src/jalview/ext/jmol/JmolParser.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/AnnotationLabels.java
src/jalview/gui/Desktop.java
src/jalview/gui/FeatureSettings.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/Jalview2XML_V1.java
src/jalview/gui/PopupMenu.java
src/jalview/gui/Preferences.java
src/jalview/gui/SequenceFetcher.java
src/jalview/gui/TreePanel.java
src/jalview/gui/UserDefinedColours.java
src/jalview/gui/WsParamSetManager.java
src/jalview/io/AlignFile.java
src/jalview/io/AppletFormatAdapter.java
src/jalview/io/BioJsHTMLOutput.java
src/jalview/io/FileFormat.java
src/jalview/io/HTMLOutput.java
src/jalview/io/HtmlSvgOutput.java
src/jalview/io/JSONFile.java
src/jalview/io/JalviewFileChooser.java
src/jalview/io/MSFfile.java
src/jalview/io/PfamFile.java
src/jalview/io/RnamlFile.java
src/jalview/io/StockholmFile.java
src/jalview/io/StructureFile.java
src/jalview/jbgui/GFinder.java
src/jalview/structure/StructureImportSettings.java
src/jalview/structure/StructureSelectionManager.java
src/jalview/util/ImageMaker.java
src/jalview/ws/dbsources/Pdb.java
src/jalview/ws/jws1/Annotate3D.java
src/jalview/ws/jws1/JPredThread.java
src/jalview/ws/jws1/SeqSearchWSThread.java
src/jalview/ws/rest/params/Alignment.java
test/jalview/analysis/AlignmentUtilsTests.java
test/jalview/analysis/DnaTest.java
test/jalview/datamodel/AlignmentAnnotationTests.java
test/jalview/datamodel/AlignmentTest.java
test/jalview/ext/jmol/JmolParserTest.java
test/jalview/ext/jmol/JmolVsJalviewPDBParserEndToEndTest.java
test/jalview/gui/AlignViewportTest.java
test/jalview/io/AnnotatedPDBFileInputTest.java
test/jalview/io/FormatAdapterTest.java
test/jalview/io/Jalview2xmlTests.java
test/jalview/io/StockholmFileTest.java
test/jalview/structure/StructureSelectionManagerTest.java
test/jalview/util/MappingUtilsTest.java
test/jalview/ws/jabaws/RNAStructExportImport.java
test/jalview/ws/sifts/SiftsClientTest.java

diff --combined src/MCview/PDBfile.java
@@@ -23,10 -23,8 +23,9 @@@ package MCview
  import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.DBRefSource;
  import jalview.datamodel.SequenceI;
- import jalview.io.FileParse;
 +import jalview.io.DataSourceType;
+ import jalview.io.FileParse;
  import jalview.io.StructureFile;
- import jalview.structure.StructureImportSettings;
  import jalview.util.MessageManager;
  
  import java.io.IOException;
@@@ -48,11 -46,10 +47,11 @@@ public class PDBfile extends StructureF
    }
  
    public PDBfile(boolean addAlignmentAnnotations, boolean predictSecStr,
 -          boolean externalSecStr, String dataObject, String protocol)
 +          boolean externalSecStr, String dataObject,
 +          DataSourceType sourceType)
            throws IOException
    {
 -    super(false, dataObject, protocol);
 +    super(false, dataObject, sourceType);
      addSettings(addAlignmentAnnotations, predictSecStr, externalSecStr);
      doParse();
    }
@@@ -67,7 -64,7 +66,7 @@@
    }
  
    @Override
--  public String print()
++  public String print(SequenceI[] seqs, boolean jvSuffix)
    {
      return null;
    }
            break;
          }
          if (line.indexOf("ATOM") == 0
-                 || (StructureImportSettings.isProcessHETATMs()
-                         && line.indexOf("HETATM") == 0 && !terFlag))
+                 || (line.indexOf("HETATM") == 0 && !terFlag))
          {
            terFlag = false;
  
@@@ -24,7 -24,6 +24,7 @@@ import jalview.analysis.AAFrequency
  import jalview.analysis.AlignmentAnnotationUtils;
  import jalview.analysis.AlignmentUtils;
  import jalview.analysis.Conservation;
 +import jalview.bin.JalviewLite;
  import jalview.commands.ChangeCaseCommand;
  import jalview.commands.EditCommand;
  import jalview.commands.EditCommand.Action;
@@@ -36,8 -35,6 +36,8 @@@ import jalview.datamodel.SequenceFeatur
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
  import jalview.io.AppletFormatAdapter;
 +import jalview.io.DataSourceType;
 +import jalview.io.FileFormat;
  import jalview.io.SequenceAnnotationReport;
  import jalview.schemes.Blosum62ColourScheme;
  import jalview.schemes.BuriedColourScheme;
@@@ -73,8 -70,6 +73,6 @@@ import java.util.Vector
  public class APopupMenu extends java.awt.PopupMenu implements
          ActionListener, ItemListener
  {
-   private static final String ALL_ANNOTATIONS = "All";
    Menu groupMenu = new Menu();
  
    MenuItem editGroupName = new MenuItem();
  
      Frame frame = new Frame();
      frame.add(cap);
 -    jalview.bin.JalviewLite.addFrame(frame, MessageManager.formatMessage(
 +    JalviewLite.addFrame(frame, MessageManager.formatMessage(
              "label.selection_output_command",
              new Object[] { e.getActionCommand() }), 600, 500);
      // JBPNote: getSelectionAsNewSequence behaviour has changed - this method
      // now returns a full copy of sequence data
      // TODO consider using getSequenceSelection instead here
  
 -    cap.setText(new jalview.io.AppletFormatAdapter().formatSequences(
 -            e.getActionCommand(), ap.av.getShowJVSuffix(), ap, true));
 +    FileFormat fileFormat = FileFormat.valueOf(e.getActionCommand());
 +    cap.setText(new AppletFormatAdapter().formatSequences(fileFormat,
 +            ap.av.getShowJVSuffix(), ap, true));
  
    }
  
        if (ap.av.applet.jmolAvailable)
        {
          new jalview.appletgui.AppletJmol(entry, new SequenceI[] { seq },
 -                null, ap, AppletFormatAdapter.URL);
 +                null, ap, DataSourceType.URL);
        }
        else
        {
          new MCview.AppletPDBViewer(entry, new SequenceI[] { seq }, null,
 -                ap, AppletFormatAdapter.URL);
 +                ap, DataSourceType.URL);
        }
  
      }
              "label.represent_group_with", new Object[] { "" }));
      revealAll.setLabel(MessageManager.getString("action.reveal_all"));
      revealSeq.setLabel(MessageManager.getString("action.reveal_sequences"));
-     menu1.setLabel(MessageManager.getString("label.group") + ":");
+     menu1.setLabel(MessageManager.getString("label.group:"));
      add(groupMenu);
      this.add(seqMenu);
      this.add(hideSeqs);
      showMenu.removeAll();
      hideMenu.removeAll();
  
-     final List<String> all = Arrays.asList(ALL_ANNOTATIONS);
+     final List<String> all = Arrays.asList(new String[] { MessageManager
+             .getString("label.all") });
      addAnnotationTypeToShowHide(showMenu, forSequences, "", all, true, true);
      addAnnotationTypeToShowHide(hideMenu, forSequences, "", all, true,
              false);
@@@ -49,9 -49,7 +49,9 @@@ import jalview.datamodel.SequenceGroup
  import jalview.datamodel.SequenceI;
  import jalview.io.AnnotationFile;
  import jalview.io.AppletFormatAdapter;
 +import jalview.io.DataSourceType;
  import jalview.io.FeaturesFile;
 +import jalview.io.FileFormat;
  import jalview.io.TCoffeeScoreFile;
  import jalview.schemes.Blosum62ColourScheme;
  import jalview.schemes.BuriedColourScheme;
@@@ -221,6 -219,7 +221,7 @@@ public class AlignFrame extends Embmenu
      {
        viewport.setColumnSelection(columnSelection);
      }
+     viewport.setScaleAboveWrapped(scaleAbove.getState());
  
      alignPanel = new AlignmentPanel(this, viewport);
      avc = new jalview.controller.AlignViewController(this, viewport,
     *          is protocol for accessing data referred to by file
     */
  
 -  public boolean parseFeaturesFile(String file, String type)
 +  public boolean parseFeaturesFile(String file, DataSourceType type)
    {
      return parseFeaturesFile(file, type, true);
    }
     * 
     * @param file
     *          file URL, content, or other resolvable path
 -   * @param type
 +   * @param paste
     *          is protocol for accessing data referred to by file
     * @param autoenabledisplay
     *          when true, display features flag will be automatically enabled if
     *          features are loaded
     * @return true if data parsed as a features file
     */
 -  public boolean parseFeaturesFile(String file, String type,
 +  public boolean parseFeaturesFile(String file, DataSourceType paste,
            boolean autoenabledisplay)
    {
      boolean featuresFile = false;
                .getFeatureRenderer().getFeatureColours();
        boolean relaxedIdMatching = viewport.applet.getDefaultParameter(
                "relaxedidmatch", false);
 -      featuresFile = new FeaturesFile(file, type).parse(
 +      featuresFile = new FeaturesFile(file, paste).parse(
                viewport.getAlignment(), colours, true, relaxedIdMatching);
      } catch (Exception ex)
      {
      CutAndPasteTransfer cap = new CutAndPasteTransfer(true, this);
      Frame frame = new Frame();
      frame.add(cap);
 -    jalview.bin.JalviewLite.addFrame(frame, MessageManager.formatMessage(
 +    JalviewLite.addFrame(frame, MessageManager.formatMessage(
              "label.alignment_output_command",
              new Object[] { e.getActionCommand() }), 600, 500);
  
 -    FeatureRenderer fr = this.alignPanel.cloneFeatureRenderer();
 +    FileFormat fileFormat = FileFormat.valueOf(e.getActionCommand());
      cap.setText(new AppletFormatAdapter(alignPanel).formatSequences(
 -            e.getActionCommand(), viewport.getAlignment(),
 +            fileFormat, viewport.getAlignment(),
              viewport.getShowJVSuffix()));
    }
  
      }
      sg.setEndRes(viewport.getAlignment().getWidth() - 1);
      viewport.setSelectionGroup(sg);
-     alignPanel.paintAlignment(true);
+     // JAL-2034 - should delegate to
+     // alignPanel to decide if overview needs
+     // updating.
+     alignPanel.paintAlignment(false);
      PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
      viewport.sendSelection();
    }
      viewport.setSelectionGroup(null);
      alignPanel.idPanel.idCanvas.searchResults = null;
      alignPanel.seqPanel.seqCanvas.highlightSearchResults(null);
-     alignPanel.paintAlignment(true);
+     // JAL-2034 - should delegate to
+     // alignPanel to decide if overview needs
+     // updating.
+     alignPanel.paintAlignment(false);
      PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
      viewport.sendSelection();
    }
      nucleotideColour.setLabel(MessageManager.getString("label.nucleotide"));
      nucleotideColour.addActionListener(this);
      modifyPID.setLabel(MessageManager
-             .getString("label.modify_identity_thereshold"));
+             .getString("label.modify_identity_threshold"));
      modifyPID.addActionListener(this);
      modifyConservation.setLabel(MessageManager
-             .getString("label.modify_conservation_thereshold"));
+             .getString("label.modify_conservation_threshold"));
      modifyConservation.addActionListener(this);
      annotationColour.setLabel(MessageManager
              .getString("action.by_annotation"));
        }
        // resolve data source
        // TODO: this code should be a refactored to an io package
 -      String protocol = AppletFormatAdapter.resolveProtocol(pdbFile, "PDB");
 +      DataSourceType protocol = AppletFormatAdapter.resolveProtocol(
 +              pdbFile, FileFormat.PDB);
        if (protocol == null)
        {
          return false;
          // make a note of the access mode and add
          if (pdbentry.getProperty() == null)
          {
 -          pdbentry.setProperty(new Hashtable());
 +          pdbentry.setProperty(new Hashtable<String, String>());
          }
 -        pdbentry.getProperty().put("protocol", protocol);
 +        pdbentry.getProperty().put("protocol", protocol.toString());
          toaddpdb.addPDBId(pdbentry);
          alignPanel.getStructureSelectionManager()
                  .registerPDBEntry(pdbentry);
    }
  
    public void newStructureView(JalviewLite applet, PDBEntry pdb,
 -          SequenceI[] seqs, String[] chains, String protocol)
 +          SequenceI[] seqs, String[] chains, DataSourceType protocol)
    {
      // Scrub any null sequences from the array
      Object[] sqch = cleanSeqChainArrays(seqs, chains);
        System.err
                .println("JalviewLite.AlignFrame:newStructureView: No sequence to bind structure to.");
      }
 -    if (protocol == null || protocol.trim().length() == 0
 -            || protocol.equals("null"))
 +    if (protocol == null)
      {
 -      protocol = (String) pdb.getProperty().get("protocol");
 +      String sourceType = pdb.getProperty().get("protocol");
 +      try
 +      {
 +        protocol = DataSourceType.valueOf(sourceType);
 +      } catch (IllegalArgumentException e)
 +      {
 +        // ignore
 +      }
        if (protocol == null)
        {
          System.err.println("Couldn't work out protocol to open structure: "
      {
        // can only do alignments with Jmol
        // find the last jmol window assigned to this alignment
 -      jalview.appletgui.AppletJmol ajm = null, tajm;
 -      Vector jmols = applet
 -              .getAppletWindow(jalview.appletgui.AppletJmol.class);
 +      AppletJmol ajm = null, tajm;
 +      Vector jmols = applet.getAppletWindow(AppletJmol.class);
        for (int i = 0, iSize = jmols.size(); i < iSize; i++)
        {
 -        tajm = (jalview.appletgui.AppletJmol) jmols.elementAt(i);
 +        tajm = (AppletJmol) jmols.elementAt(i);
          if (tajm.ap.alignFrame == this)
          {
            ajm = tajm;
      // otherwise, create a new window
      if (applet.jmolAvailable)
      {
 -      new jalview.appletgui.AppletJmol(pdb, seqs, chains, alignPanel,
 +      new AppletJmol(pdb, seqs, chains, alignPanel,
                protocol);
        applet.lastFrameX += 40;
        applet.lastFrameY += 40;
@@@ -28,11 -28,8 +28,11 @@@ import jalview.gui.Desktop
  import jalview.gui.PromptUserConfig;
  import jalview.io.AppletFormatAdapter;
  import jalview.io.BioJsHTMLOutput;
 +import jalview.io.DataSourceType;
 +import jalview.io.FileFormat;
 +import jalview.io.FileFormatException;
 +import jalview.io.FileFormatI;
  import jalview.io.FileLoader;
 -import jalview.io.FormatAdapter;
  import jalview.io.HtmlSvgOutput;
  import jalview.io.IdentifyFile;
  import jalview.io.NewickFile;
@@@ -331,7 -328,6 +331,6 @@@ public class Jalvie
              Cache.log.debug("Starting questionnaire with default url: "
                      + defurl);
              desktop.checkForQuestionnaire(defurl);
            }
          }
        }
        {
          System.err.println("CMD [-noquestionnaire] executed successfully!");
        }
-       desktop.checkForNews();
-     }
  
-     if (!isHeadlessMode())
-     {
+       if (!aparser.contains("nonews"))
+       {
+         desktop.checkForNews();
+       }
        BioJsHTMLOutput.updateBioJS();
      }
  
 -    String file = null, protocol = null, format = null, data = null;
 +    String file = null, data = null;
 +    FileFormatI format = null;
 +    DataSourceType protocol = null;
      FileLoader fileLoader = new FileLoader(!headless);
      Vector<String> getFeatures = null; // vector of das source nicknames to
                                         // fetch
        {
          try
          {
 -          String viprotocol = AppletFormatAdapter
 +          DataSourceType viprotocol = AppletFormatAdapter
                    .checkProtocol(vamsasImport);
 -          if (viprotocol == jalview.io.FormatAdapter.FILE)
 +          if (viprotocol == DataSourceType.FILE)
            {
              inSession = desktop.vamsasImport(new File(vamsasImport));
            }
 -          else if (viprotocol == FormatAdapter.URL)
 +          else if (viprotocol == DataSourceType.URL)
            {
              inSession = desktop.vamsasImport(new URL(vamsasImport));
            }
  
        protocol = AppletFormatAdapter.checkProtocol(file);
  
 -      format = new IdentifyFile().identify(file, protocol);
 +      try
 +      {
 +        format = new IdentifyFile().identify(file, protocol);
 +      } catch (FileFormatException e1)
 +      {
 +        // TODO ?
 +      }
  
        AlignFrame af = fileLoader.LoadFileWaitTillLoaded(file, protocol,
                format);
          String imageName = "unnamed.png";
          while (aparser.getSize() > 1)
          {
 -          format = aparser.nextValue();
 +          String outputFormat = aparser.nextValue();
            file = aparser.nextValue();
  
 -          if (format.equalsIgnoreCase("png"))
 +          if (outputFormat.equalsIgnoreCase("png"))
            {
              af.createPNG(new File(file));
              imageName = (new File(file)).getName();
              System.out.println("Creating PNG image: " + file);
              continue;
            }
 -          else if (format.equalsIgnoreCase("svg"))
 +          else if (outputFormat.equalsIgnoreCase("svg"))
            {
              File imageFile = new File(file);
              imageName = imageFile.getName();
              System.out.println("Creating SVG image: " + file);
              continue;
            }
 -          else if (format.equalsIgnoreCase("html"))
 +          else if (outputFormat.equalsIgnoreCase("html"))
            {
              File imageFile = new File(file);
              imageName = imageFile.getName();
              System.out.println("Creating HTML image: " + file);
              continue;
            }
 -          else if (format.equalsIgnoreCase("imgMap"))
 +          else if (outputFormat.equalsIgnoreCase("imgMap"))
            {
              af.createImageMap(new File(file), imageName);
              System.out.println("Creating image map: " + file);
              continue;
            }
 -          else if (format.equalsIgnoreCase("eps"))
 +          else if (outputFormat.equalsIgnoreCase("eps"))
            {
              File outputFile = new File(file);
              System.out.println("Creating EPS file: "
          jalview.bin.Cache.removeProperty("STARTUP_FILE");
        }
  
 -      protocol = "File";
 +      protocol = DataSourceType.FILE;
  
        if (file.indexOf("http:") > -1)
        {
 -        protocol = "URL";
 +        protocol = DataSourceType.URL;
        }
  
        if (file.endsWith(".jar"))
        {
 -        format = "Jalview";
 +        format = FileFormat.Jalview;
        }
        else
        {
 -        format = new IdentifyFile().identify(file, protocol);
 +        try
 +        {
 +          format = new IdentifyFile().identify(file, protocol);
 +        } catch (FileFormatException e)
 +        {
 +          // TODO what?
 +        }
        }
  
        startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol,
                      + "-eps FILE\tCreate EPS file FILE from alignment.\n"
                      + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n"
                      + "-noquestionnaire\tTurn off questionnaire check.\n"
+                     + "-nonews\tTurn off check for Jalview news.\n"
                      + "-nousagestats\tTurn off google analytics tracking for this session.\n"
                      + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n"
                      // +
@@@ -37,8 -37,6 +37,9 @@@ import jalview.datamodel.SequenceGroup
  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.FileParse;
  import jalview.io.IdentifyFile;
  import jalview.io.JPredFile;
@@@ -65,7 -63,6 +66,7 @@@ import java.awt.event.ActionEvent
  import java.awt.event.WindowAdapter;
  import java.awt.event.WindowEvent;
  import java.io.BufferedReader;
 +import java.io.IOException;
  import java.io.InputStreamReader;
  import java.net.URL;
  import java.util.ArrayList;
@@@ -469,17 -466,11 +470,11 @@@ public class JalviewLite extends Apple
          SequenceI rs = sel.getSequenceAt(0);
          start = rs.findIndex(start);
          end = rs.findIndex(end);
-         if (csel != null)
+         List<Integer> cs = csel.getSelected();
+         csel.clear();
+         for (Integer selectedCol : cs)
          {
-           List<Integer> cs = csel.getSelected();
-           // note - the following actually clears cs as well, since
-           // csel.getSelected returns a reference. Need to check if we need to
-           // have a concurrentModification exception thrown here
-           csel.clear();
-           for (Integer selectedCol : cs)
-           {
-             csel.addElement(rs.findIndex(selectedCol));
-           }
+           csel.addElement(rs.findIndex(selectedCol));
          }
        }
        sel.setStartRes(start);
     */
    @Override
    public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf,
-           FileFormatI format, String suffix)
+           String format, String suffix)
    {
      try
      {
++      FileFormatI theFormat = FileFormat.valueOf(format);
        boolean seqlimits = suffix.equalsIgnoreCase(TRUE);
        if (alf.viewport.getSelectionGroup() != null)
        {
          // JBPNote: getSelectionAsNewSequence behaviour has changed - this
          // method now returns a full copy of sequence data
          // TODO consider using getSequenceSelection instead here
--        String reply = new AppletFormatAdapter().formatSequences(format,
++        String reply = new AppletFormatAdapter().formatSequences(theFormat,
                  new Alignment(alf.viewport.getSelectionAsNewSequence()),
                  seqlimits);
          return reply;
        }
--    } catch (Exception ex)
++    } catch (IllegalArgumentException ex)
      {
        ex.printStackTrace();
--      return "Error retrieving alignment in " + format + " format. ";
++      return "Error retrieving alignment, possibly invalid format specifier: "
++              + format;
      }
      return "";
    }
      {
        boolean seqlimits = suffix.equalsIgnoreCase(TRUE);
  
--      String reply = new AppletFormatAdapter().formatSequences(format,
++      FileFormatI theFormat = FileFormat.valueOf(format);
++      String reply = new AppletFormatAdapter().formatSequences(theFormat,
                alf.viewport.getAlignment(), seqlimits);
        return reply;
--    } catch (Exception ex)
++    } catch (IllegalArgumentException ex)
      {
        ex.printStackTrace();
--      return "Error retrieving alignment in " + format + " format. ";
++      return "Error retrieving alignment, possibly invalid format specifier: "
++              + format;
      }
    }
  
    public void loadAnnotationFrom(AlignFrame alf, String annotation)
    {
      if (new AnnotationFile().annotateAlignmentView(alf.getAlignViewport(),
 -            annotation, AppletFormatAdapter.PASTE))
 +            annotation, DataSourceType.PASTE))
      {
        alf.alignPanel.fontChanged();
        alf.alignPanel.setScrollValues(0, 0);
      }
      else
      {
 -      alf.parseFeaturesFile(annotation, AppletFormatAdapter.PASTE);
 +      alf.parseFeaturesFile(annotation, DataSourceType.PASTE);
      }
    }
  
    public boolean loadFeaturesFrom(AlignFrame alf, String features,
            boolean autoenabledisplay)
    {
 -    return alf.parseFeaturesFile(features, AppletFormatAdapter.PASTE,
 +    return alf.parseFeaturesFile(features, DataSourceType.PASTE,
              autoenabledisplay);
    }
  
    {
      AlignmentI al = null;
  
 -    String format = new IdentifyFile().identify(text,
 -            AppletFormatAdapter.PASTE);
      try
      {
 -      al = new AppletFormatAdapter().readFile(text,
 -              AppletFormatAdapter.PASTE, format);
 +      FileFormatI format = new IdentifyFile().identify(text,
 +              DataSourceType.PASTE);
 +      al = new AppletFormatAdapter().readFile(text, DataSourceType.PASTE,
 +              format);
        if (al.getHeight() > 0)
        {
          return new AlignFrame(al, this, title, false);
        }
 -    } catch (java.io.IOException ex)
 +    } catch (IOException ex)
      {
        ex.printStackTrace();
      }
      /**
       * State variable: protocol for access to file source
       */
 -    String protocol;
 +    DataSourceType protocol;
  
      String _file; // alignment file or URL spec
  
         */
        if (path.startsWith("PASTE"))
        {
 -        protocol = AppletFormatAdapter.PASTE;
 +        protocol = DataSourceType.PASTE;
          return path.substring(5);
        }
  
         */
        if (path.indexOf("://") != -1)
        {
 -        protocol = AppletFormatAdapter.URL;
 +        protocol = DataSourceType.URL;
          return path;
        }
  
            System.err.println("Prepended document base '" + documentBase
                    + "' to make: '" + withDocBase + "'");
          }
 -        protocol = AppletFormatAdapter.URL;
 +        protocol = DataSourceType.URL;
          return withDocBase;
        }
  
        if (!withCodeBase.equals(withDocBase)
                && HttpUtils.isValidUrl(withCodeBase))
        {
 -        protocol = AppletFormatAdapter.URL;
 +        protocol = DataSourceType.URL;
          if (debug)
          {
            System.err.println("Prepended codebase '" + codeBase
         */
        if (inArchive(path))
        {
 -        protocol = AppletFormatAdapter.CLASSLOADER;
 +        protocol = DataSourceType.CLASSLOADER;
        }
        return path;
      }
          return null;
        }
        String resolvedFile = resolveFileProtocol(fileParam);
        AlignmentI al = null;
        try
        {
 +        FileFormatI format = new IdentifyFile().identify(resolvedFile,
 +                protocol);
 +        dbgMsg("File identified as '" + format + "'");
          al = new AppletFormatAdapter().readFile(resolvedFile, protocol,
                  format);
          if ((al != null) && (al.getHeight() > 0))
            // update the focus.
            currentAlignFrame = newAlignFrame;
  
 -          if (protocol == AppletFormatAdapter.PASTE)
 +          if (protocol == DataSourceType.PASTE)
            {
              newAlignFrame.setTitle(MessageManager.formatMessage(
                      "label.sequences_from", new Object[] { applet
              }
              else
              {
 -              pdbs.addElement(new Object[] { pdb, seqs, chains,
 -                  new String(protocol) });
 +              pdbs.addElement(new Object[] { pdb, seqs, chains, protocol });
              }
            }
          }
@@@ -33,7 -33,6 +33,7 @@@ import jalview.datamodel.SequenceCollec
  import jalview.datamodel.SequenceFeature;
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
 +import jalview.io.DataSourceType;
  import jalview.io.FeaturesFile;
  import jalview.util.MessageManager;
  
@@@ -170,157 -169,136 +170,136 @@@ public class AlignViewController implem
      // JBPNote this routine could also mark rows, not just columns.
      // need a decent query structure to allow all types of feature searches
      BitSet bs = new BitSet();
-     int alw, alStart;
-     SequenceCollectionI sqcol = (viewport.getSelectionGroup() == null ? viewport
-             .getAlignment() : viewport.getSelectionGroup());
-     alStart = sqcol.getStartRes();
-     alw = sqcol.getEndRes() + 1;
+     SequenceCollectionI sqcol = (viewport.getSelectionGroup() == null || extendCurrent) ? viewport
+             .getAlignment() : viewport.getSelectionGroup();
+     int nseq = findColumnsWithFeature(featureType, sqcol, bs);
+     ColumnSelection cs = viewport.getColumnSelection();
+     if (cs == null)
+     {
+       cs = new ColumnSelection();
+     }
+     if (bs.cardinality() > 0 || invert)
+     {
+       boolean changed = cs.markColumns(bs, sqcol.getStartRes(),
+               sqcol.getEndRes(), invert, extendCurrent, toggle);
+       if (changed)
+       {
+         viewport.setColumnSelection(cs);
+         alignPanel.paintAlignment(true);
+         int columnCount = invert ? (sqcol.getEndRes() - sqcol.getStartRes() + 1)
+                 - bs.cardinality()
+                 : bs.cardinality();
+         avcg.setStatus(MessageManager.formatMessage(
+                 "label.view_controller_toggled_marked",
+                 new String[] {
+                     toggle ? MessageManager.getString("label.toggled")
+                             : MessageManager.getString("label.marked"),
+                     String.valueOf(columnCount),
+                     invert ? MessageManager
+                             .getString("label.not_containing")
+                             : MessageManager.getString("label.containing"),
+                     featureType, Integer.valueOf(nseq).toString() }));
+         return true;
+       }
+     }
+     else
+     {
+       avcg.setStatus(MessageManager.formatMessage(
+               "label.no_feature_of_type_found",
+               new String[] { featureType }));
+       if (!extendCurrent)
+       {
+         cs.clear();
+         alignPanel.paintAlignment(true);
+       }
+     }
+     return false;
+   }
+   /**
+    * Sets a bit in the BitSet for each column (base 0) in the sequence
+    * collection which includes the specified feature type. Returns the number of
+    * sequences which have the feature in the selected range.
+    * 
+    * @param featureType
+    * @param sqcol
+    * @param bs
+    * @return
+    */
+   static int findColumnsWithFeature(String featureType,
+           SequenceCollectionI sqcol, BitSet bs)
+   {
+     final int startPosition = sqcol.getStartRes() + 1; // converted to base 1
+     final int endPosition = sqcol.getEndRes() + 1;
      List<SequenceI> seqs = sqcol.getSequences();
      int nseq = 0;
      for (SequenceI sq : seqs)
      {
-       int tfeat = 0;
+       boolean sequenceHasFeature = false;
        if (sq != null)
        {
-         SequenceFeature[] sf = sq.getSequenceFeatures();
-         if (sf != null)
+         SequenceFeature[] sfs = sq.getSequenceFeatures();
+         if (sfs != null)
          {
+           /*
+            * check whether the feature start/end (base 1) 
+            * overlaps the selection start/end
+            */
            int ist = sq.findIndex(sq.getStart());
            int iend = sq.findIndex(sq.getEnd());
-           if (iend < alStart || ist > alw)
+           if (iend < startPosition || ist > endPosition)
            {
              // sequence not in region
              continue;
            }
-           for (SequenceFeature sfpos : sf)
+           for (SequenceFeature sf : sfs)
            {
-             // future functionalty - featureType == null means mark columns
+             // future functionality - featureType == null means mark columns
              // containing all displayed features
-             if (sfpos != null && (featureType.equals(sfpos.getType())))
+             if (sf != null && (featureType.equals(sf.getType())))
              {
-               tfeat++;
                // optimisation - could consider 'spos,apos' like cursor argument
                // - findIndex wastes time by starting from first character and
                // counting
  
-               int i = sq.findIndex(sfpos.getBegin());
-               int j = sq.findIndex(sfpos.getEnd());
-               if (j < alStart || i > alw)
+               int i = sq.findIndex(sf.getBegin());
+               int j = sq.findIndex(sf.getEnd());
+               if (j < startPosition || i > endPosition)
                {
                  // feature is outside selected region
                  continue;
                }
-               if (i < alStart)
+               sequenceHasFeature = true;
+               if (i < startPosition)
                {
-                 i = alStart;
+                 i = startPosition;
                }
                if (i < ist)
                {
                  i = ist;
                }
-               if (j > alw)
+               if (j > endPosition)
                {
-                 j = alw;
+                 j = endPosition;
                }
                for (; i <= j; i++)
                {
-                 bs.set(i - 1);
+                 bs.set(i - 1); // convert to base 0
                }
              }
            }
          }
  
-         if (tfeat > 0)
+         if (sequenceHasFeature)
          {
            nseq++;
          }
        }
      }
-     ColumnSelection cs = viewport.getColumnSelection();
-     if (bs.cardinality() > 0 || invert)
-     {
-       boolean changed = false;
-       if (cs == null)
-       {
-         cs = new ColumnSelection();
-       }
-       else
-       {
-         if (!extendCurrent)
-         {
-           changed = !cs.isEmpty();
-           cs.clear();
-         }
-       }
-       if (invert)
-       {
-         // invert only in the currently selected sequence region
-         for (int i = bs.nextClearBit(alStart), ibs = bs.nextSetBit(alStart); i >= alStart
-                 && i < (alw);)
-         {
-           if (ibs < 0 || i < ibs)
-           {
-             changed = true;
-             if (toggle && cs.contains(i))
-             {
-               cs.removeElement(i++);
-             }
-             else
-             {
-               cs.addElement(i++);
-             }
-           }
-           else
-           {
-             i = bs.nextClearBit(ibs);
-             ibs = bs.nextSetBit(i);
-           }
-         }
-       }
-       else
-       {
-         for (int i = bs.nextSetBit(alStart); i >= alStart; i = bs
-                 .nextSetBit(i + 1))
-         {
-           changed = true;
-           if (toggle && cs.contains(i))
-           {
-             cs.removeElement(i);
-           }
-           else
-           {
-             cs.addElement(i);
-           }
-         }
-       }
-       if (changed)
-       {
-         viewport.setColumnSelection(cs);
-         alignPanel.paintAlignment(true);
-         avcg.setStatus(MessageManager.formatMessage(
-                 "label.view_controller_toggled_marked",
-                 new String[] {
-                     (toggle ? MessageManager.getString("label.toggled")
-                             : MessageManager.getString("label.marked")),
-                     (invert ? (Integer.valueOf((alw - alStart)
-                             - bs.cardinality()).toString()) : (Integer
-                             .valueOf(bs.cardinality()).toString())),
-                     featureType, Integer.valueOf(nseq).toString() }));
-         return true;
-       }
-     }
-     else
-     {
-       avcg.setStatus(MessageManager.formatMessage(
-               "label.no_feature_of_type_found",
-               new String[] { featureType }));
-       if (!extendCurrent && cs != null)
-       {
-         cs.clear();
-         alignPanel.paintAlignment(true);
-       }
-     }
-     return false;
+     return nseq;
    }
  
    @Override
    }
  
    @Override
 -  public boolean parseFeaturesFile(String file, String protocol,
 +  public boolean parseFeaturesFile(String file, DataSourceType protocol,
            boolean relaxedIdMatching)
    {
      boolean featuresFile = false;
@@@ -30,7 -30,6 +30,6 @@@ import java.util.Collections
  import java.util.Enumeration;
  import java.util.HashSet;
  import java.util.Hashtable;
- import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
@@@ -45,7 -44,7 +44,7 @@@ import java.util.Vector
   */
  public class Alignment implements AlignmentI
  {
-   protected Alignment dataset;
+   private Alignment dataset;
  
    protected List<SequenceI> sequences;
  
      /*
       * Share the same dataset sequence mappings (if any). 
       */
-     this.setCodonFrames(al.getCodonFrames());
+     if (dataset == null && al.getDataset() == null)
+     {
+       this.setCodonFrames(al.getCodonFrames());
+     }
    }
  
    /**
    }
  
    @Override
-   public void setDataset(Alignment data)
+   public void setDataset(AlignmentI data)
    {
      if (dataset == null && data == null)
      {
      }
      else if (dataset == null && data != null)
      {
-       dataset = data;
+       if (!(data instanceof Alignment))
+       {
+         throw new Error(
+                 "Implementation Error: jalview.datamodel.Alignment does not yet support other implementations of AlignmentI as its dataset reference");
+       }
+       dataset = (Alignment) data;
        for (int i = 0; i < getHeight(); i++)
        {
          SequenceI currentSeq = getSequenceAt(i);
      }
    }
  
-   /**
-    * adds a set of mappings (while ignoring any duplicates)
-    */
-   @Override
-   public void addCodonFrames(Iterable<AlignedCodonFrame> codons)
-   {
-     if (codons != null)
-     {
-       Iterator<AlignedCodonFrame> it = codons.iterator();
-       while (it.hasNext())
-       {
-         addCodonFrame(it.next());
-       }
-     }
-   }
    /*
     * (non-Javadoc)
     * 
    @Override
    public List<AlignedCodonFrame> getCodonFrames()
    {
+     // TODO: Fix this method to fix failing AlignedCodonFrame tests
+     // this behaviour is currently incorrect. method should return codon frames
+     // for just the alignment,
+     // selected from dataset
      return dataset != null ? dataset.getCodonFrames() : codonFrameList;
    }
  
    @Override
    public void append(AlignmentI toappend)
    {
-     if (toappend == this)
-     {
-       System.err.println("Self append may cause a deadlock.");
-     }
-     // TODO test this method for a future 2.5 release
+     // TODO JAL-1270 needs test coverage
      // currently tested for use in jalview.gui.SequenceFetcher
      boolean samegap = toappend.getGapCharacter() == getGapCharacter();
      char oldc = toappend.getGapCharacter();
              .getFullAlignment().getSequences() : toappend.getSequences();
      if (sqs != null)
      {
+       // avoid self append deadlock by
+       List<SequenceI> toappendsq = new ArrayList<SequenceI>();
        synchronized (sqs)
        {
          for (SequenceI addedsq : sqs)
                }
              }
            }
-           addSequence(addedsq);
+           toappendsq.add(addedsq);
          }
        }
+       for (SequenceI addedsq : toappendsq)
+       {
+         addSequence(addedsq);
+       }
      }
      AlignmentAnnotation[] alan = toappend.getAlignmentAnnotation();
      for (int a = 0; alan != null && a < alan.length; a++)
        addAnnotation(alan[a]);
      }
  
+     // use add method
      getCodonFrames().addAll(toappend.getCodonFrames());
  
      List<SequenceGroup> sg = toappend.getGroups();
     * Parameters control whether gaps in exon (mapped) and intron (unmapped)
     * regions are preserved. Gaps that connect introns to exons are treated
     * conservatively, i.e. only preserved if both intron and exon gaps are
-    * preserved.
+    * preserved. TODO: check caveats below where the implementation fails
     * 
     * @param al
+    *          - must have same dataset, and sequences in al must have equivalent
+    *          dataset sequence and start/end bounds under given mapping
     * @param preserveMappedGaps
     *          if true, gaps within and between mapped codons are preserved
     * @param preserveUnmappedGaps
            boolean preserveUnmappedGaps)
    {
      // TODO should this method signature be the one in the interface?
+     // JBPComment - yes - neither flag is used, so should be deleted.
      boolean thisIsNucleotide = this.isNucleotide();
      boolean thatIsProtein = !al.isNucleotide();
      if (!thatIsProtein && !thisIsNucleotide)
      {
        return AlignmentUtils.alignProteinAsDna(this, al);
      }
+     else if (thatIsProtein && thisIsNucleotide)
+     {
+       return AlignmentUtils.alignCdsAsProtein(this, al);
+     }
      return AlignmentUtils.alignAs(this, al);
    }
  
    @Override
    public String toString()
    {
--    return new FastaFile().print(getSequencesArray());
++    return new FastaFile().print(getSequencesArray(), true);
    }
  
    /**
@@@ -34,10 -34,10 +34,30 @@@ public class PDBEntr
  
    public enum Type
    {
--    PDB, MMCIF, FILE
++    // TODO is FILE needed; if not is this needed or can we
++    // use FileFormatI for PDB, MMCIF?
++    PDB("pdb", "xml"), MMCIF("mmcif", "mmcif"), FILE("?", "?");
++    String ext;
++
++    String format;
++
++    private Type(String fmt, String ex)
++    {
++      format = fmt;
++      ext = ex;
++    }
++
++    public String getFormat()
++    {
++      return format;
++    }
++    public String getExtension()
++    {
++      return ext;
++    }
    }
  
 -  Hashtable properties;
 +  Hashtable<String, String> properties;
  
    /*
     * (non-Javadoc)
      this.properties = property;
    }
  
 -  public Hashtable getProperty()
 +  public Hashtable<String, String> getProperty()
    {
      return properties;
    }
@@@ -1,5 -1,5 +1,6 @@@
  package jalview.ext.ensembl;
  
++import jalview.io.DataSourceType;
  import jalview.io.FileParse;
  import jalview.util.StringUtils;
  
@@@ -35,9 -35,11 +36,11 @@@ abstract class EnsemblRestClient extend
     * changes to Ensembl REST API
     * @see https://github.com/Ensembl/ensembl-rest/wiki/Change-log
     */
-   private static final String LATEST_ENSEMBLGENOMES_REST_VERSION = "4.4";
+   private static final String LATEST_ENSEMBLGENOMES_REST_VERSION = "4.6";
  
-   private static final String LATEST_ENSEMBL_REST_VERSION = "4.5";
+   private static final String LATEST_ENSEMBL_REST_VERSION = "4.6";
+   private static final String REST_CHANGE_LOG = "https://github.com/Ensembl/ensembl-rest/wiki/Change-log";
  
    private static Map<String, EnsemblInfo> domainData;
  
    protected abstract String getResponseMimeType();
  
    /**
-    * Tries to connect to Ensembl's REST 'ping' endpoint, and returns true if
-    * successful, else false
+    * Checks Ensembl's REST 'ping' endpoint, and returns true if response
+    * indicates available, else false
     * 
+    * @see http://rest.ensembl.org/documentation/info/ping
     * @return
     */
    private boolean checkEnsembl()
    {
+     HttpURLConnection conn = null;
      try
      {
        // note this format works for both ensembl and ensemblgenomes
        // info/ping.json works for ensembl only (March 2016)
        URL ping = new URL(getDomain()
                + "/info/ping?content-type=application/json");
-       HttpURLConnection conn = (HttpURLConnection) ping.openConnection();
-       int rc = conn.getResponseCode();
-       conn.disconnect();
-       if (rc >= 200 && rc < 300)
-       {
-         return true;
-       }
+       /*
+        * expect {"ping":1} if ok
+        */
+       BufferedReader br = getHttpResponse(ping, null);
+       JSONParser jp = new JSONParser();
+       JSONObject val = (JSONObject) jp.parse(br);
+       String pingString = val.get("ping").toString();
+       return pingString != null;
      } catch (Throwable t)
      {
        System.err.println("Error connecting to " + PING_URL + ": "
                + t.getMessage());
+     } finally
+     {
+       if (conn != null)
+       {
+         conn.disconnect();
+       }
      }
      return false;
    }
      URL url = getUrl(ids);
    
      BufferedReader reader = getHttpResponse(url, ids);
--    FileParse fp = new FileParse(reader, url.toString(), "HTTP_POST");
++    FileParse fp = new FileParse(reader, url.toString(), DataSourceType.URL);
      return fp;
    }
  
        if (laterVersion)
        {
          System.err.println(String.format(
-                 "Expected %s REST version %s but found %s", getDbSource(),
-                 expected,
-                 version));
+                 "Expected %s REST version %s but found %s, see %s",
+                 getDbSource(), expected, version, REST_CHANGE_LOG));
        }
        info.restVersion = version;
      } catch (Throwable t)
@@@ -22,10 -22,8 +22,9 @@@ package jalview.ext.jmol
  
  import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.Annotation;
- import jalview.datamodel.DBRefSource;
  import jalview.datamodel.SequenceI;
- import jalview.io.FileParse;
 +import jalview.io.DataSourceType;
+ import jalview.io.FileParse;
  import jalview.io.StructureFile;
  import jalview.schemes.ResidueProperties;
  import jalview.structure.StructureImportSettings;
@@@ -33,6 -31,7 +32,7 @@@ import jalview.util.MessageManager
  
  import java.io.IOException;
  import java.util.ArrayList;
+ import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Vector;
@@@ -61,10 -60,10 +61,10 @@@ public class JmolParser extends Structu
    Viewer viewer = null;
  
    public JmolParser(boolean addAlignmentAnnotations, boolean predictSecStr,
 -          boolean externalSecStr, String inFile, String type)
 +          boolean externalSecStr, String inFile, DataSourceType sourceType)
            throws IOException
    {
 -    super(inFile, type);
 +    super(inFile, sourceType);
    }
  
    public JmolParser(boolean addAlignmentAnnotations, boolean predictSecStr,
    @Override
    public void parse() throws IOException
    {
-     String dataName = getDataName();
-     if (dataName.endsWith(".cif"))
-     {
-       setDbRefType(DBRefSource.MMCIF);
-     }
-     else
-     {
-       setDbRefType(DBRefSource.PDB);
-     }
      setChains(new Vector<PDBChain>());
      Viewer jmolModel = getJmolData();
      jmolModel.openReader(getDataName(), getDataName(), getReader());
  
        if (getId() == null)
        {
-         setId(inFile.getName());
+         setId(safeName(getDataName()));
        }
        for (PDBChain chain : getChains())
        {
            prot.add(chainseq);
          }
  
-         if (StructureImportSettings.isPredictSecondaryStructure())
+         if (StructureImportSettings.isProcessSecondaryStructure())
          {
            createAnnotation(chainseq, chain, ms.at);
          }
    private List<Atom> convertSignificantAtoms(ModelSet ms)
    {
      List<Atom> significantAtoms = new ArrayList<Atom>();
+     HashMap<String, org.jmol.modelset.Atom> chainTerMap = new HashMap<String, org.jmol.modelset.Atom>();
+     org.jmol.modelset.Atom prevAtom = null;
      for (org.jmol.modelset.Atom atom : ms.at)
      {
        if (atom.getAtomName().equalsIgnoreCase("CA")
                || atom.getAtomName().equalsIgnoreCase("P"))
        {
+         if (!atomValidated(atom, prevAtom, chainTerMap))
+         {
+           continue;
+         }
          Atom curAtom = new Atom(atom.x, atom.y, atom.z);
          curAtom.atomIndex = atom.getIndex();
          curAtom.chain = atom.getChainIDStr();
-         curAtom.insCode = atom.group.getInsertionCode();
+         curAtom.insCode = atom.group.getInsertionCode() == '\000' ? ' '
+                 : atom.group.getInsertionCode();
          curAtom.name = atom.getAtomName();
          curAtom.number = atom.getAtomNumber();
          curAtom.resName = atom.getGroup3(true);
          curAtom.resNumber = atom.getResno();
          curAtom.occupancy = ms.occupancies != null ? ms.occupancies[atom
                  .getIndex()] : Float.valueOf(atom.getOccupancy100());
-         curAtom.resNumIns = "" + curAtom.resNumber + curAtom.insCode;
+         curAtom.resNumIns = ("" + curAtom.resNumber + curAtom.insCode)
+                 .trim();
          curAtom.tfactor = atom.getBfactor100() / 100f;
          curAtom.type = 0;
-         significantAtoms.add(curAtom);
+         // significantAtoms.add(curAtom);
+         // ignore atoms from subsequent models
+         if (!significantAtoms.contains(curAtom))
+         {
+           significantAtoms.add(curAtom);
+         }
+         prevAtom = atom;
        }
      }
      return significantAtoms;
    }
  
+   private boolean atomValidated(org.jmol.modelset.Atom curAtom,
+           org.jmol.modelset.Atom prevAtom,
+           HashMap<String, org.jmol.modelset.Atom> chainTerMap)
+   {
+     // System.out.println("Atom: " + curAtom.getAtomNumber()
+     // + "   Last atom index " + curAtom.group.lastAtomIndex);
+     if (chainTerMap == null || prevAtom == null)
+     {
+       return true;
+     }
+     String curAtomChId = curAtom.getChainIDStr();
+     String prevAtomChId = prevAtom.getChainIDStr();
+     // new chain encoutered
+     if (!prevAtomChId.equals(curAtomChId))
+     {
+       // On chain switch add previous chain termination to xTerMap if not exists
+       if (!chainTerMap.containsKey(prevAtomChId))
+       {
+         chainTerMap.put(prevAtomChId, prevAtom);
+       }
+       // if current atom belongs to an already terminated chain and the resNum
+       // diff < 5 then mark as valid and update termination Atom
+       if (chainTerMap.containsKey(curAtomChId))
+       {
+         if ((curAtom.getResno() - chainTerMap.get(curAtomChId).getResno()) < 5)
+         {
+           chainTerMap.put(curAtomChId, curAtom);
+           return true;
+         }
+         return false;
+       }
+     }
+     // atom with previously terminated chain encountered
+     else if (chainTerMap.containsKey(curAtomChId))
+     {
+       if ((curAtom.getResno() - chainTerMap.get(curAtomChId).getResno()) < 5)
+       {
+         chainTerMap.put(curAtomChId, curAtom);
+         return true;
+       }
+       return false;
+     }
+     // HETATM with resNum jump > 2
+     return !(curAtom.isHetero() && ((curAtom.getResno() - prevAtom
+             .getResno()) > 2));
+   }
    private void createAnnotation(SequenceI sequence, PDBChain chain,
            org.jmol.modelset.Atom[] jmolAtoms)
    {
     * Not implemented - returns null
     */
    @Override
--  public String print()
++  public String print(SequenceI[] seqs, boolean jvSuffix)
    {
      return null;
    }
@@@ -65,14 -65,10 +65,14 @@@ import jalview.gui.ViewSelectionMenu.Vi
  import jalview.io.AlignmentProperties;
  import jalview.io.AnnotationFile;
  import jalview.io.BioJsHTMLOutput;
 +import jalview.io.DataSourceType;
 +import jalview.io.FileFormat;
 +import jalview.io.FileFormatI;
  import jalview.io.FileLoader;
  import jalview.io.FormatAdapter;
  import jalview.io.HtmlSvgOutput;
  import jalview.io.IdentifyFile;
 +import jalview.io.JPredFile;
  import jalview.io.JalviewFileChooser;
  import jalview.io.JalviewFileView;
  import jalview.io.JnetAnnotationMaker;
@@@ -183,7 -179,7 +183,7 @@@ public class AlignFrame extends GAlignF
    /**
     * Last format used to load or save alignments in this window
     */
 -  String currentFileFormat = null;
 +  FileFormatI currentFileFormat = null;
  
    /**
     * Current filename for this alignment
     * @param format
     *          format of file
     */
 -  public void setFileName(String file, String format)
 +  public void setFileName(String file, FileFormatI format)
    {
      fileName = file;
      setFileFormat(format);
      showGroupConservation.setEnabled(!nucleotide);
      rnahelicesColour.setEnabled(nucleotide);
      purinePyrimidineColour.setEnabled(nucleotide);
-     showComplementMenuItem.setText(MessageManager
-             .getString(nucleotide ? "label.protein" : "label.nucleotide"));
+     showComplementMenuItem.setText(nucleotide ? MessageManager
+             .getString("label.protein") : MessageManager
+             .getString("label.nucleotide"));
      setColourSelected(jalview.bin.Cache.getDefault(
              nucleotide ? Preferences.DEFAULT_COLOUR_NUC
                      : Preferences.DEFAULT_COLOUR_PROT, "None"));
        // originating file's format
        // TODO: work out how to recover feature settings for correct view(s) when
        // file is reloaded.
 -      if (currentFileFormat.equals("Jalview"))
 +      if (currentFileFormat == FileFormat.Jalview)
        {
          JInternalFrame[] frames = Desktop.desktop.getAllFrames();
          for (int i = 0; i < frames.length; i++)
          Desktop.instance.closeAssociatedWindows();
  
          FileLoader loader = new FileLoader();
 -        String protocol = fileName.startsWith("http:") ? "URL" : "File";
 +        DataSourceType protocol = fileName.startsWith("http:") ? DataSourceType.URL
 +                : DataSourceType.FILE;
          loader.LoadFile(viewport, fileName, protocol, currentFileFormat);
        }
        else
          Rectangle bounds = this.getBounds();
  
          FileLoader loader = new FileLoader();
 -        String protocol = fileName.startsWith("http:") ? "URL" : "File";
 +        DataSourceType protocol = fileName.startsWith("http:") ? DataSourceType.URL
 +                : DataSourceType.FILE;
          AlignFrame newframe = loader.LoadFileWaitTillLoaded(fileName,
                  protocol, currentFileFormat);
  
    @Override
    public void save_actionPerformed(ActionEvent e)
    {
 -    if (fileName == null
 -            || (currentFileFormat == null || !jalview.io.FormatAdapter
 -                    .isValidIOFormat(currentFileFormat, true))
 +    if (fileName == null || (currentFileFormat == null)
              || fileName.startsWith("http"))
      {
        saveAs_actionPerformed(null);
    @Override
    public void saveAs_actionPerformed(ActionEvent e)
    {
--    JalviewFileChooser chooser = new JalviewFileChooser(
 -            jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
 -            jalview.io.AppletFormatAdapter.WRITABLE_EXTENSIONS,
 -            jalview.io.AppletFormatAdapter.WRITABLE_FNAMES,
 -            currentFileFormat, false);
++    JalviewFileChooser chooser = JalviewFileChooser.forWrite(
 +            Cache.getProperty("LAST_DIRECTORY"),
 +            // AppletFormatAdapter.WRITABLE_EXTENSIONS,
 +            // AppletFormatAdapter.WRITABLE_FNAMES,
-             currentFileFormat, false);
++            currentFileFormat.toString(), false);
  
      chooser.setFileView(new JalviewFileView());
      chooser.setDialogTitle(MessageManager
  
        fileName = chooser.getSelectedFile().getPath();
  
 -      jalview.bin.Cache.setProperty("DEFAULT_FILE_FORMAT",
 -              currentFileFormat);
 +      Cache.setProperty("DEFAULT_FILE_FORMAT",
 +              currentFileFormat.toString());
  
 -      jalview.bin.Cache.setProperty("LAST_DIRECTORY", fileName);
 -      if (currentFileFormat.indexOf(" ") > -1)
 -      {
 -        currentFileFormat = currentFileFormat.substring(0,
 -                currentFileFormat.indexOf(" "));
 -      }
 +      Cache.setProperty("LAST_DIRECTORY", fileName);
        saveAlignment(fileName, currentFileFormat);
      }
    }
  
 -  public boolean saveAlignment(String file, String format)
 +  public boolean saveAlignment(String file, FileFormatI format)
    {
      boolean success = true;
  
 -    if (format.equalsIgnoreCase("Jalview"))
 +    if (format == FileFormat.Jalview)
      {
        String shortName = title;
  
      }
      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;
 -      }
 +      // 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);
    protected void outputText_actionPerformed(ActionEvent e)
    {
  
 +    FileFormat fileFormat = FileFormat.valueOf(e.getActionCommand());
      AlignmentExportData exportData = getAlignmentForExport(
 -            e.getActionCommand(), viewport, null);
 +fileFormat,
 +            viewport, null);
      if (exportData.getSettings().isCancelled())
      {
        return;
      cap.setForInput(null);
      try
      {
 +      FileFormatI format = fileFormat;
        cap.setText(new FormatAdapter(alignPanel, exportData.getSettings())
 -              .formatSequences(e.getActionCommand(),
 +              .formatSequences(format,
                        exportData.getAlignment(),
                        exportData.getOmitHidden(),
                        exportData.getStartEndPostions(),
    }
  
    public static AlignmentExportData getAlignmentForExport(
 -          String exportFormat, AlignViewportI viewport,
 +          FileFormatI format, AlignViewportI viewport,
            AlignExportSettingI exportSettings)
    {
      AlignmentI alignmentToExport = null;
      if (settings == null)
      {
        settings = new AlignExportSettings(hasHiddenSeqs,
 -              viewport.hasHiddenColumns(), exportFormat);
 +              viewport.hasHiddenColumns(), format);
      }
      // settings.isExportAnnotations();
  
        omitHidden = viewport.getViewAsString(true);
      }
  
--    String output = new FormatAdapter().formatSequences("Fasta", seqs,
++    String output = new FormatAdapter().formatSequences(FileFormat.Fasta,
++            seqs,
              omitHidden, null);
  
      StringSelection ss = new StringSelection(output);
          return;
        }
  
 -      String str, format;
 +      String str;
 +      FileFormatI format;
        try
        {
          str = (String) contents.getTransferData(DataFlavor.stringFlavor);
            return;
          }
  
 -        format = new IdentifyFile().identify(str, "Paste");
 +        format = new IdentifyFile().identify(str, DataSourceType.PASTE);
  
        } catch (OutOfMemoryError er)
        {
        else
        {
          // parse the clipboard as an alignment.
 -        alignment = new FormatAdapter().readFile(str, "Paste", format);
 +        alignment = new FormatAdapter().readFile(str, DataSourceType.PASTE,
 +                format);
          sequences = alignment.getSequencesArray();
        }
  
      sg.setEndRes(viewport.getAlignment().getWidth() - 1);
      viewport.setSelectionGroup(sg);
      viewport.sendSelection();
-     alignPanel.paintAlignment(true);
+     // JAL-2034 - should delegate to
+     // alignPanel to decide if overview needs
+     // updating.
+     alignPanel.paintAlignment(false);
      PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
    }
  
      viewport.setSelectionGroup(null);
      alignPanel.getSeqPanel().seqCanvas.highlightSearchResults(null);
      alignPanel.getIdPanel().getIdCanvas().searchResults = null;
-     alignPanel.paintAlignment(true);
+     // JAL-2034 - should delegate to
+     // alignPanel to decide if overview needs
+     // updating.
+     alignPanel.paintAlignment(false);
      PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
      viewport.sendSelection();
    }
      {
        sg.addOrRemove(viewport.getAlignment().getSequenceAt(i), false);
      }
+     // JAL-2034 - should delegate to
+     // alignPanel to decide if overview needs
+     // updating.
  
      alignPanel.paintAlignment(true);
      PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
    @Override
    public void expandViews_actionPerformed(ActionEvent e)
    {
-     Desktop.instance.explodeViews(this);
+     Desktop.explodeViews(this);
    }
  
    /**
    }
  
    /**
-    * Set or clear 'Show Sequence Features'
-    * 
-    * @param evt
-    *          DOCUMENT ME!
-    */
-   @Override
-   public void showSeqFeaturesHeight_actionPerformed(ActionEvent evt)
-   {
-     viewport.setShowSequenceFeaturesHeight(showSeqFeaturesHeight
-             .isSelected());
-     if (viewport.isShowSequenceFeaturesHeight())
-     {
-       // ensure we're actually displaying features
-       viewport.setShowSequenceFeatures(true);
-       showSeqFeatures.setSelected(true);
-     }
-     alignPanel.paintAlignment(true);
-     if (alignPanel.getOverviewPanel() != null)
-     {
-       alignPanel.getOverviewPanel().updateOverviewImage();
-     }
-   }
-   /**
     * Action on toggle of the 'Show annotations' menu item. This shows or hides
     * the annotations panel as a whole.
     * 
        jalview.io.NewickFile fin = null;
        try
        {
 -        fin = new jalview.io.NewickFile(choice, "File");
 +        fin = new NewickFile(choice, DataSourceType.FILE);
          viewport.setCurrentTree(ShowNewickTree(fin, choice).getTree());
        } catch (Exception ex)
        {
            // object broker mechanism.
            final Vector<JMenu> wsmenu = new Vector<JMenu>();
            final IProgressIndicator af = me;
+           /*
+            * do not i18n these strings - they are hard-coded in class
+            * compbio.data.msa.Category, Jws2Discoverer.isRecalculable() and
+            * SequenceAnnotationWSClient.initSequenceAnnotationWSClient()
+            */
            final JMenu msawsmenu = new JMenu("Alignment");
            final JMenu secstrmenu = new JMenu(
                    "Secondary Structure Prediction");
            final JMenu seqsrchmenu = new JMenu("Sequence Database Search");
            final JMenu analymenu = new JMenu("Analysis");
            final JMenu dismenu = new JMenu("Protein Disorder");
-           // final JMenu msawsmenu = new
-           // JMenu(MessageManager.getString("label.alignment"));
-           // final JMenu secstrmenu = new
-           // JMenu(MessageManager.getString("label.secondary_structure_prediction"));
-           // final JMenu seqsrchmenu = new
-           // JMenu(MessageManager.getString("label.sequence_database_search"));
-           // final JMenu analymenu = new
-           // JMenu(MessageManager.getString("label.analysis"));
-           // final JMenu dismenu = new
-           // JMenu(MessageManager.getString("label.protein_disorder"));
            // JAL-940 - only show secondary structure prediction services from
            // the legacy server
            if (// Cache.getDefault("SHOW_JWS1_SERVICES", true)
    }
  
    /**
-    * Searches selected sequences for xRef products and builds the Show
-    * Cross-References menu (formerly called Show Products)
+    * Searches the alignment sequences for xRefs and builds the Show
+    * Cross-References menu (formerly called Show Products), with database
+    * sources for which cross-references are found (protein sources for a
+    * nucleotide alignment and vice versa)
     * 
-    * @return true if Show Cross-references menu should be enabled.
+    * @return true if Show Cross-references menu should be enabled
     */
    public boolean canShowProducts()
    {
-     SequenceI[] selection = viewport.getSequenceSelection();
+     SequenceI[] seqs = viewport.getAlignment().getSequencesArray();
      AlignmentI dataset = viewport.getAlignment().getDataset();
      boolean showp = false;
      try
      {
        showProducts.removeAll();
        final boolean dna = viewport.getAlignment().isNucleotide();
-       String[] ptypes = (selection == null || selection.length == 0) ? null
-               : CrossRef.findSequenceXrefTypes(dna, selection, dataset);
+       List<String> ptypes = (seqs == null || seqs.length == 0) ? null
+               : new CrossRef(seqs, dataset)
+                       .findXrefSourcesForSequences(dna);
  
-       for (int t = 0; ptypes != null && t < ptypes.length; t++)
+       for (final String source : ptypes)
        {
          showp = true;
          final AlignFrame af = this;
-         final String source = ptypes[t];
-         JMenuItem xtype = new JMenuItem(ptypes[t]);
+         JMenuItem xtype = new JMenuItem(source);
          xtype.addActionListener(new ActionListener()
          {
            @Override
            public void actionPerformed(ActionEvent e)
            {
              showProductsFor(af.viewport.getSequenceSelection(), dna, source);
            }
          });
          showProducts.add(xtype);
        }
        showProducts.setEnabled(showp);
      } catch (Exception e)
      {
-       jalview.bin.Cache.log
+       Cache.log
                .warn("canShowProducts threw an exception - please report to help@jalview.org",
                        e);
        return false;
     * @param source
     *          the database to show cross-references for
     */
-   protected void showProductsFor(final SequenceI[] sel, final boolean dna,
-           final String source)
+   protected void showProductsFor(final SequenceI[] sel,
+           final boolean _odna, final String source)
    {
      Runnable foo = new Runnable()
      {
          {
            AlignmentI alignment = AlignFrame.this.getViewport()
                    .getAlignment();
-           AlignmentI xrefs = CrossRef.findXrefSequences(sel, dna, source,
-                   alignment);
-           if (xrefs != null)
+           AlignmentI dataset = alignment.getDataset() == null ? alignment
+                   : alignment.getDataset();
+           boolean dna = alignment.isNucleotide();
+           if (_odna != dna)
            {
-             /*
-              * get display scheme (if any) to apply to features
-              */
-             FeatureSettingsModelI featureColourScheme = new SequenceFetcher()
-                     .getFeatureColourScheme(source);
-             AlignmentI al = makeCrossReferencesAlignment(
-                     alignment.getDataset(), xrefs);
-             AlignFrame newFrame = new AlignFrame(al, DEFAULT_WIDTH,
-                     DEFAULT_HEIGHT);
-             if (Cache.getDefault("HIDE_INTRONS", true))
-             {
-               newFrame.hideFeatureColumns(SequenceOntologyI.EXON, false);
-             }
-             String newtitle = String.format("%s %s %s",
-                     MessageManager.getString(dna ? "label.proteins"
-                             : "label.nucleotides"), MessageManager
-                             .getString("label.for"), getTitle());
-             newFrame.setTitle(newtitle);
+             System.err
+                     .println("Conflict: showProducts for alignment originally "
+                             + "thought to be "
+                             + (_odna ? "DNA" : "Protein")
+                             + " now searching for "
+                             + (dna ? "DNA" : "Protein") + " Context.");
+           }
+           AlignmentI xrefs = new CrossRef(sel, dataset).findXrefSequences(
+                   source, dna);
+           if (xrefs == null)
+           {
+             return;
+           }
+           /*
+            * get display scheme (if any) to apply to features
+            */
+           FeatureSettingsModelI featureColourScheme = new SequenceFetcher()
+                   .getFeatureColourScheme(source);
+           AlignmentI xrefsAlignment = makeCrossReferencesAlignment(dataset,
+                   xrefs);
+           if (!dna)
+           {
+             xrefsAlignment = AlignmentUtils.makeCdsAlignment(
+                     xrefsAlignment.getSequencesArray(), dataset, sel);
+             xrefsAlignment.alignAs(alignment);
+           }
  
-             if (!Cache.getDefault(Preferences.ENABLE_SPLIT_FRAME, true))
-             {
-               /*
-                * split frame display is turned off in preferences file
-                */
-               Desktop.addInternalFrame(newFrame, newtitle, DEFAULT_WIDTH,
-                       DEFAULT_HEIGHT);
-               return; // via finally clause
-             }
+           /*
+            * If we are opening a splitframe, make a copy of this alignment (sharing the same dataset
+            * sequences). If we are DNA, drop introns and update mappings
+            */
+           AlignmentI copyAlignment = null;
  
-             /*
-              * Make a copy of this alignment (sharing the same dataset
-              * sequences). If we are DNA, drop introns and update mappings
-              */
-             AlignmentI copyAlignment = null;
-             final SequenceI[] sequenceSelection = AlignFrame.this.viewport
-                     .getSequenceSelection();
-             List<AlignedCodonFrame> cf = xrefs.getCodonFrames();
+           if (Cache.getDefault(Preferences.ENABLE_SPLIT_FRAME, true))
+           {
              boolean copyAlignmentIsAligned = false;
              if (dna)
              {
-               copyAlignment = AlignmentUtils.makeCdsAlignment(
-                       sequenceSelection, cf, alignment);
+               copyAlignment = AlignmentUtils.makeCdsAlignment(sel, dataset,
+                       xrefsAlignment.getSequencesArray());
                if (copyAlignment.getHeight() == 0)
                {
+                 JOptionPane.showMessageDialog(AlignFrame.this,
+                         MessageManager.getString("label.cant_map_cds"),
+                         MessageManager.getString("label.operation_failed"),
+                         JOptionPane.OK_OPTION);
                  System.err.println("Failed to make CDS alignment");
                }
-               al.getCodonFrames().clear();
-               al.addCodonFrames(copyAlignment.getCodonFrames());
-               al.addCodonFrames(cf);
  
                /*
                 * pending getting Embl transcripts to 'align', 
              }
              else
              {
-               copyAlignment = AlignmentUtils.makeCopyAlignment(
-                       sequenceSelection, xrefs.getSequencesArray());
-               copyAlignment.addCodonFrames(cf);
-               al.addCodonFrames(copyAlignment.getCodonFrames());
-               al.addCodonFrames(cf);
+               copyAlignment = AlignmentUtils.makeCopyAlignment(sel,
+                       xrefs.getSequencesArray(), dataset);
              }
              copyAlignment.setGapCharacter(AlignFrame.this.viewport
                      .getGapCharacter());
  
              StructureSelectionManager ssm = StructureSelectionManager
                      .getStructureSelectionManager(Desktop.instance);
-             ssm.registerMappings(cf);
+             /*
+              * register any new mappings for sequence mouseover etc
+              * (will not duplicate any previously registered mappings)
+              */
+             ssm.registerMappings(dataset.getCodonFrames());
  
              if (copyAlignment.getHeight() <= 0)
              {
               */
              if (dna && copyAlignmentIsAligned)
              {
-               al.alignAs(copyAlignment);
+               xrefsAlignment.alignAs(copyAlignment);
              }
              else
              {
                 * align cdna to protein - currently only if 
                 * fetching and aligning Ensembl transcripts!
                 */
-               if (DBRefSource.ENSEMBL.equalsIgnoreCase(source))
+               // TODO: generalise for other sources of locus/transcript/cds data
+               if (dna && DBRefSource.ENSEMBL.equalsIgnoreCase(source))
                {
-                 copyAlignment.alignAs(al);
+                 copyAlignment.alignAs(xrefsAlignment);
                }
              }
+           }
+           /*
+            * build AlignFrame(s) according to available alignment data
+            */
+           AlignFrame newFrame = new AlignFrame(xrefsAlignment,
+                   DEFAULT_WIDTH, DEFAULT_HEIGHT);
+           if (Cache.getDefault("HIDE_INTRONS", true))
+           {
+             newFrame.hideFeatureColumns(SequenceOntologyI.EXON, false);
+           }
+           String newtitle = String.format("%s %s %s",
+                   dna ? MessageManager.getString("label.proteins")
+                           : MessageManager.getString("label.nucleotides"),
+                   MessageManager.getString("label.for"), getTitle());
+           newFrame.setTitle(newtitle);
  
-             AlignFrame copyThis = new AlignFrame(copyAlignment,
-                     AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
-             copyThis.setTitle(AlignFrame.this.getTitle());
-             boolean showSequenceFeatures = viewport
-                     .isShowSequenceFeatures();
-             newFrame.setShowSeqFeatures(showSequenceFeatures);
-             copyThis.setShowSeqFeatures(showSequenceFeatures);
-             FeatureRenderer myFeatureStyling = alignPanel.getSeqPanel().seqCanvas
-                     .getFeatureRenderer();
-             /*
-              * copy feature rendering settings to split frame
-              */
-             newFrame.alignPanel.getSeqPanel().seqCanvas
-                     .getFeatureRenderer()
-                     .transferSettings(myFeatureStyling);
-             copyThis.alignPanel.getSeqPanel().seqCanvas
-                     .getFeatureRenderer()
-                     .transferSettings(myFeatureStyling);
+           if (copyAlignment == null)
+           {
              /*
-              * apply 'database source' feature configuration
-              * if any was found
+              * split frame display is turned off in preferences file
               */
-             // TODO is this the feature colouring for the original
-             // alignment or the fetched xrefs? either could be Ensembl
-             newFrame.getViewport().applyFeaturesStyle(featureColourScheme);
-             copyThis.getViewport().applyFeaturesStyle(featureColourScheme);
-             SplitFrame sf = new SplitFrame(dna ? copyThis : newFrame,
-                     dna ? newFrame : copyThis);
-             newFrame.setVisible(true);
-             copyThis.setVisible(true);
-             String linkedTitle = MessageManager
-                     .getString("label.linked_view_title");
-             Desktop.addInternalFrame(sf, linkedTitle, -1, -1);
-             sf.adjustDivider();
+             Desktop.addInternalFrame(newFrame, newtitle, DEFAULT_WIDTH,
+                     DEFAULT_HEIGHT);
+             return; // via finally clause
            }
-         } catch (Exception e)
-         {
-           Cache.log.error("Exception when finding crossreferences", e);
+           AlignFrame copyThis = new AlignFrame(copyAlignment,
+                   AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+           copyThis.setTitle(AlignFrame.this.getTitle());
+           boolean showSequenceFeatures = viewport.isShowSequenceFeatures();
+           newFrame.setShowSeqFeatures(showSequenceFeatures);
+           copyThis.setShowSeqFeatures(showSequenceFeatures);
+           FeatureRenderer myFeatureStyling = alignPanel.getSeqPanel().seqCanvas
+                   .getFeatureRenderer();
+           /*
+            * copy feature rendering settings to split frame
+            */
+           newFrame.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
+                   .transferSettings(myFeatureStyling);
+           copyThis.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
+                   .transferSettings(myFeatureStyling);
+           /*
+            * apply 'database source' feature configuration
+            * if any was found
+            */
+           // TODO is this the feature colouring for the original
+           // alignment or the fetched xrefs? either could be Ensembl
+           newFrame.getViewport().applyFeaturesStyle(featureColourScheme);
+           copyThis.getViewport().applyFeaturesStyle(featureColourScheme);
+           SplitFrame sf = new SplitFrame(dna ? copyThis : newFrame,
+                   dna ? newFrame : copyThis);
+           newFrame.setVisible(true);
+           copyThis.setVisible(true);
+           String linkedTitle = MessageManager
+                   .getString("label.linked_view_title");
+           Desktop.addInternalFrame(sf, linkedTitle, -1, -1);
+           sf.adjustDivider();
          } catch (OutOfMemoryError e)
          {
            new OOMWarning("whilst fetching crossreferences", e);
        }
  
        /**
-        * Makes an alignment containing the given sequences. If this is of the
-        * same type as the given dataset (nucleotide/protein), then the new
-        * alignment shares the same dataset, and its dataset sequences are added
-        * to it. Otherwise a new dataset sequence is created for the
-        * cross-references.
+        * Makes an alignment containing the given sequences, and adds them to the
+        * given dataset, which is also set as the dataset for the new alignment
+        * 
+        * TODO: refactor to DatasetI method
         * 
         * @param dataset
         * @param seqs
        protected AlignmentI makeCrossReferencesAlignment(AlignmentI dataset,
                AlignmentI seqs)
        {
-         boolean sameType = dataset.isNucleotide() == seqs.isNucleotide();
          SequenceI[] sprods = new SequenceI[seqs.getHeight()];
          for (int s = 0; s < sprods.length; s++)
          {
            sprods[s] = (seqs.getSequenceAt(s)).deriveSequence();
-           if (sameType)
+           if (dataset.getSequences() == null
+                   || !dataset.getSequences().contains(
+                           sprods[s].getDatasetSequence()))
            {
-             if (dataset.getSequences() == null
-                     || !dataset.getSequences().contains(
-                             sprods[s].getDatasetSequence()))
-             {
-               dataset.addSequence(sprods[s].getDatasetSequence());
-             }
+             dataset.addSequence(sprods[s].getDatasetSequence());
            }
            sprods[s].updatePDBIds();
          }
          Alignment al = new Alignment(sprods);
-         if (sameType)
-         {
-           al.setDataset((Alignment) dataset);
-         }
-         else
-         {
-           al.createDatasetAlignment();
-         }
+         al.setDataset(dataset);
          return al;
        }
  
    /**
     * Set the file format
     * 
 -   * @param fileFormat
 +   * @param format
     */
 -  public void setFileFormat(String fileFormat)
 +  public void setFileFormat(FileFormatI format)
    {
 -    this.currentFileFormat = fileFormat;
 +    this.currentFileFormat = format;
    }
  
    /**
     * 
     * @param file
     *          contents or path to retrieve file
 -   * @param type
 +   * @param sourceType
     *          access mode of file (see jalview.io.AlignFile)
     * @return true if features file was parsed correctly.
     */
 -  public boolean parseFeaturesFile(String file, String type)
 +  public boolean parseFeaturesFile(String file, DataSourceType sourceType)
    {
 -    return avc.parseFeaturesFile(file, type,
 -            jalview.bin.Cache.getDefault("RELAXEDSEQIDMATCHING", false));
 +    return avc.parseFeaturesFile(file, sourceType,
 +            Cache.getDefault("RELAXEDSEQIDMATCHING", false));
  
    }
  
    public void drop(DropTargetDropEvent evt)
    {
      Transferable t = evt.getTransferable();
 -    java.util.List<String> files = new ArrayList<String>(), protocols = new ArrayList<String>();
 +    List<String> files = new ArrayList<String>();
 +    List<DataSourceType> protocols = new ArrayList<DataSourceType>();
  
      try
      {
          {
            String file = files.get(i).toString();
            String pdbfn = "";
 -          String protocol = FormatAdapter.checkProtocol(file);
 -          if (protocol == jalview.io.FormatAdapter.FILE)
 +          DataSourceType protocol = FormatAdapter.checkProtocol(file);
 +          if (protocol == DataSourceType.FILE)
            {
              File fl = new File(file);
              pdbfn = fl.getName();
            }
 -          else if (protocol == jalview.io.FormatAdapter.URL)
 +          else if (protocol == DataSourceType.URL)
            {
              URL url = new URL(file);
              pdbfn = url.getFile();
              }
              if (mtch != null)
              {
 -              String type = null;
 +              FileFormatI type = null;
                try
                {
                  type = new IdentifyFile().identify(file, protocol);
                }
                if (type != null)
                {
 -                if (type.equalsIgnoreCase("PDB"))
 +                if (type == FileFormat.PDB)
                  {
                    filesmatched.add(new Object[] { file, protocol, mtch });
                    continue;
                {
                  PDBEntry pe = new AssociatePdbFileWithSeq()
                          .associatePdbWithSeq((String) fm[0],
 -                                (String) fm[1], toassoc, false,
 +                                (DataSourceType) fm[1], toassoc, false,
                                  Desktop.instance);
                  if (pe != null)
                  {
     * @param file
     *          either a filename or a URL string.
     */
 -  public void loadJalviewDataFile(String file, String protocol,
 -          String format, SequenceI assocSeq)
 +  public void loadJalviewDataFile(String file, DataSourceType sourceType,
 +          FileFormatI format, SequenceI assocSeq)
    {
      try
      {
 -      if (protocol == null)
 +      if (sourceType == null)
        {
 -        protocol = FormatAdapter.checkProtocol(file);
 +        sourceType = FormatAdapter.checkProtocol(file);
        }
        // if the file isn't identified, or not positively identified as some
        // other filetype (PFAM is default unidentified alignment file type) then
        // try to parse as annotation.
 -      boolean isAnnotation = (format == null || format
 -              .equalsIgnoreCase("PFAM")) ? new AnnotationFile()
 -              .annotateAlignmentView(viewport, file, protocol) : false;
 +      boolean isAnnotation = (format == null || format == FileFormat.Pfam) ? new AnnotationFile()
 +              .annotateAlignmentView(viewport, file, sourceType) : false;
  
        if (!isAnnotation)
        {
          TCoffeeScoreFile tcf = null;
          try
          {
 -          tcf = new TCoffeeScoreFile(file, protocol);
 +          tcf = new TCoffeeScoreFile(file, sourceType);
            if (tcf.isValid())
            {
              if (tcf.annotateAlignment(viewport.getAlignment(), true))
            // try to parse it as a features file
            if (format == null)
            {
 -            format = new IdentifyFile().identify(file, protocol);
 +            format = new IdentifyFile().identify(file, sourceType);
            }
 -          if (format.equalsIgnoreCase("JnetFile"))
 +          if (format == FileFormat.Jnet)
            {
 -            jalview.io.JPredFile predictions = new jalview.io.JPredFile(
 -                    file, protocol);
 +            JPredFile predictions = new JPredFile(
 +                    file, sourceType);
              new JnetAnnotationMaker();
              JnetAnnotationMaker.add_annotation(predictions,
                      viewport.getAlignment(), 0, false);
            }
            else if (IdentifyFile.FeaturesFile.equals(format))
            {
 -            if (parseFeaturesFile(file, protocol))
 +            if (parseFeaturesFile(file, sourceType))
              {
                alignPanel.paintAlignment(true);
              }
            }
            else
            {
 -            new FileLoader().LoadFile(viewport, file, protocol, format);
 +            new FileLoader().LoadFile(viewport, file, sourceType, format);
            }
          }
        }
        }
        new OOMWarning(
                "loading data "
 -                      + (protocol != null ? (protocol.equals(FormatAdapter.PASTE) ? "from clipboard."
 -                              : "using " + protocol + " from " + file)
 +                      + (sourceType != null ? (sourceType == DataSourceType.PASTE ? "from clipboard."
 +                              : "using " + sourceType + " from " + file)
                                : ".")
                        + (format != null ? "(parsing as '" + format
                                + "' file)" : ""), oom, Desktop.desktop);
@@@ -507,17 -507,6 +507,6 @@@ public class AlignViewport extends Alig
    /**
     * DOCUMENT ME!
     * 
-    * @return DOCUMENT ME!
-    */
-   @Override
-   public ColumnSelection getColumnSelection()
-   {
-     return colSel;
-   }
-   /**
-    * DOCUMENT ME!
-    * 
     * @param tree
     *          DOCUMENT ME!
     */
  
      // TODO if we want this (e.g. to enable reload of the alignment from file),
      // we will need to add parameters to the stack.
 -    // if (!protocol.equals(AppletFormatAdapter.PASTE))
 +    // if (!protocol.equals(DataSourceType.PASTE))
      // {
      // alignFrame.setFileName(file, format);
      // }
@@@ -27,7 -27,6 +27,7 @@@ import jalview.datamodel.Annotation
  import jalview.datamodel.Sequence;
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
 +import jalview.io.FileFormat;
  import jalview.io.FormatAdapter;
  import jalview.util.MessageManager;
  
@@@ -245,6 -244,7 +245,7 @@@ public class AnnotationLabels extends J
      else if (evt.getActionCommand().equals(DELETE))
      {
        ap.av.getAlignment().deleteAnnotation(aa[selectedRow]);
+       ap.av.getCalcManager().removeWorkerForAnnotation(aa[selectedRow]);
      }
      else if (evt.getActionCommand().equals(SHOWALL))
      {
        alignmentStartEnd = av.getAlignment().getVisibleStartAndEndIndex(
                hiddenCols);
      }
 -    String output = new FormatAdapter().formatSequences("Fasta", seqs,
 -            omitHidden, alignmentStartEnd);
 +    String output = new FormatAdapter().formatSequences(FileFormat.Fasta,
 +            seqs, omitHidden, alignmentStartEnd);
  
      Toolkit.getDefaultToolkit().getSystemClipboard()
              .setContents(new StringSelection(output), Desktop.instance);
@@@ -24,11 -24,8 +24,11 @@@ import jalview.api.AlignViewportI
  import jalview.api.AlignmentViewPanel;
  import jalview.bin.Cache;
  import jalview.bin.Jalview;
 +import jalview.io.DataSourceType;
 +import jalview.io.FileFormat;
 +import jalview.io.FileFormatException;
 +import jalview.io.FileFormatI;
  import jalview.io.FileLoader;
 -import jalview.io.FormatAdapter;
  import jalview.io.IdentifyFile;
  import jalview.io.JalviewFileChooser;
  import jalview.io.JalviewFileView;
@@@ -446,12 -443,11 +446,11 @@@ public class Desktop extends jalview.jb
        public void run()
        {
          Cache.log.debug("Filechooser init thread started.");
-         FileFormat fileFormat = FileFormat.valueOf(Cache
-                 .getProperty("DEFAULT_FILE_FORMAT"));
-         new JalviewFileChooser(Cache.getProperty("LAST_DIRECTORY"),
 -        new JalviewFileChooser(
 -                jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
 -                jalview.io.AppletFormatAdapter.READABLE_EXTENSIONS,
 -                jalview.io.AppletFormatAdapter.READABLE_FNAMES,
 -                jalview.bin.Cache.getProperty("DEFAULT_FILE_FORMAT"));
++        String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
++        JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
 +        // jalview.io.AppletFormatAdapter.READABLE_EXTENSIONS,
 +        // jalview.io.AppletFormatAdapter.READABLE_FNAMES,
-                 fileFormat);
++                fileFormat, true);
          Cache.log.debug("Filechooser init thread finished.");
        }
      }).start();
          String file = (String) contents
                  .getTransferData(DataFlavor.stringFlavor);
  
 -        String format = new IdentifyFile().identify(file,
 -                FormatAdapter.PASTE);
 +        FileFormatI format = new IdentifyFile().identify(file,
 +                DataSourceType.PASTE);
  
 -        new FileLoader().LoadFile(file, FormatAdapter.PASTE, format);
 +        new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
  
        }
      } catch (Exception ex)
    {
      boolean success = true;
      Transferable t = evt.getTransferable();
 -    java.util.List<String> files = new ArrayList<String>();
 -    java.util.List<String> protocols = new ArrayList<String>();
 +    List<String> files = new ArrayList<String>();
 +    List<DataSourceType> protocols = new ArrayList<DataSourceType>();
  
      try
      {
          for (int i = 0; i < files.size(); i++)
          {
            String file = files.get(i).toString();
 -          String protocol = (protocols == null) ? FormatAdapter.FILE
 -                  : (String) protocols.get(i);
 -          String format = null;
 +          DataSourceType protocol = (protocols == null) ? DataSourceType.FILE
 +                  : protocols.get(i);
 +          FileFormatI format = null;
  
            if (file.endsWith(".jar"))
            {
 -            format = "Jalview";
 +            format = FileFormat.Jalview;
  
            }
            else
    @Override
    public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
    {
-     FileFormat fileFormat = FileFormat.valueOf(Cache
-             .getProperty("DEFAULT_FILE_FORMAT"));
--    JalviewFileChooser chooser = new JalviewFileChooser(
 -            jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
 -            jalview.io.AppletFormatAdapter.READABLE_EXTENSIONS,
 -            jalview.io.AppletFormatAdapter.READABLE_FNAMES,
 -            jalview.bin.Cache.getProperty("DEFAULT_FILE_FORMAT"));
++    String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
++    JalviewFileChooser chooser = JalviewFileChooser.forRead(
 +            Cache.getProperty("LAST_DIRECTORY"),
 +            // AppletFormatAdapter.READABLE_EXTENSIONS,
 +            // AppletFormatAdapter.READABLE_FNAMES,
-             fileFormat);
++            fileFormat, true);
  
      chooser.setFileView(new JalviewFileView());
      chooser.setDialogTitle(MessageManager
      if (value == JalviewFileChooser.APPROVE_OPTION)
      {
        String choice = chooser.getSelectedFile().getPath();
 -      jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
 +      Cache.setProperty("LAST_DIRECTORY", chooser
                .getSelectedFile().getParent());
  
 -      String format = null;
 +      FileFormatI format = null;
        if (chooser.getSelectedFormat() != null
 -              && chooser.getSelectedFormat().equals("Jalview"))
 +              && chooser.getSelectedFormat() == FileFormat.Jalview)
        {
 -        format = "Jalview";
 +        format = FileFormat.Jalview;
        }
        else
        {
 -        format = new IdentifyFile().identify(choice, FormatAdapter.FILE);
 +        try
 +        {
 +          format = new IdentifyFile().identify(choice, DataSourceType.FILE);
 +        } catch (FileFormatException e)
 +        {
 +          // format is null
 +        }
        }
  
        if (viewport != null)
        {
 -        new FileLoader().LoadFile(viewport, choice, FormatAdapter.FILE,
 +        new FileLoader().LoadFile(viewport, choice, DataSourceType.FILE,
                  format);
        }
        else
        {
 -        new FileLoader().LoadFile(choice, FormatAdapter.FILE, format);
 +        new FileLoader().LoadFile(choice, DataSourceType.FILE, format);
        }
      }
    }
     * 
     * @param e
     *          DOCUMENT ME!
 +   * @throws FileFormatException
     */
    @Override
    public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
-           throws FileFormatException
++  // throws FileFormatException
    {
      // This construct allows us to have a wider textfield
      // for viewing
      {
        if (viewport != null)
        {
 -        new FileLoader().LoadFile(viewport, url, FormatAdapter.URL,
 -                "Jalview");
 +        new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
 +                FileFormat.Jalview);
        }
        else
        {
 -        new FileLoader().LoadFile(url, FormatAdapter.URL, "Jalview");
 +        new FileLoader().LoadFile(url, DataSourceType.URL,
 +                FileFormat.Jalview);
        }
      }
      else
      {
-       FileFormatI format = new IdentifyFile().identify(url,
-               DataSourceType.URL);
 -      String format = new IdentifyFile().identify(url, FormatAdapter.URL);
++      FileFormatI format = null;
++      try
++      {
++        format = new IdentifyFile().identify(url, DataSourceType.URL);
++      } catch (FileFormatException e)
++      {
++        // TODO revise error handling, distinguish between
++        // URL not found and response not valid
++      }
  
--      if (format.equals("URL NOT FOUND"))
++      if (format == null)
        {
          JOptionPane.showInternalMessageDialog(Desktop.desktop,
                  MessageManager.formatMessage("label.couldnt_locate",
  
        if (viewport != null)
        {
 -        new FileLoader().LoadFile(viewport, url, FormatAdapter.URL, format);
 +        new FileLoader()
 +                .LoadFile(viewport, url, DataSourceType.URL, format);
        }
        else
        {
 -        new FileLoader().LoadFile(url, FormatAdapter.URL, format);
 +        new FileLoader().LoadFile(url, DataSourceType.URL, format);
        }
      }
    }
    public void saveState_actionPerformed(ActionEvent e)
    {
      JalviewFileChooser chooser = new JalviewFileChooser(
--            jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
--            new String[] { "jvp" }, new String[] { "Jalview Project" },
++            Cache.getProperty("LAST_DIRECTORY"), "jvp", "Jalview Project",
              "Jalview Project");
  
      chooser.setFileView(new JalviewFileView());
    public void loadState_actionPerformed(ActionEvent e)
    {
      JalviewFileChooser chooser = new JalviewFileChooser(
--            jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[] {
++            Cache.getProperty("LAST_DIRECTORY"), new String[] {
                  "jvp", "jar" }, new String[] { "Jalview Project",
                  "Jalview Project (old)" }, "Jalview Project");
      chooser.setFileView(new JalviewFileView());
        final File selectedFile = chooser.getSelectedFile();
        setProjectFile(selectedFile);
        final String choice = selectedFile.getAbsolutePath();
--      jalview.bin.Cache.setProperty("LAST_DIRECTORY",
++      Cache.setProperty("LAST_DIRECTORY",
                selectedFile.getParent());
        new Thread(new Runnable()
        {
     * 
     * @param af
     */
-   public void explodeViews(AlignFrame af)
+   public static void explodeViews(AlignFrame af)
    {
      int size = af.alignPanels.size();
      if (size < 2)
      if (v_client != null)
      {
        JalviewFileChooser chooser = new JalviewFileChooser(
--              jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
--              { "vdj" }, // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
--              new String[] { "Vamsas Document" }, "Vamsas Document");
++              Cache.getProperty("LAST_DIRECTORY"), "vdj",
++              "Vamsas Document", "Vamsas Document");
  
        chooser.setFileView(new JalviewFileView());
        chooser.setDialogTitle(MessageManager
          JPanel progpanel = addProgressPanel(MessageManager.formatMessage(
                  "label.saving_vamsas_doc",
                  new Object[] { choice.getName() }));
--        jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice.getParent());
++        Cache.setProperty("LAST_DIRECTORY", choice.getParent());
          String warnmsg = null;
          String warnttl = null;
          try
    }
  
    public static void transferFromDropTarget(List<String> files,
 -          List<String> protocols, DropTargetDropEvent evt, Transferable t)
 +          List<DataSourceType> protocols, DropTargetDropEvent evt,
 +          Transferable t)
            throws Exception
    {
  
                .getTransferData(DataFlavor.javaFileListFlavor))
        {
          files.add(((File)file).toString());
 -        protocols.add(FormatAdapter.FILE);
 +        protocols.add(DataSourceType.FILE);
        }
      }
      else
        {
          Cache.log.debug("Adding missing FILE protocol for "
                  + files.get(protocols.size()));
 -        protocols.add(FormatAdapter.FILE);
 +        protocols.add(DataSourceType.FILE);
        }
        for (java.util.StringTokenizer st = new java.util.StringTokenizer(
                data, "\r\n"); st.hasMoreTokens();)
          java.net.URI uri = new java.net.URI(s);
          if (uri.getScheme().toLowerCase().startsWith("http"))
          {
 -          protocols.add(FormatAdapter.URL);
 +          protocols.add(DataSourceType.URL);
            files.add(uri.toString());
          }
          else
          {
            // otherwise preserve old behaviour: catch all for file objects
            java.io.File file = new java.io.File(uri);
 -          protocols.add(FormatAdapter.FILE);
 +          protocols.add(DataSourceType.FILE);
            files.add(file.toString());
          }
        }
@@@ -174,7 -174,8 +174,8 @@@ public class FeatureSettings extends JP
        public void mousePressed(MouseEvent evt)
        {
          selectedRow = table.rowAtPoint(evt.getPoint());
-         if (SwingUtilities.isRightMouseButton(evt))
+         boolean ctrlDown = Platform.isControlDown(evt);
+         if (SwingUtilities.isRightMouseButton(evt) && !ctrlDown)
          {
            popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
                    table.getValueAt(selectedRow, 1), fr.getMinMax(),
          }
          else if (evt.getClickCount() == 2)
          {
+           boolean invertSelection = evt.isAltDown();
+           boolean toggleSelection = ctrlDown;
+           boolean extendSelection = evt.isShiftDown();
            fr.ap.alignFrame.avc.markColumnsContainingFeatures(
-                   evt.isAltDown(), evt.isShiftDown() || evt.isMetaDown(),
-                   evt.isMetaDown(),
+                   invertSelection, extendSelection, toggleSelection,
                    (String) table.getValueAt(selectedRow, 0));
          }
        }
          int newRow = table.rowAtPoint(evt.getPoint());
          if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
          {
+           /*
+            * reposition 'selectedRow' to 'newRow' (the dragged to location)
+            * this could be more than one row away for a very fast drag action
+            * so just swap it with adjacent rows until we get it there
+            */
            Object[][] data = ((FeatureTableModel) table.getModel())
                    .getData();
-           Object[] temp = data[selectedRow];
-           data[selectedRow] = data[newRow];
-           data[newRow] = temp;
+           int direction = newRow < selectedRow ? -1 : 1;
+           for (int i = selectedRow; i != newRow; i += direction)
+           {
+             Object[] temp = data[i];
+             data[i] = data[i + direction];
+             data[i + direction] = temp;
+           }
            updateFeatureRenderer(data);
            table.repaint();
            selectedRow = newRow;
                else
                {
                  // probably the color chooser!
-                 table.setValueAt(colorChooser.getColor(), selectedRow, 1);
+                 table.setValueAt(
+                         new FeatureColour(colorChooser.getColor()),
+                         selectedRow, 1);
                  table.validate();
                  me.updateFeatureRenderer(
                          ((FeatureTableModel) table.getModel()).getData(),
    void load()
    {
      JalviewFileChooser chooser = new JalviewFileChooser(
--            jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
--            new String[] { "fc" },
--            new String[] { "Sequence Feature Colours" },
--            "Sequence Feature Colours");
++            Cache.getProperty("LAST_DIRECTORY"), "fc",
++            "Sequence Feature Colours", "Sequence Feature Colours");
      chooser.setFileView(new jalview.io.JalviewFileView());
      chooser.setDialogTitle(MessageManager
              .getString("label.load_feature_colours"));
    void save()
    {
      JalviewFileChooser chooser = new JalviewFileChooser(
--            Cache.getProperty("LAST_DIRECTORY"),
--            new String[] { "fc" },
--            new String[] { "Sequence Feature Colours" },
--            "Sequence Feature Colours");
++            Cache.getProperty("LAST_DIRECTORY"), "fc",
++            "Sequence Feature Colours", "Sequence Feature Colours");
      chooser.setFileView(new jalview.io.JalviewFileView());
      chooser.setDialogTitle(MessageManager
              .getString("label.save_feature_colours"));
@@@ -36,7 -36,6 +36,8 @@@ import jalview.datamodel.StructureViewe
  import jalview.datamodel.StructureViewerModel.StructureData;
  import jalview.ext.varna.RnaModel;
  import jalview.gui.StructureViewer.ViewerType;
 +import jalview.io.DataSourceType;
++import jalview.io.FileFormat;
  import jalview.schemabinding.version2.AlcodMap;
  import jalview.schemabinding.version2.AlcodonFrame;
  import jalview.schemabinding.version2.Annotation;
@@@ -109,6 -108,7 +110,7 @@@ import java.lang.reflect.InvocationTarg
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.util.ArrayList;
+ import java.util.Arrays;
  import java.util.Enumeration;
  import java.util.HashMap;
  import java.util.HashSet;
@@@ -166,7 -166,9 +168,9 @@@ public class Jalview2XM
     */
    Map<String, SequenceI> seqRefIds = null;
  
-   Vector<Object[]> frefedSequence = null;
+   Map<String, SequenceI> incompleteSeqs = null;
+   List<SeqFref> frefedSequence = null;
  
    boolean raiseGUI = true; // whether errors are raised in dialog boxes or not
  
        {
          seqsToIds.clear();
        }
+       if (incompleteSeqs != null)
+       {
+         incompleteSeqs.clear();
+       }
        // seqRefIds = null;
        // seqsToIds = null;
      }
      {
        seqRefIds = new HashMap<String, SequenceI>();
      }
+     if (incompleteSeqs == null)
+     {
+       incompleteSeqs = new HashMap<String, SequenceI>();
+     }
+     if (frefedSequence == null)
+     {
+       frefedSequence = new ArrayList<SeqFref>();
+     }
    }
  
    public Jalview2XML()
      this.raiseGUI = raiseGUI;
    }
  
-   public void resolveFrefedSequences()
+   /**
+    * base class for resolving forward references to sequences by their ID
+    * 
+    * @author jprocter
+    *
+    */
+   abstract class SeqFref
    {
-     if (frefedSequence.size() > 0)
+     String sref;
+     String type;
+     public SeqFref(String _sref, String type)
+     {
+       sref = _sref;
+       this.type = type;
+     }
+     public String getSref()
+     {
+       return sref;
+     }
+     public SequenceI getSrefSeq()
+     {
+       return seqRefIds.get(sref);
+     }
+     public boolean isResolvable()
+     {
+       return seqRefIds.get(sref) != null;
+     }
+     public SequenceI getSrefDatasetSeq()
      {
-       int r = 0, rSize = frefedSequence.size();
-       while (r < rSize)
+       SequenceI sq = seqRefIds.get(sref);
+       if (sq != null)
        {
-         Object[] ref = frefedSequence.elementAt(r);
-         if (ref != null)
+         while (sq.getDatasetSequence() != null)
          {
-           String sref = (String) ref[0];
-           if (seqRefIds.containsKey(sref))
-           {
-             if (ref[1] instanceof jalview.datamodel.Mapping)
-             {
-               SequenceI seq = seqRefIds.get(sref);
-               while (seq.getDatasetSequence() != null)
-               {
-                 seq = seq.getDatasetSequence();
-               }
-               ((jalview.datamodel.Mapping) ref[1]).setTo(seq);
-             }
-             else
-             {
-               if (ref[1] instanceof jalview.datamodel.AlignedCodonFrame)
-               {
-                 SequenceI seq = seqRefIds.get(sref);
-                 while (seq.getDatasetSequence() != null)
-                 {
-                   seq = seq.getDatasetSequence();
-                 }
-                 if (ref[2] != null
-                         && ref[2] instanceof jalview.datamodel.Mapping)
-                 {
-                   jalview.datamodel.Mapping mp = (jalview.datamodel.Mapping) ref[2];
-                   ((jalview.datamodel.AlignedCodonFrame) ref[1]).addMap(
-                           seq, mp.getTo(), mp.getMap());
-                 }
-                 else
-                 {
-                   System.err
-                           .println("IMPLEMENTATION ERROR: Unimplemented forward sequence references for AlcodonFrames involving "
-                                   + ref[2].getClass() + " type objects.");
-                 }
-               }
-               else
-               {
-                 System.err
-                         .println("IMPLEMENTATION ERROR: Unimplemented forward sequence references for "
-                                 + ref[1].getClass() + " type objects.");
-               }
-             }
-             frefedSequence.remove(r);
-             rSize--;
-           }
-           else
+           sq = sq.getDatasetSequence();
+         }
+       }
+       return sq;
+     }
+     /**
+      * @return true if the forward reference was fully resolved
+      */
+     abstract boolean resolve();
+     @Override
+     public String toString()
+     {
+       return type + " reference to " + sref;
+     }
+   }
+   /**
+    * create forward reference for a mapping
+    * 
+    * @param sref
+    * @param _jmap
+    * @return
+    */
+   public SeqFref newMappingRef(final String sref,
+           final jalview.datamodel.Mapping _jmap)
+   {
+     SeqFref fref = new SeqFref(sref, "Mapping")
+     {
+       public jalview.datamodel.Mapping jmap = _jmap;
+       @Override
+       boolean resolve()
+       {
+         SequenceI seq = getSrefDatasetSeq();
+         if (seq == null)
+         {
+           return false;
+         }
+         jmap.setTo(seq);
+         return true;
+       }
+     };
+     return fref;
+   }
+   public SeqFref newAlcodMapRef(final String sref,
+           final AlignedCodonFrame _cf, final jalview.datamodel.Mapping _jmap)
+   {
+     SeqFref fref = new SeqFref(sref, "Codon Frame")
+     {
+       AlignedCodonFrame cf = _cf;
+       public jalview.datamodel.Mapping mp = _jmap;
+       @Override
+       boolean resolve()
+       {
+         SequenceI seq = getSrefDatasetSeq();
+         if (seq == null)
+         {
+           return false;
+         }
+         cf.addMap(seq, mp.getTo(), mp.getMap());
+         return true;
+       }
+     };
+     return fref;
+   }
+   public void resolveFrefedSequences()
+   {
+     Iterator<SeqFref> nextFref=frefedSequence.iterator();
+     int toresolve=frefedSequence.size();
+     int unresolved=0,failedtoresolve=0;
+     while (nextFref.hasNext()) {
+       SeqFref ref = nextFref.next();
+       if (ref.isResolvable())
+       {
+         try {
+           if (ref.resolve())
            {
-             System.err
-                     .println("IMPLEMENTATION WARNING: Unresolved forward reference for hash string "
-                             + ref[0]
-                             + " with objecttype "
-                             + ref[1].getClass());
-             r++;
+             nextFref.remove();
+           } else {
+             failedtoresolve++;
            }
-         }
-         else
+         } catch (Exception x) {
+           System.err.println("IMPLEMENTATION ERROR: Failed to resolve forward reference for sequence "+ref.getSref());
+           x.printStackTrace();
+           failedtoresolve++;
+         } 
+       } else {
+         unresolved++;
+       }
+     }
+     if (unresolved>0)
+     {
+       System.err.println("Jalview Project Import: There were " + unresolved
+               + " forward references left unresolved on the stack.");
+     }
+     if (failedtoresolve>0)
+     {
+       System.err.println("SERIOUS! " + failedtoresolve
+               + " resolvable forward references failed to resolve.");
+     }
+     if (incompleteSeqs != null && incompleteSeqs.size() > 0)
+     {
+       System.err.println("Jalview Project Import: There are "
+               + incompleteSeqs.size()
+               + " sequences which may have incomplete metadata.");
+       if (incompleteSeqs.size() < 10)
+       {
+         for (SequenceI s : incompleteSeqs.values())
          {
-           // empty reference
-           frefedSequence.remove(r);
-           rSize--;
+           System.err.println(s.toString());
          }
        }
+       else
+       {
+         System.err
+                 .println("Too many to report. Skipping output of incomplete sequences.");
+       }
      }
    }
  
      {
        return;
      }
+     saveAllFrames(Arrays.asList(frames), jout);
+   }
  
+   /**
+    * core method for storing state for a set of AlignFrames.
+    * 
+    * @param frames
+    *          - frames involving all data to be exported (including containing
+    *          splitframes)
+    * @param jout
+    *          - project output stream
+    */
+   private void saveAllFrames(List<AlignFrame> frames, JarOutputStream jout)
+   {
      Hashtable<String, AlignFrame> dsses = new Hashtable<String, AlignFrame>();
  
      /*
        List<String> viewIds = new ArrayList<String>();
  
        // REVERSE ORDER
-       for (int i = frames.length - 1; i > -1; i--)
+       for (int i = frames.size() - 1; i > -1; i--)
        {
-         AlignFrame af = frames[i];
+         AlignFrame af = frames.get(i);
          // skip ?
          if (skipList != null
                  && skipList
    {
      try
      {
-       int ap = 0;
-       int apSize = af.alignPanels.size();
        FileOutputStream fos = new FileOutputStream(jarFile);
        JarOutputStream jout = new JarOutputStream(fos);
-       Hashtable<String, AlignFrame> dsses = new Hashtable<String, AlignFrame>();
-       List<String> viewIds = new ArrayList<String>();
+       List<AlignFrame> frames = new ArrayList<AlignFrame>();
  
-       for (AlignmentPanel apanel : af.alignPanels)
+       // resolve splitframes
+       if (af.getViewport().getCodingComplement() != null)
        {
-         String jfileName = apSize == 1 ? fileName : fileName + ap;
-         ap++;
-         if (!jfileName.endsWith(".xml"))
-         {
-           jfileName = jfileName + ".xml";
-         }
-         saveState(apanel, jfileName, jout, viewIds);
-         String dssid = getDatasetIdRef(af.getViewport().getAlignment()
-                 .getDataset());
-         if (!dsses.containsKey(dssid))
-         {
-           dsses.put(dssid, af);
-         }
+         frames = ((SplitFrame) af.getSplitViewContainer()).getAlignFrames();
+       }
+       else
+       {
+         frames.add(af);
        }
-       writeDatasetFor(dsses, fileName, jout);
+       saveAllFrames(frames, jout);
        try
        {
          jout.flush();
            if (entry.getProperty() != null && !entry.getProperty().isEmpty())
            {
              PdbentryItem item = new PdbentryItem();
 -            Hashtable properties = entry.getProperty();
 -            Enumeration en2 = properties.keys();
 +            Hashtable<String, String> properties = entry.getProperty();
 +            Enumeration<String> en2 = properties.keys();
              while (en2.hasMoreElements())
              {
                Property prop = new Property();
 -              String key = en2.nextElement().toString();
 +              String key = en2.nextElement();
                prop.setName(key);
 -              prop.setValue(properties.get(key).toString());
 +              prop.setValue(properties.get(key));
                item.addProperty(prop);
              }
              pdb.addPdbentryItem(item);
        jal = av.getAlignment();
      }
      // SAVE MAPPINGS
-     if (jal.getCodonFrames() != null)
+     // FOR DATASET
+     if (storeDS && jal.getCodonFrames() != null)
      {
        List<AlignedCodonFrame> jac = jal.getCodonFrames();
        for (AlignedCodonFrame acf : jac)
        {
          AlcodonFrame alc = new AlcodonFrame();
          if (acf.getProtMappings() != null
                  && acf.getProtMappings().length > 0)
          {
+           boolean hasMap = false;
            SequenceI[] dnas = acf.getdnaSeqs();
            jalview.datamodel.Mapping[] pmaps = acf.getProtMappings();
            for (int m = 0; m < pmaps.length; m++)
              alcmap.setMapping(createVamsasMapping(pmaps[m], dnas[m], null,
                      false));
              alc.addAlcodMap(alcmap);
+             hasMap = true;
+           }
+           if (hasMap)
+           {
+             vamsasSet.addAlcodonFrame(alc);
            }
          }
          // TODO: delete this ? dead code from 2.8.3->2.9 ?
      if (jds.getDatasetSequence() != null)
      {
        vamsasSeq.setDsseqid(seqHash(jds.getDatasetSequence()));
      }
      else
      {
-       vamsasSeq.setDsseqid(id); // so we can tell which sequences really are
+       // seqId==dsseqid so we can tell which sequences really are
        // dataset sequences only
+       vamsasSeq.setDsseqid(id);
        dbrefs = jds.getDBRefs();
+       if (parentseq == null)
+       {
+         parentseq = jds;
+       }
      }
      if (dbrefs != null)
      {
        if (jmp.getTo() != null)
        {
          MappingChoice mpc = new MappingChoice();
-         if (recurse
-                 && (parentseq != jmp.getTo() || parentseq
-                         .getDatasetSequence() != jmp.getTo()))
+         // check/create ID for the sequence referenced by getTo()
+         String jmpid = "";
+         SequenceI ps = null;
+         if (parentseq != jmp.getTo()
+                 && parentseq.getDatasetSequence() != jmp.getTo())
          {
-           mpc.setSequence(createVamsasSequence(false, seqHash(jmp.getTo()),
-                   jmp.getTo(), jds));
+           // chaining dbref rather than a handshaking one
+           jmpid = seqHash(ps = jmp.getTo());
          }
          else
          {
-           String jmpid = "";
-           SequenceI ps = null;
-           if (parentseq != jmp.getTo()
-                   && parentseq.getDatasetSequence() != jmp.getTo())
-           {
-             // chaining dbref rather than a handshaking one
-             jmpid = seqHash(ps = jmp.getTo());
-           }
-           else
-           {
-             jmpid = seqHash(ps = parentseq);
-           }
-           mpc.setDseqFor(jmpid);
-           if (!seqRefIds.containsKey(mpc.getDseqFor()))
-           {
-             jalview.bin.Cache.log.debug("creatign new DseqFor ID");
-             seqRefIds.put(mpc.getDseqFor(), ps);
-           }
-           else
-           {
-             jalview.bin.Cache.log.debug("reusing DseqFor ID");
-           }
+           jmpid = seqHash(ps = parentseq);
+         }
+         mpc.setDseqFor(jmpid);
+         if (!seqRefIds.containsKey(mpc.getDseqFor()))
+         {
+           jalview.bin.Cache.log.debug("creatign new DseqFor ID");
+           seqRefIds.put(mpc.getDseqFor(), ps);
+         }
+         else
+         {
+           jalview.bin.Cache.log.debug("reusing DseqFor ID");
          }
          mp.setMappingChoice(mpc);
        }
      }
      }
      if (seqRefIds == null)
      {
-       seqRefIds = new HashMap<String, SequenceI>();
-     }
-     if (frefedSequence == null)
-     {
-       frefedSequence = new Vector<Object[]>();
+       initSeqRefs();
      }
      AlignFrame af = null, _af = null;
+     IdentityHashMap<AlignmentI, AlignmentI> importedDatasets = new IdentityHashMap<AlignmentI, AlignmentI>();
      Map<String, AlignFrame> gatherToThisFrame = new HashMap<String, AlignFrame>();
      final String file = jprovider.getFilename();
      try
            if (true) // !skipViewport(object))
            {
              _af = loadFromObject(object, file, true, jprovider);
-             if (object.getJalviewModelSequence().getViewportCount() > 0)
+             if (_af != null
+                     && object.getJalviewModelSequence().getViewportCount() > 0)
              {
-               af = _af;
-               if (af.viewport.isGatherViewsHere())
+               if (af == null)
+               {
+                 // store a reference to the first view
+                 af = _af;
+               }
+               if (_af.viewport.isGatherViewsHere())
                {
-                 gatherToThisFrame.put(af.viewport.getSequenceSetId(), af);
+                 // if this is a gathered view, keep its reference since
+                 // after gathering views, only this frame will remain
+                 af = _af;
+                 gatherToThisFrame.put(_af.viewport.getSequenceSetId(), _af);
                }
+               // Save dataset to register mappings once all resolved
+               importedDatasets.put(af.viewport.getAlignment().getDataset(),
+                       af.viewport.getAlignment().getDataset());
              }
            }
            entryCount++;
        e.printStackTrace();
      }
  
-     if (Desktop.instance != null)
-     {
-       Desktop.instance.stopLoading();
-     }
      /*
       * Regather multiple views (with the same sequence set id) to the frame (if
       * any) that is flagged as the one to gather to, i.e. convert them to tabbed
      }
  
      restoreSplitFrames();
+     for (AlignmentI ds : importedDatasets.keySet())
+     {
+       if (ds.getCodonFrames() != null)
+       {
+         StructureSelectionManager.getStructureSelectionManager(
+                 Desktop.instance).registerMappings(ds.getCodonFrames());
+       }
+     }
      if (errorMessage != null)
      {
        reportErrors();
      }
+     if (Desktop.instance != null)
+     {
+       Desktop.instance.stopLoading();
+     }
      return af;
    }
  
      // LOAD SEQUENCES
  
      List<SequenceI> hiddenSeqs = null;
-     jalview.datamodel.Sequence jseq;
  
      List<SequenceI> tmpseqs = new ArrayList<SequenceI>();
  
      {
        String seqId = jseqs[i].getId();
  
-       if (seqRefIds.get(seqId) != null)
+       SequenceI tmpSeq = seqRefIds.get(seqId);
+       if (tmpSeq != null)
        {
-         tmpseqs.add(seqRefIds.get(seqId));
+         if (!incompleteSeqs.containsKey(seqId))
+         {
+           // may not need this check, but keep it for at least 2.9,1 release
+           if (tmpSeq.getStart()!=jseqs[i].getStart() || tmpSeq.getEnd()!=jseqs[i].getEnd())
+           { 
+             System.err
+                     .println("Warning JAL-2154 regression: updating start/end for sequence "
+                     + tmpSeq.toString());
+           }
+         } else {
+           incompleteSeqs.remove(seqId);
+         }
+         tmpSeq.setStart(jseqs[i].getStart());
+         tmpSeq.setEnd(jseqs[i].getEnd());
+         tmpseqs.add(tmpSeq);
          multipleView = true;
        }
        else
        {
-         jseq = new jalview.datamodel.Sequence(vamsasSeq[vi].getName(),
+         tmpSeq = new jalview.datamodel.Sequence(vamsasSeq[vi].getName(),
                  vamsasSeq[vi].getSequence());
-         jseq.setDescription(vamsasSeq[vi].getDescription());
-         jseq.setStart(jseqs[i].getStart());
-         jseq.setEnd(jseqs[i].getEnd());
-         jseq.setVamsasId(uniqueSetSuffix + seqId);
-         seqRefIds.put(vamsasSeq[vi].getId(), jseq);
-         tmpseqs.add(jseq);
+         tmpSeq.setDescription(vamsasSeq[vi].getDescription());
+         tmpSeq.setStart(jseqs[i].getStart());
+         tmpSeq.setEnd(jseqs[i].getEnd());
+         tmpSeq.setVamsasId(uniqueSetSuffix + seqId);
+         seqRefIds.put(vamsasSeq[vi].getId(), tmpSeq);
+         tmpseqs.add(tmpSeq);
          vi++;
        }
  
            hiddenSeqs = new ArrayList<SequenceI>();
          }
  
-         hiddenSeqs.add(seqRefIds.get(seqId));
+         hiddenSeqs.add(tmpSeq);
        }
      }
  
      SequenceI[] orderedSeqs = tmpseqs
              .toArray(new SequenceI[tmpseqs.size()]);
  
-     Alignment al = new Alignment(orderedSeqs);
+     AlignmentI al = null;
+     // so we must create or recover the dataset alignment before going further
+     // ///////////////////////////////
+     if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "")
+     {
+       // older jalview projects do not have a dataset - so creat alignment and
+       // dataset
+       al = new Alignment(orderedSeqs);
+       al.setDataset(null);
+     }
+     else
+     {
+       boolean isdsal = object.getJalviewModelSequence().getViewportCount() == 0;
+       if (isdsal)
+       {
+         // we are importing a dataset record, so
+         // recover reference to an alignment already materialsed as dataset
+         al = getDatasetFor(vamsasSet.getDatasetId());
+       }
+       if (al == null)
+       {
+         // materialse the alignment
+         al = new Alignment(orderedSeqs);
+       }
+       if (isdsal)
+       {
+         addDatasetRef(vamsasSet.getDatasetId(), al);
+       }
+       // finally, verify all data in vamsasSet is actually present in al
+       // passing on flag indicating if it is actually a stored dataset
+       recoverDatasetFor(vamsasSet, al, isdsal);
+     }
  
      if (referenceseqForView != null)
      {
        al.setProperty(ssp.getKey(), ssp.getValue());
      }
  
-     // /
-     // SequenceFeatures are added to the DatasetSequence,
-     // so we must create or recover the dataset before loading features
-     // ///////////////////////////////
-     if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "")
-     {
-       // older jalview projects do not have a dataset id.
-       al.setDataset(null);
-     }
-     else
-     {
-       // recover dataset - passing on flag indicating if this a 'viewless'
-       // sequence set (a.k.a. a stored dataset for the project)
-       recoverDatasetFor(vamsasSet, al, object.getJalviewModelSequence()
-               .getViewportCount() == 0);
-     }
      // ///////////////////////////////
  
      Hashtable pdbloaded = new Hashtable(); // TODO nothing writes to this??
              else
              {
                // defer to later
-               frefedSequence.add(new Object[] { maps[m].getDnasq(), cf,
-                   mapping });
+               frefedSequence.add(newAlcodMapRef(maps[m].getDnasq(), cf,
+                       mapping));
              }
            }
+           al.addCodonFrame(cf);
          }
-         al.addCodonFrame(cf);
        }
      }
  
        StructureData filedat = oldFiles.get(id);
        String pdbFile = filedat.getFilePath();
        SequenceI[] seq = filedat.getSeqList().toArray(new SequenceI[0]);
 -      binding.getSsm().setMapping(seq, null, pdbFile,
 -              jalview.io.AppletFormatAdapter.FILE);
 +      binding.getSsm().setMapping(seq, null, pdbFile, DataSourceType.FILE);
        binding.addSequenceForStructFile(pdbFile, seq);
      }
      // and add the AlignmentPanel's reference to the view panel
    }
  
    AlignFrame loadViewport(String file, JSeq[] JSEQ,
-           List<SequenceI> hiddenSeqs, Alignment al,
+           List<SequenceI> hiddenSeqs, AlignmentI al,
            JalviewModelSequence jms, Viewport view, String uniqueSeqSetId,
            String viewId, List<JvAnnotRow> autoAlan)
    {
      af = new AlignFrame(al, view.getWidth(), view.getHeight(),
              uniqueSeqSetId, viewId);
  
--    af.setFileName(file, "Jalview");
++    af.setFileName(file, FileFormat.Jalview);
  
      for (int i = 0; i < JSEQ.length; i++)
      {
        }
      }
      af.setMenusFromViewport(af.viewport);
+     af.setTitle(view.getTitle());
      // TODO: we don't need to do this if the viewport is aready visible.
      /*
       * Add the AlignFrame to the desktop (it may be 'gathered' later), unless it
    }
  
    private ColourSchemeI constructAnnotationColour(
-           AnnotationColours viewAnnColour, AlignFrame af, Alignment al,
+           AnnotationColours viewAnnColour, AlignFrame af, AlignmentI al,
            JalviewModelSequence jms, boolean checkGroupAnnColour)
    {
      boolean propagateAnnColour = false;
      return cs;
    }
  
-   private void reorderAutoannotation(AlignFrame af, Alignment al,
+   private void reorderAutoannotation(AlignFrame af, AlignmentI al,
            List<JvAnnotRow> autoAlan)
    {
      // copy over visualization settings for autocalculated annotation in the
      }
    }
  
-   private void recoverDatasetFor(SequenceSet vamsasSet, Alignment al,
+   private void recoverDatasetFor(SequenceSet vamsasSet, AlignmentI al,
            boolean ignoreUnrefed)
    {
-     jalview.datamodel.Alignment ds = getDatasetFor(vamsasSet.getDatasetId());
+     jalview.datamodel.AlignmentI ds = getDatasetFor(vamsasSet
+             .getDatasetId());
      Vector dseqs = null;
      if (ds == null)
      {
     * TODO use AlignmentI here and in related methods - needs
     * AlignmentI.getDataset() changed to return AlignmentI instead of Alignment
     */
-   Hashtable<String, Alignment> datasetIds = null;
+   Hashtable<String, AlignmentI> datasetIds = null;
  
-   IdentityHashMap<Alignment, String> dataset2Ids = null;
+   IdentityHashMap<AlignmentI, String> dataset2Ids = null;
  
-   private Alignment getDatasetFor(String datasetId)
+   private AlignmentI getDatasetFor(String datasetId)
    {
      if (datasetIds == null)
      {
-       datasetIds = new Hashtable<String, Alignment>();
+       datasetIds = new Hashtable<String, AlignmentI>();
        return null;
      }
      if (datasetIds.containsKey(datasetId))
      return null;
    }
  
-   private void addDatasetRef(String datasetId, Alignment dataset)
+   private void addDatasetRef(String datasetId, AlignmentI dataset)
    {
      if (datasetIds == null)
      {
-       datasetIds = new Hashtable<String, Alignment>();
+       datasetIds = new Hashtable<String, AlignmentI>();
      }
      datasetIds.put(datasetId, dataset);
    }
     * @param dataset
     * @return
     */
-   private String getDatasetIdRef(Alignment dataset)
+   private String getDatasetIdRef(AlignmentI dataset)
    {
      if (dataset.getDataset() != null)
      {
        // make a new datasetId and record it
        if (dataset2Ids == null)
        {
-         dataset2Ids = new IdentityHashMap<Alignment, String>();
+         dataset2Ids = new IdentityHashMap<AlignmentI, String>();
        }
        else
        {
          }
          else
          {
-           frefedSequence.add(new Object[] { dsfor, jmap });
+           frefedSequence.add(newMappingRef(dsfor, jmap));
          }
        }
        else
            djs.setEnd(jmap.getMap().getToHighest());
            djs.setVamsasId(uniqueSetSuffix + sqid);
            jmap.setTo(djs);
+           incompleteSeqs.put(sqid, djs);
            seqRefIds.put(sqid, djs);
  
          }
@@@ -35,6 -35,6 +35,7 @@@ import jalview.binding.Tree
  import jalview.binding.UserColours;
  import jalview.binding.Viewport;
  import jalview.datamodel.PDBEntry;
++import jalview.io.FileFormat;
  import jalview.schemes.ColourSchemeI;
  import jalview.schemes.ColourSchemeProperty;
  import jalview.schemes.ResidueProperties;
@@@ -305,7 -305,7 +306,7 @@@ public class Jalview2XML_V
  
      AlignFrame af = new AlignFrame(al, view.getWidth(), view.getHeight());
  
--    af.setFileName(file, "Jalview");
++    af.setFileName(file, FileFormat.Jalview);
  
      for (int i = 0; i < JSEQ.length; i++)
      {
@@@ -38,7 -38,6 +38,8 @@@ import jalview.datamodel.Sequence
  import jalview.datamodel.SequenceFeature;
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
 +import jalview.io.FileFormat;
++import jalview.io.FileFormatI;
  import jalview.io.FormatAdapter;
  import jalview.io.SequenceAnnotationReport;
  import jalview.schemes.AnnotationColourGradient;
@@@ -92,8 -91,6 +93,6 @@@ import javax.swing.JRadioButtonMenuItem
   */
  public class PopupMenu extends JPopupMenu
  {
-   private static final String ALL_ANNOTATIONS = "All";
    JMenu groupMenu = new JMenu();
  
    JMenuItem groupName = new JMenuItem();
  
      if (sg != null && sg.getSize() > 0)
      {
-       groupName.setText(MessageManager.formatMessage("label.name_param",
-               new Object[] { sg.getName() }));
        groupName.setText(MessageManager
                .getString("label.edit_name_and_description_current_group"));
  
      showMenu.removeAll();
      hideMenu.removeAll();
  
-     final List<String> all = Arrays.asList(ALL_ANNOTATIONS);
+     final List<String> all = Arrays.asList(new String[] { MessageManager
+             .getString("label.all") });
      addAnnotationTypeToShowHide(showMenu, forSequences, "", all, true, true);
      addAnnotationTypeToShowHide(hideMenu, forSequences, "", all, true,
              false);
     */
    private void jbInit() throws Exception
    {
-     groupMenu.setText(MessageManager.getString("label.group"));
      groupMenu.setText(MessageManager.getString("label.selection"));
      groupName.setText(MessageManager.getString("label.name"));
      groupName.addActionListener(new java.awt.event.ActionListener()
      // or we simply trust the user wants
      // wysiwig behaviour
  
-     FileFormat fileFormat = FileFormat.forName(e.getActionCommand());
 -    cap.setText(new FormatAdapter(ap).formatSequences(e.getActionCommand(),
 -            ap, true));
++    FileFormatI fileFormat = FileFormat.forName(e.getActionCommand());
 +    cap.setText(new FormatAdapter(ap).formatSequences(fileFormat, ap, true));
    }
  
    public void sequenceFeature_actionPerformed()
@@@ -24,7 -24,6 +24,6 @@@ import jalview.analysis.AnnotationSorte
  import jalview.bin.Cache;
  import jalview.gui.Help.HelpId;
  import jalview.gui.StructureViewer.ViewerType;
- import jalview.io.FileFormat;
  import jalview.io.JalviewFileChooser;
  import jalview.io.JalviewFileView;
  import jalview.jbgui.GPreferences;
@@@ -684,14 -683,12 +683,14 @@@ public class Preferences extends GPrefe
    @Override
    public void startupFileTextfield_mouseClicked()
    {
-     FileFormat fileFormat = FileFormat.valueOf(Cache.getProperty("DEFAULT_FILE_FORMAT"));
--    JalviewFileChooser chooser = new JalviewFileChooser(
-     // fixme push into enum
-             Cache.getProperty("LAST_DIRECTORY"), new String[] {
-                 "fa, fasta, fastq", "aln", "pfam", "msf", "pir", "blc",
-                 "jar" }, new String[] { "Fasta", "Clustal", "PFAM", "MSF",
-                 "PIR", "BLC", "Jalview" },
-             fileFormat);
 -            jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[] {
 -                "fa, fasta, fastq", "aln", "pfam", "msf", "pir", "blc",
 -                "jar" }, new String[] { "Fasta", "Clustal", "PFAM", "MSF",
 -                "PIR", "BLC", "Jalview" },
 -            jalview.bin.Cache.getProperty("DEFAULT_FILE_FORMAT"));
++    String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
++    JalviewFileChooser chooser = JalviewFileChooser.forRead(
++            Cache.getProperty("LAST_DIRECTORY"), fileFormat, true);
++    // new String[] {
++    // "fa, fasta, fastq", "aln", "pfam", "msf", "pir", "blc",
++    // "jar" }, new String[] { "Fasta", "Clustal", "PFAM", "MSF",
++    // "PIR", "BLC", "Jalview" },
++    // fileFormat);
      chooser.setFileView(new JalviewFileView());
      chooser.setDialogTitle(MessageManager
              .getString("label.select_startup_file"));
  
      if (value == JalviewFileChooser.APPROVE_OPTION)
      {
 -      jalview.bin.Cache.applicationProperties.setProperty(
 -              "DEFAULT_FILE_FORMAT", chooser.getSelectedFormat());
 +      Cache.applicationProperties.setProperty("DEFAULT_FILE_FORMAT",
 +              chooser
 +                      .getSelectedFormat().toString());
        startupFileTextfield.setText(chooser.getSelectedFile()
                .getAbsolutePath());
      }
@@@ -28,7 -28,6 +28,7 @@@ import jalview.datamodel.SequenceFeatur
  import jalview.datamodel.SequenceI;
  import jalview.fts.service.pdb.PDBFTSPanel;
  import jalview.fts.service.uniprot.UniprotFTSPanel;
 +import jalview.io.FileFormatI;
  import jalview.io.gff.SequenceOntologyI;
  import jalview.util.DBRefUtils;
  import jalview.util.MessageManager;
@@@ -837,10 -836,8 +837,8 @@@ public class SequenceFetcher extends JP
        Cache.log.info(
                "Error retrieving " + accession
                + " from " + proxy.getDbName(), e);
-     } finally
-     {
-       return success;
      }
+     return success;
    }
  
    /**
  
      for (String q : queries)
      {
-       DBRefEntry[] found = null;
        DBRefEntry dbr = new DBRefEntry();
        dbr.setSource(proxy.getDbSource());
        dbr.setVersion(null);
        {
          if (rs[r] != null)
          {
-           found = DBRefUtils.searchRefs(rs[r].getDBRefs(), accId);
-           if (found != null && found.length > 0)
+           List<DBRefEntry> found = DBRefUtils.searchRefs(rs[r].getDBRefs(),
+                   accId);
+           if (!found.isEmpty())
            {
              rfound = true;
              break;
    }
  
    AlignmentI parseResult(AlignmentI al, String title,
 -          String currentFileFormat,
 +          FileFormatI currentFileFormat,
            FeatureSettingsModelI preferredFeatureColours)
    {
  
@@@ -42,6 -42,6 +42,7 @@@ import jalview.io.JalviewFileView
  import jalview.io.NewickFile;
  import jalview.jbgui.GTreePanel;
  import jalview.schemes.ResidueProperties;
++import jalview.util.ImageMaker;
  import jalview.util.MessageManager;
  import jalview.viewmodel.AlignmentViewport;
  
@@@ -520,8 -520,8 +521,8 @@@ public class TreePanel extends GTreePan
      {
        // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]);
  
-       Alignment al = new Alignment((SequenceI[]) alAndColsel[0]);
-       Alignment dataset = (av != null && av.getAlignment() != null) ? av
+       AlignmentI al = new Alignment((SequenceI[]) alAndColsel[0]);
+       AlignmentI dataset = (av != null && av.getAlignment() != null) ? av
                .getAlignment().getDataset() : null;
        if (dataset != null)
        {
  
      try
      {
--      jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(
--              jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
--              { "eps" }, new String[] { "Encapsulated Postscript" },
--              "Encapsulated Postscript");
--      chooser.setFileView(new jalview.io.JalviewFileView());
++      JalviewFileChooser chooser = new JalviewFileChooser(
++              Cache.getProperty("LAST_DIRECTORY"),
++              ImageMaker.EPS_EXTENSION, ImageMaker.EPS_EXTENSION,
++              ImageMaker.EPS_EXTENSION);
++      chooser.setFileView(new JalviewFileView());
        chooser.setDialogTitle(MessageManager
                .getString("label.create_eps_from_tree"));
        chooser.setToolTipText(MessageManager.getString("action.save"));
  
        int value = chooser.showSaveDialog(this);
  
--      if (value != jalview.io.JalviewFileChooser.APPROVE_OPTION)
++      if (value != JalviewFileChooser.APPROVE_OPTION)
        {
          return;
        }
  
--      jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
--              .getSelectedFile().getParent());
++      Cache.setProperty("LAST_DIRECTORY", chooser.getSelectedFile()
++              .getParent());
  
        FileOutputStream out = new FileOutputStream(chooser.getSelectedFile());
        EpsGraphics2D pg = new EpsGraphics2D("Tree", out, 0, 0, width, height);
  
      try
      {
--      jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(
--              jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
--              { "png" }, new String[] { "Portable network graphics" },
--              "Portable network graphics");
++      JalviewFileChooser chooser = new JalviewFileChooser(
++              Cache.getProperty("LAST_DIRECTORY"),
++              ImageMaker.PNG_EXTENSION, ImageMaker.PNG_DESCRIPTION,
++              ImageMaker.PNG_DESCRIPTION);
  
        chooser.setFileView(new jalview.io.JalviewFileView());
        chooser.setDialogTitle(MessageManager
@@@ -21,6 -21,6 +21,7 @@@
  package jalview.gui;
  
  import jalview.api.structures.JalviewStructureDisplayI;
++import jalview.bin.Cache;
  import jalview.datamodel.SequenceGroup;
  import jalview.io.JalviewFileChooser;
  import jalview.jbgui.GUserDefinedColours;
@@@ -597,9 -597,9 +598,8 @@@ public class UserDefinedColours extend
      lowerCaseButtons = new ArrayList<JButton>();
  
      JalviewFileChooser chooser = new JalviewFileChooser(
--            jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
--            new String[] { "jc" }, new String[] { "Jalview User Colours" },
--            "Jalview User Colours");
++            Cache.getProperty("LAST_DIRECTORY"), "jc",
++            "Jalview User Colours", "Jalview User Colours");
      chooser.setFileView(new jalview.io.JalviewFileView());
      chooser.setDialogTitle(MessageManager
              .getString("label.load_colour_scheme"));
        userColourSchemes.remove(schemeName.getText());
      }
      JalviewFileChooser chooser = new JalviewFileChooser(
--            jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
--            new String[] { "jc" }, new String[] { "Jalview User Colours" },
--            "Jalview User Colours");
++            Cache.getProperty("LAST_DIRECTORY"), "jc",
++            "Jalview User Colours", "Jalview User Colours");
  
      chooser.setFileView(new jalview.io.JalviewFileView());
      chooser.setDialogTitle(MessageManager
@@@ -22,6 -22,6 +22,7 @@@ package jalview.gui
  
  import jalview.bin.Cache;
  import jalview.io.JalviewFileChooser;
++import jalview.io.JalviewFileView;
  import jalview.util.MessageManager;
  import jalview.ws.params.ParamDatastoreI;
  import jalview.ws.params.ParamManager;
@@@ -185,11 -185,11 +186,9 @@@ public class WsParamSetManager implemen
      if (filename == null)
      {
        JalviewFileChooser chooser = new JalviewFileChooser(
--              jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
--              { "wsparams" },
--              new String[] { "Web Service Parameter File" },
--              "Web Service Parameter File");
--      chooser.setFileView(new jalview.io.JalviewFileView());
++              Cache.getProperty("LAST_DIRECTORY"), "wsparams",
++              "Web Service Parameter File", "Web Service Parameter File");
++      chooser.setFileView(new JalviewFileView());
        chooser.setDialogTitle(MessageManager
                .getString("label.choose_filename_for_param_file"));
        chooser.setToolTipText(MessageManager.getString("action.save"));
@@@ -40,7 -40,7 +40,7 @@@ import java.util.Vector
   * @author $author$
   * @version $Revision$
   */
 -public abstract class AlignFile extends FileParse
 +public abstract class AlignFile extends FileParse implements AlignmentFileI
  {
    int noSeqs = 0;
  
@@@ -71,6 -71,8 +71,6 @@@
  
    long end;
  
 -  boolean jvSuffix = true;
 -
    private boolean parseCalled;
  
    /**
      initData();
    }
  
 +  public AlignFile(SequenceI[] seqs)
 +  {
 +    this();
 +    setSeqs(seqs);
 +  }
 +
    /**
     * Constructor which parses the data from a file of some specified type.
     * 
     * @param dataObject
     *          Filename, URL or Pasted String to read from.
 -   * @param type
 +   * @param sourceType
     *          What type of file to read from (File, URL, Pasted String)
     */
 -  public AlignFile(String dataObject, String type) throws IOException
 +  public AlignFile(String dataObject, DataSourceType sourceType)
 +          throws IOException
    {
 -    this(true, dataObject, type);
 +    this(true, dataObject, sourceType);
    }
  
    /**
     *          if false, need to call 'doParse()' to begin parsing data
     * @param dataObject
     *          Filename, URL or Pasted String to read from.
 -   * @param type
 +   * @param sourceType
     *          What type of file to read from (File, URL)
     * @throws IOException
     */
 -  public AlignFile(boolean parseImmediately, String dataObject, String type)
 +  public AlignFile(boolean parseImmediately, String dataObject,
 +          DataSourceType sourceType)
            throws IOException
    {
 -    super(dataObject, type);
 +    super(dataObject, sourceType);
      initData();
      if (parseImmediately)
      {
    /**
     * Return the Sequences in the seqs Vector as an array of Sequences
     */
 +  @Override
    public SequenceI[] getSeqsAsArray()
    {
      SequenceI[] s = new SequenceI[seqs.size()];
     * 
     * @param al
     */
 +  @Override
    public void addAnnotations(AlignmentI al)
    {
      addProperties(al);
     * @param s
     *          DOCUMENT ME!
     */
 -  protected void setSeqs(SequenceI[] s)
 +  @Override
 +  public void setSeqs(SequenceI[] s)
    {
      seqs = new Vector<SequenceI>();
  
    public abstract void parse() throws IOException;
  
    /**
 -   * Print out in alignment file format the Sequences in the seqs Vector.
 -   */
 -  public abstract String print();
 -
 -  public void addJVSuffix(boolean b)
 -  {
 -    jvSuffix = b;
 -  }
 -
 -  /**
     * A general parser for ids.
     * 
     * @String id Id to be parsed
    }
  
    /**
--   * Creates the output id. Adds prefix Uniprot format source|id And suffix
--   * Jalview /start-end
++   * Creates the output id. Adds prefix Uniprot format source|id and optionally
++   * suffix Jalview /start-end
 +   * 
 +   * @param jvsuffix
     * 
     * @String id Id to be parsed
     */
 +  String printId(SequenceI seq, boolean jvsuffix)
 +  {
 +    return seq.getDisplayId(jvsuffix);
 +  }
 +
+   String printId(SequenceI seq)
+   {
 -    return seq.getDisplayId(jvSuffix);
++    return printId(seq, true);
+   }
    /**
     * vector of String[] treeName, newickString pairs
     */
      return newickStrings == null ? 0 : newickStrings.size();
    }
  
 +  @Override
    public void addGroups(AlignmentI al)
    {
  
@@@ -26,8 -26,9 +26,10 @@@ import jalview.datamodel.Alignment
  import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.AlignmentView;
+ import jalview.datamodel.PDBEntry.Type;
 +import jalview.datamodel.SequenceI;
++import jalview.ext.jmol.JmolParser;
  import jalview.structure.StructureImportSettings;
 -import jalview.util.MessageManager;
  
  import java.io.File;
  import java.io.IOException;
@@@ -47,6 -48,14 +49,6 @@@ public class AppletFormatAdapte
  {
    private AlignmentViewPanel viewpanel;
  
 -  public static String FILE = "File";
 -
 -  public static String URL = "URL";
 -
 -  public static String PASTE = "Paste";
 -
 -  public static String CLASSLOADER = "ClassLoader";
 -
    /**
     * add jalview-derived non-secondary structure annotation from PDB structure
     */
@@@ -62,7 -71,7 +64,7 @@@
     */
    boolean serviceSecondaryStruct = false;
  
 -  private AlignFile alignFile = null;
 +  private AlignmentFileI alignFile = null;
  
    String inFile;
  
@@@ -78,7 -87,7 +80,7 @@@
     */
    public static final String[] READABLE_FORMATS = new String[] { "BLC",
        "CLUSTAL", "FASTA", "MSF", "PileUp", "PIR", "PFAM", "STH", "PDB",
 -      "JnetFile", "RNAML", PhylipFile.FILE_DESC, JSONFile.FILE_DESC,
 +      "JnetFile", "RNAML", "PHYLIP", "JSON",
        IdentifyFile.FeaturesFile, "HTML", "mmCIF" };
  
    /**
@@@ -87,8 -96,8 +89,8 @@@
     */
    public static final String[] READABLE_EXTENSIONS = new String[] {
        "fa, fasta, mfa, fastq", "aln", "pfam", "msf", "pir", "blc", "amsa",
 -      "sto,stk", "xml,rnaml", PhylipFile.FILE_EXT, JSONFile.FILE_EXT,
 -      ".gff2,gff3", "jar,jvp", HtmlFile.FILE_EXT, "cif" };
 +      "sto,stk", "xml,rnaml", "phy", "json",
 +      ".gff2,gff3", "jar,jvp", "html", "cif" };
  
    /**
     * List of readable formats by application in order corresponding to
     */
    public static final String[] READABLE_FNAMES = new String[] { "Fasta",
        "Clustal", "PFAM", "MSF", "PIR", "BLC", "AMSA", "Stockholm", "RNAML",
 -      PhylipFile.FILE_DESC, JSONFile.FILE_DESC, IdentifyFile.FeaturesFile,
 +      "PHYLIP", "JSON", IdentifyFile.FeaturesFile,
        "Jalview", HtmlFile.FILE_DESC, "mmCIF" };
  
    /**
     */
    public static final String[] WRITEABLE_FORMATS = new String[] { "BLC",
        "CLUSTAL", "FASTA", "MSF", "PileUp", "PIR", "PFAM", "AMSA", "STH",
 -      PhylipFile.FILE_DESC, JSONFile.FILE_DESC };
 +      "PHYLIP", "JSON" };
  
    /**
     * List of extensions corresponding to file format types in WRITABLE_FNAMES
     */
    public static final String[] WRITABLE_EXTENSIONS = new String[] {
        "fa, fasta, mfa, fastq", "aln", "pfam", "msf", "pir", "blc", "amsa",
 -      "sto,stk", PhylipFile.FILE_EXT, JSONFile.FILE_EXT, "jvp" };
 +      "sto,stk", "phy", "json", "jvp" };
  
    /**
     * List of writable formats by the application. Order must correspond with the
     */
    public static final String[] WRITABLE_FNAMES = new String[] { "Fasta",
        "Clustal", "PFAM", "MSF", "PIR", "BLC", "AMSA", "STH",
 -      PhylipFile.FILE_DESC, JSONFile.FILE_DESC, "Jalview" };
 +      "PHYLIP", "JSON", "Jalview" };
  
    public static String INVALID_CHARACTERS = "Contains invalid characters";
  
    }
  
    /**
 -   * check that this format is valid for reading
 -   *
 -   * @param format
 -   *          a format string to be compared with READABLE_FORMATS
 -   * @return true if format is readable
 -   */
 -  public static final boolean isValidFormat(String format)
 -  {
 -    return isValidFormat(format, false);
 -  }
 -
 -  /**
 -   * validate format is valid for IO
 -   *
 -   * @param format
 -   *          a format string to be compared with either READABLE_FORMATS or
 -   *          WRITEABLE_FORMATS
 -   * @param forwriting
 -   *          when true, format is checked for containment in WRITEABLE_FORMATS
 -   * @return true if format is valid
 -   */
 -  public static final boolean isValidFormat(String format,
 -          boolean forwriting)
 -  {
 -    if (format == null)
 -    {
 -      return false;
 -    }
 -    boolean valid = false;
 -    String[] format_list = (forwriting) ? WRITEABLE_FORMATS
 -            : READABLE_FORMATS;
 -    for (String element : format_list)
 -    {
 -      if (element.equalsIgnoreCase(format))
 -      {
 -        return true;
 -      }
 -    }
 -
 -    return valid;
 -  }
 -
 -  /**
     * Constructs the correct filetype parser for a characterised datasource
     *
     * @param inFile
     *          data/data location
 -   * @param type
 +   * @param sourceType
     *          type of datasource
 -   * @param format
 -   *          File format of data provided by datasource
 +   * @param fileFormat
     *
 -   * @return DOCUMENT ME!
 +   * @return
     */
 -  public AlignmentI readFile(String inFile, String type, String format)
 -          throws java.io.IOException
 +  public AlignmentI readFile(String file, DataSourceType sourceType,
 +          FileFormatI fileFormat) throws IOException
    {
 -    // TODO: generalise mapping between format string and io. class instances
 -    // using Constructor.invoke reflection
 -    this.inFile = inFile;
 +    this.inFile = file;
      try
      {
 -      if (format.equals("FASTA"))
 -      {
 -        alignFile = new FastaFile(inFile, type);
 -      }
 -      else if (format.equals("MSF"))
 -      {
 -        alignFile = new MSFfile(inFile, type);
 -      }
 -      else if (format.equals("PileUp"))
 -      {
 -        alignFile = new PileUpfile(inFile, type);
 -      }
 -      else if (format.equals("CLUSTAL"))
 -      {
 -        alignFile = new ClustalFile(inFile, type);
 -      }
 -      else if (format.equals("BLC"))
 -      {
 -        alignFile = new BLCFile(inFile, type);
 -      }
 -      else if (format.equals("PIR"))
 -      {
 -        alignFile = new PIRFile(inFile, type);
 -      }
 -      else if (format.equals("PFAM"))
 -      {
 -        alignFile = new PfamFile(inFile, type);
 -      }
 -      else if (format.equals("JnetFile"))
 -      {
 -        alignFile = new JPredFile(inFile, type);
 -        ((JPredFile) alignFile).removeNonSequences();
 -      }
 -      else if (format.equals("PDB"))
 +      if (fileFormat == FileFormat.PDB || fileFormat == FileFormat.MMCif)
        {
-         StructureImportSettings.addSettings(annotFromStructure,
-                 localSecondaryStruct, serviceSecondaryStruct);
-         alignFile = fileFormat.getAlignmentFile(inFile, sourceType);
+         // TODO obtain config value from preference settings.
+         // Set value to 'true' to test PDB processing with Jmol: JAL-1213
+         boolean isParseWithJMOL = StructureImportSettings
+                 .getDefaultPDBFileParser().equalsIgnoreCase(
+                         StructureImportSettings.StructureParser.JMOL_PARSER
+                                 .toString());
+         if (isParseWithJMOL)
+         {
+           StructureImportSettings.addSettings(annotFromStructure,
+                   localSecondaryStruct, serviceSecondaryStruct);
 -          alignFile = new jalview.ext.jmol.JmolParser(annotFromStructure,
++          alignFile = new JmolParser(annotFromStructure,
+                   localSecondaryStruct, serviceSecondaryStruct, inFile,
 -                  type);
++                  sourceType);
+         }
+         else
+         {
+           StructureImportSettings.addSettings(annotFromStructure,
+                   localSecondaryStruct, serviceSecondaryStruct);
+           StructureImportSettings.setShowSeqFeatures(true);
+           alignFile = new MCview.PDBfile(annotFromStructure,
+                   localSecondaryStruct, serviceSecondaryStruct, inFile,
 -                  type);
++                  sourceType);
+         }
 -        ((StructureFile) alignFile).setDbRefType(format);
 -      }
 -      else if (format.equalsIgnoreCase("mmCIF"))
 -      {
 -        StructureImportSettings.addSettings(annotFromStructure,
 -                localSecondaryStruct, serviceSecondaryStruct);
 -        alignFile = new jalview.ext.jmol.JmolParser(annotFromStructure,
 -                localSecondaryStruct, serviceSecondaryStruct, inFile, type);
 -        ((StructureFile) alignFile).setDbRefType(format);
 -      }
 -      else if (format.equals("STH"))
 -      {
 -        alignFile = new StockholmFile(inFile, type);
++        ((StructureFile) alignFile)
++                .setDbRefType(fileFormat == FileFormat.PDB ? Type.PDB
++                        : Type.MMCIF);
        }
 -      else if (format.equals("SimpleBLAST"))
 -      {
 -        alignFile = new SimpleBlastFile(inFile, type);
 -      }
 -      else if (format.equals(PhylipFile.FILE_DESC))
 -      {
 -        alignFile = new PhylipFile(inFile, type);
 -      }
 -      else if (format.equals(JSONFile.FILE_DESC))
 -      {
 -        alignFile = new JSONFile(inFile, type);
 -      }
 -      else if (format.equals(HtmlFile.FILE_DESC))
 -      {
 -        alignFile = new HtmlFile(inFile, type);
 -      }
 -      else if (format.equals("RNAML"))
 -      {
 -        alignFile = new RnamlFile(inFile, type);
 -      }
 -      else if (format.equals(IdentifyFile.FeaturesFile))
 +      else
        {
 -        alignFile = new FeaturesFile(true, inFile, type);
 -      }
 -      return buildAlignmentFrom(alignFile);
 +        alignFile = fileFormat.getAlignmentFile(inFile, sourceType);
 +      }
 +        // new FastaFile(inFile, sourceType);
 +        // new MSFfile(inFile, sourceType);
 +        // new PileUpfile(inFile, sourceType);
 +        // new ClustalFile(inFile, sourceType);
 +        // new BLCFile(inFile, sourceType);
 +        // new PIRFile(inFile, sourceType);
 +        // new PfamFile(inFile, sourceType);
 +        // alignFile = new JPredFile(inFile, sourceType);
 +        // ((JPredFile) alignFile).removeNonSequences();
 +        // new StockholmFile(inFile, sourceType);
 +        // new SimpleBlastFile(inFile, sourceType);
 +        // new PhylipFile(inFile, sourceType);
 +        // new JSONFile(inFile, sourceType);
 +        // new HtmlFile(inFile, sourceType);
 +        // new RnamlFile(inFile, sourceType);
 +//        alignFile = new FeaturesFile(true, inFile, sourceType);
 +      return buildAlignmentFromFile();
      } catch (Exception e)
      {
        e.printStackTrace();
 -      System.err.println("Failed to read alignment using the '" + format
 +      System.err.println("Failed to read alignment using the '"
 +              + fileFormat
                + "' reader.\n" + e);
  
        if (e.getMessage() != null
                && e.getMessage().startsWith(INVALID_CHARACTERS))
        {
 -        throw new java.io.IOException(e.getMessage());
 +        throw new IOException(e.getMessage());
        }
  
        // Finally test if the user has pasted just the sequence, no id
 -      if (type.equalsIgnoreCase("Paste"))
 +      if (sourceType == DataSourceType.PASTE)
        {
          try
          {
            // Possible sequence is just residues with no label
 -          alignFile = new FastaFile(">UNKNOWN\n" + inFile, "Paste");
 -          return buildAlignmentFrom(alignFile);
 +          alignFile = new FastaFile(">UNKNOWN\n" + inFile,
 +                  DataSourceType.PASTE);
 +          return buildAlignmentFromFile();
  
          } catch (Exception ex)
          {
            if (ex.toString().startsWith(INVALID_CHARACTERS))
            {
 -            throw new java.io.IOException(e.getMessage());
 +            throw new IOException(e.getMessage());
            }
  
            ex.printStackTrace();
          }
        }
 -      if (format.equalsIgnoreCase("HTML"))
 +      if (fileFormat == FileFormat.Html)
        {
          throw new IOException(e.getMessage());
        }
 -      // If we get to this stage, the format was not supported
 -      throw new java.io.IOException(SUPPORTED_FORMATS);
      }
 +    throw new FileFormatException(SUPPORTED_FORMATS);
    }
  
    /**
     * @param format
     *          File format of data that will be provided by datasource
     *
 -   * @return DOCUMENT ME!
 +   * @return
     */
 -  public AlignmentI readFromFile(FileParse source, String format)
 -          throws java.io.IOException
 +  public AlignmentI readFromFile(FileParse source, FileFormatI format)
 +          throws IOException
    {
 -    // TODO: generalise mapping between format string and io. class instances
 -    // using Constructor.invoke reflection
 -    // This is exactly the same as the readFile method except we substitute
 -    // 'inFile, type' with 'source'
      this.inFile = source.getInFile();
 -    String type = source.type;
 +    DataSourceType type = source.dataSourceType;
      try
      {
 -      if (format.equals("FASTA"))
 -      {
 -        alignFile = new FastaFile(source);
 -      }
 -      else if (format.equals("MSF"))
 -      {
 -        alignFile = new MSFfile(source);
 -      }
 -      else if (format.equals("PileUp"))
 -      {
 -        alignFile = new PileUpfile(source);
 -      }
 -      else if (format.equals("CLUSTAL"))
 -      {
 -        alignFile = new ClustalFile(source);
 -      }
 -      else if (format.equals("BLC"))
 -      {
 -        alignFile = new BLCFile(source);
 -      }
 -      else if (format.equals("PIR"))
 -      {
 -        alignFile = new PIRFile(source);
 -      }
 -      else if (format.equals("PFAM"))
 -      {
 -        alignFile = new PfamFile(source);
 -      }
 -      else if (format.equals("JnetFile"))
 -      {
 -        alignFile = new JPredFile(source);
 -        ((JPredFile) alignFile).removeNonSequences();
 -      }
 -      else if (format.equals("PDB"))
 +      if (format == FileFormat.PDB || format == FileFormat.MMCif)
        {
-         StructureImportSettings.addSettings(annotFromStructure,
-                 localSecondaryStruct, serviceSecondaryStruct);
-         alignFile = format.getAlignmentFile(source);
+         // TODO obtain config value from preference settings
+         boolean isParseWithJMOL = false;
+         if (isParseWithJMOL)
+         {
+           StructureImportSettings.addSettings(annotFromStructure,
+                   localSecondaryStruct, serviceSecondaryStruct);
 -          alignFile = new jalview.ext.jmol.JmolParser(annotFromStructure,
++          alignFile = new JmolParser(annotFromStructure,
+                   localSecondaryStruct, serviceSecondaryStruct, source);
+         }
+         else
+         {
+           StructureImportSettings.setShowSeqFeatures(true);
+           alignFile = new MCview.PDBfile(annotFromStructure,
+                   localSecondaryStruct, serviceSecondaryStruct, source);
+         }
+         ((StructureFile) alignFile).setDbRefType(Type.PDB);
        }
 -      else if (format.equalsIgnoreCase("mmCIF"))
 -      {
 -        StructureImportSettings.addSettings(annotFromStructure,
 -                localSecondaryStruct, serviceSecondaryStruct);
 -        alignFile = new jalview.ext.jmol.JmolParser(annotFromStructure,
 -                localSecondaryStruct, serviceSecondaryStruct, source);
 -        ((StructureFile) alignFile).setDbRefType(Type.MMCIF);
 -      }
 -      else if (format.equals("STH"))
 -      {
 -        alignFile = new StockholmFile(source);
 -      }
 -      else if (format.equals("RNAML"))
 -      {
 -        alignFile = new RnamlFile(source);
 -      }
 -      else if (format.equals("SimpleBLAST"))
 -      {
 -        alignFile = new SimpleBlastFile(source);
 -      }
 -      else if (format.equals(PhylipFile.FILE_DESC))
 -      {
 -        alignFile = new PhylipFile(source);
 -      }
 -      else if (format.equals(IdentifyFile.FeaturesFile))
 -      {
 -        alignFile = new FeaturesFile(inFile, type);
 -      }
 -      else if (format.equals(JSONFile.FILE_DESC))
 -      {
 -        alignFile = new JSONFile(source);
 -      }
 -      else if (format.equals(HtmlFile.FILE_DESC))
 +      else
        {
 -        alignFile = new HtmlFile(source);
 +        alignFile = format.getAlignmentFile(source);
        }
  
 -      return buildAlignmentFrom(alignFile);
 +      return buildAlignmentFromFile();
  
      } catch (Exception e)
      {
        if (e.getMessage() != null
                && e.getMessage().startsWith(INVALID_CHARACTERS))
        {
 -        throw new java.io.IOException(e.getMessage());
 +        throw new FileFormatException(e.getMessage());
        }
  
        // Finally test if the user has pasted just the sequence, no id
 -      if (type.equalsIgnoreCase("Paste"))
 +      if (type == DataSourceType.PASTE)
        {
          try
          {
            // Possible sequence is just residues with no label
 -          alignFile = new FastaFile(">UNKNOWN\n" + inFile, "Paste");
 -          return buildAlignmentFrom(alignFile);
 +          alignFile = new FastaFile(">UNKNOWN\n" + inFile,
 +                  DataSourceType.PASTE);
 +          return buildAlignmentFromFile();
  
          } catch (Exception ex)
          {
            if (ex.toString().startsWith(INVALID_CHARACTERS))
            {
 -            throw new java.io.IOException(e.getMessage());
 +            throw new IOException(e.getMessage());
            }
  
            ex.printStackTrace();
        }
  
        // If we get to this stage, the format was not supported
 -      throw new java.io.IOException(SUPPORTED_FORMATS);
 +      throw new FileFormatException(SUPPORTED_FORMATS);
      }
    }
  
     * boilerplate method to handle data from an AlignFile and construct a new
     * alignment or import to an existing alignment
     * 
 -   * @param alignFile2
     * @return AlignmentI instance ready to pass to a UI constructor
     */
 -  private AlignmentI buildAlignmentFrom(AlignFile alignFile2)
 +  private AlignmentI buildAlignmentFromFile()
    {
      // Standard boilerplate for creating alignment from parser
      // alignFile.configureForView(viewpanel);
     * @param selectedOnly
     * @return flatfile in a string
     */
 -  public String formatSequences(String format, boolean jvsuffix,
 +  public String formatSequences(FileFormatI format, boolean jvsuffix,
            AlignmentViewPanel ap, boolean selectedOnly)
    {
  
     *
     * @return alignment flat file contents
     */
 -  public String formatSequences(String format, AlignmentI alignment,
 +  public String formatSequences(FileFormatI format, AlignmentI alignment,
            boolean jvsuffix)
    {
      try
      {
 -      AlignFile afile = null;
 -      if (format.equalsIgnoreCase("FASTA"))
 -      {
 -        afile = new FastaFile();
 -      }
 -      else if (format.equalsIgnoreCase("MSF"))
 -      {
 -        afile = new MSFfile();
 -      }
 -      else if (format.equalsIgnoreCase("PileUp"))
 -      {
 -        afile = new PileUpfile();
 -      }
 -      else if (format.equalsIgnoreCase("CLUSTAL"))
 -      {
 -        afile = new ClustalFile();
 -      }
 -      else if (format.equalsIgnoreCase("BLC"))
 -      {
 -        afile = new BLCFile();
 -      }
 -      else if (format.equalsIgnoreCase("PIR"))
 -      {
 -        afile = new PIRFile();
 -      }
 -      else if (format.equalsIgnoreCase("PFAM"))
 -      {
 -        afile = new PfamFile();
 -      }
 -      else if (format.equalsIgnoreCase("STH"))
 -      {
 -        afile = new StockholmFile(alignment);
 -      }
 -      else if (format.equalsIgnoreCase("AMSA"))
 -      {
 -        afile = new AMSAFile(alignment);
 -      }
 -      else if (format.equalsIgnoreCase(PhylipFile.FILE_DESC))
 -      {
 -        afile = new PhylipFile();
 -      }
 -      else if (format.equalsIgnoreCase(JSONFile.FILE_DESC))
 -      {
 -        afile = new JSONFile();
 -      }
 -      else if (format.equalsIgnoreCase("RNAML"))
 -      {
 -        afile = new RnamlFile();
 -      }
 -
 -      else
 -      {
 -        throw new Exception(
 -                MessageManager
 -                        .getString("error.implementation_error_unknown_file_format_string"));
 -      }
 +      AlignmentFileI afile = format.getAlignmentFile();
  
        afile.setNewlineString(newline);
 -      afile.addJVSuffix(jvsuffix);
        afile.setExportSettings(exportSettings);
        afile.configureForView(viewpanel);
  
        // check whether we were given a specific alignment to export, rather than
        // the one in the viewpanel
 +      SequenceI[] seqs = null;
        if (viewpanel == null || viewpanel.getAlignment() == null
                || viewpanel.getAlignment() != alignment)
        {
 -        afile.setSeqs(alignment.getSequencesArray());
 +        seqs = alignment.getSequencesArray();
        }
        else
        {
 -        afile.setSeqs(viewpanel.getAlignment().getSequencesArray());
 +        seqs = viewpanel.getAlignment().getSequencesArray();
        }
  
 -      String afileresp = afile.print();
 +      String afileresp = afile.print(seqs, jvsuffix);
        if (afile.hasWarningMessage())
        {
          System.err.println("Warning raised when writing as " + format
      return null;
    }
  
 -  public static String checkProtocol(String file)
 +  public static DataSourceType checkProtocol(String file)
    {
 -    String protocol = FILE;
 +    DataSourceType protocol = DataSourceType.FILE;
      String ft = file.toLowerCase().trim();
      if (ft.indexOf("http:") == 0 || ft.indexOf("https:") == 0
              || ft.indexOf("file:") == 0)
      {
 -      protocol = URL;
 +      protocol = DataSourceType.URL;
      }
      return protocol;
    }
            System.gc();
            long memf = -r.totalMemory() + r.freeMemory();
            long t1 = -System.currentTimeMillis();
 -          AlignmentI al = afa.readFile(args[i], FILE,
 -                  new IdentifyFile().identify(args[i], FILE));
 +          AlignmentI al = afa
 +                  .readFile(args[i], DataSourceType.FILE,
 +                          new IdentifyFile().identify(args[i],
 +                                  DataSourceType.FILE));
            t1 += System.currentTimeMillis();
            System.gc();
            memf += r.totalMemory() - r.freeMemory();
              try
              {
                System.out.println(new AppletFormatAdapter().formatSequences(
 -                      "FASTA", al, true));
 +                      FileFormat.Fasta, al, true));
              } catch (Exception e)
              {
                System.err
     * @param format
     * @return protocol that yields the data parsable as the given type
     */
 -  public static String resolveProtocol(String file, String format)
 +  public static DataSourceType resolveProtocol(String file,
 +          FileFormatI format)
    {
      return resolveProtocol(file, format, false);
    }
  
 -  public static String resolveProtocol(String file, String format,
 -          boolean debug)
 +  public static DataSourceType resolveProtocol(String file,
 +          FileFormatI format, boolean debug)
    {
      // TODO: test thoroughly!
 -    String protocol = null;
 +    DataSourceType protocol = null;
      if (debug)
      {
        System.out.println("resolving datasource started with:\n>>file\n"
          System.err.println("Resource '" + file + "' was "
                  + (rtn ? "" : "not") + " located by classloader.");
        }
 -      ;
        if (rtn)
        {
 -        protocol = AppletFormatAdapter.CLASSLOADER;
 +        protocol = DataSourceType.CLASSLOADER;
        }
  
      } catch (Exception ex)
  
      if (file.indexOf("://") > -1)
      {
 -      protocol = AppletFormatAdapter.URL;
 +      protocol = DataSourceType.URL;
      }
      else
      {
        // skipping codebase prepend check.
 -      protocol = AppletFormatAdapter.FILE;
 +      protocol = DataSourceType.FILE;
      }
      FileParse fp = null;
      try
        {
          System.out.println("Accessing as paste.");
        }
 -      protocol = AppletFormatAdapter.PASTE;
 +      protocol = DataSourceType.PASTE;
        fp = null;
        try
        {
      {
        return null;
      }
 -    if (format == null || format.length() == 0)
 +    if (format == null)
      {
        return protocol;
      }
      {
        try
        {
 -        String idformat = new jalview.io.IdentifyFile().identify(file,
 +        FileFormatI idformat = new IdentifyFile().identify(file,
                  protocol);
          if (idformat == null)
          {
            System.err.println("File deemed not accessible via " + protocol);
            e.printStackTrace();
          }
 -        ;
 -
        }
      }
      return null;
    }
  
 -  public AlignFile getAlignFile()
 +  public AlignmentFileI getAlignFile()
    {
      return alignFile;
    }
 -
 -  public void setAlignFile(AlignFile alignFile)
 -  {
 -    this.alignFile = alignFile;
 -  }
  }
@@@ -22,13 -22,12 +22,15 @@@ package jalview.io
  
  import jalview.api.AlignExportSettingI;
  import jalview.api.AlignmentViewPanel;
++import jalview.bin.Cache;
  import jalview.datamodel.AlignmentExportData;
  import jalview.exceptions.NoFileSelectedException;
 +import jalview.gui.AlignFrame;
  import jalview.gui.IProgressIndicator;
  import jalview.gui.OOMWarning;
  import jalview.json.binding.biojs.BioJSReleasePojo;
  import jalview.json.binding.biojs.BioJSRepositoryPojo;
++import jalview.util.ImageMaker;
  import jalview.util.MessageManager;
  
  import java.io.BufferedInputStream;
@@@ -126,11 -125,11 +128,11 @@@ public class BioJsHTMLOutpu
          }
  
        };
 -      AlignmentExportData exportData = jalview.gui.AlignFrame
 -              .getAlignmentForExport(JSONFile.FILE_DESC,
 +      AlignmentExportData exportData = AlignFrame
 +              .getAlignmentForExport(FileFormat.Json,
                        ap.getAlignViewport(), exportSettings);
        String bioJSON = new FormatAdapter(ap, exportData.getSettings())
 -              .formatSequences(JSONFile.FILE_DESC, exportData
 +              .formatSequences(FileFormat.Json, exportData
                        .getAlignment(), exportData.getOmitHidden(),
                        exportData.getStartEndPostions(), ap
                                .getAlignViewport().getColumnSelection());
      }
  
      JalviewFileChooser jvFileChooser = new JalviewFileChooser(
--            jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
--            new String[] { "html" }, new String[] { "HTML files" },
--            "HTML files");
++            Cache.getProperty("LAST_DIRECTORY"), ImageMaker.HTML_EXTENSION,
++            ImageMaker.HTML_EXTENSION, ImageMaker.HTML_EXTENSION);
      jvFileChooser.setFileView(new JalviewFileView());
  
      jvFileChooser.setDialogTitle(MessageManager
      int fileChooserOpt = jvFileChooser.showSaveDialog(null);
      if (fileChooserOpt == JalviewFileChooser.APPROVE_OPTION)
      {
--      jalview.bin.Cache.setProperty("LAST_DIRECTORY", jvFileChooser
++      Cache.setProperty("LAST_DIRECTORY", jvFileChooser
                .getSelectedFile().getParent());
        selectedFile = jvFileChooser.getSelectedFile().getPath();
      }
index bca365f,0000000..a7113f6
mode 100644,000000..100644
--- /dev/null
@@@ -1,529 -1,0 +1,530 @@@
 +package jalview.io;
 +
++import jalview.datamodel.PDBEntry;
 +import jalview.ext.jmol.JmolParser;
 +import jalview.structure.StructureImportSettings;
 +
 +import java.io.IOException;
 +import java.util.HashMap;
 +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)
 +            throws IOException
 +    {
 +      return new FastaFile(source);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile()
 +    {
 +      return new FastaFile();
 +    }
 +  },
 +  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)
 +            throws IOException
 +    {
 +      return new PfamFile(source);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile()
 +    {
 +      return new PfamFile();
 +    }
 +  },
 +  Stockholm("STH", "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)
 +            throws IOException
 +    {
 +      return new StockholmFile(source);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile()
 +    {
 +      return new StockholmFile();
 +    }
 +
 +  },
 +
 +  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)
 +            throws IOException
 +    {
 +      return new PIRFile(source);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile()
 +    {
 +      return new PIRFile();
 +    }
 +  },
 +  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)
 +            throws IOException
 +    {
 +      return new BLCFile(source);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile()
 +    {
 +      return new BLCFile();
 +    }
 +  },
 +  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)
 +            throws IOException
 +    {
 +      return new HtmlFile(source);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile()
 +    {
 +      return new HtmlFile();
 +    }
 +
 +    @Override
 +    public boolean isComplexAlignFile()
 +    {
 +      return true;
 +    }
 +
 +  },
 +  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)
 +            throws IOException
 +    {
 +      return new RnamlFile(source);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile()
 +    {
 +      return new RnamlFile();
 +    }
 +
 +  },
 +  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)
 +            throws IOException
 +    {
 +      return new JSONFile(source);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile()
 +    {
 +      return new JSONFile();
 +    }
 +
 +    @Override
 +    public boolean isComplexAlignFile()
 +    {
 +      return true;
 +    }
 +
 +  },
 +  Pileup("PileUp", "?", false, false)
 +  {
 +    @Override
 +    public AlignmentFileI getAlignmentFile(String inFile,
 +            DataSourceType sourceType) throws IOException
 +    {
 +      return new PileUpfile(inFile, sourceType);
 +    }    @Override
 +    public AlignmentFileI getAlignmentFile(FileParse source)
 +            throws IOException
 +    {
 +      return new PileUpfile(source);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile()
 +    {
 +      return new PileUpfile();
 +    }
 +
 +  },
 +  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)
 +            throws IOException
 +    {
 +      return new MSFfile(source);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile()
 +    {
 +      return new MSFfile();
 +    }
 +
 +  },
 +  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)
 +            throws IOException
 +    {
 +      return new ClustalFile(source);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile()
 +    {
 +      return new ClustalFile();
 +    }
 +  },
 +  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)
 +            throws IOException
 +    {
 +      return new PhylipFile(source);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile()
 +    {
 +      return new PhylipFile();
 +    }
 +  },
 +  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)
 +            throws IOException
 +    {
 +      JPredFile af = new JPredFile(source);
 +      af.removeNonSequences();
 +      return af;
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile()
 +    {
 +      return null; // todo is this called?
 +    }
 +
 +  },
 +  Features("GFF or Jalview features", "gff2,gff3", false, false)
 +  {
 +    @Override
 +    public AlignmentFileI getAlignmentFile(String inFile,
 +            DataSourceType sourceType) throws IOException
 +    {
 +      return new FeaturesFile(true, inFile, sourceType);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile(FileParse source)
 +            throws IOException
 +    {
 +      return new FeaturesFile(source);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile()
 +    {
 +      return new FeaturesFile();
 +    }
 +  },
 +  PDB("PDB", "", false, 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
-               .getCurrentDefaultFormat().equalsIgnoreCase("PDB");
++      boolean isParseWithJMOL = StructureImportSettings
++              .getDefaultStructureFileFormat() != PDBEntry.Type.PDB;
 +      if (isParseWithJMOL)
 +      {
 +        return new JmolParser(
 +                StructureImportSettings.isVisibleChainAnnotation(),
-                 StructureImportSettings.isPredictSecondaryStructure(),
++                StructureImportSettings.isProcessSecondaryStructure(),
 +                StructureImportSettings.isExternalSecondaryStructure(),
 +                inFile,
 +                sourceType);
 +      }
 +      else
 +      {
 +        StructureImportSettings.setShowSeqFeatures(true);
 +        return new MCview.PDBfile(
 +                StructureImportSettings.isVisibleChainAnnotation(),
-                 StructureImportSettings.isPredictSecondaryStructure(),
++                StructureImportSettings.isProcessSecondaryStructure(),
 +                StructureImportSettings.isExternalSecondaryStructure(),
 +                inFile,
 +                sourceType);
 +      }
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile(FileParse source)
 +            throws IOException
 +    {
-       boolean isParseWithJMOL = !StructureImportSettings
-               .getCurrentDefaultFormat().equalsIgnoreCase("PDB");
++      boolean isParseWithJMOL = StructureImportSettings
++              .getDefaultStructureFileFormat() != PDBEntry.Type.PDB;
 +      if (isParseWithJMOL)
 +      {
 +        return new JmolParser(
 +                StructureImportSettings.isVisibleChainAnnotation(),
-                 StructureImportSettings.isPredictSecondaryStructure(),
++                StructureImportSettings.isProcessSecondaryStructure(),
 +                StructureImportSettings.isExternalSecondaryStructure(),
 +                source);
 +      }
 +      else
 +      {
 +        StructureImportSettings.setShowSeqFeatures(true);
 +        return new MCview.PDBfile(
 +                StructureImportSettings.isVisibleChainAnnotation(),
-                 StructureImportSettings.isPredictSecondaryStructure(),
++                StructureImportSettings.isProcessSecondaryStructure(),
 +                StructureImportSettings.isExternalSecondaryStructure(),
 +                source);
 +      }
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile()
 +    {
 +      return new JmolParser(); // todo or null?
 +    }
 +  },
 +  MMCif("mmCIF", "cif", false, false)
 +  {
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile(String inFile,
 +            DataSourceType sourceType) throws IOException
 +    {
 +      return new JmolParser(
 +              StructureImportSettings.isVisibleChainAnnotation(),
-               StructureImportSettings.isPredictSecondaryStructure(),
++              StructureImportSettings.isProcessSecondaryStructure(),
 +              StructureImportSettings.isExternalSecondaryStructure(),
 +              inFile, sourceType);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile(FileParse source)
 +            throws IOException
 +    {
 +      return new JmolParser(
 +              StructureImportSettings.isVisibleChainAnnotation(),
-               StructureImportSettings.isPredictSecondaryStructure(),
++              StructureImportSettings.isProcessSecondaryStructure(),
 +              StructureImportSettings.isExternalSecondaryStructure(),
 +              source);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile()
 +    {
 +      return new JmolParser(); // todo or null?
 +    }
 +  },
 +  Jalview("Jalview", "jar,jvp", true, false)
 +  {
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile(String inFile,
 +            DataSourceType sourceType) throws IOException
 +    {
 +      return null;
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile(FileParse source)
 +            throws IOException
 +    {
 +      return null;
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile()
 +    {
 +      return null;
 +    }
 +  };
 +
 +  /**
 +   * 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())
 +    {
 +      names.put(format.toString().toUpperCase(), format);
 +    }
 +  }
 +
 +  private boolean writable;
 +
 +  private boolean readable;
 +
 +  private String extensions;
 +
 +  private String name;
 +
 +  @Override
 +  public boolean isComplexAlignFile()
 +  {
 +    return false;
 +  }
 +
 +  @Override
 +  public String getShortDescription()
 +  {
 +    return toString();
 +  }
 +
 +  /**
 +   * 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()
 +  {
 +    return readable;
 +  }
 +
 +  @Override
 +  public boolean isWritable()
 +  {
 +    return writable;
 +  }
 +
 +  /**
 +   * Constructor
 +   * 
 +   * @param shortName
 +   * @param extensions
 +   *          comma-separated list of file extensions associated with the format
 +   * @param isReadable
 +   * @param isWritable
 +   */
 +  private FileFormat(String shortName, String extensions,
 +          boolean isReadable, boolean isWritable)
 +  {
 +    this.name = shortName;
 +    this.extensions = extensions;
 +    this.readable = isReadable;
 +    this.writable = isWritable;
 +  }
 +
 +  @Override
 +  public String getExtensions()
 +  {
 +    return extensions;
 +  }
 +}
   */
  package jalview.io;
  
++import jalview.bin.Cache;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.SequenceI;
  import jalview.gui.AlignViewport;
  import jalview.gui.AlignmentPanel;
  import jalview.gui.FeatureRenderer;
  import jalview.gui.SequenceRenderer;
++import jalview.util.BrowserLauncher;
++import jalview.util.ImageMaker;
  import jalview.util.MessageManager;
  
  import java.awt.Color;
  import java.awt.Font;
++import java.io.FileWriter;
  import java.io.PrintWriter;
  
  public class HTMLOutput
@@@ -52,9 -52,9 +56,8 @@@
      fr.transferSettings(fr1);
  
      JalviewFileChooser chooser = new JalviewFileChooser(
--            jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
--            new String[] { "html" }, new String[] { "HTML files" },
--            "HTML files");
++            Cache.getProperty("LAST_DIRECTORY"), ImageMaker.HTML_EXTENSION,
++            "HTML files", "HTML files");
  
      chooser.setFileView(new JalviewFileView());
      chooser.setDialogTitle(MessageManager.getString("label.save_as_html"));
      if (value == JalviewFileChooser.APPROVE_OPTION)
      {
        String choice = chooser.getSelectedFile().getPath();
--      jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
++      Cache.setProperty("LAST_DIRECTORY", chooser
                .getSelectedFile().getParent());
  
        try
        {
--        PrintWriter out = new java.io.PrintWriter(new java.io.FileWriter(
--                choice));
++        PrintWriter out = new PrintWriter(new FileWriter(choice));
          out.println("<HTML>");
          out.println("<style type=\"text/css\">");
          out.println("<!--");
  
          out.println("\n</body>\n</html>");
          out.close();
--        jalview.util.BrowserLauncher.openURL("file:///" + choice);
++        BrowserLauncher.openURL("file:///" + choice);
        } catch (Exception ex)
        {
          ex.printStackTrace();
@@@ -22,6 -22,6 +22,7 @@@ package jalview.io
  
  import jalview.api.AlignExportSettingI;
  import jalview.api.FeatureRenderer;
++import jalview.bin.Cache;
  import jalview.datamodel.AlignmentExportData;
  import jalview.datamodel.SequenceI;
  import jalview.gui.AlignViewport;
@@@ -30,6 -30,6 +31,7 @@@ import jalview.gui.HTMLOptions
  import jalview.gui.IProgressIndicator;
  import jalview.gui.OOMWarning;
  import jalview.math.AlignmentDimension;
++import jalview.util.ImageMaker;
  import jalview.util.MessageManager;
  
  import java.awt.Color;
@@@ -114,7 -114,8 +116,7 @@@ public class HtmlSvgOutpu
          try
          {
            setProgressMessage(null);
 -          setProgressMessage(MessageManager
 -.formatMessage(
 +          setProgressMessage(MessageManager.formatMessage(
                    "status.exporting_alignment_as_x_file", "HTML"));
            AlignmentDimension aDimension = ap.getAlignmentDimension();
            SVGGraphics2D g1 = new SVGGraphics2D(aDimension.getWidth(),
  
              };
              AlignmentExportData exportData = jalview.gui.AlignFrame
 -                    .getAlignmentForExport(JSONFile.FILE_DESC, av,
 +                    .getAlignmentForExport(FileFormat.Json, av,
                              exportSettings);
              jsonData = new FormatAdapter(ap, exportData.getSettings())
 -                    .formatSequences(JSONFile.FILE_DESC,
 +                    .formatSequences(FileFormat.Json,
                              exportData.getAlignment(),
                              exportData.getOmitHidden(),
                              exportData.getStartEndPostions(),
  
    static JalviewFileChooser getHTMLChooser()
    {
--    return new jalview.io.JalviewFileChooser(
--            jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
--            new String[] { "html" },
--            new String[] { "Hypertext Markup Language" },
--            "Hypertext Markup Language");
++    return new JalviewFileChooser(Cache.getProperty("LAST_DIRECTORY"),
++            ImageMaker.HTML_EXTENSION, ImageMaker.HTML_EXTENSION,
++            ImageMaker.HTML_EXTENSION);
    }
  
    public int printUnwrapped(int pwidth, int pheight, int pi, Graphics... pg)
@@@ -71,6 -71,10 +71,6 @@@ public class JSONFile extends AlignFil
  
    private String application = "Jalview";
  
 -  public static final String FILE_EXT = "json";
 -
 -  public static final String FILE_DESC = "JSON";
 -
    private String globalColourScheme;
  
    private boolean showSeqFeatures;
      super(source);
    }
  
 -  public JSONFile(String inFile, String type) throws IOException
 +  public JSONFile(String inFile, DataSourceType sourceType)
 +          throws IOException
    {
 -    super(inFile, type);
 +    super(inFile, sourceType);
    }
  
    @Override
    }
  
    @Override
 -  public String print()
 +  public String print(SequenceI[] sqs, boolean jvsuffix)
    {
      String jsonOutput = null;
      try
        }
  
        int count = 0;
 -      for (SequenceI seq : seqs)
 +      for (SequenceI seq : sqs)
        {
          StringBuilder name = new StringBuilder();
          name.append(seq.getName()).append("/").append(seq.getStart())
        if (exportSettings.isExportFeatures())
        {
          jsonAlignmentPojo
 -                .setSeqFeatures(sequenceFeatureToJsonPojo(seqs, fr));
 +                .setSeqFeatures(sequenceFeatureToJsonPojo(sqs, fr));
        }
  
        if (exportSettings.isExportGroups() && seqGroups != null
    }
  
    public List<SequenceFeaturesPojo> sequenceFeatureToJsonPojo(
 -          List<SequenceI> seqs, FeatureRenderer fr)
 +          SequenceI[] sqs, FeatureRenderer fr)
    {
      displayedFeatures = (fr == null) ? null : fr.getFeaturesDisplayed();
      List<SequenceFeaturesPojo> sequenceFeaturesPojo = new ArrayList<SequenceFeaturesPojo>();
 -    for (SequenceI seq : seqs)
 +    if (sqs == null)
 +    {
 +      return sequenceFeaturesPojo;
 +    }
 +
 +    for (SequenceI seq : sqs)
      {
        SequenceI dataSetSequence = seq.getDatasetSequence();
        SequenceFeature[] seqFeatures = (dataSetSequence == null) ? null
    @Override
    public void configureForView(AlignmentViewPanel avpanel)
    {
+     if (avpanel == null)
+     {
+       return;
+     }
      super.configureForView(avpanel);
      AlignViewportI viewport = avpanel.getAlignViewport();
      AlignmentI alignment = viewport.getAlignment();
@@@ -31,6 -31,6 +31,9 @@@ import java.awt.HeadlessException
  import java.awt.event.MouseAdapter;
  import java.awt.event.MouseEvent;
  import java.io.File;
++import java.util.ArrayList;
++import java.util.Collections;
++import java.util.List;
  import java.util.StringTokenizer;
  import java.util.Vector;
  
@@@ -41,6 -41,6 +44,7 @@@ import javax.swing.JOptionPane
  import javax.swing.JPanel;
  import javax.swing.JScrollPane;
  import javax.swing.SpringLayout;
++import javax.swing.plaf.basic.BasicFileChooserUI;
  
  /**
   * Enhanced file chooser dialog box.
   */
  public class JalviewFileChooser extends JFileChooser
  {
-   public JalviewFileChooser(String dir)
++  /**
++   * Factory method to return a file chooser that offers readable alignment file
++   * formats
++   * 
++   * @param directory
++   * @param selected
++   * @param selectAll
++   * @return
++   */
++  public static JalviewFileChooser forRead(String directory,
++          String selected, boolean selectAll)
 +  {
-     super(safePath(dir));
-     setAccessory(new RecentlyOpened());
++    List<String> extensions = new ArrayList<String>();
++    List<String> descs = new ArrayList<String>();
++    for (FileFormatI format : FileFormat.values())
++    {
++      if (format.isReadable())
++      {
++        extensions.add(format.getExtensions());
++        descs.add(format.getShortDescription());
++      }
++    }
++    return new JalviewFileChooser(directory,
++            extensions.toArray(new String[extensions.size()]),
++            descs.toArray(new String[descs.size()]),
++            selected);
 +  }
 +
-   private static File safePath(String dir)
++  /**
++   * Factory method to return a file chooser that offers writable alignment file
++   * formats
++   * 
++   * @param directory
++   * @param selected
++   * @param selectAll
++   * @return
++   */
++  public static JalviewFileChooser forWrite(String directory,
++          String selected, boolean selectAll)
 +  {
-     if (dir == null)
++    // TODO in Java 8, forRead and forWrite can be a single method
++    // 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())
 +    {
-       return null;
++      if (format.isWritable())
++      {
++        extensions.add(format.getExtensions());
++        descs.add(format.getShortDescription());
++      }
 +    }
++    return new JalviewFileChooser(directory,
++            extensions.toArray(new String[extensions.size()]),
++            descs.toArray(new String[descs.size()]), selected);
++  }
 +
-     File f = new File(dir);
-     if (f.getName().indexOf(':') > -1)
-     {
-       return null;
-     }
-     return f;
+   public JalviewFileChooser(String dir)
+   {
+     super(safePath(dir));
+     setAccessory(new RecentlyOpened());
    }
  
-   public JalviewFileChooser(String dir, String[] suffix, String[] desc,
-           String selected, boolean selectAll)
++  public JalviewFileChooser(String dir, String extension, String desc,
++          String selected)
 +  {
 +    super(safePath(dir));
-     init(suffix, desc, selected, selectAll);
++    init(Collections.singletonList(new String[] { extension, desc }),
++            selected);
 +  }
 +
-   public JalviewFileChooser(String dir, String[] suffix, String[] desc,
++  public JalviewFileChooser(String dir, String[] extensions, String[] descs,
 +          String selected)
 +  {
 +    super(safePath(dir));
-     init(suffix, desc, selected, true);
++    if (extensions.length == descs.length)
++    {
++      List<String[]> formats = new ArrayList<String[]>();
++      for (int i = 0; i < extensions.length; i++)
++      {
++        formats.add(new String[] { extensions[i], descs[i] });
++      }
++      init(formats, selected);
++    }
++    else
++    {
++      System.err.println("JalviewFileChooser arguments mismatch: "
++              + extensions + ", " + descs);
++    }
 +  }
 +
-   public JalviewFileChooser(String property, FileFormatI currentFileFormat,
-           boolean b)
+   private static File safePath(String dir)
    {
-     todo write this
-     // TODO Auto-generated constructor stub
+     if (dir == null)
+     {
+       return null;
+     }
+     File f = new File(dir);
+     if (f.getName().indexOf(':') > -1)
+     {
+       return null;
+     }
+     return f;
    }
  
-   void init(String[] suffix, String[] desc, String selected,
-           boolean selectAll)
 -  public JalviewFileChooser(String dir, String[] suffix, String[] desc,
 -          String selected, boolean selectAll)
 -  {
 -    super(safePath(dir));
 -    init(suffix, desc, selected, selectAll);
 -  }
 -
 -  public JalviewFileChooser(String dir, String[] suffix, String[] desc,
 -          String selected)
 -  {
 -    super(safePath(dir));
 -    init(suffix, desc, selected, true);
 -  }
 -
 -  void init(String[] suffix, String[] desc, String selected,
 -          boolean selectAll)
++  /**
++   * 
++   * @param formats
++   *          a list of {extensions, description} for each file format
++   * @param selected
++   */
++  void init(List<String[]> formats, String selected)
    {
  
      JalviewFileFilter chosen = null;
  
      // SelectAllFilter needs to be set first before adding further
      // file filters to fix bug on Mac OSX
--    setAcceptAllFileFilterUsed(selectAll);
++    setAcceptAllFileFilterUsed(true);
  
--    for (int i = 0; i < suffix.length; i++)
++    for (String[] format : formats)
      {
--      JalviewFileFilter jvf = new JalviewFileFilter(suffix[i], desc[i]);
++      JalviewFileFilter jvf = new JalviewFileFilter(format[0], format[1]);
        addChoosableFileFilter(jvf);
--      if ((selected != null) && selected.equalsIgnoreCase(desc[i]))
++      if ((selected != null) && selected.equalsIgnoreCase(format[1]))
        {
          chosen = jvf;
        }
  
      try
      {
--      if (getUI() instanceof javax.swing.plaf.basic.BasicFileChooserUI)
++      if (getUI() instanceof BasicFileChooserUI)
        {
--        final javax.swing.plaf.basic.BasicFileChooserUI ui = (javax.swing.plaf.basic.BasicFileChooserUI) getUI();
--        final String name = ui.getFileName().trim();
++        final BasicFileChooserUI fcui = (BasicFileChooserUI) getUI();
++        final String name = fcui.getFileName().trim();
  
          if ((name == null) || (name.length() == 0))
          {
            @Override
            public void run()
            {
--            String currentName = ui.getFileName();
++            String currentName = fcui.getFileName();
              if ((currentName == null) || (currentName.length() == 0))
              {
--              ui.setFileName(name);
++              fcui.setFileName(name);
              }
            }
          });
      }
    }
  
-   public FileFormat getSelectedFormat()
 -  public String getSelectedFormat()
++  public FileFormatI getSelectedFormat()
    {
      if (getFileFilter() == null)
      {
      }
  
      String format = getFileFilter().getDescription();
 -
 -    if (format.toUpperCase().startsWith("JALVIEW"))
 -    {
 -      format = "Jalview";
 -    }
 -    else if (format.toUpperCase().startsWith("FASTA"))
 -    {
 -      format = "FASTA";
 -    }
 -    else if (format.toUpperCase().startsWith("MSF"))
 -    {
 -      format = "MSF";
 -    }
 -    else if (format.toUpperCase().startsWith("CLUSTAL"))
 -    {
 -      format = "CLUSTAL";
 -    }
 -    else if (format.toUpperCase().startsWith("BLC"))
 -    {
 -      format = "BLC";
 -    }
 -    else if (format.toUpperCase().startsWith("PIR"))
 -    {
 -      format = "PIR";
 -    }
 -    else if (format.toUpperCase().startsWith("PFAM"))
 -    {
 -      format = "PFAM";
 -    }
 -    else if (format.toUpperCase().startsWith(PhylipFile.FILE_DESC))
 -    {
 -      format = PhylipFile.FILE_DESC;
 -    }
 -
 -    return format;
 +    return FileFormat.valueOf(format);
    }
  
    @Override
@@@ -26,9 -26,10 +26,10 @@@ import jalview.util.Comparison
  import jalview.util.Format;
  
  import java.io.IOException;
+ import java.util.ArrayList;
  import java.util.Hashtable;
+ import java.util.List;
  import java.util.StringTokenizer;
- import java.util.Vector;
  
  /**
   * DOCUMENT ME!
@@@ -51,16 -52,15 +52,15 @@@ public class MSFfile extends AlignFil
     * 
     * @param inFile
     *          DOCUMENT ME!
-    * @param sourceType
+    * @param type
     *          DOCUMENT ME!
     * 
     * @throws IOException
     *           DOCUMENT ME!
     */
-   public MSFfile(String inFile, DataSourceType sourceType)
-           throws IOException
 -  public MSFfile(String inFile, String type) throws IOException
++  public MSFfile(String inFile, DataSourceType type) throws IOException
    {
-     super(inFile, sourceType);
+     super(inFile, type);
    }
  
    public MSFfile(FileParse source) throws IOException
    }
  
    /**
-    * DOCUMENT ME!
+    * Read and parse MSF sequence data
     */
    @Override
    public void parse() throws IOException
    {
-     int i = 0;
      boolean seqFlag = false;
-     String key = new String();
-     Vector headers = new Vector();
-     Hashtable seqhash = new Hashtable();
-     String line;
+     List<String> headers = new ArrayList<String>();
+     Hashtable<String, StringBuilder> seqhash = new Hashtable<String, StringBuilder>();
  
      try
      {
+       String line;
        while ((line = nextLine()) != null)
        {
          StringTokenizer str = new StringTokenizer(line);
  
+         String key = null;
          while (str.hasMoreTokens())
          {
            String inStr = str.nextToken();
            if (inStr.indexOf("Name:") != -1)
            {
              key = str.nextToken();
-             headers.addElement(key);
+             headers.add(key);
            }
  
-           // if line has // set SeqFlag to 1 so we know sequences are coming
+           // if line has // set SeqFlag so we know sequences are coming
            if (inStr.indexOf("//") != -1)
            {
              seqFlag = true;
            }
  
            // Process lines as sequence lines if seqFlag is set
-           if ((inStr.indexOf("//") == -1) && (seqFlag == true))
+           if ((inStr.indexOf("//") == -1) && seqFlag)
            {
-             // seqeunce id is the first field
+             // sequence id is the first field
              key = inStr;
  
-             StringBuffer tempseq;
+             StringBuilder tempseq;
  
              // Get sequence from hash if it exists
              if (seqhash.containsKey(key))
              {
-               tempseq = (StringBuffer) seqhash.get(key);
+               tempseq = seqhash.get(key);
              }
              else
              {
-               tempseq = new StringBuffer();
+               tempseq = new StringBuilder(64);
                seqhash.put(key, tempseq);
              }
  
              while (str.hasMoreTokens())
              {
                // append the word to the sequence
-               tempseq.append(str.nextToken());
+               String sequenceBlock = str.nextToken();
+               tempseq.append(sequenceBlock);
              }
            }
          }
      this.noSeqs = headers.size();
  
      // Add sequences to the hash
-     for (i = 0; i < headers.size(); i++)
+     for (int i = 0; i < headers.size(); i++)
      {
-       if (seqhash.get(headers.elementAt(i)) != null)
+       if (seqhash.get(headers.get(i)) != null)
        {
-         String head = headers.elementAt(i).toString();
+         String head = headers.get(i);
          String seq = seqhash.get(head).toString();
  
          if (maxLength < head.length())
            maxLength = head.length();
          }
  
-         // Replace ~ with a sensible gap character
-         seq = seq.replace('~', '-');
+         /*
+          * replace ~ (leading/trailing positions) with the gap character;
+          * use '.' as this is the internal gap character required by MSF
+          */
+         seq = seq.replace('~', '.');
  
          Sequence newSeq = parseId(head);
  
        else
        {
          System.err.println("MSFFile Parser: Can't find sequence for "
-                 + headers.elementAt(i));
+                 + headers.get(i));
        }
      }
    }
      return check % 10000;
    }
  
+   /**
+    * DOCUMENT ME!
+    * 
+    * @param s
+    *          DOCUMENT ME!
+    * @param is_NA
+    *          DOCUMENT ME!
+    * 
+    * @return DOCUMENT ME!
+    */
 -  public String print(SequenceI[] sqs)
 +  @Override
-   public String print(SequenceI[] sqs, boolean jvsuffix)
++  public String print(SequenceI[] sqs, boolean jvSuffix)
    {
  
      boolean is_NA = Comparison.isNucleotide(sqs);
  
      SequenceI[] s = new SequenceI[sqs.length];
  
-     StringBuffer out = new StringBuffer("!!" + (is_NA ? "NA" : "AA")
-             + "_MULTIPLE_ALIGNMENT 1.0");
+     StringBuilder out = new StringBuilder(256);
+     out.append("!!").append(is_NA ? "NA" : "AA")
+             .append("_MULTIPLE_ALIGNMENT 1.0");
      // TODO: JBPNote : Jalview doesn't remember NA or AA yet.
      out.append(newline);
      out.append(newline);
  
      while ((i < sqs.length) && (sqs[i] != null))
      {
-       // Replace all internal gaps with . and external spaces with ~
-       s[i] = new Sequence(sqs[i].getName(), sqs[i].getSequenceAsString()
-               .replace('-', '.'), sqs[i].getStart(), sqs[i].getEnd());
+       /*
+        * modify to MSF format: uses '.' for internal gaps, 
+        * and '~' for leading or trailing gaps
+        */
+       String seqString = sqs[i].getSequenceAsString()
+               .replace('-', '.');
  
-       StringBuffer sb = new StringBuffer();
-       sb.append(s[i].getSequence());
+       StringBuilder sb = new StringBuilder(seqString);
  
        for (int ii = 0; ii < sb.length(); ii++)
        {
            break;
          }
        }
+       s[i] = new Sequence(sqs[i].getName(), sb.toString(),
+               sqs[i].getStart(), sqs[i].getEnd());
  
-       s[i].setSequence(sb.toString());
-       if (s[i].getSequence().length > max)
+       if (sb.length() > max)
        {
-         max = s[i].getSequence().length;
+         max = sb.length();
        }
  
        i++;
      while ((i < s.length) && (s[i] != null))
      {
  
-       nameBlock[i] = new String("  Name: " + printId(s[i], jvsuffix) + " ");
 -      nameBlock[i] = new String("  Name: " + printId(s[i]) + " ");
++      nameBlock[i] = new String("  Name: " + printId(s[i], jvSuffix) + " ");
  
        idBlock[i] = new String("Len: "
                + maxLenpad.form(s[i].getSequence().length) + "  Check: "
  
        while ((j < s.length) && (s[j] != null))
        {
-         String name = printId(s[j], jvsuffix);
 -        String name = printId(s[j]);
++        String name = printId(s[j], jvSuffix);
  
          out.append(new Format("%-" + maxid + "s").form(name + " "));
  
  
      return out.toString();
    }
 -
 -  /**
 -   * DOCUMENT ME!
 -   * 
 -   * @return DOCUMENT ME!
 -   */
 -  @Override
 -  public String print()
 -  {
 -    return print(getSeqsAsArray());
 -  }
  }
@@@ -36,10 -36,9 +36,10 @@@ public class PfamFile extends AlignFil
    {
    }
  
 -  public PfamFile(String inFile, String type) throws IOException
 +  public PfamFile(String inFile, DataSourceType sourceType)
 +          throws IOException
    {
 -    super(inFile, type);
 +    super(inFile, sourceType);
    }
  
    public PfamFile(FileParse source) throws IOException
        }
        if (spces + 1 < line.length())
        {
-         tempseq.append(line.substring(spces + 1));
+         tempseq.append(line.substring(spces + 1).trim());
        }
      }
  
      }
    }
  
 -  public String print(SequenceI[] s)
 +  @Override
 +  public String print(SequenceI[] s, boolean jvsuffix)
    {
      StringBuffer out = new StringBuffer("");
  
  
      while ((i < s.length) && (s[i] != null))
      {
 -      String tmp = printId(s[i]);
 +      String tmp = printId(s[i], jvsuffix);
  
        if (s[i].getSequence().length > max)
        {
  
      while ((j < s.length) && (s[j] != null))
      {
 -      out.append(new Format("%-" + maxid + "s").form(printId(s[j]) + " "));
 +      out.append(new Format("%-" + maxid + "s")
 +              .form(printId(s[j], jvsuffix) + " "));
  
        out.append(s[j].getSequenceAsString());
        out.append(newline);
  
      return out.toString();
    }
 -
 -  @Override
 -  public String print()
 -  {
 -    return print(getSeqsAsArray());
 -  }
  }
@@@ -20,7 -20,7 +20,7 @@@
   */
  package jalview.io;
  
- import jalview.analysis.SecStrConsensus.SimpleBP;
+ import jalview.analysis.Rna;
  import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.Annotation;
  import jalview.datamodel.Sequence;
@@@ -32,6 -32,7 +32,7 @@@ import java.io.FileNotFoundException
  import java.io.FileReader;
  import java.io.IOException;
  import java.util.ArrayList;
+ import java.util.List;
  
  import com.stevesoft.pat.Regex;
  
@@@ -53,10 -54,9 +54,9 @@@ public class RnamlFile extends AlignFil
  
    }
  
-   public RnamlFile(String inFile, DataSourceType sourceType)
-           throws IOException
 -  public RnamlFile(String inFile, String type) throws IOException
++  public RnamlFile(String inFile, DataSourceType type) throws IOException
    {
-     super(inFile, sourceType);
+     super(inFile, type);
  
    }
  
  
      result = RNAFactory.loadSecStrRNAML(getReader());
  
-     ArrayList<ArrayList> allarray = new ArrayList();
-     ArrayList<ArrayList<SimpleBP>> BP = new ArrayList();
-     ArrayList strucinarray = new ArrayList();
-     SequenceI[] seqs = new SequenceI[result.size()];
+     // ArrayList<ArrayList> allarray = new ArrayList();
+     // ArrayList<ArrayList<SimpleBP>> BP = new ArrayList();
+     // ArrayList strucinarray = new ArrayList();
+     SequenceI[] sqs = new SequenceI[result.size()];
  
      for (int i = 0; i < result.size(); i++)
      {
            id += "." + i;
          }
        }
-       seqs[i] = new Sequence(id, seq, begin, end);
+       sqs[i] = new Sequence(id, seq, begin, end);
  
-       seqs[i].setEnd(seqs[i].findPosition(seqs[i].getLength()));
+       sqs[i].setEnd(sqs[i].findPosition(sqs[i].getLength()));
        String[] annot = new String[rna.length()];
        Annotation[] ann = new Annotation[rna.length()];
  
        }
        for (int k = 0; k < rna.length(); k++)
        {
-         ann[k] = new Annotation(annot[k], "",
-                 jalview.schemes.ResidueProperties.getRNASecStrucState(
-                         annot[k]).charAt(0), 0f);
+         ann[k] = new Annotation(annot[k], "", Rna.getRNASecStrucState(
+                 annot[k]).charAt(0), 0f);
        }
  
        AlignmentAnnotation align = new AlignmentAnnotation(
                        + current.getID()
                        : "", ann);
  
-       seqs[i].addAlignmentAnnotation(align);
-       seqs[i].setRNA(result.get(i));
+       sqs[i].addAlignmentAnnotation(align);
+       sqs[i].setRNA(result.get(i));
  
-       allarray.add(strucinarray);
+       // allarray.add(strucinarray);
  
        annotations.addElement(align);
-       BP.add(align.bps);
+       // BP.add(align.bps);
  
      }
  
-     setSeqs(seqs);
+     setSeqs(sqs);
    }
  
 -  public static String print(SequenceI[] s)
 -  {
 -    return "not yet implemented";
 -  }
 -
    @Override
-   public String print(SequenceI[] sqs, boolean jvsuffix)
 -  public String print()
++  public String print(SequenceI[] s, boolean jvSuffix)
    {
 -    System.out.print("print :");
 -    return print(getSeqsAsArray());
 +    return "not yet implemented";
    }
  
-   public ArrayList getRNA()
+   public List<RNA> getRNA()
    {
      return result;
    }
@@@ -23,6 -23,7 +23,7 @@@
   */
  package jalview.io;
  
+ import jalview.analysis.Rna;
  import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.Annotation;
@@@ -31,6 -32,7 +32,7 @@@ import jalview.datamodel.Mapping
  import jalview.datamodel.Sequence;
  import jalview.datamodel.SequenceFeature;
  import jalview.datamodel.SequenceI;
+ import jalview.schemes.ResidueProperties;
  import jalview.util.Format;
  import jalview.util.MessageManager;
  
@@@ -72,8 -74,12 +74,12 @@@ import fr.orsay.lri.varna.models.rna.RN
   */
  public class StockholmFile extends AlignFile
  {
-   // static Logger logger = Logger.getLogger("jalview.io.StockholmFile");
-   protected ArrayList<RNA> result;
+   private static final Regex OPEN_PAREN = new Regex("(<|\\[)", "(");
+   private static final Regex CLOSE_PAREN = new Regex("(>|\\])", ")");
+   private static final Regex DETECT_BRACKETS = new Regex(
+           "(<|>|\\[|\\]|\\(|\\))");
  
    StringBuffer out; // output buffer
  
      this.al = al;
    }
  
-   public StockholmFile(String inFile, DataSourceType sourceType)
 -  public StockholmFile(String inFile, String type) throws IOException
++  public StockholmFile(String inFile, DataSourceType type)
 +          throws IOException
    {
-     super(inFile, sourceType);
+     super(inFile, type);
    }
  
    public StockholmFile(FileParse source) throws IOException
      fr = new FileReader(inFile);
  
      BufferedReader r = new BufferedReader(fr);
-     result = null;
+     List<RNA> result = null;
      try
      {
        result = RNAFactory.loadSecStrStockholm(r);
  
        for (int k = 0; k < rna.length(); k++)
        {
-         ann[k] = new Annotation(annot[k], "",
-                 jalview.schemes.ResidueProperties.getRNASecStrucState(
-                         annot[k]).charAt(0), 0f);
+         ann[k] = new Annotation(annot[k], "", Rna.getRNASecStrucState(
+                 annot[k]).charAt(0), 0f);
  
        }
        AlignmentAnnotation align = new AlignmentAnnotation("Sec. str.",
            }
            else
            {
-             // throw new IOException(MessageManager.formatMessage(
-             // "exception.error_parsing_line", new String[] { line }));
+             // throw new IOException("Error parsing " + line);
              System.err.println(">> missing annotation: " + line);
            }
          }
    }
  
    protected static AlignmentAnnotation parseAnnotationRow(
-           Vector annotation, String label, String annots)
+           Vector<AlignmentAnnotation> annotation, String label,
+           String annots)
    {
      String convert1, convert2 = null;
  
-     // Convert all bracket types to parentheses
-     Regex openparen = new Regex("(<|\\[)", "(");
-     Regex closeparen = new Regex("(>|\\])", ")");
-     // Detect if file is RNA by looking for bracket types
-     Regex detectbrackets = new Regex("(<|>|\\[|\\]|\\(|\\))");
-     convert1 = openparen.replaceAll(annots);
-     convert2 = closeparen.replaceAll(convert1);
+     convert1 = OPEN_PAREN.replaceAll(annots);
+     convert2 = CLOSE_PAREN.replaceAll(convert1);
      annots = convert2;
  
      String type = label;
        {
          // if (" .-_".indexOf(pos) == -1)
          {
-           if (detectbrackets.search(pos))
+           if (DETECT_BRACKETS.search(pos))
            {
-             ann.secondaryStructure = jalview.schemes.ResidueProperties
-                     .getRNASecStrucState(pos).charAt(0);
+             ann.secondaryStructure = Rna.getRNASecStrucState(
+                     pos).charAt(0);
            }
            else
            {
-             ann.secondaryStructure = jalview.schemes.ResidueProperties
-                     .getDssp3state(pos).charAt(0);
+             ann.secondaryStructure = ResidueProperties.getDssp3state(pos)
+                     .charAt(0);
            }
  
            if (ann.secondaryStructure == pos.charAt(0))
        els[i] = ann;
      }
      AlignmentAnnotation annot = null;
-     Enumeration e = annotation.elements();
+     Enumeration<AlignmentAnnotation> e = annotation.elements();
      while (e.hasMoreElements())
      {
-       annot = (AlignmentAnnotation) e.nextElement();
+       annot = e.nextElement();
        if (annot.label.equals(type))
        {
          break;
      return annot;
    }
  
 -  public String print(SequenceI[] s)
 +  @Override
-   public String print(SequenceI[] s, boolean jvsuffix)
++  public String print(SequenceI[] s, boolean jvSuffix)
    {
-     // out.append("# STOCKHOLM 1.0");
-     // out.append(newline);
++    out = new StringBuffer();
++    out.append("# STOCKHOLM 1.0");
++    out.append(newline);
 +
      // find max length of id
      int max = 0;
      int maxid = 0;
      Hashtable dataRef = null;
      while ((in < s.length) && (s[in] != null))
      {
-       String tmp = printId(s[in], jvsuffix);
 -      String tmp = printId(s[in]);
++      String tmp = printId(s[in], jvSuffix);
        if (s[in].getSequence().length > max)
        {
          max = s[in].getSequence().length;
  
              // out.append("#=GR ");
              out.append(new Format("%-" + maxid + "s").form("#=GR "
-                     + printId(s[i], jvsuffix) + " " + key + " "));
 -                    + printId(s[i]) + " " + key + " "));
++                    + printId(s[i], jvSuffix) + " " + key + " "));
              ann = alAnot[j].annotations;
              boolean isrna = alAnot[j].isValidStruc();
              String seq = "";
          }
        }
  
 -      out.append(new Format("%-" + maxid + "s").form(printId(s[i]) + " "));
 +      out.append(new Format("%-" + maxid + "s")
-               .form(printId(s[i], jvsuffix) + " "));
++              .form(printId(s[i], jvSuffix) + " "));
        out.append(s[i].getSequenceAsString());
        out.append(newline);
        i++;
          out.append(newline);
        }
      }
-     // out.append("//");
-     // out.append(newline);
++
++    out.append("//");
++    out.append(newline);
++
      return out.toString();
    }
  
      return seq;
    }
  
 -  @Override
+   public String print()
+   {
+     out = new StringBuffer();
+     out.append("# STOCKHOLM 1.0");
+     out.append(newline);
 -    print(getSeqsAsArray());
++    print(getSeqsAsArray(), false);
+     out.append("//");
+     out.append(newline);
+     return out.toString();
+   }
    private static Hashtable typeIds = null;
    static
    {
      if (typeIds == null)
@@@ -8,6 -8,7 +8,7 @@@ import jalview.datamodel.AlignmentI
  import jalview.datamodel.DBRefEntry;
  import jalview.datamodel.DBRefSource;
  import jalview.datamodel.PDBEntry;
+ import jalview.datamodel.PDBEntry.Type;
  import jalview.datamodel.SequenceI;
  import jalview.structure.StructureImportSettings;
  
@@@ -25,7 -26,7 +26,7 @@@ public abstract class StructureFile ext
  
    private String id;
  
-   private String dbRefType;
+   private PDBEntry.Type dbRefType;
  
    /**
     * set to true to add derived sequence annotations (temp factor read from
  
    private Vector<PDBChain> chains;
  
 -  public StructureFile(String inFile, String type) throws IOException
 +  public StructureFile(String inFile, DataSourceType sourceType)
 +          throws IOException
    {
 -    super(inFile, type);
 +    super(inFile, sourceType);
    }
  
    public StructureFile(FileParse fp) throws IOException
      this.visibleChainAnnotation = StructureImportSettings
              .isVisibleChainAnnotation();
      this.predictSecondaryStructure = StructureImportSettings
-             .isPredictSecondaryStructure();
+             .isProcessSecondaryStructure();
      this.externalSecondaryStructure = StructureImportSettings
              .isExternalSecondaryStructure();
  
    }
  
 -  public StructureFile(boolean parseImmediately, String dataObject, String type)
 -          throws IOException
 +  public StructureFile(boolean parseImmediately, String dataObject,
 +          DataSourceType sourceType) throws IOException
    {
 -    super(parseImmediately, dataObject, type);
 +    super(parseImmediately, dataObject, sourceType);
    }
  
    public StructureFile(boolean a, FileParse fp) throws IOException
      DBRefEntry sourceDBRef = new DBRefEntry();
      sourceDBRef.setAccessionId(getId());
      sourceDBRef.setSource(DBRefSource.PDB);
-     sourceDBRef.setStartRes(pdbSequence.getStart());
-     sourceDBRef.setEndRes(pdbSequence.getEnd());
      pdbSequence.setSourceDBRef(sourceDBRef);
      pdbSequence.addPDBId(entry);
      pdbSequence.addDBRef(sourceDBRef);
                  new Object[] {});
          AlignmentI al = ((AlignmentI) cl.getMethod("getRNAMLFor",
                  new Class[] { FileParse.class }).invoke(annotate3d,
 -                new Object[] { new FileParse(getDataName(), type) }));
 +                new Object[] { new FileParse(getDataName(), dataSourceType) }));
          for (SequenceI sq : al.getSequences())
          {
            if (sq.getDatasetSequence() != null)
              boolean.class, boolean.class, boolean.class, FileParse.class });
          final Object[] args = new Object[] { visibleChainAnnotation,
              predictSecondaryStructure, externalSecondaryStructure,
 -            new FileParse(getDataName(), type) };
 +            new FileParse(getDataName(), dataSourceType) };
  
          StructureImportSettings.setShowSeqFeatures(false);
          StructureImportSettings.setVisibleChainAnnotation(false);
          StructureImportSettings
-                 .setPredictSecondaryStructure(predictSecondaryStructure);
+                 .setProcessSecondaryStructure(predictSecondaryStructure);
          StructureImportSettings
                  .setExternalSecondaryStructure(externalSecondaryStructure);
          Object jmf = constructor.newInstance(args);
    {
      for (PDBChain chain : getChains())
      {
-       if (chain.id.equalsIgnoreCase(id))
+       if (chain.id.equals(id))
        {
          return chain;
        }
      this.chains = chains;
    }
  
-   public String getDbRefType()
+   public Type getDbRefType()
    {
      return dbRefType;
    }
  
    public void setDbRefType(String dbRefType)
    {
+     this.dbRefType = Type.valueOf(dbRefType);
+   }
+   public void setDbRefType(Type dbRefType)
+   {
      this.dbRefType = dbRefType;
    }
  
@@@ -21,6 -21,6 +21,8 @@@
  package jalview.jbgui;
  
  import jalview.datamodel.AlignmentI;
++import jalview.io.DataSourceType;
++import jalview.io.FileFormat;
  import jalview.io.FormatAdapter;
  import jalview.util.MessageManager;
  
@@@ -99,6 -99,6 +101,7 @@@ public class GFinder extends JPane
      findAll.setText(MessageManager.getString("action.find_all"));
      findAll.addActionListener(new java.awt.event.ActionListener()
      {
++      @Override
        public void actionPerformed(ActionEvent e)
        {
          findAll_actionPerformed(e);
      findNext.setText(MessageManager.getString("action.find_next"));
      findNext.addActionListener(new java.awt.event.ActionListener()
      {
++      @Override
        public void actionPerformed(ActionEvent e)
        {
          findNext_actionPerformed(e);
      createNewGroup.setText(MessageManager.getString("label.new_feature"));
      createNewGroup.addActionListener(new java.awt.event.ActionListener()
      {
++      @Override
        public void actionPerformed(ActionEvent e)
        {
          createNewGroup_actionPerformed(e);
      textfield.setLineWrap(true);
      textfield.addCaretListener(new CaretListener()
      {
++      @Override
        public void caretUpdate(CaretEvent e)
        {
          textfield_caretUpdate(e);
      });
      textfield.addKeyListener(new java.awt.event.KeyAdapter()
      {
++      @Override
        public void keyPressed(KeyEvent e)
        {
          textfield_keyPressed(e);
      {
        SwingUtilities.invokeLater(new Runnable()
        {
++        @Override
          public void run()
          {
            String str = textfield.getText();
            AlignmentI al = null;
            try
            {
--            al = new FormatAdapter().readFile(str, "Paste", "FASTA");
++            al = new FormatAdapter().readFile(str, DataSourceType.PASTE,
++                    FileFormat.Fasta);
            } catch (Exception ex)
            {
            }
@@@ -1,7 -1,15 +1,15 @@@
  package jalview.structure;
  
- import jalview.datamodel.DBRefSource;
+ import jalview.datamodel.PDBEntry;
+ import jalview.datamodel.PDBEntry.Type;
+ /**
+  * bean holding settings for structure IO. TODO: tests for validation of values
+  * TODO: tests for race conditions (all fields are static, is that correct ?)
+  * 
+  * @author tcofoegbu
+  *
+  */
  public class StructureImportSettings
  {
    /**
@@@ -14,7 -22,7 +22,7 @@@
     * Set true to predict secondary structure (using JMol for protein, Annotate3D
     * for RNA)
     */
-   private static boolean predictSecStr = false;
+   private static boolean processSecStr = false;
  
    /**
     * Set true (with predictSecondaryStructure=true) to predict secondary
  
    private static boolean showSeqFeatures = true;
  
-   private static boolean processHETATMs = false;
+   public enum StructureParser
+   {
+     JMOL_PARSER, JALVIEW_PARSER
+   }
  
-   private static String currentDefaultFormat = DBRefSource.PDB;
+   /**
+    * Determines the default file format for structure files to be downloaded
+    * from the PDB sequence fetcher. Possible options include: PDB|mmCIF
+    */
+   private static PDBEntry.Type defaultStructureFileFormat = Type.PDB;
  
+   /**
+    * Determines the parser used for parsing PDB format file. Possible options
+    * are : JMolParser|JalveiwParser
+    */
+   private static StructureParser defaultPDBFileParser = StructureParser.JMOL_PARSER;
    public static void addSettings(boolean addAlignmentAnnotations,
-           boolean predictSecStr, boolean externalSecStr)
+           boolean processSecStr, boolean externalSecStr)
    {
      StructureImportSettings.visibleChainAnnotation = addAlignmentAnnotations;
-     StructureImportSettings.predictSecStr = predictSecStr;
+     StructureImportSettings.processSecStr = processSecStr;
      StructureImportSettings.externalSecondaryStructure = externalSecStr;
      StructureImportSettings.showSeqFeatures = true;
    }
      StructureImportSettings.visibleChainAnnotation = visibleChainAnnotation;
    }
  
-   public static boolean isPredictSecondaryStructure()
+   public static boolean isProcessSecondaryStructure()
    {
-     return predictSecStr;
+     return processSecStr;
    }
  
-   public static void setPredictSecondaryStructure(
-           boolean predictSecondaryStructure)
+   public static void setProcessSecondaryStructure(
+           boolean processSecondaryStructure)
    {
-     StructureImportSettings.predictSecStr = predictSecondaryStructure;
+     StructureImportSettings.processSecStr = processSecondaryStructure;
    }
  
    public static boolean isExternalSecondaryStructure()
      StructureImportSettings.showSeqFeatures = showSeqFeatures;
    }
  
-   public static String getCurrentDefaultFormat()
 -  public static String getDefaultStructureFileFormat()
++  public static PDBEntry.Type getDefaultStructureFileFormat()
+   {
 -    return defaultStructureFileFormat.toString();
++    return defaultStructureFileFormat;
+   }
+   public static void setDefaultStructureFileFormat(
+           String defaultStructureFileFormat)
    {
-     return currentDefaultFormat;
+     StructureImportSettings.defaultStructureFileFormat = PDBEntry.Type
+             .valueOf(defaultStructureFileFormat.toUpperCase());
    }
  
-   public static void setCurrentDefaultFormat(String currentDefaultFormat)
+   public static String getDefaultPDBFileParser()
    {
-     StructureImportSettings.currentDefaultFormat = currentDefaultFormat;
+     return defaultPDBFileParser.toString();
    }
  
-   public static boolean isProcessHETATMs()
+   public static void setDefaultPDBFileParser(
+           StructureParser defaultPDBFileParser)
    {
-     return processHETATMs;
+     StructureImportSettings.defaultPDBFileParser = defaultPDBFileParser;
    }
  
-   public static void setProcessHETATMs(boolean processHETATMs)
+   public static void setDefaultPDBFileParser(String defaultPDBFileParser)
    {
-     StructureImportSettings.processHETATMs = processHETATMs;
+     StructureImportSettings.defaultPDBFileParser = StructureParser
+             .valueOf(defaultPDBFileParser.toUpperCase());
    }
  
  }
@@@ -32,9 -32,8 +32,9 @@@ import jalview.datamodel.Annotation
  import jalview.datamodel.PDBEntry;
  import jalview.datamodel.SearchResults;
  import jalview.datamodel.SequenceI;
 +import jalview.ext.jmol.JmolParser;
  import jalview.gui.IProgressIndicator;
 -import jalview.io.AppletFormatAdapter;
 +import jalview.io.DataSourceType;
  import jalview.io.StructureFile;
  import jalview.util.MappingUtils;
  import jalview.util.MessageManager;
@@@ -317,14 -316,14 +317,14 @@@ public class StructureSelectionManage
     *          (may be nill, individual elements may be nill)
     * @param pdbFile
     *          - structure data resource
 -   * @param protocol
 +   * @param paste
     *          - how to resolve data from resource
     * @return null or the structure data parsed as a pdb file
     */
    synchronized public StructureFile setMapping(SequenceI[] sequence,
 -          String[] targetChains, String pdbFile, String protocol)
 +          String[] targetChains, String pdbFile, DataSourceType paste)
    {
 -    return setMapping(true, sequence, targetChains, pdbFile, protocol);
 +    return setMapping(true, sequence, targetChains, pdbFile, paste);
    }
  
  
     *          (may be nill, individual elements may be nill)
     * @param pdbFile
     *          - structure data resource
 -   * @param protocol
 +   * @param sourceType
     *          - how to resolve data from resource
     * @return null or the structure data parsed as a pdb file
     */
    synchronized public StructureFile setMapping(boolean forStructureView,
            SequenceI[] sequenceArray, String[] targetChainIds,
            String pdbFile,
 -          String protocol)
 + DataSourceType sourceType)
    {
      /*
       * There will be better ways of doing this in the future, for now we'll use
      try
      {
  
-       if (pdbFile != null && isCIFFile(pdbFile))
+       boolean isParseWithJMOL = StructureImportSettings
+               .getDefaultPDBFileParser().equalsIgnoreCase(
+                       StructureImportSettings.StructureParser.JMOL_PARSER
+                               .toString());
+       if (isParseWithJMOL || (pdbFile != null && isCIFFile(pdbFile)))
        {
 -        pdb = new jalview.ext.jmol.JmolParser(addTempFacAnnot, parseSecStr,
 -                secStructServices, pdbFile, protocol);
 +        pdb = new JmolParser(addTempFacAnnot, parseSecStr,
 +                secStructServices, pdbFile, sourceType);
        }
        else
        {
          pdb = new PDBfile(addTempFacAnnot, parseSecStr, secStructServices,
 -                pdbFile, protocol);
 +                pdbFile, sourceType);
        }
  
        if (pdb.getId() != null && pdb.getId().trim().length() > 0
 -              && AppletFormatAdapter.FILE.equals(protocol))
 +              && sourceType == DataSourceType.FILE)
        {
          registerPDBFile(pdb.getId().trim(), pdbFile);
        }
          continue;
        }
  
 -      if (protocol.equals(jalview.io.AppletFormatAdapter.PASTE))
 +      if (sourceType == DataSourceType.PASTE)
        {
          pdbFile = "INLINE" + pdb.getId();
        }
@@@ -20,6 -20,6 +20,7 @@@
   */
  package jalview.util;
  
++import jalview.bin.Cache;
  import jalview.bin.Jalview;
  import jalview.gui.EPSOptions;
  import jalview.gui.IProgressIndicator;
@@@ -42,6 -42,6 +43,22 @@@ import org.jibble.epsgraphics.EpsGraphi
  
  public class ImageMaker
  {
++  public static final String SVG_DESCRIPTION = "Scalable Vector Graphics";
++
++  public static final String SVG_EXTENSION = "svg";
++
++  public static final String EPS_DESCRIPTION = "Encapsulated Postscript";
++
++  public static final String EPS_EXTENSION = "eps";
++
++  public static final String PNG_EXTENSION = "png";
++
++  public static final String PNG_DESCRIPTION = "Portable  network graphics";
++
++  public static final String HTML_EXTENSION = "html";
++
++  public static final String HTML_DESCRIPTION = "Hypertext Markup Language";
++
    EpsGraphics2D pg;
  
    SVGGraphics2D g2;
          out.close();
          break;
        case PNG:
--        ImageIO.write(bi, "png", out);
++        ImageIO.write(bi, PNG_EXTENSION, out);
          out.flush();
          out.close();
          break;
      {
        return null;
      }
--    return new jalview.io.JalviewFileChooser(
--            jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
--            new String[] { "png" },
--            new String[] { "Portable network graphics" },
--            "Portable network graphics");
++    return new JalviewFileChooser(Cache.getProperty("LAST_DIRECTORY"),
++            PNG_EXTENSION, PNG_DESCRIPTION, PNG_DESCRIPTION);
    }
  
    static JalviewFileChooser getEPSChooser()
      {
        return null;
      }
--    return new jalview.io.JalviewFileChooser(
--            jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
--            new String[] { "eps" },
--            new String[] { "Encapsulated Postscript" },
--            "Encapsulated Postscript");
++    return new JalviewFileChooser(Cache.getProperty("LAST_DIRECTORY"),
++            EPS_EXTENSION, EPS_DESCRIPTION, EPS_DESCRIPTION);
    }
  
    private void setProgressMessage(String message)
      {
        return null;
      }
--    return new jalview.io.JalviewFileChooser(
--            jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
--            new String[] { "svg" },
--            new String[] { "Scalable Vector Graphics" },
--            "Scalable Vector Graphics");
++    return new JalviewFileChooser(Cache.getProperty("LAST_DIRECTORY"),
++            SVG_EXTENSION, SVG_DESCRIPTION, SVG_DESCRIPTION);
    }
  }
@@@ -27,7 -27,8 +27,11 @@@ import jalview.datamodel.AlignmentI
  import jalview.datamodel.DBRefEntry;
  import jalview.datamodel.DBRefSource;
  import jalview.datamodel.PDBEntry;
+ import jalview.datamodel.PDBEntry.Type;
  import jalview.datamodel.SequenceI;
++import jalview.io.DataSourceType;
++import jalview.io.FileFormat;
++import jalview.io.FileFormatI;
  import jalview.io.FormatAdapter;
  import jalview.io.PDBFeatureSettings;
  import jalview.structure.StructureImportSettings;
@@@ -132,13 -133,12 +136,11 @@@ public class Pdb extends EbiFileRetriev
        stopQuery();
        return null;
      }
-     String ext = StructureImportSettings.getCurrentDefaultFormat()
-             .equalsIgnoreCase("mmcif") ? ".cif"
-             : ".xml";
 -    String ext = StructureImportSettings.getDefaultStructureFileFormat()
 -            .equalsIgnoreCase(Type.MMCIF.toString()) ? ".cif" : ".xml";
++    Type pdbFileFormat = StructureImportSettings
++            .getDefaultStructureFileFormat();
++    String ext = "." + pdbFileFormat.getExtension();
      EBIFetchClient ebi = new EBIFetchClient();
--    file = ebi.fetchDataAsFile("pdb:" + id,
-             StructureImportSettings.getCurrentDefaultFormat().toLowerCase(),
-             ext)
 -            StructureImportSettings.getDefaultStructureFileFormat().toLowerCase(),
 -            ext)
++    file = ebi.fetchDataAsFile("pdb:" + id, pdbFileFormat.getFormat(), ext)
              .getAbsolutePath();
      stopQuery();
      if (file == null)
      }
      try
      {
--
++      // convert Type.PDB/MMCIF to FileFormat.PDB/MMCIF
++      // todo get rid of Type?
++      FileFormatI fileFormat = FileFormat.valueOf(pdbFileFormat.toString());
        pdbAlignment = new FormatAdapter().readFile(file,
-               jalview.io.DataSourceType.FILE,
-               StructureImportSettings.getCurrentDefaultFormat());
 -              jalview.io.AppletFormatAdapter.FILE,
 -              StructureImportSettings.getDefaultStructureFileFormat());
++              DataSourceType.FILE, fileFormat);
        if (pdbAlignment != null)
        {
          List<SequenceI> toremove = new ArrayList<SequenceI>();
@@@ -21,6 -21,6 +21,7 @@@
  package jalview.ws.jws1;
  
  import jalview.datamodel.AlignmentI;
++import jalview.io.FileFormat;
  import jalview.io.FileParse;
  import jalview.io.FormatAdapter;
  import jalview.io.InputStreamParser;
@@@ -86,7 -86,7 +87,8 @@@ public class Annotate3
        while (r.hasNext())
        {
          FileParse fp = new InputStreamParser(r.next(), source.getDataName());
--        AlignmentI nal = new FormatAdapter().readFromFile(fp, "RNAML");
++        AlignmentI nal = new FormatAdapter().readFromFile(fp,
++                FileFormat.Rnaml);
          if (al == null)
          {
            al = nal;
@@@ -21,7 -21,6 +21,7 @@@
  package jalview.ws.jws1;
  
  import jalview.analysis.AlignSeq;
 +import jalview.analysis.SeqsetUtils;
  import jalview.bin.Cache;
  import jalview.datamodel.Alignment;
  import jalview.datamodel.AlignmentAnnotation;
@@@ -32,13 -31,7 +32,13 @@@ import jalview.datamodel.SequenceI
  import jalview.gui.AlignFrame;
  import jalview.gui.Desktop;
  import jalview.gui.WebserviceInfo;
 +import jalview.io.DataSourceType;
 +import jalview.io.FileFormatI;
  import jalview.io.FormatAdapter;
 +import jalview.io.IdentifyFile;
 +import jalview.io.JPredFile;
 +import jalview.io.JnetAnnotationMaker;
 +import jalview.io.PileUpfile;
  import jalview.util.Comparison;
  import jalview.util.MessageManager;
  import jalview.ws.AWsJob;
@@@ -78,7 -71,6 +78,7 @@@ class JPredThread extends JWS1Thread im
       * @return true if getResultSet will return a valid alignment and prediction
       *         result.
       */
 +    @Override
      public boolean hasResults()
      {
        if (subjobComplete && result != null && result.isFinished()
@@@ -90,7 -82,6 +90,7 @@@
        return false;
      }
  
 +    @Override
      public boolean hasValidInput()
      {
        if (sequence != null)
  
        JpredResult result = (JpredResult) this.result;
  
 -      jalview.bin.Cache.log.debug("Parsing output from JNet job.");
 +      Cache.log.debug("Parsing output from JNet job.");
        // JPredFile prediction = new JPredFile("C:/JalviewX/files/jpred.txt",
        // "File");
 -      jalview.io.JPredFile prediction = new jalview.io.JPredFile(
 -              result.getPredfile(), "Paste");
 +      JPredFile prediction = new JPredFile(
 +              result.getPredfile(), DataSourceType.PASTE);
        SequenceI[] preds = prediction.getSeqsAsArray();
 -      jalview.bin.Cache.log.debug("Got prediction profile.");
 +      Cache.log.debug("Got prediction profile.");
  
        if ((this.msa != null) && (result.getAligfile() != null))
        {
 -        jalview.bin.Cache.log.debug("Getting associated alignment.");
 +        Cache.log.debug("Getting associated alignment.");
          // we ignore the returned alignment if we only predicted on a single
          // sequence
 -        String format = new jalview.io.IdentifyFile().identify(
 -                result.getAligfile(), "Paste");
 +        FileFormatI format = new IdentifyFile().identify(
 +                result.getAligfile(), DataSourceType.PASTE);
  
 -        if (jalview.io.FormatAdapter.isValidFormat(format))
 +        if (format != null)
          {
            SequenceI sqs[];
            if (predMap != null)
            else
            {
              al = new FormatAdapter().readFile(result.getAligfile(),
 -                    "Paste", format);
 +                    DataSourceType.PASTE, format);
              sqs = new SequenceI[al.getHeight()];
  
              for (int i = 0, j = al.getHeight(); i < j; i++)
              {
                sqs[i] = al.getSequenceAt(i);
              }
 -            if (!jalview.analysis.SeqsetUtils.deuniquify(SequenceInfo, sqs))
 +            if (!SeqsetUtils.deuniquify(SequenceInfo, sqs))
              {
                throw (new Exception(
                        MessageManager
            {
              al.setDataset(null);
            }
 -          jalview.io.JnetAnnotationMaker.add_annotation(prediction, al,
 +          JnetAnnotationMaker.add_annotation(prediction, al,
                    FirstSeq, false, predMap);
  
          }
          {
            throw (new Exception(MessageManager.formatMessage(
                    "exception.unknown_format_for_file", new String[] {
 -                      format, result.getAligfile() })));
 +                      format.toString(), result.getAligfile() })));
          }
        }
        else
          if (msf.length > 1)
          {
            msa = new vamsas.objects.simple.Msfalignment();
 -          jalview.io.PileUpfile pileup = new jalview.io.PileUpfile();
 -          msa.setMsf(pileup.print(msf));
 +          PileUpfile pileup = new PileUpfile();
-           msa.setMsf(pileup.print(msf));
++          msa.setMsf(pileup.print(msf, true));
          }
        }
      }
      }
    }
  
 +  @Override
    public void StartJob(AWsJob j)
    {
      if (!(j instanceof JPredJob))
      }
    }
  
 +  @Override
    public void parseResult()
    {
      int results = 0; // number of result sets received
        wsInfo.showResultsNewFrame
                .addActionListener(new java.awt.event.ActionListener()
                {
 +                @Override
                  public void actionPerformed(java.awt.event.ActionEvent evt)
                  {
                    displayResults(true);
        wsInfo.mergeResults
                .addActionListener(new java.awt.event.ActionListener()
                {
 +                @Override
                  public void actionPerformed(java.awt.event.ActionEvent evt)
                  {
                    displayResults(false);
      }
    }
  
 +  @Override
    public void pollJob(AWsJob job) throws Exception
    {
      ((JPredJob) job).result = server.getresult(job.getJobId());
    }
  
 +  @Override
    public boolean isCancellable()
    {
      return false;
    }
  
 +  @Override
    public void cancelJob()
    {
      throw new Error(MessageManager.getString("error.implementation_error"));
    }
  
 +  @Override
    public boolean canMergeResults()
    {
      return false;
@@@ -24,6 -24,7 +24,7 @@@ import jalview.analysis.AlignSeq
  import jalview.api.FeatureColourI;
  import jalview.bin.Cache;
  import jalview.datamodel.Alignment;
+ import jalview.datamodel.AlignmentI;
  import jalview.datamodel.AlignmentView;
  import jalview.datamodel.SequenceI;
  import jalview.gui.AlignFrame;
@@@ -172,7 -173,7 +173,7 @@@ class SeqSearchWSThread extends JWS1Thr
       * 
       * @return null or { Alignment(+features and annotation), NewickFile)}
       */
-     public Object[] getAlignment(Alignment dataset,
+     public Object[] getAlignment(AlignmentI dataset,
              Map<String, FeatureColourI> featureColours)
      {
  
            if (inFile != null && inFile.length() > 0)
            {
              new jalview.io.AnnotationFile().readAnnotationFile(al, inFile,
 -                    jalview.io.AppletFormatAdapter.PASTE);
 +                    jalview.io.DataSourceType.PASTE);
            }
          } catch (Exception e)
          {
            if (inFile != null && inFile.length() > 0)
            {
              jalview.io.FeaturesFile ff = new jalview.io.FeaturesFile(
 -                    inFile, jalview.io.AppletFormatAdapter.PASTE);
 +                    inFile, jalview.io.DataSourceType.PASTE);
              ff.parse(al, featureColours, false);
            }
          } catch (Exception e)
            if (inFile != null && inFile.length() > 0)
            {
              nf = new jalview.io.NewickFile(inFile,
 -                    jalview.io.AppletFormatAdapter.PASTE);
 +                    jalview.io.DataSourceType.PASTE);
              if (!nf.isValid())
              {
                nf.close();
  
    String alTitle; // name which will be used to form new alignment window.
  
-   Alignment dataset; // dataset to which the new alignment will be
+   AlignmentI dataset; // dataset to which the new alignment will be
  
    // associated.
  
    SeqSearchWSThread(ext.vamsas.SeqSearchI server, String wsUrl,
            WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
            String wsname, String title, AlignmentView _msa, String db,
-           Alignment seqset)
+           AlignmentI seqset)
    {
      this(server, wsUrl, wsinfo, alFrame, _msa, wsname, db);
      OutputHeader = wsInfo.getProgressText();
@@@ -21,9 -21,6 +21,9 @@@
  package jalview.ws.rest.params;
  
  import jalview.datamodel.AlignmentI;
 +import jalview.io.FileFormat;
 +import jalview.io.FileFormatI;
 +import jalview.io.FormatAdapter;
  import jalview.ws.params.OptionI;
  import jalview.ws.params.simple.BooleanOption;
  import jalview.ws.params.simple.Option;
@@@ -58,7 -55,7 +58,7 @@@ public class Alignment extends InputTyp
      super(new Class[] { AlignmentI.class });
    }
  
 -  String format = "FASTA";
 +  FileFormatI format = FileFormat.Fasta;
  
    molType type;
  
@@@ -82,7 -79,7 +82,7 @@@
          PrintWriter pw = new PrintWriter(
                  new OutputStreamWriter(new BufferedOutputStream(
                          new FileOutputStream(fa)), "UTF-8"));
 -        pw.append(new jalview.io.FormatAdapter().formatSequences(format,
 +        pw.append(new FormatAdapter().formatSequences(format,
                  alignment, jvsuffix));
          pw.close();
          return new FileBody(fa, "text/plain");
@@@ -94,7 -91,7 +94,7 @@@
      }
      else
      {
 -      jalview.io.FormatAdapter fa = new jalview.io.FormatAdapter();
 +      FormatAdapter fa = new FormatAdapter();
        fa.setNewlineString("\r\n");
        return new StringBody(
                (fa.formatSequences(format, alignment, jvsuffix)));
      {
        prms.add("jvsuffix");
      }
 -    ;
      if (writeAsFile)
      {
        prms.add("writeasfile");
      }
 -    ;
      return prms;
    }
  
  
      if (tok.startsWith("format"))
      {
--      for (String fmt : jalview.io.FormatAdapter.WRITEABLE_FORMATS)
++      for (FileFormatI fmt : FileFormat.values())
        {
--        if (val.equalsIgnoreCase(fmt))
++        if (fmt.isWritable() && val.equalsIgnoreCase(fmt.toString()))
          {
            format = fmt;
            return true;
        }
        warnings.append("Invalid alignment format '" + val
                + "'. Must be one of (");
--      for (String fmt : jalview.io.FormatAdapter.WRITEABLE_FORMATS)
++      for (FileFormatI fmt : FileFormat.values())
        {
--        warnings.append(" " + fmt);
++        if (fmt.isWritable())
++        {
++          warnings.append(" " + fmt).toString();
++        }
        }
        warnings.append(")\n");
      }
              "Append jalview style /start-end suffix to ID", false, false,
              writeAsFile, null));
  
--    lst.add(new Option("format", "Alignment upload format", true, "FASTA",
--            format, Arrays
++    lst.add(new Option("format", "Alignment upload format", true,
++            FileFormat.Fasta.toString(),
++ format.toString(), Arrays
                      .asList(jalview.io.FormatAdapter.WRITEABLE_FORMATS),
              null));
      lst.add(createMolTypeOption("type", "Sequence type", false, type, null));
@@@ -22,6 -22,7 +22,7 @@@ package jalview.analysis
  
  import static org.testng.AssertJUnit.assertEquals;
  import static org.testng.AssertJUnit.assertFalse;
+ import static org.testng.AssertJUnit.assertNotNull;
  import static org.testng.AssertJUnit.assertNull;
  import static org.testng.AssertJUnit.assertSame;
  import static org.testng.AssertJUnit.assertTrue;
@@@ -40,9 -41,6 +41,9 @@@ import jalview.datamodel.Sequence
  import jalview.datamodel.SequenceFeature;
  import jalview.datamodel.SequenceI;
  import jalview.io.AppletFormatAdapter;
 +import jalview.io.DataSourceType;
 +import jalview.io.FileFormat;
 +import jalview.io.FileFormatI;
  import jalview.io.FormatAdapter;
  import jalview.util.MapList;
  import jalview.util.MappingUtils;
@@@ -50,7 -48,6 +51,6 @@@
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.Arrays;
- import java.util.Iterator;
  import java.util.LinkedHashMap;
  import java.util.List;
  import java.util.Map;
@@@ -72,15 -69,14 +72,15 @@@ public class AlignmentUtilsTest
        SequenceI s1 = ts.deriveSequence().getSubSequence(i, i + 7);
        al.addSequence(s1);
      }
 -    System.out.println(new AppletFormatAdapter().formatSequences("Clustal",
 +    System.out.println(new AppletFormatAdapter().formatSequences(
 +            FileFormat.Clustal,
              al, true));
      for (int flnk = -1; flnk < 25; flnk++)
      {
        AlignmentI exp = AlignmentUtils.expandContext(al, flnk);
        System.out.println("\nFlank size: " + flnk);
        System.out.println(new AppletFormatAdapter().formatSequences(
 -              "Clustal", exp, true));
 +              FileFormat.Clustal, exp, true));
        if (flnk == -1)
        {
          /*
    {
      final String data = ">Seq1Name\nKQYL\n" + ">Seq2Name\nRFPW\n"
              + ">Seq1Name\nABCD\n";
 -    AlignmentI al = loadAlignment(data, "FASTA");
 +    AlignmentI al = loadAlignment(data, FileFormat.Fasta);
      Map<String, List<SequenceI>> map = AlignmentUtils
              .getSequencesByName(al);
      assertEquals(2, map.keySet().size());
     * @return
     * @throws IOException
     */
 -  protected AlignmentI loadAlignment(final String data, String format)
 +  protected AlignmentI loadAlignment(final String data, FileFormatI format)
            throws IOException
    {
      AlignmentI a = new FormatAdapter().readFile(data,
 -            AppletFormatAdapter.PASTE, format);
 +            DataSourceType.PASTE, format);
      a.setDataset(null);
      return a;
    }
    @Test(groups = { "Functional" })
    public void testMakeCdsAlignment()
    {
+     /*
+      * scenario:
+      *     dna1 --> [4, 6] [10,12]        --> pep1 
+      *     dna2 --> [1, 3] [7, 9] [13,15] --> pep1 
+      */
      SequenceI dna1 = new Sequence("dna1", "aaaGGGcccTTTaaa");
      SequenceI dna2 = new Sequence("dna2", "GGGcccTTTaaaCCC");
      SequenceI pep1 = new Sequence("pep1", "GF");
      SequenceI pep2 = new Sequence("pep2", "GFP");
+     pep1.addDBRef(new DBRefEntry("UNIPROT", "0", "pep1"));
+     pep2.addDBRef(new DBRefEntry("UNIPROT", "0", "pep2"));
      dna1.createDatasetSequence();
      dna2.createDatasetSequence();
      pep1.createDatasetSequence();
      pep2.createDatasetSequence();
      AlignmentI dna = new Alignment(new SequenceI[] { dna1, dna2 });
      dna.setDataset(null);
  
-     List<AlignedCodonFrame> mappings = new ArrayList<AlignedCodonFrame>();
+     /*
+      * need a sourceDbRef if we are to construct dbrefs to the CDS
+      * sequence
+      */
+     DBRefEntry dbref = new DBRefEntry("ENSEMBL", "0", "dna1");
+     dna1.getDatasetSequence().setSourceDBRef(dbref);
+     dbref = new DBRefEntry("ENSEMBL", "0", "dna2");
+     dna2.getDatasetSequence().setSourceDBRef(dbref);
+     /*
+      * CDS sequences are 'discovered' from dna-to-protein mappings on the alignment
+      * dataset (e.g. added from dbrefs by CrossRef.findXrefSequences)
+      */
      MapList map = new MapList(new int[] { 4, 6, 10, 12 },
              new int[] { 1, 2 }, 3, 1);
      AlignedCodonFrame acf = new AlignedCodonFrame();
      acf.addMap(dna1.getDatasetSequence(), pep1.getDatasetSequence(), map);
-     mappings.add(acf);
+     dna.addCodonFrame(acf);
      map = new MapList(new int[] { 1, 3, 7, 9, 13, 15 }, new int[] { 1, 3 },
              3, 1);
      acf = new AlignedCodonFrame();
      acf.addMap(dna2.getDatasetSequence(), pep2.getDatasetSequence(), map);
-     mappings.add(acf);
+     dna.addCodonFrame(acf);
  
      /*
       * execute method under test:
       */
      AlignmentI cds = AlignmentUtils.makeCdsAlignment(new SequenceI[] {
-         dna1, dna2 }, mappings, dna);
+         dna1, dna2 }, dna.getDataset(), null);
  
+     /*
+      * verify cds sequences
+      */
      assertEquals(2, cds.getSequences().size());
-     assertEquals("GGGTTT", cds.getSequenceAt(0)
-             .getSequenceAsString());
-     assertEquals("GGGTTTCCC", cds.getSequenceAt(1)
-             .getSequenceAsString());
+     assertEquals("GGGTTT", cds.getSequenceAt(0).getSequenceAsString());
+     assertEquals("GGGTTTCCC", cds.getSequenceAt(1).getSequenceAsString());
  
      /*
       * verify shared, extended alignment dataset
       */
      assertSame(dna.getDataset(), cds.getDataset());
-     assertTrue(dna.getDataset().getSequences()
-             .contains(cds.getSequenceAt(0).getDatasetSequence()));
-     assertTrue(dna.getDataset().getSequences()
-             .contains(cds.getSequenceAt(1).getDatasetSequence()));
+     SequenceI cds1Dss = cds.getSequenceAt(0).getDatasetSequence();
+     SequenceI cds2Dss = cds.getSequenceAt(1).getDatasetSequence();
+     assertTrue(dna.getDataset().getSequences().contains(cds1Dss));
+     assertTrue(dna.getDataset().getSequences().contains(cds2Dss));
+     /*
+      * verify CDS has a dbref with mapping to peptide
+      */
+     assertNotNull(cds1Dss.getDBRefs());
+     assertEquals(1, cds1Dss.getDBRefs().length);
+     dbref = cds1Dss.getDBRefs()[0];
+     assertEquals("UNIPROT", dbref.getSource());
+     assertEquals("0", dbref.getVersion());
+     assertEquals("pep1", dbref.getAccessionId());
+     assertNotNull(dbref.getMap());
+     assertSame(pep1.getDatasetSequence(), dbref.getMap().getTo());
+     MapList cdsMapping = new MapList(new int[] { 1, 6 },
+             new int[] { 1, 2 }, 3, 1);
+     assertEquals(cdsMapping, dbref.getMap().getMap());
+     /*
+      * verify peptide has added a dbref with reverse mapping to CDS
+      */
+     assertNotNull(pep1.getDBRefs());
+     assertEquals(2, pep1.getDBRefs().length);
+     dbref = pep1.getDBRefs()[1];
+     assertEquals("ENSEMBL", dbref.getSource());
+     assertEquals("0", dbref.getVersion());
+     assertEquals("CDS|dna1", dbref.getAccessionId());
+     assertNotNull(dbref.getMap());
+     assertSame(cds1Dss, dbref.getMap().getTo());
+     assertEquals(cdsMapping.getInverse(), dbref.getMap().getMap());
  
      /*
-      * Verify mappings from CDS to peptide and cDNA to CDS
+      * Verify mappings from CDS to peptide, cDNA to CDS, and cDNA to peptide
       * the mappings are on the shared alignment dataset
+      * 6 mappings, 2*(DNA->CDS), 2*(DNA->Pep), 2*(CDS->Pep) 
       */
-     assertSame(dna.getCodonFrames(), cds.getCodonFrames());
-     List<AlignedCodonFrame> cdsMappings = cds.getCodonFrames();
-     assertEquals(2, cdsMappings.size());
-     
+     List<AlignedCodonFrame> cdsMappings = cds.getDataset().getCodonFrames();
+     assertEquals(6, cdsMappings.size());
+     /*
+      * verify that mapping sets for dna and cds alignments are different
+      * [not current behaviour - all mappings are on the alignment dataset]  
+      */
+     // select -> subselect type to test.
+     // Assert.assertNotSame(dna.getCodonFrames(), cds.getCodonFrames());
+     // assertEquals(4, dna.getCodonFrames().size());
+     // assertEquals(4, cds.getCodonFrames().size());
      /*
+      * Two mappings involve pep1 (dna to pep1, cds to pep1)
       * Mapping from pep1 to GGGTTT in first new exon sequence
       */
-     List<AlignedCodonFrame> pep1Mapping = MappingUtils
+     List<AlignedCodonFrame> pep1Mappings = MappingUtils
              .findMappingsForSequence(pep1, cdsMappings);
-     assertEquals(1, pep1Mapping.size());
+     assertEquals(2, pep1Mappings.size());
+     List<AlignedCodonFrame> mappings = MappingUtils
+             .findMappingsForSequence(cds.getSequenceAt(0), pep1Mappings);
+     assertEquals(1, mappings.size());
      // map G to GGG
-     SearchResults sr = MappingUtils
-             .buildSearchResults(pep1, 1, cdsMappings);
+     SearchResults sr = MappingUtils.buildSearchResults(pep1, 1, mappings);
      assertEquals(1, sr.getResults().size());
      Match m = sr.getResults().get(0);
-     assertSame(cds.getSequenceAt(0).getDatasetSequence(),
-             m.getSequence());
+     assertSame(cds1Dss, m.getSequence());
      assertEquals(1, m.getStart());
      assertEquals(3, m.getEnd());
      // map F to TTT
-     sr = MappingUtils.buildSearchResults(pep1, 2, cdsMappings);
+     sr = MappingUtils.buildSearchResults(pep1, 2, mappings);
      m = sr.getResults().get(0);
-     assertSame(cds.getSequenceAt(0).getDatasetSequence(),
-             m.getSequence());
+     assertSame(cds1Dss, m.getSequence());
      assertEquals(4, m.getStart());
      assertEquals(6, m.getEnd());
  
      /*
-      * Mapping from pep2 to GGGTTTCCC in second new exon sequence
+      * Two mappings involve pep2 (dna to pep2, cds to pep2)
+      * Verify mapping from pep2 to GGGTTTCCC in second new exon sequence
       */
-     List<AlignedCodonFrame> pep2Mapping = MappingUtils
+     List<AlignedCodonFrame> pep2Mappings = MappingUtils
              .findMappingsForSequence(pep2, cdsMappings);
-     assertEquals(1, pep2Mapping.size());
+     assertEquals(2, pep2Mappings.size());
+     mappings = MappingUtils.findMappingsForSequence(cds.getSequenceAt(1),
+             pep2Mappings);
+     assertEquals(1, mappings.size());
      // map G to GGG
-     sr = MappingUtils.buildSearchResults(pep2, 1, cdsMappings);
+     sr = MappingUtils.buildSearchResults(pep2, 1, mappings);
      assertEquals(1, sr.getResults().size());
      m = sr.getResults().get(0);
-     assertSame(cds.getSequenceAt(1).getDatasetSequence(),
-             m.getSequence());
+     assertSame(cds2Dss, m.getSequence());
      assertEquals(1, m.getStart());
      assertEquals(3, m.getEnd());
      // map F to TTT
-     sr = MappingUtils.buildSearchResults(pep2, 2, cdsMappings);
+     sr = MappingUtils.buildSearchResults(pep2, 2, mappings);
      m = sr.getResults().get(0);
-     assertSame(cds.getSequenceAt(1).getDatasetSequence(),
-             m.getSequence());
+     assertSame(cds2Dss, m.getSequence());
      assertEquals(4, m.getStart());
      assertEquals(6, m.getEnd());
      // map P to CCC
-     sr = MappingUtils.buildSearchResults(pep2, 3, cdsMappings);
+     sr = MappingUtils.buildSearchResults(pep2, 3, mappings);
      m = sr.getResults().get(0);
-     assertSame(cds.getSequenceAt(1).getDatasetSequence(),
-             m.getSequence());
+     assertSame(cds2Dss, m.getSequence());
      assertEquals(7, m.getStart());
      assertEquals(9, m.getEnd());
    }
      pep1.createDatasetSequence();
      pep2.createDatasetSequence();
      pep3.createDatasetSequence();
-     dna1.addSequenceFeature(new SequenceFeature("CDS", "cds1", 4, 6, 0f,
-             null));
-     dna1.addSequenceFeature(new SequenceFeature("CDS", "cds2", 10, 12, 0f,
-             null));
-     dna1.addSequenceFeature(new SequenceFeature("CDS", "cds3", 1, 3, 0f,
-             null));
-     dna1.addSequenceFeature(new SequenceFeature("CDS", "cds4", 7, 9, 0f,
-             null));
-     dna1.addSequenceFeature(new SequenceFeature("CDS", "cds5", 1, 3, 0f,
-             null));
-     dna1.addSequenceFeature(new SequenceFeature("CDS", "cds6", 10, 12, 0f,
-             null));
      pep1.getDatasetSequence().addDBRef(
              new DBRefEntry("EMBLCDS", "2", "A12345"));
      pep2.getDatasetSequence().addDBRef(
              new DBRefEntry("EMBLCDS", "4", "A12347"));
  
      /*
+      * Create the CDS alignment
+      */
+     AlignmentI dna = new Alignment(new SequenceI[] { dna1 });
+     dna.setDataset(null);
+     /*
       * Make the mappings from dna to protein
       */
-     List<AlignedCodonFrame> mappings = new ArrayList<AlignedCodonFrame>();
      // map ...GGG...TTT to GF
      MapList map = new MapList(new int[] { 4, 6, 10, 12 },
              new int[] { 1, 2 }, 3, 1);
      AlignedCodonFrame acf = new AlignedCodonFrame();
      acf.addMap(dna1.getDatasetSequence(), pep1.getDatasetSequence(), map);
-     mappings.add(acf);
+     dna.addCodonFrame(acf);
  
      // map aaa...ccc to KP
      map = new MapList(new int[] { 1, 3, 7, 9 }, new int[] { 1, 2 }, 3, 1);
      acf = new AlignedCodonFrame();
      acf.addMap(dna1.getDatasetSequence(), pep2.getDatasetSequence(), map);
-     mappings.add(acf);
+     dna.addCodonFrame(acf);
  
      // map aaa......TTT to KF
      map = new MapList(new int[] { 1, 3, 10, 12 }, new int[] { 1, 2 }, 3, 1);
      acf = new AlignedCodonFrame();
      acf.addMap(dna1.getDatasetSequence(), pep3.getDatasetSequence(), map);
-     mappings.add(acf);
-     /*
-      * Create the CDS alignment; also augments the dna-to-protein mappings with
-      * exon-to-protein and exon-to-dna mappings
-      */
-     AlignmentI dna = new Alignment(new SequenceI[] { dna1 });
-     dna.setDataset(null);
+     dna.addCodonFrame(acf);
  
      /*
       * execute method under test
       */
      AlignmentI cdsal = AlignmentUtils.makeCdsAlignment(
-             new SequenceI[] { dna1 }, mappings, dna);
+             new SequenceI[] { dna1 }, dna.getDataset(), null);
  
      /*
       * Verify we have 3 cds sequences, mapped to pep1/2/3 respectively
      SequenceI cdsSeq = cds.get(0);
      assertEquals("GGGTTT", cdsSeq.getSequenceAsString());
      // assertEquals("dna1|A12345", cdsSeq.getName());
-     assertEquals("dna1|pep1", cdsSeq.getName());
+     assertEquals("CDS|dna1", cdsSeq.getName());
      // assertEquals(1, cdsSeq.getDBRefs().length);
      // DBRefEntry cdsRef = cdsSeq.getDBRefs()[0];
      // assertEquals("EMBLCDS", cdsRef.getSource());
      cdsSeq = cds.get(1);
      assertEquals("aaaccc", cdsSeq.getSequenceAsString());
      // assertEquals("dna1|A12346", cdsSeq.getName());
-     assertEquals("dna1|pep2", cdsSeq.getName());
+     assertEquals("CDS|dna1", cdsSeq.getName());
      // assertEquals(1, cdsSeq.getDBRefs().length);
      // cdsRef = cdsSeq.getDBRefs()[0];
      // assertEquals("EMBLCDS", cdsRef.getSource());
      cdsSeq = cds.get(2);
      assertEquals("aaaTTT", cdsSeq.getSequenceAsString());
      // assertEquals("dna1|A12347", cdsSeq.getName());
-     assertEquals("dna1|pep3", cdsSeq.getName());
+     assertEquals("CDS|dna1", cdsSeq.getName());
      // assertEquals(1, cdsSeq.getDBRefs().length);
      // cdsRef = cdsSeq.getDBRefs()[0];
      // assertEquals("EMBLCDS", cdsRef.getSource());
       * Verify there are mappings from each cds sequence to its protein product
       * and also to its dna source
       */
-     Iterator<AlignedCodonFrame> newMappingsIterator = cdsal
-             .getCodonFrames().iterator();
+     List<AlignedCodonFrame> newMappings = cdsal.getCodonFrames();
  
-     // mappings for dna1 - exon1 - pep1
-     AlignedCodonFrame cdsMapping = newMappingsIterator.next();
-     List<Mapping> dnaMappings = cdsMapping.getMappingsFromSequence(dna1);
-     assertEquals(3, dnaMappings.size());
-     assertSame(cds.get(0).getDatasetSequence(), dnaMappings.get(0)
-             .getTo());
-     assertEquals("G(1) in CDS should map to G(4) in DNA", 4, dnaMappings
-             .get(0).getMap().getToPosition(1));
-     List<Mapping> peptideMappings = cdsMapping.getMappingsFromSequence(cds
-             .get(0).getDatasetSequence());
-     assertEquals(1, peptideMappings.size());
-     assertSame(pep1.getDatasetSequence(), peptideMappings.get(0).getTo());
-     // mappings for dna1 - cds2 - pep2
-     assertSame(cds.get(1).getDatasetSequence(), dnaMappings.get(1)
-             .getTo());
-     assertEquals("c(4) in CDS should map to c(7) in DNA", 7, dnaMappings
-             .get(1).getMap().getToPosition(4));
-     peptideMappings = cdsMapping.getMappingsFromSequence(cds.get(1)
-             .getDatasetSequence());
-     assertEquals(1, peptideMappings.size());
-     assertSame(pep2.getDatasetSequence(), peptideMappings.get(0).getTo());
-     // mappings for dna1 - cds3 - pep3
-     assertSame(cds.get(2).getDatasetSequence(), dnaMappings.get(2)
+     /*
+      * 6 mappings involve dna1 (to pep1/2/3, cds1/2/3) 
+      */
+     List<AlignedCodonFrame> dnaMappings = MappingUtils
+             .findMappingsForSequence(dna1, newMappings);
+     assertEquals(6, dnaMappings.size());
+     /*
+      * dna1 to pep1
+      */
+     List<AlignedCodonFrame> mappings = MappingUtils
+             .findMappingsForSequence(pep1, dnaMappings);
+     assertEquals(1, mappings.size());
+     assertEquals(1, mappings.get(0).getMappings().size());
+     assertSame(pep1.getDatasetSequence(), mappings.get(0).getMappings()
+             .get(0).getMapping().getTo());
+     /*
+      * dna1 to cds1
+      */
+     List<AlignedCodonFrame> dnaToCds1Mappings = MappingUtils
+             .findMappingsForSequence(cds.get(0), dnaMappings);
+     Mapping mapping = dnaToCds1Mappings.get(0).getMappings().get(0)
+             .getMapping();
+     assertSame(cds.get(0).getDatasetSequence(), mapping
              .getTo());
-     assertEquals("T(4) in CDS should map to T(10) in DNA", 10, dnaMappings
-             .get(2).getMap().getToPosition(4));
-     peptideMappings = cdsMapping.getMappingsFromSequence(cds.get(2)
-             .getDatasetSequence());
-     assertEquals(1, peptideMappings.size());
-     assertSame(pep3.getDatasetSequence(), peptideMappings.get(0).getTo());
+     assertEquals("G(1) in CDS should map to G(4) in DNA", 4, mapping
+             .getMap().getToPosition(1));
+     /*
+      * dna1 to pep2
+      */
+     mappings = MappingUtils.findMappingsForSequence(pep2, dnaMappings);
+     assertEquals(1, mappings.size());
+     assertEquals(1, mappings.get(0).getMappings().size());
+     assertSame(pep2.getDatasetSequence(), mappings.get(0).getMappings()
+             .get(0).getMapping().getTo());
+     /*
+      * dna1 to cds2
+      */
+     List<AlignedCodonFrame> dnaToCds2Mappings = MappingUtils
+             .findMappingsForSequence(cds.get(1), dnaMappings);
+     mapping = dnaToCds2Mappings.get(0).getMappings().get(0).getMapping();
+     assertSame(cds.get(1).getDatasetSequence(), mapping.getTo());
+     assertEquals("c(4) in CDS should map to c(7) in DNA", 7, mapping
+             .getMap().getToPosition(4));
+     /*
+      * dna1 to pep3
+      */
+     mappings = MappingUtils.findMappingsForSequence(pep3, dnaMappings);
+     assertEquals(1, mappings.size());
+     assertEquals(1, mappings.get(0).getMappings().size());
+     assertSame(pep3.getDatasetSequence(), mappings.get(0).getMappings()
+             .get(0).getMapping().getTo());
+     /*
+      * dna1 to cds3
+      */
+     List<AlignedCodonFrame> dnaToCds3Mappings = MappingUtils
+             .findMappingsForSequence(cds.get(2), dnaMappings);
+     mapping = dnaToCds3Mappings.get(0).getMappings().get(0).getMapping();
+     assertSame(cds.get(2).getDatasetSequence(), mapping.getTo());
+     assertEquals("T(4) in CDS should map to T(10) in DNA", 10, mapping
+             .getMap().getToPosition(4));
    }
  
    @Test(groups = { "Functional" })
      dna3.createDatasetSequence();
      pep1.createDatasetSequence();
      pep2.createDatasetSequence();
-     dna1.addSequenceFeature(new SequenceFeature("CDS", "cds1", 4, 8, 0f,
-             null));
-     dna1.addSequenceFeature(new SequenceFeature("CDS", "cds2", 9, 12, 0f,
-             null));
-     dna1.addSequenceFeature(new SequenceFeature("CDS", "cds3", 16, 18, 0f,
-             null));
-     dna2.addSequenceFeature(new SequenceFeature("CDS", "cds", 4, 8, 0f,
-             null));
-     dna2.addSequenceFeature(new SequenceFeature("CDS", "cds", 12, 12, 0f,
-             null));
-     dna2.addSequenceFeature(new SequenceFeature("CDS", "cds", 16, 18, 0f,
-             null));
+     AlignmentI dna = new Alignment(new SequenceI[] { dna1, dna2, dna3 });
+     dna.setDataset(null);
    
-     List<AlignedCodonFrame> mappings = new ArrayList<AlignedCodonFrame>();
      MapList map = new MapList(new int[] { 4, 12, 16, 18 },
              new int[] { 1, 4 }, 3, 1);
      AlignedCodonFrame acf = new AlignedCodonFrame();
      acf.addMap(dna1.getDatasetSequence(), pep1.getDatasetSequence(), map);
-     mappings.add(acf);
+     dna.addCodonFrame(acf);
      map = new MapList(new int[] { 4, 8, 12, 12, 16, 18 },
              new int[] { 1, 3 },
              3, 1);
      acf = new AlignedCodonFrame();
      acf.addMap(dna2.getDatasetSequence(), pep2.getDatasetSequence(), map);
-     mappings.add(acf);
+     dna.addCodonFrame(acf);
    
-     AlignmentI dna = new Alignment(new SequenceI[] { dna1, dna2, dna3 });
-     dna.setDataset(null);
      AlignmentI cds = AlignmentUtils.makeCdsAlignment(new SequenceI[] {
-         dna1, dna2, dna3 }, mappings, dna);
+         dna1, dna2, dna3 }, dna.getDataset(), null);
      List<SequenceI> cdsSeqs = cds.getSequences();
      assertEquals(2, cdsSeqs.size());
      assertEquals("GGGCCCTTTGGG", cdsSeqs.get(0).getSequenceAsString());
              .contains(cdsSeqs.get(1).getDatasetSequence()));
  
      /*
-      * Verify updated mappings
+      * Verify 6 mappings: dna1 to cds1, cds1 to pep1, dna1 to pep1
+      * and the same for dna2/cds2/pep2
       */
-     List<AlignedCodonFrame> cdsMappings = cds.getCodonFrames();
-     assertEquals(2, cdsMappings.size());
+     List<AlignedCodonFrame> mappings = cds.getCodonFrames();
+     assertEquals(6, mappings.size());
    
      /*
-      * Mapping from pep1 to GGGTTT in first new CDS sequence
+      * 2 mappings involve pep1
       */
-     List<AlignedCodonFrame> pep1Mapping = MappingUtils
-             .findMappingsForSequence(pep1, cdsMappings);
-     assertEquals(1, pep1Mapping.size());
+     List<AlignedCodonFrame> pep1Mappings = MappingUtils
+             .findMappingsForSequence(pep1, mappings);
+     assertEquals(2, pep1Mappings.size());
      /*
+      * Get mapping of pep1 to cds1 and verify it
       * maps GPFG to 1-3,4-6,7-9,10-12
       */
-     SearchResults sr = MappingUtils
-             .buildSearchResults(pep1, 1, cdsMappings);
+     List<AlignedCodonFrame> pep1CdsMappings = MappingUtils
+             .findMappingsForSequence(cds.getSequenceAt(0), pep1Mappings);
+     assertEquals(1, pep1CdsMappings.size());
+     SearchResults sr = MappingUtils.buildSearchResults(pep1, 1,
+             pep1CdsMappings);
      assertEquals(1, sr.getResults().size());
      Match m = sr.getResults().get(0);
      assertEquals(cds.getSequenceAt(0).getDatasetSequence(),
              m.getSequence());
      assertEquals(1, m.getStart());
      assertEquals(3, m.getEnd());
-     sr = MappingUtils.buildSearchResults(pep1, 2, cdsMappings);
+     sr = MappingUtils.buildSearchResults(pep1, 2, pep1CdsMappings);
      m = sr.getResults().get(0);
      assertEquals(4, m.getStart());
      assertEquals(6, m.getEnd());
-     sr = MappingUtils.buildSearchResults(pep1, 3, cdsMappings);
+     sr = MappingUtils.buildSearchResults(pep1, 3, pep1CdsMappings);
      m = sr.getResults().get(0);
      assertEquals(7, m.getStart());
      assertEquals(9, m.getEnd());
-     sr = MappingUtils.buildSearchResults(pep1, 4, cdsMappings);
+     sr = MappingUtils.buildSearchResults(pep1, 4, pep1CdsMappings);
      m = sr.getResults().get(0);
      assertEquals(10, m.getStart());
      assertEquals(12, m.getEnd());
    
      /*
-      * GPG in pep2 map to 1-3,4-6,7-9 in second CDS sequence
+      * Get mapping of pep2 to cds2 and verify it
+      * maps GPG in pep2 to 1-3,4-6,7-9 in second CDS sequence
       */
-     List<AlignedCodonFrame> pep2Mapping = MappingUtils
-             .findMappingsForSequence(pep2, cdsMappings);
-     assertEquals(1, pep2Mapping.size());
-     sr = MappingUtils.buildSearchResults(pep2, 1, cdsMappings);
+     List<AlignedCodonFrame> pep2Mappings = MappingUtils
+             .findMappingsForSequence(pep2, mappings);
+     assertEquals(2, pep2Mappings.size());
+     List<AlignedCodonFrame> pep2CdsMappings = MappingUtils
+             .findMappingsForSequence(cds.getSequenceAt(1), pep2Mappings);
+     assertEquals(1, pep2CdsMappings.size());
+     sr = MappingUtils.buildSearchResults(pep2, 1, pep2CdsMappings);
      assertEquals(1, sr.getResults().size());
      m = sr.getResults().get(0);
      assertEquals(cds.getSequenceAt(1).getDatasetSequence(),
              m.getSequence());
      assertEquals(1, m.getStart());
      assertEquals(3, m.getEnd());
-     sr = MappingUtils.buildSearchResults(pep2, 2, cdsMappings);
+     sr = MappingUtils.buildSearchResults(pep2, 2, pep2CdsMappings);
      m = sr.getResults().get(0);
      assertEquals(4, m.getStart());
      assertEquals(6, m.getEnd());
-     sr = MappingUtils.buildSearchResults(pep2, 3, cdsMappings);
+     sr = MappingUtils.buildSearchResults(pep2, 3, pep2CdsMappings);
      m = sr.getResults().get(0);
      assertEquals(7, m.getStart());
      assertEquals(9, m.getEnd());
    public void testComputePeptideVariants()
    {
      /*
-      * scenario: AAATTTCCC codes for KFP, with variants
-      *           GAA -> E
-      *           CAA -> Q
-      *           AAG synonymous
-      *           AAT -> N
-      *              TTC synonymous
-      *                 CAC,CGC -> H,R (as one variant)
+      * scenario: AAATTTCCC codes for KFP
+      * variants:
+      *           GAA -> E             source: Ensembl
+      *           CAA -> Q             source: dbSNP
+      *           AAG synonymous       source: COSMIC
+      *           AAT -> N             source: Ensembl
+      *           ...TTC synonymous    source: dbSNP
+      *           ......CAC,CGC -> H,R source: COSMIC
+      *                 (one variant with two alleles)
       */
      SequenceI peptide = new Sequence("pep/10-12", "KFP");
  
       * two distinct variants for codon 1 position 1
       * second one has clinical significance
       */
+     String ensembl = "Ensembl";
+     String dbSnp = "dbSNP";
+     String cosmic = "COSMIC";
      SequenceFeature sf1 = new SequenceFeature("sequence_variant", "", 1, 1,
-             0f, null);
+             0f, ensembl);
      sf1.setValue("alleles", "A,G"); // GAA -> E
      sf1.setValue("ID", "var1.125A>G");
      SequenceFeature sf2 = new SequenceFeature("sequence_variant", "", 1, 1,
-             0f, null);
+             0f, dbSnp);
      sf2.setValue("alleles", "A,C"); // CAA -> Q
      sf2.setValue("ID", "var2");
      sf2.setValue("clinical_significance", "Dodgy");
      SequenceFeature sf3 = new SequenceFeature("sequence_variant", "", 3, 3,
-             0f, null);
+             0f, cosmic);
      sf3.setValue("alleles", "A,G"); // synonymous
      sf3.setValue("ID", "var3");
      sf3.setValue("clinical_significance", "None");
      SequenceFeature sf4 = new SequenceFeature("sequence_variant", "", 3, 3,
-             0f, null);
+             0f, ensembl);
      sf4.setValue("alleles", "A,T"); // AAT -> N
      sf4.setValue("ID", "sequence_variant:var4"); // prefix gets stripped off
      sf4.setValue("clinical_significance", "Benign");
      SequenceFeature sf5 = new SequenceFeature("sequence_variant", "", 6, 6,
-             0f, null);
+             0f, dbSnp);
      sf5.setValue("alleles", "T,C"); // synonymous
      sf5.setValue("ID", "var5");
      sf5.setValue("clinical_significance", "Bad");
      SequenceFeature sf6 = new SequenceFeature("sequence_variant", "", 8, 8,
-             0f, null);
+             0f, cosmic);
      sf6.setValue("alleles", "C,A,G"); // CAC,CGC -> H,R
      sf6.setValue("ID", "var6");
      sf6.setValue("clinical_significance", "Good");
  
      /*
       * verify added sequence features for
-      * var1 K -> E
-      * var2 K -> Q
-      * var4 K -> N
-      * var6 P -> H
-      * var6 P -> R
+      * var1 K -> E Ensembl
+      * var2 K -> Q dbSNP
+      * var4 K -> N Ensembl
+      * var6 P -> H COSMIC
+      * var6 P -> R COSMIC
       */
      SequenceFeature[] sfs = peptide.getSequenceFeatures();
      assertEquals(5, sfs.length);
      SequenceFeature sf = sfs[0];
      assertEquals(1, sf.getBegin());
      assertEquals(1, sf.getEnd());
      assertEquals(
              "p.Lys1Glu var1.125A>G|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var1.125A%3EG",
              sf.links.get(0));
-     assertEquals("Jalview", sf.getFeatureGroup());
+     assertEquals(ensembl, sf.getFeatureGroup());
      sf = sfs[1];
      assertEquals(1, sf.getBegin());
      assertEquals(1, sf.getEnd());
      assertEquals(
              "p.Lys1Gln var2|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var2",
              sf.links.get(0));
-     assertEquals("Jalview", sf.getFeatureGroup());
+     assertEquals(dbSnp, sf.getFeatureGroup());
      sf = sfs[2];
      assertEquals(1, sf.getBegin());
      assertEquals(1, sf.getEnd());
      assertEquals(
              "p.Lys1Asn var4|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var4",
              sf.links.get(0));
-     assertEquals("Jalview", sf.getFeatureGroup());
+     assertEquals(ensembl, sf.getFeatureGroup());
+     // var5 generates two distinct protein variant features
      sf = sfs[3];
      assertEquals(3, sf.getBegin());
      assertEquals(3, sf.getEnd());
      assertEquals(
              "p.Pro3His var6|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var6",
              sf.links.get(0));
-     // var5 generates two distinct protein variant features
-     assertEquals("Jalview", sf.getFeatureGroup());
+     assertEquals(cosmic, sf.getFeatureGroup());
      sf = sfs[4];
      assertEquals(3, sf.getBegin());
      assertEquals(3, sf.getEnd());
      assertEquals(
              "p.Pro3Arg var6|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var6",
              sf.links.get(0));
-     assertEquals("Jalview", sf.getFeatureGroup());
+     assertEquals(cosmic, sf.getFeatureGroup());
    }
  
    /**
      assertEquals('T', map.get(11).get(seq1).charValue());
      assertEquals('T', map.get(12).get(seq1).charValue());
    }
+   /**
+    * Test for the case where the products for which we want CDS are specified.
+    * This is to represent the case where EMBL has CDS mappings to both Uniprot
+    * and EMBLCDSPROTEIN. makeCdsAlignment() should only return the mappings for
+    * the protein sequences specified.
+    */
+   @Test(groups = { "Functional" })
+   public void testMakeCdsAlignment_filterProducts()
+   {
+     SequenceI dna1 = new Sequence("dna1", "aaaGGGcccTTTaaa");
+     SequenceI dna2 = new Sequence("dna2", "GGGcccTTTaaaCCC");
+     SequenceI pep1 = new Sequence("Uniprot|pep1", "GF");
+     SequenceI pep2 = new Sequence("Uniprot|pep2", "GFP");
+     SequenceI pep3 = new Sequence("EMBL|pep3", "GF");
+     SequenceI pep4 = new Sequence("EMBL|pep4", "GFP");
+     dna1.createDatasetSequence();
+     dna2.createDatasetSequence();
+     pep1.createDatasetSequence();
+     pep2.createDatasetSequence();
+     pep3.createDatasetSequence();
+     pep4.createDatasetSequence();
+     AlignmentI dna = new Alignment(new SequenceI[] { dna1, dna2 });
+     dna.setDataset(null);
+     AlignmentI emblPeptides = new Alignment(new SequenceI[] { pep3, pep4 });
+     emblPeptides.setDataset(null);
+   
+     AlignedCodonFrame acf = new AlignedCodonFrame();
+     MapList map = new MapList(new int[] { 4, 6, 10, 12 },
+             new int[] { 1, 2 }, 3, 1);
+     acf.addMap(dna1.getDatasetSequence(), pep1.getDatasetSequence(), map);
+     acf.addMap(dna1.getDatasetSequence(), pep3.getDatasetSequence(), map);
+     dna.addCodonFrame(acf);
+     acf = new AlignedCodonFrame();
+     map = new MapList(new int[] { 1, 3, 7, 9, 13, 15 }, new int[] { 1, 3 },
+             3, 1);
+     acf.addMap(dna2.getDatasetSequence(), pep2.getDatasetSequence(), map);
+     acf.addMap(dna2.getDatasetSequence(), pep4.getDatasetSequence(), map);
+     dna.addCodonFrame(acf);
+   
+     /*
+      * execute method under test to find CDS for EMBL peptides only
+      */
+     AlignmentI cds = AlignmentUtils.makeCdsAlignment(new SequenceI[] {
+         dna1, dna2 }, dna.getDataset(), emblPeptides.getSequencesArray());
+   
+     assertEquals(2, cds.getSequences().size());
+     assertEquals("GGGTTT", cds.getSequenceAt(0).getSequenceAsString());
+     assertEquals("GGGTTTCCC", cds.getSequenceAt(1).getSequenceAsString());
+   
+     /*
+      * verify shared, extended alignment dataset
+      */
+     assertSame(dna.getDataset(), cds.getDataset());
+     assertTrue(dna.getDataset().getSequences()
+             .contains(cds.getSequenceAt(0).getDatasetSequence()));
+     assertTrue(dna.getDataset().getSequences()
+             .contains(cds.getSequenceAt(1).getDatasetSequence()));
+   
+     /*
+      * Verify mappings from CDS to peptide, cDNA to CDS, and cDNA to peptide
+      * the mappings are on the shared alignment dataset
+      */
+     List<AlignedCodonFrame> cdsMappings = cds.getDataset().getCodonFrames();
+     /*
+      * 6 mappings, 2*(DNA->CDS), 2*(DNA->Pep), 2*(CDS->Pep) 
+      */
+     assertEquals(6, cdsMappings.size());
+   
+     /*
+      * verify that mapping sets for dna and cds alignments are different
+      * [not current behaviour - all mappings are on the alignment dataset]  
+      */
+     // select -> subselect type to test.
+     // Assert.assertNotSame(dna.getCodonFrames(), cds.getCodonFrames());
+     // assertEquals(4, dna.getCodonFrames().size());
+     // assertEquals(4, cds.getCodonFrames().size());
+   
+     /*
+      * Two mappings involve pep3 (dna to pep3, cds to pep3)
+      * Mapping from pep3 to GGGTTT in first new exon sequence
+      */
+     List<AlignedCodonFrame> pep3Mappings = MappingUtils
+             .findMappingsForSequence(pep3, cdsMappings);
+     assertEquals(2, pep3Mappings.size());
+     List<AlignedCodonFrame> mappings = MappingUtils
+             .findMappingsForSequence(cds.getSequenceAt(0), pep3Mappings);
+     assertEquals(1, mappings.size());
+   
+     // map G to GGG
+     SearchResults sr = MappingUtils.buildSearchResults(pep3, 1, mappings);
+     assertEquals(1, sr.getResults().size());
+     Match m = sr.getResults().get(0);
+     assertSame(cds.getSequenceAt(0).getDatasetSequence(), m.getSequence());
+     assertEquals(1, m.getStart());
+     assertEquals(3, m.getEnd());
+     // map F to TTT
+     sr = MappingUtils.buildSearchResults(pep3, 2, mappings);
+     m = sr.getResults().get(0);
+     assertSame(cds.getSequenceAt(0).getDatasetSequence(), m.getSequence());
+     assertEquals(4, m.getStart());
+     assertEquals(6, m.getEnd());
+   
+     /*
+      * Two mappings involve pep4 (dna to pep4, cds to pep4)
+      * Verify mapping from pep4 to GGGTTTCCC in second new exon sequence
+      */
+     List<AlignedCodonFrame> pep4Mappings = MappingUtils
+             .findMappingsForSequence(pep4, cdsMappings);
+     assertEquals(2, pep4Mappings.size());
+     mappings = MappingUtils.findMappingsForSequence(cds.getSequenceAt(1),
+             pep4Mappings);
+     assertEquals(1, mappings.size());
+     // map G to GGG
+     sr = MappingUtils.buildSearchResults(pep4, 1, mappings);
+     assertEquals(1, sr.getResults().size());
+     m = sr.getResults().get(0);
+     assertSame(cds.getSequenceAt(1).getDatasetSequence(), m.getSequence());
+     assertEquals(1, m.getStart());
+     assertEquals(3, m.getEnd());
+     // map F to TTT
+     sr = MappingUtils.buildSearchResults(pep4, 2, mappings);
+     m = sr.getResults().get(0);
+     assertSame(cds.getSequenceAt(1).getDatasetSequence(), m.getSequence());
+     assertEquals(4, m.getStart());
+     assertEquals(6, m.getEnd());
+     // map P to CCC
+     sr = MappingUtils.buildSearchResults(pep4, 3, mappings);
+     m = sr.getResults().get(0);
+     assertSame(cds.getSequenceAt(1).getDatasetSequence(), m.getSequence());
+     assertEquals(7, m.getStart());
+     assertEquals(9, m.getEnd());
+   }
+   /**
+    * Test the method that just copies aligned sequences, provided all sequences
+    * to be aligned share the aligned sequence's dataset
+    */
+   @Test(groups = "Functional")
+   public void testAlignAsSameSequences()
+   {
+     SequenceI dna1 = new Sequence("dna1", "cccGGGTTTaaa");
+     SequenceI dna2 = new Sequence("dna2", "CCCgggtttAAA");
+     AlignmentI al1 = new Alignment(new SequenceI[] { dna1, dna2 });
+     ((Alignment) al1).createDatasetAlignment();
+     SequenceI dna3 = new Sequence(dna1);
+     SequenceI dna4 = new Sequence(dna2);
+     assertSame(dna3.getDatasetSequence(), dna1.getDatasetSequence());
+     assertSame(dna4.getDatasetSequence(), dna2.getDatasetSequence());
+     String seq1 = "-cc-GG-GT-TT--aaa";
+     dna3.setSequence(seq1);
+     String seq2 = "C--C-Cgg--gtt-tAA-A-";
+     dna4.setSequence(seq2);
+     AlignmentI al2 = new Alignment(new SequenceI[] { dna3, dna4 });
+     ((Alignment) al2).createDatasetAlignment();
+     
+     assertTrue(AlignmentUtils.alignAsSameSequences(al1, al2));
+     assertEquals(seq1, al1.getSequenceAt(0).getSequenceAsString());
+     assertEquals(seq2, al1.getSequenceAt(1).getSequenceAsString());
+     /*
+      * add another sequence to 'aligned' - should still succeed, since
+      * unaligned sequences still share a dataset with aligned sequences
+      */
+     SequenceI dna5 = new Sequence("dna5", "CCCgggtttAAA");
+     dna5.createDatasetSequence();
+     al2.addSequence(dna5);
+     assertTrue(AlignmentUtils.alignAsSameSequences(al1, al2));
+     assertEquals(seq1, al1.getSequenceAt(0).getSequenceAsString());
+     assertEquals(seq2, al1.getSequenceAt(1).getSequenceAsString());
+     /*
+      * add another sequence to 'unaligned' - should fail, since now not
+      * all unaligned sequences share a dataset with aligned sequences
+      */
+     SequenceI dna6 = new Sequence("dna6", "CCCgggtttAAA");
+     dna6.createDatasetSequence();
+     al1.addSequence(dna6);
+     // JAL-2110 JBP Comment: what's the use case for this behaviour ?
+     assertFalse(AlignmentUtils.alignAsSameSequences(al1, al2));
+   }
+   @Test(groups = "Functional")
+   public void testAlignAsSameSequencesMultipleSubSeq()
+   {
+     SequenceI dna1 = new Sequence("dna1", "cccGGGTTTaaa");
+     SequenceI dna2 = new Sequence("dna2", "CCCgggtttAAA");
+     SequenceI as1 = dna1.deriveSequence();
+     SequenceI as2 = dna1.deriveSequence().getSubSequence(3, 7);
+     SequenceI as3 = dna2.deriveSequence();
+     as1.insertCharAt(6, 5, '-');
+     String s_as1 = as1.getSequenceAsString();
+     as2.insertCharAt(6, 5, '-');
+     String s_as2 = as2.getSequenceAsString();
+     as3.insertCharAt(6, 5, '-');
+     String s_as3 = as3.getSequenceAsString();
+     AlignmentI aligned = new Alignment(new SequenceI[] { as1, as2, as3 });
+     // why do we need to cast this still ?
+     ((Alignment) aligned).createDatasetAlignment();
+     SequenceI uas1 = dna1.deriveSequence();
+     SequenceI uas2 = dna1.deriveSequence().getSubSequence(3, 7);
+     SequenceI uas3 = dna2.deriveSequence();
+     AlignmentI tobealigned = new Alignment(new SequenceI[] { uas1, uas2,
+         uas3 });
+     ((Alignment) tobealigned).createDatasetAlignment();
+     assertTrue(AlignmentUtils.alignAsSameSequences(tobealigned, aligned));
+     assertEquals(s_as1, uas1.getSequenceAsString());
+     assertEquals(s_as2, uas2.getSequenceAsString());
+     assertEquals(s_as3, uas3.getSequenceAsString());
+   }
+     
  }
@@@ -29,10 -29,9 +29,11 @@@ import jalview.datamodel.AlignedCodon
  import jalview.datamodel.Alignment;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.ColumnSelection;
+ import jalview.datamodel.Sequence;
  import jalview.datamodel.SequenceI;
  import jalview.gui.AlignViewport;
 +import jalview.io.DataSourceType;
 +import jalview.io.FileFormat;
  import jalview.io.FormatAdapter;
  
  import java.io.IOException;
@@@ -121,8 -120,8 +122,8 @@@ public class DnaTes
            throws IOException
    {
      AlignmentI alf = new FormatAdapter().readFile(
 -            JAL_1312_example_align_fasta, jalview.io.FormatAdapter.PASTE,
 -            "FASTA");
 +            JAL_1312_example_align_fasta, DataSourceType.PASTE,
 +            FileFormat.Fasta);
      ColumnSelection cs = new ColumnSelection();
      AlignViewportI av = new AlignViewport(alf, cs);
      Dna dna = new Dna(av, new int[] { 0, alf.getWidth() - 1 });
            throws IOException
    {
      AlignmentI alf = new FormatAdapter().readFile(
 -            JAL_1312_example_align_fasta, jalview.io.FormatAdapter.PASTE,
 -            "FASTA");
 +            JAL_1312_example_align_fasta, DataSourceType.PASTE,
 +            FileFormat.Fasta);
      int vwidth = 15;
      for (int ipos = 0; ipos + vwidth < alf.getWidth(); ipos += vwidth)
      {
    public void testTranslateCdna_simple() throws IOException
    {
      AlignmentI alf = new FormatAdapter().readFile(fasta,
 -            FormatAdapter.PASTE, "FASTA");
 +            DataSourceType.PASTE, FileFormat.Fasta);
      ColumnSelection cs = new ColumnSelection();
      AlignViewportI av = new AlignViewport(alf, cs);
      Dna dna = new Dna(av, new int[] { 0, alf.getWidth() - 1 });
    public void testTranslateCdna_hiddenColumns() throws IOException
    {
      AlignmentI alf = new FormatAdapter().readFile(fasta,
 -            FormatAdapter.PASTE, "FASTA");
 -    ColumnSelection cs = new jalview.datamodel.ColumnSelection();
 +            DataSourceType.PASTE, FileFormat.Fasta);
 +    ColumnSelection cs = new ColumnSelection();
      cs.hideColumns(6, 14); // hide codons 3/4/5
      cs.hideColumns(24, 35); // hide codons 9-12
      cs.hideColumns(177, 191); // hide codons 60-64
    @Test(groups = "Functional")
    public void testReverseSequence()
    {
-     String seq = "AcGtUrYkMbVdHNX";
+     String seq = "-Ac-GtU--rYkMbVdHNX-";
+     String seqRev = new StringBuilder(seq).reverse().toString();
  
      // reverse:
      SequenceI reversed = Dna.reverseSequence("Seq1", seq, false);
-     assertEquals(new StringBuilder(seq).reverse()
-             .toString(), reversed.getSequenceAsString());
+     assertEquals(1, reversed.getStart());
+     assertEquals(15, reversed.getEnd());
+     assertEquals(20, reversed.getLength());
+     assertEquals(seqRev, reversed.getSequenceAsString());
      assertEquals("Seq1|rev", reversed.getName());
  
      // reverse complement:
      SequenceI revcomp = Dna.reverseSequence("Seq1", seq, true);
-     assertEquals("XNDhBvKmRyAaCgT", revcomp.getSequenceAsString());
+     assertEquals("-XNDhBvKmRy--AaC-gT-", revcomp.getSequenceAsString());
      assertEquals("Seq1|revcomp", revcomp.getName());
    }
+   @Test(groups = "Functional")
+   public void testReverseCdna()
+   {
+     String seq = "-Ac-GtU--rYkMbVdHNX-";
+     String seqRev = new StringBuilder(seq).reverse().toString();
+     String seqDs = seq.replaceAll("-", "");
+     String seqDsRev = new StringBuilder(seqDs).reverse().toString();
+     SequenceI dna = new Sequence("Seq1", seq);
+     Alignment al = new Alignment(new SequenceI[] {dna});
+     al.createDatasetAlignment();
+     assertEquals(seqDs, al.getSequenceAt(0).getDatasetSequence()
+             .getSequenceAsString());
+     ColumnSelection cs = new ColumnSelection();
+     AlignViewportI av = new AlignViewport(al, cs);
+     Dna testee = new Dna(av, new int[] { 0, al.getWidth() - 1 });
+     AlignmentI reversed = testee.reverseCdna(false);
+     assertEquals(1, reversed.getHeight());
+     assertEquals(seqRev, reversed.getSequenceAt(0).getSequenceAsString());
+     assertEquals(seqDsRev, reversed.getSequenceAt(0).getDatasetSequence()
+             .getSequenceAsString());
+   }
  }
@@@ -25,7 -25,6 +25,7 @@@ import static org.testng.AssertJUnit.as
  
  import jalview.analysis.AlignSeq;
  import jalview.io.AppletFormatAdapter;
 +import jalview.io.FileFormat;
  
  import org.testng.annotations.Test;
  
@@@ -120,8 -119,7 +120,8 @@@ public class AlignmentAnnotationTest
      alSeq2.setEnd(sqTo.getStart() + align.getSeq2End() - 1);
      alSeq2.setDatasetSequence(sqTo);
      System.out.println(new AppletFormatAdapter()
 -            .formatSequences("STH", new Alignment(new SequenceI[] { sqFrom,
 +.formatSequences(
 +            FileFormat.Stockholm, new Alignment(new SequenceI[] { sqFrom,
                  alSeq1, sqTo, alSeq2 }), true));
  
      Mapping mp = align.getMappingFromS1(false);
      AlignmentI all = new Alignment(new SequenceI[] { alSeq1, alSeq2 });
      all.addAnnotation(almap1);
      all.addAnnotation(almap2);
 -    System.out.println(new AppletFormatAdapter().formatSequences("STH",
 +    System.out.println(new AppletFormatAdapter().formatSequences(
 +            FileFormat.Stockholm,
              all, true));
  
      for (int p = 0; p < alSeq1.getLength(); p++)
      assertEquals(1, ann.annotations[1].value, 0.001);
      assertEquals(2, ann.annotations[2].value, 0.001);
    }
+   /**
+    * Test the method that defaults rna symbol to the one matching the preceding
+    * unmatched opening bracket (if any)
+    */
+   @Test(groups = { "Functional" })
+   public void testGetDefaultRnaHelixSymbol()
+   {
+     AlignmentAnnotation ann = new AlignmentAnnotation("SS",
+             "secondary structure", null);
+     assertEquals("(", ann.getDefaultRnaHelixSymbol(4));
+     Annotation[] anns = new Annotation[20];
+     ann.annotations = anns;
+     assertEquals("(", ann.getDefaultRnaHelixSymbol(4));
+     anns[1] = new Annotation("(", "S", '(', 0f);
+     assertEquals("(", ann.getDefaultRnaHelixSymbol(0));
+     assertEquals("(", ann.getDefaultRnaHelixSymbol(1));
+     assertEquals(")", ann.getDefaultRnaHelixSymbol(2));
+     assertEquals(")", ann.getDefaultRnaHelixSymbol(3));
+     /*
+      * .(.[.{.<.}.>.).].
+      */
+     anns[1] = new Annotation("(", "S", '(', 0f);
+     anns[3] = new Annotation("[", "S", '[', 0f);
+     anns[5] = new Annotation("{", "S", '{', 0f);
+     anns[7] = new Annotation("<", "S", '<', 0f);
+     anns[9] = new Annotation("}", "S", '}', 0f);
+     anns[11] = new Annotation(">", "S", '>', 0f);
+     anns[13] = new Annotation(")", "S", ')', 0f);
+     anns[15] = new Annotation("]", "S", ']', 0f);
+     String expected = "(())]]}}>>>>]]]](";
+     for (int i = 0; i < expected.length(); i++)
+     {
+       assertEquals("column " + i, String.valueOf(expected.charAt(i)),
+               ann.getDefaultRnaHelixSymbol(i));
+     }
+     /*
+      * .(.[.(.).{.}.<.].D.
+      */
+     anns[1] = new Annotation("(", "S", '(', 0f);
+     anns[3] = new Annotation("[", "S", '[', 0f);
+     anns[5] = new Annotation("(", "S", '(', 0f);
+     anns[7] = new Annotation(")", "S", ')', 0f);
+     anns[9] = new Annotation("{", "S", '{', 0f);
+     anns[11] = new Annotation("}", "S", '}', 0f);
+     anns[13] = new Annotation("<", "S", '>', 0f);
+     anns[15] = new Annotation("]", "S", ']', 0f);
+     anns[17] = new Annotation("D", "S", 'D', 0f);
+     expected = "(())]]))]]}}]]>>>>dd";
+     for (int i = 0; i < expected.length(); i++)
+     {
+       assertEquals("column " + i, String.valueOf(expected.charAt(i)),
+               ann.getDefaultRnaHelixSymbol(i));
+     }
+   }
 -}
 +}
@@@ -27,9 -27,7 +27,9 @@@ import static org.testng.AssertJUnit.as
  import static org.testng.AssertJUnit.assertSame;
  import static org.testng.AssertJUnit.assertTrue;
  
 -import jalview.io.AppletFormatAdapter;
 +import jalview.io.DataSourceType;
 +import jalview.io.FileFormat;
 +import jalview.io.FileFormatI;
  import jalview.io.FormatAdapter;
  import jalview.util.MapList;
  
@@@ -94,11 -92,11 +94,11 @@@ public class AlignmentTes
     * @return
     * @throws IOException
     */
 -  protected AlignmentI loadAlignment(final String data, String format)
 +  protected AlignmentI loadAlignment(final String data, FileFormatI format)
            throws IOException
    {
 -    AlignmentI a = new FormatAdapter().readFile(data,
 -            AppletFormatAdapter.PASTE, format);
 +    AlignmentI a = new FormatAdapter().readFile(data, DataSourceType.PASTE,
 +            format);
      a.setDataset(null);
      return a;
    }
    @BeforeMethod(alwaysRun = true)
    public void setUp() throws IOException
    {
 -    al = loadAlignment(TEST_DATA, "STH");
 +    al = loadAlignment(TEST_DATA, FileFormat.Stockholm);
      int i = 0;
      for (AlignmentAnnotation ann : al.getAlignmentAnnotation())
      {
    public void testAlignAs_dnaAsDna() throws IOException
    {
      // aligned cDNA:
 -    AlignmentI al1 = loadAlignment(CDNA_SEQS_1, "FASTA");
 +    AlignmentI al1 = loadAlignment(CDNA_SEQS_1, FileFormat.Fasta);
      // unaligned cDNA:
 -    AlignmentI al2 = loadAlignment(CDNA_SEQS_2, "FASTA");
 +    AlignmentI al2 = loadAlignment(CDNA_SEQS_2, FileFormat.Fasta);
  
      /*
       * Make mappings between sequences. The 'aligned cDNA' is playing the role
    public void testAlignAs_proteinAsCdna() throws IOException
    {
      // see also AlignmentUtilsTests
 -    AlignmentI al1 = loadAlignment(CDNA_SEQS_1, "FASTA");
 -    AlignmentI al2 = loadAlignment(AA_SEQS_1, "FASTA");
 +    AlignmentI al1 = loadAlignment(CDNA_SEQS_1, FileFormat.Fasta);
 +    AlignmentI al2 = loadAlignment(AA_SEQS_1, FileFormat.Fasta);
      makeMappings(al1, al2);
  
      // Fudge - alignProteinAsCdna expects mappings to be on protein
     * 
     * @throws IOException
     */
-   @Test(groups = { "Functional" }, enabled = false)
+   @Test(groups = { "Functional" }, enabled = true)
    // TODO review / update this test after redesign of alignAs method
    public void testAlignAs_cdnaAsProtein() throws IOException
    {
      /*
       * Load alignments and add mappings for cDNA to protein
       */
 -    AlignmentI al1 = loadAlignment(CDNA_SEQS_1, "FASTA");
 -    AlignmentI al2 = loadAlignment(AA_SEQS_1, "FASTA");
 +    AlignmentI al1 = loadAlignment(CDNA_SEQS_1, FileFormat.Fasta);
 +    AlignmentI al2 = loadAlignment(AA_SEQS_1, FileFormat.Fasta);
      makeMappings(al1, al2);
  
      /*
       * Realign DNA; currently keeping existing gaps in introns only
       */
      ((Alignment) al1).alignAs(al2, false, true);
-     assertEquals("ACG---GCUCCA------ACT", al1.getSequenceAt(0)
+     assertEquals("ACG---GCUCCA------ACT---", al1.getSequenceAt(0)
              .getSequenceAsString());
      assertEquals("---CGT---TAACGA---AGT---", al1.getSequenceAt(1)
              .getSequenceAsString());
     * 
     * @throws IOException
     */
-   @Test(groups = { "Functional" }, enabled = false)
+   @Test(groups = { "Functional" }, enabled = true)
    // TODO review / update this test after redesign of alignAs method
    public void testAlignAs_cdnaAsProtein_singleSequence() throws IOException
    {
       * Load alignments and add mappings from nucleotide to protein (or from
       * first to second if both the same type)
       */
 -    AlignmentI al1 = loadAlignment(fromSeqs, "FASTA");
 -    AlignmentI al2 = loadAlignment(toSeqs, "FASTA");
 +    AlignmentI al1 = loadAlignment(fromSeqs, FileFormat.Fasta);
 +    AlignmentI al2 = loadAlignment(toSeqs, FileFormat.Fasta);
      makeMappings(al1, al2);
  
      /*
        acf.addMap(seqFrom, seqTo, ml);
      }
  
+     /*
+      * not sure whether mappings 'belong' or protein or nucleotide
+      * alignment, so adding to both ;~)
+      */
      alFrom.addCodonFrame(acf);
+     alTo.addCodonFrame(acf);
    }
  
    /**
      String dna1 = "A-Aa-gG-GCC-cT-TT";
      String dna2 = "c--CCGgg-TT--T-AA-A";
      AlignmentI al1 = loadAlignment(">Dna1/6-17\n" + dna1
 -            + "\n>Dna2/20-31\n" + dna2 + "\n", "FASTA");
 +            + "\n>Dna2/20-31\n" + dna2 + "\n", FileFormat.Fasta);
      AlignmentI al2 = loadAlignment(
 -            ">Pep1/7-9\n-P--YK\n>Pep2/11-13\nG-T--F\n", "FASTA");
 +            ">Pep1/7-9\n-P--YK\n>Pep2/11-13\nG-T--F\n", FileFormat.Fasta);
      AlignedCodonFrame acf = new AlignedCodonFrame();
      // Seq1 has intron at dna positions 3,4,9 so splice is AAG GCC TTT
      // Seq2 has intron at dna positions 1,5,6 so splice is CCG TTT AAA
    @Test(groups = "Functional")
    public void testCopyConstructor() throws IOException
    {
 -    AlignmentI protein = loadAlignment(AA_SEQS_1, FormatAdapter.PASTE);
 +    AlignmentI protein = loadAlignment(AA_SEQS_1, FileFormat.Fasta);
      // create sequence and alignment datasets
      protein.setDataset(null);
      AlignedCodonFrame acf = new AlignedCodonFrame();
      // TODO should the copy constructor copy the dataset?
      // or make a new one referring to the same dataset sequences??
      assertNull(copy.getDataset());
+     // TODO test metadata is copied when AlignmentI is a dataset
      // assertArrayEquals(copy.getDataset().getSequencesArray(), protein
      // .getDataset().getSequencesArray());
    }
    public void testCreateDatasetAlignment() throws IOException
    {
      AlignmentI protein = new FormatAdapter().readFile(AA_SEQS_1,
 -            AppletFormatAdapter.PASTE, "FASTA");
 +            DataSourceType.PASTE, FileFormat.Fasta);
      /*
       * create a dataset sequence on first sequence
       * leave the second without one
      // TODO promote this method to AlignmentI
      ((Alignment) protein).createDatasetAlignment();
  
-     // TODO this method should return AlignmentI not Alignment !!
-     Alignment ds = protein.getDataset();
+     AlignmentI ds = protein.getDataset();
  
      // side-effect: dataset created on second sequence
      assertNotNull(protein.getSequenceAt(1).getDatasetSequence());
@@@ -28,10 -28,10 +28,10 @@@ import jalview.datamodel.Alignment
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.SequenceI;
  import jalview.gui.AlignFrame;
--import jalview.io.AppletFormatAdapter;
 +import jalview.io.DataSourceType;
  import jalview.io.FileLoader;
  import jalview.structure.StructureImportSettings;
+ import jalview.structure.StructureImportSettings.StructureParser;
  
  import java.util.Vector;
  
@@@ -90,6 -90,9 +90,9 @@@ public class JmolParserTes
              Boolean.TRUE.toString());
      Cache.applicationProperties.setProperty("ADD_SS_ANN",
              Boolean.TRUE.toString());
+     StructureImportSettings.setDefaultStructureFileFormat("PDB");
+     StructureImportSettings
+             .setDefaultPDBFileParser(StructureParser.JALVIEW_PARSER);
    }
  
    @Test(groups = { "Functional" })
      for (String f : testFile)
      {
        FileLoader fl = new jalview.io.FileLoader(false);
--      AlignFrame af = fl
-               .LoadFileWaitTillLoaded(f, DataSourceType.FILE);
 -              .LoadFileWaitTillLoaded(f, AppletFormatAdapter.FILE);
++      AlignFrame af = fl.LoadFileWaitTillLoaded(f, DataSourceType.FILE);
        validateSecStrRows(af.getViewport().getAlignment());
      }
    }
    @Test(groups = { "Functional" })
    public void testFileParser() throws Exception
    {
      for (String pdbStr : testFile)
      {
        PDBfile mctest = new PDBfile(false, false, false, pdbStr,
 -              AppletFormatAdapter.FILE);
 +              DataSourceType.FILE);
        JmolParser jtest = new JmolParser(false, false, false, pdbStr,
-               jalview.io.DataSourceType.FILE);
-       Vector<SequenceI> seqs = jtest.getSeqs(), mcseqs = mctest.getSeqs();
-       assertTrue(
-               "No sequences extracted from testfile\n"
-                       + (jtest.hasWarningMessage() ? jtest.getWarningMessage()
-                               : "(No warnings raised)"), seqs != null
-                       && seqs.size() > 0);
-       for (SequenceI sq : seqs)
-       {
-         assertEquals("JMol didn't process " + pdbStr
-                 + " to the same sequence as MCView",
-                 sq.getSequenceAsString(), mcseqs.remove(0)
-                         .getSequenceAsString());
-         AlignmentI al = new Alignment(new SequenceI[] { sq });
-         validateSecStrRows(al);
-       }
-     }
-     StructureImportSettings.setProcessHETATMs(true);
-     for (String pdbStr : testFile)
-     {
-       PDBfile mctest = new PDBfile(false, false, false, pdbStr,
 -              jalview.io.AppletFormatAdapter.FILE);
 +              DataSourceType.FILE);
-       JmolParser jtest = new JmolParser(false, false, false, pdbStr,
-               jalview.io.DataSourceType.FILE);
        Vector<SequenceI> seqs = jtest.getSeqs(), mcseqs = mctest.getSeqs();
  
        assertTrue(
          validateSecStrRows(al);
        }
      }
    }
  
    private void validateSecStrRows(AlignmentI al)
    public void testParse_missingResidues() throws Exception
    {
      PDBfile mctest = new PDBfile(false, false, false,
--            pastePDBDataWithChainBreak,
-             DataSourceType.PASTE);
 -            AppletFormatAdapter.PASTE);
++            pastePDBDataWithChainBreak, DataSourceType.PASTE);
      boolean annotFromStructure = false;
      boolean localSecondaryStruct = false;
      boolean serviceSecondaryStruct = false;
      JmolParser jtest = new JmolParser(annotFromStructure,
              localSecondaryStruct, serviceSecondaryStruct,
--            pastePDBDataWithChainBreak,
-             jalview.io.DataSourceType.PASTE);
 -            jalview.io.AppletFormatAdapter.PASTE);
++            pastePDBDataWithChainBreak, DataSourceType.PASTE);
      Vector<SequenceI> seqs = jtest.getSeqs();
      Vector<SequenceI> mcseqs = mctest.getSeqs();
  
    public void testParse_alternativeResidues() throws Exception
    {
      PDBfile mctest = new PDBfile(false, false, false, pdbWithAltLoc,
 -            AppletFormatAdapter.PASTE);
 +            DataSourceType.PASTE);
      boolean annotFromStructure = false;
      boolean localSecondaryStruct = false;
      boolean serviceSecondaryStruct = false;
      JmolParser jtest = new JmolParser(annotFromStructure,
              localSecondaryStruct, serviceSecondaryStruct, pdbWithAltLoc,
-             jalview.io.DataSourceType.PASTE);
 -            jalview.io.AppletFormatAdapter.PASTE);
++            DataSourceType.PASTE);
      Vector<SequenceI> seqs = jtest.getSeqs();
      Vector<SequenceI> mcseqs = mctest.getSeqs();
    
index 0000000,8a89830..01b4adf
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,102 +1,102 @@@
+ package jalview.ext.jmol;
+ import jalview.datamodel.SequenceI;
 -import jalview.io.AppletFormatAdapter;
++import jalview.io.DataSourceType;
+ import java.io.File;
+ import java.io.IOException;
+ import java.util.HashSet;
+ import java.util.Set;
+ import java.util.Vector;
+ import MCview.PDBfile;
+ /**
+  * This is not a unit test, rather it is a bulk End-to-End scan for sequences
+  * consistency for PDB files parsed with JmolParser vs. Jalview's PDBfile
+  * parser. The directory of PDB files to test must be provided in the launch
+  * args.
+  * 
+  * @author tcnofoegbu
+  *
+  */
+ public class JmolVsJalviewPDBParserEndToEndTest
+ {
+   public static void main(String[] args)
+   {
+     if (args == null || args[0] == null)
+     {
+       System.err
+               .println("You must provide a PDB directory in the launch argument");
+       return;
+     }
+     // args[0] must provide the directory of PDB files to run the test with
+     String testDir = args[0];
+     System.out.println("PDB directory : " + testDir);
+     File pdbDir = new File(testDir);
+     String testFiles[] = pdbDir.list();
+     testFileParser(testDir, testFiles);
+   }
+   public static void testFileParser(String testDir, String[] testFiles)
+   {
+     Set<String> failedFiles = new HashSet<String>();
+     int totalSeqScanned = 0, totalFail = 0;
+     for (String pdbStr : testFiles)
+     {
+       String testFile = testDir + "/" + pdbStr;
+       PDBfile mctest = null;
+       JmolParser jtest = null;
+       try
+       {
+         mctest = new PDBfile(false, false, false, testFile,
 -                AppletFormatAdapter.FILE);
++                DataSourceType.FILE);
+         jtest = new JmolParser(false, false, false, testFile,
 -                jalview.io.AppletFormatAdapter.FILE);
++                DataSourceType.FILE);
+       } catch (IOException e)
+       {
+         System.err.println("Exception thrown while parsing : " + pdbStr);
+       }
+       Vector<SequenceI> seqs = jtest.getSeqs();
+       Vector<SequenceI> mcseqs = mctest.getSeqs();
+       for (SequenceI sq : seqs)
+       {
+         try
+         {
+         String testSeq = mcseqs.remove(0).getSequenceAsString();
+           if (!sq.getSequenceAsString().equals(testSeq))
+         {
+           ++totalFail;
+             System.err.println("Test Failed for " + pdbStr + ". Diff:");
+           System.err.println(sq.getSequenceAsString());
+           System.err.println(testSeq);
+           failedFiles.add(pdbStr);
+         }
+           ++totalSeqScanned;
+         } catch (Exception e)
+         {
+           e.printStackTrace();
+         }
+       }
+     }
+     int count = 0;
+     System.out.println("\n\nTotal sequence Scanned : " + totalSeqScanned);
+     System.out.println("Total sequence passed : "
+             + (totalSeqScanned - totalFail));
+     System.out.println("Total sequence failed : " + totalFail);
+     System.out
+             .println("Success rate: "
+                     + ((totalSeqScanned - totalFail) * 100)
+                     / totalSeqScanned + "%");
+     System.out.println("\nList of " + failedFiles.size()
+             + " file(s) with sequence diffs:");
+     for (String problemFile : failedFiles)
+     {
+       System.out.println(++count + ". " + problemFile);
+     }
+   }
+ }
@@@ -33,9 -33,10 +33,10 @@@ import jalview.datamodel.PDBEntry
  import jalview.datamodel.PDBEntry.Type;
  import jalview.datamodel.Sequence;
  import jalview.datamodel.SequenceI;
 +import jalview.io.DataSourceType;
  import jalview.io.FileLoader;
 -import jalview.io.FormatAdapter;
  import jalview.structure.StructureSelectionManager;
+ import jalview.util.MapList;
  
  import java.util.ArrayList;
  import java.util.List;
@@@ -131,10 -132,15 +132,15 @@@ public class AlignViewportTes
       * alignment with reference to mappings
       */
      AlignFrame af1 = new FileLoader().LoadFileWaitTillLoaded(
 -            ">Seq1\nCAGT\n", FormatAdapter.PASTE);
 +            ">Seq1\nCAGT\n", DataSourceType.PASTE);
  
+     SequenceI s1 = af1.getViewport().getAlignment().getSequenceAt(0);
      AlignedCodonFrame acf1 = new AlignedCodonFrame();
+     acf1.addMap(s1, s1, new MapList(new int[] { 1, 4 }, new int[] { 1, 4 },
+             1, 1));
      AlignedCodonFrame acf2 = new AlignedCodonFrame();
+     acf2.addMap(s1, s1, new MapList(new int[] { 1, 4 }, new int[] { 4, 1 },
+             1, 1));
  
      List<AlignedCodonFrame> mappings = new ArrayList<AlignedCodonFrame>();
      mappings.add(acf1);
      ssm.resetAll();
  
      AlignFrame af1 = new FileLoader().LoadFileWaitTillLoaded(
 -            ">Seq1\nRSVQ\n", FormatAdapter.PASTE);
 +            ">Seq1\nRSVQ\n", DataSourceType.PASTE);
      AlignFrame af2 = new FileLoader().LoadFileWaitTillLoaded(
 -            ">Seq2\nDGEL\n", FormatAdapter.PASTE);
 +            ">Seq2\nDGEL\n", DataSourceType.PASTE);
+     SequenceI cs1 = new Sequence("cseq1", "CCCGGGTTTAAA");
+     SequenceI cs2 = new Sequence("cseq2", "CTTGAGTCTAGA");
+     SequenceI s1 = af1.getViewport().getAlignment().getSequenceAt(0);
+     SequenceI s2 = af2.getViewport().getAlignment().getSequenceAt(0);
+     // need to be distinct
      AlignedCodonFrame acf1 = new AlignedCodonFrame();
+     acf1.addMap(cs1, s1, new MapList(new int[] { 1, 4 },
+             new int[] { 1, 12 }, 1, 3));
      AlignedCodonFrame acf2 = new AlignedCodonFrame();
+     acf2.addMap(cs2, s2, new MapList(new int[] { 1, 4 },
+             new int[] { 1, 12 }, 1, 3));
      AlignedCodonFrame acf3 = new AlignedCodonFrame();
+     acf3.addMap(cs2, cs2, new MapList(new int[] { 1, 12 }, new int[] { 1,
+         12 }, 1, 1));
  
      List<AlignedCodonFrame> mappings1 = new ArrayList<AlignedCodonFrame>();
      mappings1.add(acf1);
      ssm.resetAll();
  
      AlignFrame af1 = new FileLoader().LoadFileWaitTillLoaded(
 -            ">Seq1\nRSVQ\n", FormatAdapter.PASTE);
 +            ">Seq1\nRSVQ\n", DataSourceType.PASTE);
      AlignFrame af2 = new FileLoader().LoadFileWaitTillLoaded(
 -            ">Seq2\nDGEL\n", FormatAdapter.PASTE);
 +            ">Seq2\nDGEL\n", DataSourceType.PASTE);
+     SequenceI cs1 = new Sequence("cseq1", "CCCGGGTTTAAA");
+     SequenceI cs2 = new Sequence("cseq2", "CTTGAGTCTAGA");
+     SequenceI s1 = af1.getViewport().getAlignment().getSequenceAt(0);
+     SequenceI s2 = af2.getViewport().getAlignment().getSequenceAt(0);
+     // need to be distinct
      AlignedCodonFrame acf1 = new AlignedCodonFrame();
+     acf1.addMap(cs1, s1, new MapList(new int[] { 1, 4 },
+             new int[] { 1, 12 }, 1, 3));
      AlignedCodonFrame acf2 = new AlignedCodonFrame();
+     acf2.addMap(cs2, s2, new MapList(new int[] { 1, 4 },
+             new int[] { 1, 12 }, 1, 3));
      AlignedCodonFrame acf3 = new AlignedCodonFrame();
+     acf3.addMap(cs2, cs2, new MapList(new int[] { 1, 12 }, new int[] { 1,
+         12 }, 1, 1));
  
      List<AlignedCodonFrame> mappings1 = new ArrayList<AlignedCodonFrame>();
      mappings1.add(acf1);
@@@ -32,6 -32,7 +32,7 @@@ import jalview.datamodel.SequenceFeatur
  import jalview.datamodel.SequenceI;
  import jalview.gui.AlignFrame;
  import jalview.structure.StructureImportSettings;
+ import jalview.structure.StructureImportSettings.StructureParser;
  
  import java.io.File;
  
@@@ -62,11 -63,13 +63,13 @@@ public class AnnotatedPDBFileInputTes
              Boolean.TRUE.toString());
      FileLoader loader = new FileLoader(false);
      AlignFrame af = loader.LoadFileWaitTillLoaded("examples/1gaq.txt",
 -            FormatAdapter.FILE);
 +            DataSourceType.FILE);
      al = af.getViewport().getAlignment();
      pdbId = al.getSequenceAt(0).getDatasetSequence().getAllPDBEntries()
              .get(0).getId();
-     StructureImportSettings.setCurrentDefaultFormat("PDB");
+     StructureImportSettings.setDefaultStructureFileFormat("PDB");
+     // StructureImportSettings
+     // .setDefaultPDBFileParser(StructureParser.JALVIEW_PARSER);
    }
  
    @Test(groups = { "Functional" })
        {
  
          System.out.println("CalcId: " + aa.getCalcId());
+         if (StructureImportSettings.getDefaultPDBFileParser().equals(
+                 StructureParser.JALVIEW_PARSER))
+         {
          assertTrue(MCview.PDBfile.isCalcIdForFile(aa, pdbId));
+         }
        }
      }
    }
      SequenceFeature[] sf = al.getSequenceAt(0).getSequenceFeatures();
      assertEquals(296, sf.length);
      assertEquals("RESNUM", sf[0].getType());
-     assertEquals("GLU:  19  1gaqA", sf[0].getDescription());
+     assertEquals("GLU:19 1gaqA", sf[0].getDescription());
      assertEquals("RESNUM", sf[295].getType());
-     assertEquals("TYR: 314  1gaqA", sf[295].getDescription());
+     assertEquals("TYR:314 1gaqA", sf[295].getDescription());
  
      /*
       * 1GAQ/B
      sf = al.getSequenceAt(1).getSequenceFeatures();
      assertEquals(98, sf.length);
      assertEquals("RESNUM", sf[0].getType());
-     assertEquals("ALA:   1  1gaqB", sf[0].getDescription());
+     assertEquals("ALA:1 1gaqB", sf[0].getDescription());
      assertEquals("RESNUM", sf[97].getType());
-     assertEquals("ALA:  98  1gaqB", sf[97].getDescription());
+     assertEquals("ALA:98 1gaqB", sf[97].getDescription());
  
      /*
       * 1GAQ/C
      sf = al.getSequenceAt(2).getSequenceFeatures();
      assertEquals(296, sf.length);
      assertEquals("RESNUM", sf[0].getType());
-     assertEquals("GLU:  19  1gaqC", sf[0].getDescription());
+     assertEquals("GLU:19 1gaqC", sf[0].getDescription());
      assertEquals("RESNUM", sf[295].getType());
-     assertEquals("TYR: 314  1gaqC", sf[295].getDescription());
+     assertEquals("TYR:314 1gaqC", sf[295].getDescription());
    }
  
    @Test(groups = { "Functional" })
      String tfile = File.createTempFile("JalviewTest", ".jvp")
              .getAbsolutePath();
      AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
 -            inFile, FormatAdapter.FILE);
 +            inFile, DataSourceType.FILE);
      assertTrue("Didn't read input file " + inFile, af != null);
      assertTrue("Failed to store as a project.",
 -            af.saveAlignment(tfile, "Jalview"));
 +            af.saveAlignment(tfile, FileFormat.Jalview));
      af.closeMenuItem_actionPerformed(true);
      af = null;
      af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(tfile,
 -            FormatAdapter.FILE);
 +            DataSourceType.FILE);
      assertTrue("Failed to import new project", af != null);
      for (SequenceI asq : af.getViewport().getAlignment().getSequences())
      {
index 0000000,81e336e..6bf954a
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,138 +1,135 @@@
+ package jalview.io;
+ import static org.testng.AssertJUnit.assertEquals;
+ import static org.testng.AssertJUnit.assertNotNull;
+ import static org.testng.AssertJUnit.fail;
+ import jalview.datamodel.AlignmentI;
+ import jalview.datamodel.SequenceI;
+ import java.io.IOException;
+ import java.util.ArrayList;
 -import java.util.Arrays;
+ import java.util.List;
+ import org.testng.annotations.DataProvider;
+ import org.testng.annotations.Test;
+ public class FormatAdapterTest
+ {
+   /**
+    * Test saving and re-reading in a specified format
+    * 
+    * @throws IOException
+    */
+   @Test(groups = { "Functional" }, dataProvider = "formats")
 -  public void testRoundTrip(String format) throws IOException
++  public void testRoundTrip(FileFormatI format) throws IOException
+   {
+     try
+     {
+       AlignmentI al = new FormatAdapter().readFile("examples/uniref50.fa",
 -              FormatAdapter.FILE, "FASTA");
++              DataSourceType.FILE, FileFormat.Fasta);
+       /*
+        * 'gap' is the gap character used in the alignment data file here,
+        * not the user preferred gap character
+        */
+       char gap = al.getGapCharacter();
+       assertNotNull(al);
+       SequenceI[] seqs = al.getSequencesArray();
+       String formatted = new FormatAdapter().formatSequences(format, al,
+               false);
+       AlignmentI reloaded = new FormatAdapter().readFile(formatted,
 -              FormatAdapter.PASTE, format);
++              DataSourceType.PASTE, format);
+       List<SequenceI> reread = reloaded.getSequences();
+       assertEquals("Wrong number of reloaded sequences", seqs.length,
+               reread.size());
+       int i = 0;
+       for (SequenceI seq : reread)
+       {
+         String sequenceString = seq.getSequenceAsString();
+         /*
+          * special case: MSF always uses '.' as gap character
+          */
+         sequenceString = adjustForGapTreatment(sequenceString, gap, format);
+         assertEquals(
+                 String.format("Sequence %d: %s", i,
+                         seqs[i].getName()), seqs[i].getSequenceAsString(),
+                 sequenceString);
+         i++;
+       }
+     } catch (IOException e)
+     {
+       fail(String
+               .format("Format %s failed with %s", format, e.getMessage()));
+     }
+   }
+   /**
+    * Optionally change the gap character in the string to the given character,
+    * depending on the sequence file format
+    * 
+    * @param sequenceString
+    *          a sequence (as written in 'format' format)
+    * @param gap
+    *          the sequence's original gap character
+    * @param format
+    * @return
+    */
+   String adjustForGapTreatment(String sequenceString, char gap,
 -          String format)
++          FileFormatI format)
+   {
 -    if ("MSF".equals(format))
++    if (format == FileFormat.MSF)
+     {
+       /*
+        * MSF forces gap character to '.', so change it back
+        * for comparison purposes
+        */
+       sequenceString = sequenceString.replace('.', gap);
+     }
+     return sequenceString;
+   }
+   /**
+    * Data provider that serves alignment formats that are both readable and
+    * writable
+    * 
+    * @return
+    */
+   @DataProvider(name = "formats")
+   static Object[][] getFormats()
+   {
 -    List<String> both = new ArrayList<String>();
 -    String[] readable = FormatAdapter.READABLE_FORMATS;
 -    List<String> writeable = Arrays.asList(FormatAdapter.WRITEABLE_FORMATS);
 -    for (String r : readable)
++    List<FileFormatI> both = new ArrayList<FileFormatI>();
++    for (FileFormat format : FileFormat.values())
+     {
 -      if (writeable.contains(r))
++      if (format.isReadable() && format.isWritable())
+       {
 -        both.add(r);
++        both.add(format);
+       }
+     }
+     Object[][] formats = new Object[both.size()][];
+     int i = 0;
 -    for (String format : both)
++    for (FileFormatI format : both)
+     {
+       formats[i] = new Object[] { format };
+       i++;
+     }
+     return formats;
+   }
+   /**
+    * Enable this to isolate testing to a single file format
+    * 
+    * @throws IOException
+    */
+   @Test(groups = { "Functional" }, enabled = false)
+   public void testOneFormatRoundTrip() throws IOException
+   {
 -    testRoundTrip("JSON");
++    testRoundTrip(FileFormat.Json);
+   }
+ }
@@@ -30,21 -30,26 +30,26 @@@ import jalview.api.AlignViewportI
  import jalview.api.AlignmentViewPanel;
  import jalview.api.ViewStyleI;
  import jalview.bin.Cache;
+ import jalview.bin.Jalview;
  import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.HiddenSequences;
+ import jalview.datamodel.PDBEntry;
  import jalview.datamodel.SequenceCollectionI;
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
  import jalview.gui.AlignFrame;
+ import jalview.gui.AlignmentPanel;
  import jalview.gui.Desktop;
  import jalview.gui.Jalview2XML;
  import jalview.schemes.AnnotationColourGradient;
  import jalview.schemes.ColourSchemeI;
+ import jalview.structure.StructureImportSettings;
  import jalview.viewmodel.AlignmentViewport;
  
  import java.io.File;
  import java.util.ArrayList;
+ import java.util.Date;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
@@@ -65,8 -70,19 +70,19 @@@ public class Jalview2xmlTest
    @BeforeClass(alwaysRun = true)
    public static void setUpBeforeClass() throws Exception
    {
-     jalview.bin.Jalview.main(new String[] { "-props",
-         "test/jalview/io/testProps.jvprops" });
+     /*
+      * use read-only test properties file
+      */
+     Cache.loadProperties("test/jalview/io/testProps.jvprops");
+     /*
+      * set news feed last read to a future time to ensure no
+      * 'unread' news item is displayed
+      */
+     Date oneHourFromNow = new Date(System.currentTimeMillis() + 3600 * 1000);
+     Cache.setDateProperty("JALVIEW_NEWS_RSS_LASTMODIFIED", oneHourFromNow);
+     Jalview.main(new String[] {});
    }
  
    /**
@@@ -75,7 -91,7 +91,7 @@@
    @AfterClass(alwaysRun = true)
    public static void tearDownAfterClass() throws Exception
    {
-     jalview.gui.Desktop.instance.closeAll_actionPerformed(null);
+     Desktop.instance.closeAll_actionPerformed(null);
    }
  
    int countDsAnn(jalview.viewmodel.AlignmentViewport avp)
      String tfile = File.createTempFile("JalviewTest", ".jvp")
              .getAbsolutePath();
      AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
 -            inFile, FormatAdapter.FILE);
 +            inFile, DataSourceType.FILE);
      assertTrue("Didn't read input file " + inFile, af != null);
      int olddsann = countDsAnn(af.getViewport());
      assertTrue("Didn't find any dataset annotations", olddsann > 0);
              "Couldn't apply RNA helices colourscheme",
              af.getViewport().getGlobalColourScheme() instanceof jalview.schemes.RNAHelicesColour);
      assertTrue("Failed to store as a project.",
 -            af.saveAlignment(tfile, "Jalview"));
 +            af.saveAlignment(tfile, FileFormat.Jalview));
      af.closeMenuItem_actionPerformed(true);
      af = null;
      af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(tfile,
 -            FormatAdapter.FILE);
 +            DataSourceType.FILE);
      assertTrue("Failed to import new project", af != null);
      int newdsann = countDsAnn(af.getViewport());
      assertTrue(
      String tfile = File.createTempFile("JalviewTest", ".jvp")
              .getAbsolutePath();
      AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
 -            inFile, FormatAdapter.FILE);
 +            inFile, DataSourceType.FILE);
      assertTrue("Didn't read input file " + inFile, af != null);
 -    af.loadJalviewDataFile(inAnnot, FormatAdapter.FILE, null, null);
 +    af.loadJalviewDataFile(inAnnot, DataSourceType.FILE, null, null);
      assertTrue(
              "Didn't set T-coffee colourscheme",
              af.getViewport().getGlobalColourScheme().getClass()
                              .getViewport().getGlobalColourScheme())) != null);
  
      assertTrue("Failed to store as a project.",
 -            af.saveAlignment(tfile, "Jalview"));
 +            af.saveAlignment(tfile, FileFormat.Jalview));
      af.closeMenuItem_actionPerformed(true);
      af = null;
      af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(tfile,
 -            FormatAdapter.FILE);
 +            DataSourceType.FILE);
      assertTrue("Failed to import new project", af != null);
      assertTrue(
              "Didn't set T-coffee colourscheme for imported project.",
      String tfile = File.createTempFile("JalviewTest", ".jvp")
              .getAbsolutePath();
      AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
 -            inFile, FormatAdapter.FILE);
 +            inFile, DataSourceType.FILE);
      assertTrue("Didn't read input file " + inFile, af != null);
 -    af.loadJalviewDataFile(inAnnot, FormatAdapter.FILE, null, null);
 +    af.loadJalviewDataFile(inAnnot, DataSourceType.FILE, null, null);
      AlignmentAnnotation[] aa = af.getViewport().getAlignment()
              .getSequenceAt(0).getAnnotation("IUPredWS (Short)");
      assertTrue(
      sg.addSequence(af.getViewport().getAlignment().getSequenceAt(2), true);
      af.alignPanel.alignmentChanged();
      assertTrue("Failed to store as a project.",
 -            af.saveAlignment(tfile, "Jalview"));
 +            af.saveAlignment(tfile, FileFormat.Jalview));
      af.closeMenuItem_actionPerformed(true);
      af = null;
      af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(tfile,
 -            FormatAdapter.FILE);
 +            DataSourceType.FILE);
      assertTrue("Failed to import new project", af != null);
  
      // check for group and alignment colourschemes
      int origCount = Desktop.getAlignFrames() == null ? 0 : Desktop
              .getAlignFrames().length;
      AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
 -            "examples/exampleFile_2_7.jar", FormatAdapter.FILE);
 +            "examples/exampleFile_2_7.jar", DataSourceType.FILE);
      assertTrue("Didn't read in the example file correctly.", af != null);
      assertTrue("Didn't gather the views in the example file.",
              Desktop.getAlignFrames().length == 1 + origCount);
    @Test(groups = { "Functional" })
    public void viewRefPdbAnnotation() throws Exception
    {
-     Cache.applicationProperties.setProperty("STRUCT_FROM_PDB",
-             Boolean.TRUE.toString());
-     Cache.applicationProperties.setProperty("ADD_SS_ANN",
-             Boolean.TRUE.toString());
+     // TODO: Make this pass without setting StructureParser.JALVIEW_PARSER
+     // StructureImportSettings
+     // .setDefaultPDBFileParser(StructureParser.JALVIEW_PARSER);
+     StructureImportSettings.setProcessSecondaryStructure(true);
+     StructureImportSettings.setVisibleChainAnnotation(true);
      AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
 -            "examples/exampleFile_2_7.jar", FormatAdapter.FILE);
 +            "examples/exampleFile_2_7.jar", DataSourceType.FILE);
      assertTrue("Didn't read in the example file correctly.", af != null);
      AlignmentViewPanel sps = null;
      for (AlignmentViewPanel ap : af.alignPanel.alignFrame.getAlignPanels())
    public void testCopyViewSettings() throws Exception
    {
      AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
 -            "examples/exampleFile_2_7.jar", FormatAdapter.FILE);
 +            "examples/exampleFile_2_7.jar", DataSourceType.FILE);
      assertTrue("Didn't read in the example file correctly.", af != null);
      AlignmentViewPanel sps = null, groups = null;
      for (AlignmentViewPanel ap : af.alignPanel.alignFrame.getAlignPanels())
    }
  
    /**
-    * test store and recovery of expanded views - currently this is disabled
-    * since the Desktop.explodeViews method doesn't seem to result in the views
-    * being expanded to distinct align frames when executed programmatically.
+    * test store and recovery of expanded views
     * 
     * @throws Exception
     */
    @Test(groups = { "Functional" }, enabled = true)
    public void testStoreAndRecoverExpandedviews() throws Exception
    {
+     Desktop.instance.closeAll_actionPerformed(null);
      AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
 -            "examples/exampleFile_2_7.jar", FormatAdapter.FILE);
 +            "examples/exampleFile_2_7.jar", DataSourceType.FILE);
      assertTrue("Didn't read in the example file correctly.", af != null);
+     Assert.assertEquals(Desktop.getAlignFrames().length, 1);
      String afid = af.getViewport().getSequenceSetId();
-     {
-       final AlignFrame xaf = af;
-       af = null;
-       new Thread(new Runnable()
-       {
-         @Override
-         public void run()
-         {
-           Desktop.instance.explodeViews(xaf);
-         }
-       }).start();
-       Thread.sleep(1000);
-     }
-     // int times = 0;
-     // while (++times < 5 && Desktop.getAlignFrames().length < )
-     // {
-     // Thread.sleep(300);
-     // }
+     // check FileLoader returned a reference to the one alignFrame that is
+     // actually on the Desktop
+     assertTrue(
+             "Jalview2XML.loadAlignFrame() didn't return correct AlignFrame reference for multiple view window",
+             af == Desktop.getAlignFrameFor(af.getViewport()));
+     Desktop.explodeViews(af);
      int oldviews = Desktop.getAlignFrames().length;
      Assert.assertEquals(Desktop.getAlignFrames().length,
              Desktop.getAlignmentPanels(afid).length);
        Assert.assertEquals(Desktop.getAlignFrames().length, 0);
      }
      af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
 -            tfile.getAbsolutePath(), FormatAdapter.FILE);
 +            tfile.getAbsolutePath(), DataSourceType.FILE);
      Assert.assertNotNull(af);
      Assert.assertEquals(
              Desktop.getAlignFrames().length,
    {
      Desktop.instance.closeAll_actionPerformed(null);
      AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
 -            "examples/exampleFile_2_7.jar", FormatAdapter.FILE);
 +            "examples/exampleFile_2_7.jar", DataSourceType.FILE);
      assertTrue("Didn't read in the example file correctly.", af != null);
      String afid = af.getViewport().getSequenceSetId();
  
      }
  
      af = new FileLoader().LoadFileWaitTillLoaded(
 -            tfile.getAbsolutePath(), FormatAdapter.FILE);
 +            tfile.getAbsolutePath(), DataSourceType.FILE);
      afid = af.getViewport().getSequenceSetId();
  
      for (AlignmentViewPanel ap : Desktop.getAlignmentPanels(afid))
    {
      Desktop.instance.closeAll_actionPerformed(null);
      AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
 -            "examples/uniref50.fa", FormatAdapter.FILE);
 +            "examples/uniref50.fa", DataSourceType.FILE);
      assertTrue("Didn't read in the example file correctly.", af != null);
      String afid = af.getViewport().getSequenceSetId();
      // make a second view of the alignment
      }
    
      af = new FileLoader().LoadFileWaitTillLoaded(
 -            tfile.getAbsolutePath(), FormatAdapter.FILE);
 +            tfile.getAbsolutePath(), DataSourceType.FILE);
      afid = af.getViewport().getSequenceSetId();
    
      for (AlignmentViewPanel ap : Desktop.getAlignmentPanels(afid))
                hidden.size(), hs.getSize());
      }
    }
+   /**
+    * Test save and reload of PDBEntry in Jalview project
+    * 
+    * @throws Exception
+    */
+   @Test(groups = { "Functional" })
+   public void testStoreAndRecoverPDBEntry() throws Exception
+   {
+     Desktop.instance.closeAll_actionPerformed(null);
+     String exampleFile = "examples/3W5V.pdb";
+     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(exampleFile,
 -            FormatAdapter.FILE);
++            DataSourceType.FILE);
+     assertTrue("Didn't read in the example file correctly.", af != null);
+     String afid = af.getViewport().getSequenceSetId();
+     AlignmentPanel[] alignPanels = Desktop.getAlignmentPanels(afid);
+     System.out.println();
+     AlignmentViewPanel ap = alignPanels[0];
+     String tfileBase = new File(".").getAbsolutePath().replace(".", "");
+     String testFile = tfileBase + exampleFile;
+     AlignmentI alignment = ap.getAlignment();
+     System.out.println("blah");
+     SequenceI[] seqs = alignment.getSequencesArray();
+     Assert.assertNotNull(seqs[0]);
+     Assert.assertNotNull(seqs[1]);
+     Assert.assertNotNull(seqs[2]);
+     Assert.assertNotNull(seqs[3]);
+     Assert.assertNotNull(seqs[0].getDatasetSequence());
+     Assert.assertNotNull(seqs[1].getDatasetSequence());
+     Assert.assertNotNull(seqs[2].getDatasetSequence());
+     Assert.assertNotNull(seqs[3].getDatasetSequence());
+     PDBEntry[] pdbEntries = new PDBEntry[4];
+     pdbEntries[0] = new PDBEntry("3W5V", "A", null, testFile);
+     pdbEntries[1] = new PDBEntry("3W5V", "B", null, testFile);
+     pdbEntries[2] = new PDBEntry("3W5V", "C", null, testFile);
+     pdbEntries[3] = new PDBEntry("3W5V", "D", null, testFile);
+     Assert.assertTrue(seqs[0].getDatasetSequence().getAllPDBEntries()
+             .get(0).equals(pdbEntries[0]));
+     Assert.assertTrue(seqs[1].getDatasetSequence().getAllPDBEntries()
+             .get(0).equals(pdbEntries[1]));
+     Assert.assertTrue(seqs[2].getDatasetSequence().getAllPDBEntries()
+             .get(0).equals(pdbEntries[2]));
+     Assert.assertTrue(seqs[3].getDatasetSequence().getAllPDBEntries()
+             .get(0).equals(pdbEntries[3]));
+     File tfile = File.createTempFile("testStoreAndRecoverPDBEntry", ".jvp");
+     try
+     {
+       new Jalview2XML(false).saveState(tfile);
+     } catch (Throwable e)
+     {
+       Assert.fail("Didn't save the state", e);
+     }
+     Desktop.instance.closeAll_actionPerformed(null);
+     if (Desktop.getAlignFrames() != null)
+     {
+       Assert.assertEquals(Desktop.getAlignFrames().length, 0);
+     }
+     AlignFrame restoredFrame = new FileLoader().LoadFileWaitTillLoaded(
 -            tfile.getAbsolutePath(), FormatAdapter.FILE);
++            tfile.getAbsolutePath(), DataSourceType.FILE);
+     String rfid = restoredFrame.getViewport().getSequenceSetId();
+     AlignmentPanel[] rAlignPanels = Desktop.getAlignmentPanels(rfid);
+     AlignmentViewPanel rap = rAlignPanels[0];
+     AlignmentI rAlignment = rap.getAlignment();
+     System.out.println("blah");
+     SequenceI[] rseqs = rAlignment.getSequencesArray();
+     Assert.assertNotNull(rseqs[0]);
+     Assert.assertNotNull(rseqs[1]);
+     Assert.assertNotNull(rseqs[2]);
+     Assert.assertNotNull(rseqs[3]);
+     Assert.assertNotNull(rseqs[0].getDatasetSequence());
+     Assert.assertNotNull(rseqs[1].getDatasetSequence());
+     Assert.assertNotNull(rseqs[2].getDatasetSequence());
+     Assert.assertNotNull(rseqs[3].getDatasetSequence());
+     // The Asserts below are expected to fail until the PDB chainCode is
+     // recoverable from a Jalview projects
+     Assert.assertTrue(rseqs[0].getDatasetSequence().getAllPDBEntries()
+             .get(0).equals(pdbEntries[0]));
+     Assert.assertTrue(rseqs[1].getDatasetSequence().getAllPDBEntries()
+             .get(0).equals(pdbEntries[1]));
+     Assert.assertTrue(rseqs[2].getDatasetSequence().getAllPDBEntries()
+             .get(0).equals(pdbEntries[2]));
+     Assert.assertTrue(rseqs[3].getDatasetSequence().getAllPDBEntries()
+             .get(0).equals(pdbEntries[3]));
+   }
  }
@@@ -23,6 -23,7 +23,7 @@@ package jalview.io
  import static org.testng.AssertJUnit.assertEquals;
  import static org.testng.AssertJUnit.assertNotNull;
  import static org.testng.AssertJUnit.assertTrue;
+ import static org.testng.AssertJUnit.fail;
  
  import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.AlignmentI;
@@@ -46,15 -47,15 +47,15 @@@ public class StockholmFileTes
    @Test(groups = { "Functional" })
    public void pfamFileIO() throws Exception
    {
 -    testFileIOwithFormat(new File(PfamFile), "STH", -1, 0);
 +    testFileIOwithFormat(new File(PfamFile), FileFormat.Stockholm, -1, 0);
    }
  
    @Test(groups = { "Functional" })
    public void pfamFileDataExtraction() throws Exception
    {
      AppletFormatAdapter af = new AppletFormatAdapter();
 -    AlignmentI al = af.readFile(PfamFile, af.FILE,
 -            new IdentifyFile().identify(PfamFile, af.FILE));
 +    AlignmentI al = af.readFile(PfamFile, DataSourceType.FILE,
 +            new IdentifyFile().identify(PfamFile, DataSourceType.FILE));
      int numpdb = 0;
      for (SequenceI sq : al.getSequences())
      {
@@@ -71,7 -72,7 +72,7 @@@
    @Test(groups = { "Functional" })
    public void rfamFileIO() throws Exception
    {
 -    testFileIOwithFormat(new File(RfamFile), "STH", 2, 1);
 +    testFileIOwithFormat(new File(RfamFile), FileFormat.Stockholm, 2, 1);
    }
  
    /**
@@@ -85,7 -86,7 +86,7 @@@
     *          f
     */
  
 -  public static void testFileIOwithFormat(File f, String ioformat,
 +  public static void testFileIOwithFormat(File f, FileFormatI ioformat,
            int naliannot, int nminseqann)
    {
      System.out.println("Reading file: " + f);
@@@ -94,8 -95,8 +95,8 @@@
      {
        AppletFormatAdapter rf = new AppletFormatAdapter();
  
 -      AlignmentI al = rf.readFile(ff, AppletFormatAdapter.FILE,
 -              new IdentifyFile().identify(ff, AppletFormatAdapter.FILE));
 +      AlignmentI al = rf.readFile(ff, DataSourceType.FILE,
 +              new IdentifyFile().identify(ff, DataSourceType.FILE));
  
        assertNotNull("Couldn't read supplied alignment data.", al);
  
                + outputfile + "\n<<EOF\n");
        // test for consistency in io
        AlignmentI al_input = new AppletFormatAdapter().readFile(outputfile,
 -              AppletFormatAdapter.PASTE, ioformat);
 +              DataSourceType.PASTE, ioformat);
        assertNotNull("Couldn't parse reimported alignment data.", al_input);
  
 -      String identifyoutput = new IdentifyFile().identify(outputfile,
 -              AppletFormatAdapter.PASTE);
 +      FileFormatI identifyoutput = new IdentifyFile().identify(outputfile,
 +              DataSourceType.PASTE);
        assertNotNull("Identify routine failed for outputformat " + ioformat,
                identifyoutput);
        assertTrue(
     *          'secondary' or generated alignment from some datapreserving
     *          transformation
     * @param ignoreFeatures
-    *          when true, differences in seuqence feature annotation are ignored.
+    *          when true, differences in sequence feature annotation are ignored
     */
    public static void testAlignmentEquivalence(AlignmentI al,
            AlignmentI al_input, boolean ignoreFeatures)
      assertNotNull("Original alignment was null", al);
      assertNotNull("Generated alignment was null", al_input);
  
-     assertTrue(
-             "Alignment dimension mismatch: originl contains "
-                     + al.getHeight() + " and generated has "
-                     + al_input.getHeight() + " sequences; original has "
-                     + al.getWidth() + " and generated has "
-                     + al_input.getWidth() + " columns.",
+     assertTrue("Alignment dimension mismatch: original: " + al.getHeight()
+             + "x" + al.getWidth() + ", generated: " + al_input.getHeight()
+             + "x" + al_input.getWidth(),
              al.getHeight() == al_input.getHeight()
                      && al.getWidth() == al_input.getWidth());
  
      // note - at moment we do not distinguish between alignment without any
      // annotation rows and alignment with no annotation row vector
      // we might want to revise this in future
-     int aa_new_size = (aa_new == null ? 0 : aa_new.length), aa_original_size = (aa_original == null ? 0
-             : aa_original.length);
-     Map<Integer, java.util.BitSet> orig_groups = new HashMap<Integer, java.util.BitSet>(), new_groups = new HashMap<Integer, java.util.BitSet>();
+     int aa_new_size = (aa_new == null ? 0 : aa_new.length);
+     int aa_original_size = (aa_original == null ? 0 : aa_original.length);
+     Map<Integer, BitSet> orig_groups = new HashMap<Integer, BitSet>();
+     Map<Integer, BitSet> new_groups = new HashMap<Integer, BitSet>();
  
      if (aa_new != null && aa_original != null)
      {
            assertTrue("Different alignment annotation at position " + i,
                    equalss(aa_original[i], aa_new[i]));
            // compare graphGroup or graph properties - needed to verify JAL-1299
-           assertTrue("Graph type not identical.",
-                   aa_original[i].graph == aa_new[i].graph);
-           assertTrue("Visibility not identical.",
-                   aa_original[i].visible == aa_new[i].visible);
-           assertTrue(
-                   "Threshold line not identical.",
-                   aa_original[i].threshold == null ? aa_new[i].threshold == null
-                           : aa_original[i].threshold
-                                   .equals(aa_new[i].threshold));
+           assertEquals("Graph type not identical.", aa_original[i].graph,
+                   aa_new[i].graph);
+           assertEquals("Visibility not identical.", aa_original[i].visible,
+                   aa_new[i].visible);
+           assertEquals("Threshold line not identical.",
+                   aa_original[i].threshold, aa_new[i].threshold);
            // graphGroup may differ, but pattern should be the same
-           Integer o_ggrp = new Integer(aa_original[i].graphGroup + 2), n_ggrp = new Integer(
-                   aa_new[i].graphGroup + 2);
-           BitSet orig_g = orig_groups.get(o_ggrp), new_g = new_groups
-                   .get(n_ggrp);
+           Integer o_ggrp = new Integer(aa_original[i].graphGroup + 2);
+           Integer n_ggrp = new Integer(aa_new[i].graphGroup + 2);
+           BitSet orig_g = orig_groups.get(o_ggrp);
+           BitSet new_g = new_groups.get(n_ggrp);
            if (orig_g == null)
            {
              orig_groups.put(o_ggrp, orig_g = new BitSet());
            {
              new_groups.put(n_ggrp, new_g = new BitSet());
            }
-           assertTrue("Graph Group pattern differs at annotation " + i,
-                   orig_g.equals(new_g));
+           assertEquals("Graph Group pattern differs at annotation " + i,
+                   orig_g, new_g);
            orig_g.set(i);
            new_g.set(i);
          }
          }
        }
      }
-     assertTrue(
-             "Generated and imported alignment have different annotation sets ("
-                     + aa_new_size + " != " + aa_original_size + ")",
-             aa_new_size == aa_original_size);
+     assertEquals(
+             "Generated and imported alignment have different annotation sets",
+             aa_new_size, aa_original_size);
  
      // check sequences, annotation and features
      SequenceI[] seq_original = new SequenceI[al.getSequencesArray().length];
          {
            String ss_original = seq_original[i].getSequenceAsString();
            String ss_new = seq_new[in].getSequenceAsString();
-           assertTrue("The sequences " + name + "/" + start + "-" + end
-                   + " are not equal", ss_original.equals(ss_new));
+           assertEquals("The sequences " + name + "/" + start + "-" + end
+                   + " are not equal", ss_original, ss_new);
  
            assertTrue(
                    "Sequence Features were not equivalent"
                      .getSequenceFeatures().length];
              sequenceFeatures_new = seq_new[in].getSequenceFeatures();
  
-             assertTrue("different number of features", seq_original[i]
-                     .getSequenceFeatures().length == seq_new[in]
+             assertEquals("different number of features",
+                     seq_original[i].getSequenceFeatures().length,
+                     seq_new[in]
                      .getSequenceFeatures().length);
  
              for (int feat = 0; feat < seq_original[i].getSequenceFeatures().length; feat++)
              {
-               assertTrue("Different features",
-                       sequenceFeatures_original[feat]
-                               .equals(sequenceFeatures_new[feat]));
+               assertEquals("Different features",
+                       sequenceFeatures_original[feat],
+                       sequenceFeatures_new[feat]);
              }
            }
            // compare alignment annotation
            else if (al.getSequenceAt(i).getAnnotation() != null
                    && al_input.getSequenceAt(in).getAnnotation() == null)
            {
-             assertTrue("Annotations differed between sequences ("
+             fail("Annotations differed between sequences ("
                      + al.getSequenceAt(i).getName() + ") and ("
-                     + al_input.getSequenceAt(i).getName() + ")", false);
+                     + al_input.getSequenceAt(i).getName() + ")");
            }
            break;
          }
@@@ -27,8 -27,9 +27,9 @@@ import jalview.datamodel.AlignedCodonFr
  import jalview.datamodel.Sequence;
  import jalview.datamodel.SequenceFeature;
  import jalview.datamodel.SequenceI;
 -import jalview.io.FormatAdapter;
 +import jalview.io.DataSourceType;
  import jalview.io.StructureFile;
+ import jalview.util.MapList;
  
  import java.util.ArrayList;
  import java.util.List;
@@@ -51,7 -52,11 +52,11 @@@ public class StructureSelectionManagerT
    public void testRegisterMapping()
    {
      AlignedCodonFrame acf1 = new AlignedCodonFrame();
+     acf1.addMap(new Sequence("s1", "ttt"), new Sequence("p1", "p"),
+             new MapList(new int[] { 1, 3 }, new int[] { 1, 1 }, 1, 1));
      AlignedCodonFrame acf2 = new AlignedCodonFrame();
+     acf2.addMap(new Sequence("s2", "ttt"), new Sequence("p2", "p"),
+             new MapList(new int[] { 1, 3 }, new int[] { 1, 1 }, 1, 1));
  
      ssm.registerMapping(acf1);
      assertEquals(1, ssm.getSequenceMappings().size());
    public void testRegisterMappings()
    {
      AlignedCodonFrame acf1 = new AlignedCodonFrame();
+     acf1.addMap(new Sequence("s1", "ttt"), new Sequence("p1", "p"),
+             new MapList(new int[] { 1, 3 }, new int[] { 1, 1 }, 1, 1));
      AlignedCodonFrame acf2 = new AlignedCodonFrame();
+     acf2.addMap(new Sequence("s2", "ttt"), new Sequence("p2", "p"),
+             new MapList(new int[] { 1, 3 }, new int[] { 1, 1 }, 1, 1));
      AlignedCodonFrame acf3 = new AlignedCodonFrame();
+     acf3.addMap(new Sequence("s3", "ttt"), new Sequence("p3", "p"),
+             new MapList(new int[] { 1, 3 }, new int[] { 1, 1 }, 1, 1));
  
      List<AlignedCodonFrame> set1 = new ArrayList<AlignedCodonFrame>();
      set1.add(acf1);
      sm.setProcessSecondaryStructure(true);
      sm.setAddTempFacAnnot(true);
      StructureFile pmap = sm.setMapping(true, new SequenceI[] { seq },
 -            new String[] { null }, "examples/1gaq.txt", FormatAdapter.FILE);
 +            new String[] { null }, "examples/1gaq.txt", DataSourceType.FILE);
      assertTrue(pmap != null);
  
      assertEquals(3, pmap.getSeqs().size());
      SequenceFeature sf = pmap.getSeqs().get(0).getSequenceFeatures()[0];
      assertEquals("RESNUM", sf.getType());
      assertEquals("1gaq", sf.getFeatureGroup());
-     assertEquals("GLU:  19  1gaqA", sf.getDescription());
+     assertEquals("GLU:19 1gaqA", sf.getDescription());
  
      /*
       * Verify a RESNUM sequence feature in the StructureSelectionManager mapped
      sf = map.sequence.getSequenceFeatures()[0];
      assertEquals("RESNUM", sf.getType());
      assertEquals("1gaq", sf.getFeatureGroup());
-     assertEquals("ALA:   1  1gaqB", sf.getDescription());
+     assertEquals("ALA:1 1gaqB", sf.getDescription());
    }
  }
@@@ -39,9 -39,7 +39,9 @@@ import jalview.datamodel.Sequence
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
  import jalview.gui.AlignViewport;
 -import jalview.io.AppletFormatAdapter;
 +import jalview.io.DataSourceType;
 +import jalview.io.FileFormat;
 +import jalview.io.FileFormatI;
  import jalview.io.FormatAdapter;
  
  import java.awt.Color;
@@@ -199,10 -197,10 +199,10 @@@ public class MappingUtilsTes
       * viewport).
       */
      AlignmentI cdna = loadAlignment(">Seq1\nACG\n>Seq2\nTGA\n>Seq3\nTAC\n",
 -            "FASTA");
 +            FileFormat.Fasta);
      cdna.setDataset(null);
      AlignmentI protein = loadAlignment(">Seq1\nK\n>Seq2\nL\n>Seq3\nQ\n",
 -            "FASTA");
 +            FileFormat.Fasta);
      protein.setDataset(null);
      AlignedCodonFrame acf = new AlignedCodonFrame();
      MapList map = new MapList(new int[] { 1, 3 }, new int[] { 1, 1 }, 3, 1);
     * @return
     * @throws IOException
     */
 -  protected AlignmentI loadAlignment(final String data, String format)
 +  protected AlignmentI loadAlignment(final String data, FileFormatI format)
            throws IOException
    {
      AlignmentI a = new FormatAdapter().readFile(data,
 -            AppletFormatAdapter.PASTE, format);
 +            DataSourceType.PASTE, format);
      a.setDataset(null);
      return a;
    }
       */
      AlignmentI cdna = loadAlignment(">Seq1/10-18\nAC-GctGtC-T\n"
              + ">Seq2/20-27\nTc-GA-G-T-Tc\n" + ">Seq3/30-38\nTtTT-AaCGg-\n",
 -            "FASTA");
 +            FileFormat.Fasta);
      cdna.setDataset(null);
      AlignmentI protein = loadAlignment(
              ">Seq1/40-41\n-K-P\n>Seq2/50-51\nL--Q\n>Seq3/60-61\nG--S\n",
 -            "FASTA");
 +            FileFormat.Fasta);
      protein.setDataset(null);
  
      // map first dna to first protein seq
       * viewport).
       */
      AlignmentI cdna = loadAlignment(
 -            ">Seq1\nACGGCA\n>Seq2\nTGACAG\n>Seq3\nTACGTA\n", "FASTA");
 +            ">Seq1\nACGGCA\n>Seq2\nTGACAG\n>Seq3\nTACGTA\n",
 +            FileFormat.Fasta);
      cdna.setDataset(null);
      AlignmentI protein = loadAlignment(">Seq1\nKA\n>Seq2\nLQ\n>Seq3\nQV\n",
 -            "FASTA");
 +            FileFormat.Fasta);
      protein.setDataset(null);
      AlignedCodonFrame acf = new AlignedCodonFrame();
      MapList map = new MapList(new int[] { 1, 6 }, new int[] { 1, 2 }, 3, 1);
       */
      AlignmentI cdna = loadAlignment(
              ">Seq1\nA-CG-GC--AT-CA\n>Seq2\n-TG-AC-AG-T-AT\n>Seq3\n-T--ACG-TAAT-G\n",
 -            "FASTA");
 +            FileFormat.Fasta);
      cdna.setDataset(null);
      AlignmentI protein = loadAlignment(
 -            ">Seq1\n-KA-S\n>Seq2\n--L-QY\n>Seq3\nQ-V-M\n", "FASTA");
 +            ">Seq1\n-KA-S\n>Seq2\n--L-QY\n>Seq3\nQ-V-M\n", FileFormat.Fasta);
      protein.setDataset(null);
      AlignedCodonFrame acf = new AlignedCodonFrame();
      MapList map = new MapList(new int[] { 1, 9 }, new int[] { 1, 3 }, 3, 1);
      assertEquals(0, result.size());
    }
  
+   /**
+    * just like the one above, but this time, we provide a set of sequences to
+    * subselect the mapping search
+    */
+   @Test(groups = { "Functional" })
+   public void testFindMappingsBetweenSequenceAndOthers()
+   {
+     SequenceI seq1 = new Sequence("Seq1", "ABC");
+     SequenceI seq2 = new Sequence("Seq2", "ABC");
+     SequenceI seq3 = new Sequence("Seq3", "ABC");
+     SequenceI seq4 = new Sequence("Seq4", "ABC");
+     seq1.createDatasetSequence();
+     seq2.createDatasetSequence();
+     seq3.createDatasetSequence();
+     seq4.createDatasetSequence();
+     /*
+      * Create mappings from seq1 to seq2, seq2 to seq1, seq3 to seq1
+      */
+     AlignedCodonFrame acf1 = new AlignedCodonFrame();
+     MapList map = new MapList(new int[] { 1, 3 }, new int[] { 1, 3 }, 1, 1);
+     acf1.addMap(seq1.getDatasetSequence(), seq2.getDatasetSequence(), map);
+     AlignedCodonFrame acf2 = new AlignedCodonFrame();
+     acf2.addMap(seq2.getDatasetSequence(), seq1.getDatasetSequence(), map);
+     AlignedCodonFrame acf3 = new AlignedCodonFrame();
+     acf3.addMap(seq3.getDatasetSequence(), seq1.getDatasetSequence(), map);
+     List<AlignedCodonFrame> mappings = new ArrayList<AlignedCodonFrame>();
+     mappings.add(acf1);
+     mappings.add(acf2);
+     mappings.add(acf3);
+     /*
+      * Seq1 has three mappings
+      */
+     List<AlignedCodonFrame> result = MappingUtils
+             .findMappingsForSequenceAndOthers(seq1, mappings,
+                     new Alignment(new SequenceI[] { seq1, seq2 }));
+     assertTrue(result.contains(acf1));
+     assertTrue(result.contains(acf2));
+     assertFalse("Did not expect to find mapping acf3 - subselect failed",
+             result.contains(acf3));
+     assertEquals(2, result.size());
+   }
    @Test(groups = { "Functional" })
    public void testMapEditCommand()
    {
@@@ -28,8 -28,7 +28,8 @@@ import jalview.datamodel.AlignmentAnnot
  import jalview.datamodel.AlignmentI;
  import jalview.gui.Jalview2XML;
  import jalview.io.AnnotationFile;
 -import jalview.io.FileLoader;
 +import jalview.io.DataSourceType;
 +import jalview.io.FileFormat;
  import jalview.io.FormatAdapter;
  import jalview.io.StockholmFileTest;
  import jalview.ws.jws2.Jws2Discoverer;
@@@ -51,6 -50,7 +51,7 @@@ import org.testng.annotations.AfterClas
  import org.testng.annotations.BeforeClass;
  import org.testng.annotations.Test;
  
+ import compbio.metadata.Argument;
  import compbio.metadata.WrongParameterException;
  
  public class RNAStructExportImport
@@@ -90,9 -90,9 +91,9 @@@
        Assert.fail("no web service");
      }
  
 -    FileLoader fl = new FileLoader(false);
 +    jalview.io.FileLoader fl = new jalview.io.FileLoader(false);
  
 -    af = fl.LoadFileWaitTillLoaded(testseqs, FormatAdapter.FILE);
 +    af = fl.LoadFileWaitTillLoaded(testseqs, jalview.io.DataSourceType.FILE);
  
      assertNotNull("Couldn't load test data ('" + testseqs + "')", af);
  
        } catch (InterruptedException x)
        {
        }
-       ;
      } while (af.getViewport().getCalcManager().isWorking());
  
      AlignmentI orig_alig = af.getViewport().getAlignment();
        } catch (InterruptedException x)
        {
        }
-       ;
      } while (af.getViewport().getCalcManager().isWorking());
  
      AlignmentI orig_alig = af.getViewport().getAlignment();
      try
      {
        // what format would be appropriate for RNAalifold annotations?
 -      String aligfileout = new FormatAdapter().formatSequences("PFAM",
 -              al.getSequencesArray());
 +      String aligfileout = FileFormat.Pfam.getAlignmentFile().print(
 +              al.getSequencesArray(), true);
  
        String anfileout = new AnnotationFile()
                .printAnnotationsForAlignment(al);
-       assertTrue(
+       assertNotNull(
                "Test "
                        + testname
                        + "\nAlignment annotation file was not regenerated. Null string",
-               anfileout != null);
+               anfileout);
        assertTrue(
                "Test "
                        + testname
  
        // again what format would be appropriate?
        AlignmentI al_new = new FormatAdapter().readFile(aligfileout,
 -              FormatAdapter.PASTE, "PFAM");
 +              DataSourceType.PASTE, FileFormat.Pfam);
        assertTrue(
                "Test "
                        + testname
                        + "\nregenerated annotation file did not annotate alignment.",
                new AnnotationFile().readAnnotationFile(al_new, anfileout,
 -                      FormatAdapter.PASTE));
 +                      DataSourceType.PASTE));
  
        // test for consistency in io
        StockholmFileTest.testAlignmentEquivalence(al, al_new, false);
    @Test(groups = { "Functional" })
    public void testRnaalifoldSettingsRecovery()
    {
-     List<compbio.metadata.Argument> opts = new ArrayList<compbio.metadata.Argument>();
-     for (compbio.metadata.Argument rg : (List<compbio.metadata.Argument>) rnaalifoldws
+     List<Argument> opts = new ArrayList<Argument>();
+     for (Argument rg : (List<Argument>) rnaalifoldws
              .getRunnerConfig().getArguments())
      {
        if (rg.getDescription().contains("emperature"))
@@@ -25,7 -25,7 +25,7 @@@ import jalview.datamodel.DBRefEntry
  import jalview.datamodel.DBRefSource;
  import jalview.datamodel.Sequence;
  import jalview.datamodel.SequenceI;
 -import jalview.io.AppletFormatAdapter;
 +import jalview.io.DataSourceType;
  import jalview.structure.StructureMapping;
  import jalview.xml.binding.sifts.Entry.Entity;
  
@@@ -182,7 -182,7 +182,7 @@@ public class SiftsClientTes
      try
      {
        pdbFile = new PDBfile(false, false, false, "test/jalview/io/"
 -              + testPDBId + ".pdb", AppletFormatAdapter.FILE);
 +              + testPDBId + ".pdb", DataSourceType.FILE);
        siftsClient = new SiftsClient(pdbFile);
      } catch (Exception e)
      {
  
      // TODO delete when auto-fetching of DBRefEntry is implemented
      DBRefEntry dbRef = new DBRefEntry("uniprot", "", "P00221");
-     dbRef.setStartRes(1);
-     dbRef.setEndRes(147);
      testSeq.addDBRef(dbRef);
      // testSeq.setSourceDBRef(dbRef);
  
        DBRefEntryI expectedDBRef = new DBRefEntry();
        expectedDBRef.setSource(DBRefSource.UNIPROT);
        expectedDBRef.setAccessionId("P00221");
-       expectedDBRef.setStartRes(1);
-       expectedDBRef.setEndRes(147);
        expectedDBRef.setVersion("");
        Assert.assertEquals(actualValidSrcDBRef, expectedDBRef);
      } catch (Exception e)
      DBRefEntryI validDBRef = new DBRefEntry();
      validDBRef.setSource(DBRefSource.UNIPROT);
      validDBRef.setAccessionId("P00221");
-     validDBRef.setStartRes(1);
-     validDBRef.setEndRes(147);
      validDBRef.setVersion("");
      Assert.assertTrue(siftsClient.isValidDBRefEntry(validDBRef));
    }
      try
      {
        pdbFile = new PDBfile(false, false, false, "test/jalview/io/2nq2"
 -              + ".pdb", AppletFormatAdapter.FILE);
 +              + ".pdb", DataSourceType.FILE);
        siftsClientX = new SiftsClient(pdbFile);
      } catch (Exception e)
      {