Merge branch 'develop' into trialMerge
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Fri, 25 Nov 2016 14:21:34 +0000 (14:21 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Fri, 25 Nov 2016 14:21:34 +0000 (14:21 +0000)
Conflicts:
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/AppletJmol.java
src/jalview/appletgui/AppletJmolBinding.java
src/jalview/appletgui/ExtJmol.java
src/jalview/datamodel/PDBEntry.java
src/jalview/ext/ensembl/EnsemblRestClient.java
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/ext/jmol/JmolParser.java
src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AppJmolBinding.java
src/jalview/gui/Desktop.java
src/jalview/gui/FeatureSettings.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/JalviewChimeraBindingModel.java
src/jalview/gui/StructureChooser.java
src/jalview/io/AppletFormatAdapter.java
src/jalview/io/BioJsHTMLOutput.java
src/jalview/io/HTMLOutput.java
src/jalview/io/HtmlSvgOutput.java
src/jalview/io/StructureFile.java
src/jalview/structure/StructureSelectionManager.java
src/jalview/structures/models/AAStructureBindingModel.java
src/jalview/ws/dbsources/Pdb.java
src/jalview/ws/dbsources/Xfam.java
test/MCview/PDBfileTest.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/FeaturesFileTest.java
test/jalview/io/Jalview2xmlTests.java
test/jalview/io/PfamFormatInputTest.java
test/jalview/io/gff/ExonerateHelperTest.java
test/jalview/structure/Mapping.java
test/jalview/structures/models/AAStructureBindingModelTest.java

86 files changed:
1  2 
src/MCview/AppletPDBCanvas.java
src/MCview/PDBCanvas.java
src/MCview/PDBViewer.java
src/MCview/PDBfile.java
src/jalview/api/AlignViewControllerI.java
src/jalview/appletgui/APopupMenu.java
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/AppletJmol.java
src/jalview/appletgui/AppletJmolBinding.java
src/jalview/appletgui/ExtJmol.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/JalviewJmolBinding.java
src/jalview/ext/jmol/JmolParser.java
src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/AnnotationLabels.java
src/jalview/gui/AppJmolBinding.java
src/jalview/gui/ChimeraViewFrame.java
src/jalview/gui/CutAndPasteTransfer.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/JalviewChimeraBindingModel.java
src/jalview/gui/PopupMenu.java
src/jalview/gui/Preferences.java
src/jalview/gui/SequenceFetcher.java
src/jalview/gui/StructureChooser.java
src/jalview/gui/StructureViewerBase.java
src/jalview/io/AnnotationFile.java
src/jalview/io/AppletFormatAdapter.java
src/jalview/io/FastaFile.java
src/jalview/io/FeaturesFile.java
src/jalview/io/FileFormat.java
src/jalview/io/HTMLOutput.java
src/jalview/io/IdentifyFile.java
src/jalview/io/JalviewFileView.java
src/jalview/io/MSFfile.java
src/jalview/io/PfamFile.java
src/jalview/io/StockholmFile.java
src/jalview/io/StructureFile.java
src/jalview/jbgui/GAlignFrame.java
src/jalview/structure/StructureImportSettings.java
src/jalview/structure/StructureSelectionManager.java
src/jalview/structures/models/AAStructureBindingModel.java
src/jalview/util/ImageMaker.java
src/jalview/ws/dbsources/Pdb.java
src/jalview/ws/dbsources/Pfam.java
test/MCview/PDBfileTest.java
test/jalview/analysis/AlignmentUtilsTests.java
test/jalview/analysis/DnaTest.java
test/jalview/analysis/FinderTest.java
test/jalview/analysis/scoremodels/FeatureScoreModelTest.java
test/jalview/controller/AlignViewControllerTest.java
test/jalview/datamodel/AlignmentTest.java
test/jalview/ext/ensembl/EnsemblSeqProxyTest.java
test/jalview/ext/jmol/JmolParserTest.java
test/jalview/ext/jmol/JmolVsJalviewPDBParserEndToEndTest.java
test/jalview/gui/AlignViewportTest.java
test/jalview/gui/AnnotationChooserTest.java
test/jalview/gui/PopupMenuTest.java
test/jalview/io/AnnotatedPDBFileInputTest.java
test/jalview/io/AnnotationFileIOTest.java
test/jalview/io/CrossRef2xmlTests.java
test/jalview/io/FeaturesFileTest.java
test/jalview/io/FormatAdapterTest.java
test/jalview/io/JSONFileTest.java
test/jalview/io/Jalview2xmlTests.java
test/jalview/io/NewickFileTests.java
test/jalview/io/PfamFormatInputTest.java
test/jalview/io/StockholmFileTest.java
test/jalview/io/gff/ExonerateHelperTest.java
test/jalview/io/gff/GffTests.java
test/jalview/structure/Mapping.java
test/jalview/structure/StructureSelectionManagerTest.java
test/jalview/structures/models/AAStructureBindingModelTest.java
test/jalview/util/MappingUtilsTest.java
test/jalview/ws/jabaws/DisorderAnnotExportImport.java
test/jalview/ws/jabaws/RNAStructExportImport.java
test/jalview/ws/sifts/SiftsClientTest.java

Simple merge
Simple merge
Simple merge
Simple merge
@@@ -96,7 -94,19 +96,19 @@@ public interface AlignViewController
     * @return true if parsing resulted in something being imported to the view or
     *         dataset
     */
 -  public boolean parseFeaturesFile(String file, String protocol,
 +  public boolean parseFeaturesFile(String file, DataSourceType sourceType,
            boolean relaxedIdMatching);
  
+   /**
+    * mark columns containing highlighted regions (e.g. from search, structure
+    * highlight, or a mouse over event in another viewer)
+    * 
+    * @param invert
+    * @param extendCurrent
+    * @param toggle
+    * @return
+    */
+   boolean markHighlightedColumns(boolean invert, boolean extendCurrent,
+           boolean toggle);
  }
Simple merge
@@@ -358,14 -355,14 +357,14 @@@ public class AlignFrame extends Embmenu
     * 
     * @param file
     *          file URL, content, or other resolvable path
-    * @param paste
 -   * @param type
++   * @param sourceType
     *          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, DataSourceType paste,
 -  public boolean parseFeaturesFile(String file, String type,
++  public boolean parseFeaturesFile(String file, DataSourceType sourceType,
            boolean autoenabledisplay)
    {
      boolean featuresFile = false;
                .getFeatureRenderer().getFeatureColours();
        boolean relaxedIdMatching = viewport.applet.getDefaultParameter(
                "relaxedidmatch", false);
-       featuresFile = new FeaturesFile(file, paste).parse(
 -      featuresFile = new FeaturesFile(file, type).parse(
++      featuresFile = new FeaturesFile(file, sourceType).parse(
                viewport.getAlignment(), colours, true, relaxedIdMatching);
      } catch (Exception ex)
      {
        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)
      {
-       String sourceType = pdb.getProperty().get("protocol");
 -      protocol = (String) pdb.getProperty("protocol");
++      String sourceType = (String) pdb.getProperty("protocol");
 +      try
 +      {
 +        protocol = DataSourceType.valueOf(sourceType);
 +      } catch (IllegalArgumentException e)
 +      {
 +        // ignore
 +      }
        if (protocol == null)
        {
          System.err.println("Couldn't work out protocol to open structure: "
@@@ -291,15 -289,12 +290,12 @@@ public class AppletJmol extends Embmenu
          closeViewer();
        }
      });
-     if (pdbentry.getProperty() == null)
-     {
-       pdbentry.setProperty(new Hashtable<String, String>());
-       pdbentry.getProperty().put("protocol", protocol.toString());
-     }
+     pdbentry.setProperty("protocol", protocol);
 -
      if (pdbentry.getFile() != null)
++    
      {
        // import structure data from pdbentry.getFile based on given protocol
 -      if (protocol.equals(AppletFormatAdapter.PASTE))
 +      if (protocol == DataSourceType.PASTE)
        {
          // TODO: JAL-623 : correctly record file contents for matching up later
          // pdbentry.getProperty().put("pdbfilehash",""+pdbentry.getFile().hashCode());
@@@ -46,9 -43,9 +44,9 @@@ class AppletJmolBinding extends Jalview
  
    public AppletJmolBinding(AppletJmol appletJmol,
            StructureSelectionManager sSm, PDBEntry[] pdbentry,
-           SequenceI[][] seq, String[][] chains, DataSourceType protocol)
 -          SequenceI[][] seq, String protocol)
++          SequenceI[][] seq, DataSourceType protocol)
    {
-     super(sSm, pdbentry, seq, chains, protocol);
+     super(sSm, pdbentry, seq, protocol);
      appletJmolBinding = appletJmol;
    }
  
@@@ -49,12 -48,12 +49,12 @@@ public class ExtJmol extends JalviewJmo
  
    private AlignmentPanel ap;
  
 -  protected ExtJmol(jalview.appletgui.AlignFrame alframe,
 +  protected ExtJmol(AlignFrame alframe,
-           PDBEntry[] pdbentry, SequenceI[][] seq, String[][] chains,
+           PDBEntry[] pdbentry, SequenceI[][] seq,
 -          String protocol)
 +          DataSourceType protocol)
    {
      super(alframe.alignPanel.getStructureSelectionManager(), pdbentry, seq,
-             chains, protocol);
+             protocol);
    }
  
    public ExtJmol(Viewer viewer, AlignmentPanel alignPanel,
@@@ -654,7 -657,28 +668,28 @@@ public class Jalvie
              System.out.println("Creating HTML image: " + file);
              continue;
            }
 -          else if (format.equalsIgnoreCase("biojsmsa"))
++          else if (outputFormat.equalsIgnoreCase("biojsmsa"))
+           {
+             if (file == null)
+             {
+               System.err.println("The output html file must not be null");
+               return;
+             }
+             try
+             {
+               BioJsHTMLOutput
+                       .refreshVersionInfo(BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
+             } catch (URISyntaxException e)
+             {
+               e.printStackTrace();
+             }
+             BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
+             bjs.exportHTML(file);
+             System.out.println("Creating BioJS MSA Viwer HTML file: "
+                     + 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);
Simple merge
Simple merge
@@@ -30,39 -44,43 +44,63 @@@ public class PDBEntr
  
    private String id;
  
-   private String chainCode;
    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<String, String> properties;
+     /**
+      * case insensitive matching for Type enum
+      * 
+      * @param value
+      * @return
+      */
+     public static Type getType(String value)
+     {
+       for (Type t : Type.values())
+       {
+         if (t.toString().equalsIgnoreCase(value))
+         {
+           return t;
+         }
+       }
+       return null;
+     }
  
-   /*
-    * (non-Javadoc)
-    * 
-    * @see java.lang.Object#equals(java.lang.Object)
+     /**
+      * case insensitive equivalence for strings resolving to PDBEntry type
+      * 
+      * @param t
+      * @return
+      */
+     public boolean matches(String t)
+     {
+       return (this.toString().equalsIgnoreCase(t));
+     }
+   }
 -
+   /**
+    * Answers true if obj is a PDBEntry with the same id and chain code (both
+    * ignoring case), file, type and properties
     */
    @Override
    public boolean equals(Object obj)
@@@ -1,6 -1,25 +1,26 @@@
+ /*
+  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+  * Copyright (C) $$Year-Rel$$ The Jalview Authors
+  * 
+  * This file is part of Jalview.
+  * 
+  * Jalview is free software: you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License 
+  * as published by the Free Software Foundation, either version 3
+  * of the License, or (at your option) any later version.
+  *  
+  * Jalview is distributed in the hope that it will be useful, but 
+  * WITHOUT ANY WARRANTY; without even the implied warranty 
+  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+  * PURPOSE.  See the GNU General Public License for more details.
+  * 
+  * You should have received a copy of the GNU General Public License
+  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+  * The Jalview Authors are detailed in the 'AUTHORS' file.
+  */
  package jalview.ext.ensembl;
  
 +import jalview.io.DataSourceType;
  import jalview.io.FileParse;
  import jalview.util.StringUtils;
  
@@@ -207,9 -238,14 +239,14 @@@ abstract class EnsemblRestClient extend
            throws IOException
    {
      URL url = getUrl(ids);
-   
      BufferedReader reader = getHttpResponse(url, ids);
+     if (reader == null)
+     {
+       // request failed
+       return null;
+     }
 -    FileParse fp = new FileParse(reader, url.toString(), "HTTP_POST");
 +    FileParse fp = new FileParse(reader, url.toString(), DataSourceType.URL);
      return fp;
    }
  
@@@ -103,10 -97,10 +97,10 @@@ public abstract class JalviewJmolBindin
    public Viewer viewer;
  
    public JalviewJmolBinding(StructureSelectionManager ssm,
-           PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String[][] chains,
+           PDBEntry[] pdbentry, SequenceI[][] sequenceIs,
 -          String protocol)
 +          DataSourceType protocol)
    {
-     super(ssm, pdbentry, sequenceIs, chains, protocol);
+     super(ssm, pdbentry, sequenceIs, protocol);
      /*
       * viewer = JmolViewer.allocateViewer(renderPanel, new SmarterJmolAdapter(),
       * "jalviewJmol", ap.av.applet .getDocumentBase(),
@@@ -22,8 -22,8 +22,9 @@@ package jalview.ext.jmol
  
  import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.Annotation;
+ import jalview.datamodel.PDBEntry;
  import jalview.datamodel.SequenceI;
 +import jalview.io.DataSourceType;
  import jalview.io.FileParse;
  import jalview.io.StructureFile;
  import jalview.schemes.ResidueProperties;
@@@ -60,15 -59,12 +60,13 @@@ public class JmolParser extends Structu
  {
    Viewer viewer = null;
  
-   public JmolParser(boolean addAlignmentAnnotations, boolean predictSecStr,
-           boolean externalSecStr, String inFile, DataSourceType sourceType)
 -  public JmolParser(String inFile, String type) throws IOException
++  public JmolParser(String inFile, DataSourceType sourceType)
 +          throws IOException
    {
 -    super(inFile, type);
 +    super(inFile, sourceType);
    }
  
-   public JmolParser(boolean addAlignmentAnnotations, boolean predictSecStr,
-           boolean externalSecStr, FileParse fp) throws IOException
+   public JmolParser(FileParse fp) throws IOException
    {
      super(fp);
    }
@@@ -64,6 -64,9 +65,10 @@@ public abstract class JalviewChimeraBin
  
    private static final String ALPHACARBON = "CA";
  
+   private List<String> chainNames = new ArrayList<String>();
+   private Hashtable<String, String> chainFile = new Hashtable<String, String>();
++  
    /*
     * Object through which we talk to Chimera
     */
     * @param ssm
     * @param pdbentry
     * @param sequenceIs
     * @param protocol
     */
    public JalviewChimeraBinding(StructureSelectionManager ssm,
-           PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String[][] chains,
-           DataSourceType protocol)
 -          PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String protocol)
++          PDBEntry[] pdbentry, SequenceI[][] sequenceIs, DataSourceType protocol)
    {
-     super(ssm, pdbentry, sequenceIs, chains, protocol);
+     super(ssm, pdbentry, sequenceIs, protocol);
      viewer = new ChimeraManager(
              new ext.edu.ucsf.rbvi.strucviz2.StructureManager(true));
    }
@@@ -5031,9 -4826,11 +4830,12 @@@ public class AlignFrame extends GAlignF
    @Override
    public void drop(DropTargetDropEvent evt)
    {
+     // JAL-1552 - acceptDrop required before getTransferable call for
+     // Java's Transferable for native dnd
+     evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
      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
      {
                }
                if (type != null)
                {
-                 if (FileFormat.PDB.equals(type))
 -                if (StructureFile.isStructureFile(type))
++                if (FileFormat.PDB.equals(type) || FileFormat.MMCif.equals(type))
                  {
                    filesmatched.add(new Object[] { file, protocol, mtch });
                    continue;
Simple merge
Simple merge
@@@ -42,10 -41,9 +42,9 @@@ public class AppJmolBinding extends Jal
    private FeatureRenderer fr = null;
  
    public AppJmolBinding(AppJmol appJmol, StructureSelectionManager sSm,
-           PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String[][] chains,
-           DataSourceType protocol)
 -          PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String protocol)
++          PDBEntry[] pdbentry, SequenceI[][] sequenceIs, DataSourceType protocol)
    {
-     super(sSm, pdbentry, sequenceIs, chains, protocol);
+     super(sSm, pdbentry, sequenceIs, protocol);
      appJmolWindow = appJmol;
    }
  
@@@ -28,9 -28,10 +28,10 @@@ import jalview.datamodel.PDBEntry
  import jalview.datamodel.SequenceI;
  import jalview.ext.rbvi.chimera.JalviewChimeraBinding;
  import jalview.gui.StructureViewer.ViewerType;
 -import jalview.io.AppletFormatAdapter;
 +import jalview.io.DataSourceType;
  import jalview.io.JalviewFileChooser;
  import jalview.io.JalviewFileView;
+ import jalview.io.StructureFile;
  import jalview.schemes.BuriedColourScheme;
  import jalview.schemes.ColourSchemeI;
  import jalview.schemes.HelixColourScheme;
@@@ -955,9 -950,12 +953,12 @@@ public class Desktop extends jalview.jb
    public void drop(DropTargetDropEvent evt)
    {
      boolean success = true;
+     // JAL-1552 - acceptDrop required before getTransferable call for
+     // Java's Transferable for native dnd
+     evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
      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
      {
      if (v_client != null)
      {
        JalviewFileChooser chooser = new JalviewFileChooser(
-               Cache.getProperty("LAST_DIRECTORY"), "vdj",
 -              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",// TODO: VAMSAS DOCUMENT EXTENSION is VDJ
 +              "Vamsas Document", "Vamsas Document");
  
        chooser.setFileView(new JalviewFileView());
        chooser.setDialogTitle(MessageManager
     */
    public void setVamsasUpdate(boolean b)
    {
--    jalview.bin.Cache.log.debug("Setting gui for Vamsas update "
++    Cache.log.debug("Setting gui for Vamsas update "
              + (b ? "in progress" : "finished"));
  
      if (vamUpdate != null)
        for (Object file : (List) t
                .getTransferData(DataFlavor.javaFileListFlavor))
        {
-         files.add(((File)file).toString());
+         files.add(((File) file).toString());
 -        protocols.add(FormatAdapter.FILE);
 +        protocols.add(DataSourceType.FILE);
        }
      }
      else
Simple merge
Simple merge
@@@ -35,10 -36,8 +36,9 @@@ 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;
  import jalview.structure.StructureSelectionManager;
  import jalview.util.MessageManager;
  import jalview.util.jarInputStreamProvider;
@@@ -33,12 -32,12 +33,12 @@@ public class JalviewChimeraBindingMode
  
    private FeatureRenderer fr = null;
  
    public JalviewChimeraBindingModel(ChimeraViewFrame chimeraViewFrame,
            StructureSelectionManager ssm, PDBEntry[] pdbentry,
-           SequenceI[][] sequenceIs, String[][] chains,
-           DataSourceType protocol)
 -          SequenceI[][] sequenceIs, String protocol)
++          SequenceI[][] sequenceIs, DataSourceType protocol)
    {
-     super(ssm, pdbentry, sequenceIs, chains, protocol);
+     super(ssm, pdbentry, sequenceIs, protocol);
      cvf = chimeraViewFrame;
    }
  
Simple merge
Simple merge
Simple merge
@@@ -32,8 -32,8 +32,9 @@@ import jalview.fts.api.FTSRestClientI
  import jalview.fts.core.FTSRestRequest;
  import jalview.fts.core.FTSRestResponse;
  import jalview.fts.service.pdb.PDBFTSRestClient;
 +import jalview.io.DataSourceType;
  import jalview.jbgui.GStructureChooser;
+ import jalview.structure.StructureMapping;
  import jalview.structure.StructureSelectionManager;
  import jalview.util.MessageManager;
  import jalview.ws.DBRefFetcher;
@@@ -65,8 -65,6 +66,8 @@@ import javax.swing.table.AbstractTableM
  public class StructureChooser extends GStructureChooser implements
          IProgressIndicator
  {
-   private boolean structuresDiscovered = false;
++  private static int MAX_QLENGTH = 7820;
 +
    private SequenceI selectedSequence;
  
    private SequenceI[] selectedSequences;
  
    private boolean isValidPBDEntry;
  
+   private boolean cachedPDBExists;
 -  private static int MAX_QLENGHT = 7820;
 -
    public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
            AlignmentPanel ap)
    {
      StringBuilder queryBuilder = new StringBuilder();
      Set<String> seqRefs = new LinkedHashSet<String>();
  
-     if (seq.getAllPDBEntries() != null)
+     if (seq.getAllPDBEntries() != null
 -            && queryBuilder.length() < MAX_QLENGHT)
++            && queryBuilder.length() < MAX_QLENGTH)
      {
        for (PDBEntry entry : seq.getAllPDBEntries())
        {
      {
        for (DBRefEntry dbRef : seq.getDBRefs())
        {
-         if (isValidSeqName(getDBRefId(dbRef)))
+         if (isValidSeqName(getDBRefId(dbRef))
 -                && queryBuilder.length() < MAX_QLENGHT)
++                && queryBuilder.length() < MAX_QLENGTH)
          {
            if (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT))
            {
            int pdbIdColIndex = getResultTable().getColumn("PDB Id")
                    .getModelIndex();
            int refSeqColIndex = getResultTable().getColumn("Ref Sequence")
-               .getModelIndex();
+                   .getModelIndex();
            int[] selectedRows = getResultTable().getSelectedRows();
-       PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
-       int count = 0;
-       ArrayList<SequenceI> selectedSeqsToView = new ArrayList<SequenceI>();
-       for (int row : selectedRows)
-       {
+           PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
+           int count = 0;
 -          ArrayList<SequenceI> selectedSeqsToView = new ArrayList<SequenceI>();
++          List<SequenceI> selectedSeqsToView = new ArrayList<SequenceI>();
+           for (int row : selectedRows)
+           {
              String pdbIdStr = getResultTable().getValueAt(row,
-                     pdbIdColIndex)
-                 .toString();
+                     pdbIdColIndex).toString();
              SequenceI selectedSeq = (SequenceI) getResultTable()
-                     .getValueAt(row,
-                 refSeqColIndex);
-         selectedSeqsToView.add(selectedSeq);
+                     .getValueAt(row, refSeqColIndex);
+             selectedSeqsToView.add(selectedSeq);
              PDBEntry pdbEntry = selectedSeq.getPDBEntry(pdbIdStr);
              if (pdbEntry == null)
              {
                pdbEntry = getFindEntry(pdbIdStr,
                        selectedSeq.getAllPDBEntries());
              }
-         if (pdbEntry == null)
-         {
-           pdbEntry = new PDBEntry();
-           pdbEntry.setId(pdbIdStr);
-           pdbEntry.setType(PDBEntry.Type.PDB);
-           selectedSeq.getDatasetSequence().addPDBId(pdbEntry);
-         }
-         pdbEntriesToView[count++] = pdbEntry;
-       }
-       SequenceI[] selectedSeqs = selectedSeqsToView
-               .toArray(new SequenceI[selectedSeqsToView.size()]);
+             if (pdbEntry == null)
+             {
+               pdbEntry = new PDBEntry();
+               pdbEntry.setId(pdbIdStr);
+               pdbEntry.setType(PDBEntry.Type.PDB);
+               selectedSeq.getDatasetSequence().addPDBId(pdbEntry);
+             }
+             pdbEntriesToView[count++] = pdbEntry;
+           }
+           SequenceI[] selectedSeqs = selectedSeqsToView
+                   .toArray(new SequenceI[selectedSeqsToView.size()]);
            launchStructureViewer(ssm, pdbEntriesToView, ap, selectedSeqs);
-     }
-     else if (currentView == VIEWS_LOCAL_PDB)
-     {
-       int[] selectedRows = tbl_local_pdb.getSelectedRows();
-       PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
-       int count = 0;
+         }
+         else if (currentView == VIEWS_LOCAL_PDB)
+         {
+           int[] selectedRows = tbl_local_pdb.getSelectedRows();
+           PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
+           int count = 0;
            int pdbIdColIndex = tbl_local_pdb.getColumn("PDB Id")
                    .getModelIndex();
-       int refSeqColIndex = tbl_local_pdb.getColumn("Ref Sequence")
-               .getModelIndex();
-       ArrayList<SequenceI> selectedSeqsToView = new ArrayList<SequenceI>();
-       for (int row : selectedRows)
-       {
-         PDBEntry pdbEntry = (PDBEntry) tbl_local_pdb.getValueAt(row,
-                 pdbIdColIndex);
-         pdbEntriesToView[count++] = pdbEntry;
-         SequenceI selectedSeq = (SequenceI) tbl_local_pdb.getValueAt(row,
-                 refSeqColIndex);
-         selectedSeqsToView.add(selectedSeq);
-       }
-       SequenceI[] selectedSeqs = selectedSeqsToView
-               .toArray(new SequenceI[selectedSeqsToView.size()]);
+           int refSeqColIndex = tbl_local_pdb.getColumn("Ref Sequence")
+                   .getModelIndex();
 -          ArrayList<SequenceI> selectedSeqsToView = new ArrayList<SequenceI>();
++          List<SequenceI> selectedSeqsToView = new ArrayList<SequenceI>();
+           for (int row : selectedRows)
+           {
+             PDBEntry pdbEntry = (PDBEntry) tbl_local_pdb.getValueAt(row,
+                     pdbIdColIndex);
+             pdbEntriesToView[count++] = pdbEntry;
+             SequenceI selectedSeq = (SequenceI) tbl_local_pdb.getValueAt(
+                     row, refSeqColIndex);
+             selectedSeqsToView.add(selectedSeq);
+           }
+           SequenceI[] selectedSeqs = selectedSeqsToView
+                   .toArray(new SequenceI[selectedSeqsToView.size()]);
            launchStructureViewer(ssm, pdbEntriesToView, ap, selectedSeqs);
-     }
-     else if (currentView == VIEWS_ENTER_ID)
-     {
-       SequenceI userSelectedSeq = ((AssociateSeqOptions) idInputAssSeqPanel
-               .getCmb_assSeq().getSelectedItem()).getSequence();
-       if (userSelectedSeq != null)
-       {
-         selectedSequence = userSelectedSeq;
-       }
+         }
+         else if (currentView == VIEWS_ENTER_ID)
+         {
+           SequenceI userSelectedSeq = ((AssociateSeqOptions) idInputAssSeqPanel
+                   .getCmb_assSeq().getSelectedItem()).getSequence();
+           if (userSelectedSeq != null)
+           {
+             selectedSequence = userSelectedSeq;
+           }
  
-       String pdbIdStr = txt_search.getText();
-       PDBEntry pdbEntry = selectedSequence.getPDBEntry(pdbIdStr);
-       if (pdbEntry == null)
-       {
-         pdbEntry = new PDBEntry();
+           String pdbIdStr = txt_search.getText();
+           PDBEntry pdbEntry = selectedSequence.getPDBEntry(pdbIdStr);
+           if (pdbEntry == null)
+           {
+             pdbEntry = new PDBEntry();
              if (pdbIdStr.split(":").length > 1)
              {
                pdbEntry.setId(pdbIdStr.split(":")[0]);
              {
                pdbEntry.setId(pdbIdStr);
              }
-         pdbEntry.setType(PDBEntry.Type.PDB);
-         selectedSequence.getDatasetSequence().addPDBId(pdbEntry);
-       }
+             pdbEntry.setType(PDBEntry.Type.PDB);
+             selectedSequence.getDatasetSequence().addPDBId(pdbEntry);
+           }
  
-       PDBEntry[] pdbEntriesToView = new PDBEntry[] { pdbEntry };
+           PDBEntry[] pdbEntriesToView = new PDBEntry[] { pdbEntry };
            launchStructureViewer(ssm, pdbEntriesToView, ap,
                    new SequenceI[] { selectedSequence });
 -        }
 -        else if (currentView == VIEWS_FROM_FILE)
 -        {
 -          SequenceI userSelectedSeq = ((AssociateSeqOptions) fileChooserAssSeqPanel
 -                  .getCmb_assSeq().getSelectedItem()).getSequence();
 -          if (userSelectedSeq != null)
 -          {
 -            selectedSequence = userSelectedSeq;
 -          }
 -          PDBEntry fileEntry = new AssociatePdbFileWithSeq()
 -                  .associatePdbWithSeq(selectedPdbFileName,
 -                          jalview.io.AppletFormatAdapter.FILE,
 -                          selectedSequence, true, Desktop.instance);
 +    }
 +    else if (currentView == VIEWS_FROM_FILE)
 +    {
 +      SequenceI userSelectedSeq = ((AssociateSeqOptions) fileChooserAssSeqPanel
 +              .getCmb_assSeq().getSelectedItem()).getSequence();
 +      if (userSelectedSeq != null)
 +      {
 +        selectedSequence = userSelectedSeq;
 +      }
 +      PDBEntry fileEntry = new AssociatePdbFileWithSeq()
 +              .associatePdbWithSeq(selectedPdbFileName,
 +                          DataSourceType.FILE,
 +                      selectedSequence, true, Desktop.instance);
  
            launchStructureViewer(ssm, new PDBEntry[] { fileEntry }, ap,
                    new SequenceI[] { selectedSequence });
Simple merge
@@@ -205,9 -290,7 +205,7 @@@ public class AppletFormatAdapte
          {
            StructureImportSettings.addSettings(annotFromStructure,
                    localSecondaryStruct, serviceSecondaryStruct);
-           alignFile = new JmolParser(annotFromStructure,
-                   localSecondaryStruct, serviceSecondaryStruct, inFile,
-                   sourceType);
 -          alignFile = new jalview.ext.jmol.JmolParser(inFile, type);
++          alignFile = new JmolParser(inFile,                  sourceType);
          }
          else
          {
Simple merge
Simple merge
index d0ce86e,0000000..d52045c
mode 100644,000000..100644
--- /dev/null
@@@ -1,611 -1,0 +1,594 @@@
 +package jalview.io;
 +
 +import jalview.datamodel.AlignmentI;
 +import jalview.datamodel.PDBEntry;
 +import jalview.ext.jmol.JmolParser;
 +import jalview.structure.StructureImportSettings;
 +
 +import java.io.IOException;
 +import java.util.ArrayList;
 +import java.util.HashMap;
 +import java.util.List;
 +import java.util.Map;
 +
 +public enum FileFormat implements FileFormatI
 +{
 +  Fasta("Fasta", "fa, fasta, mfa, fastq", true, true)
 +  {
 +    @Override
 +    public AlignmentFileI getAlignmentFile(String inFile,
 +            DataSourceType sourceType) throws IOException
 +    {
 +      return new FastaFile(inFile, sourceType);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile(FileParse source)
 +            throws IOException
 +    {
 +      return new FastaFile(source);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile(AlignmentI al)
 +    {
 +      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(AlignmentI al)
 +    {
 +      return new PfamFile();
 +    }
 +  },
 +  Stockholm("Stockholm", "sto,stk", true, true)
 +  {
 +    @Override
 +    public AlignmentFileI getAlignmentFile(String inFile,
 +            DataSourceType sourceType) throws IOException
 +    {
 +      return new StockholmFile(inFile, sourceType);
 +    }
 +    @Override
 +    public AlignmentFileI getAlignmentFile(FileParse source)
 +            throws IOException
 +    {
 +      return new StockholmFile(source);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile(AlignmentI al)
 +    {
 +      return new StockholmFile(al);
 +    }
 +
 +  },
 +
 +  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(AlignmentI al)
 +    {
 +      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(AlignmentI al)
 +    {
 +      return new BLCFile();
 +    }
 +  },
 +  AMSA("AMSA", "amsa", true, true)
 +  {
 +    @Override
 +    public AlignmentFileI getAlignmentFile(String inFile,
 +            DataSourceType sourceType) throws IOException
 +    {
 +      return new AMSAFile(inFile, sourceType);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile(FileParse source)
 +            throws IOException
 +    {
 +      return new AMSAFile(source);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile(AlignmentI al)
 +    {
 +      return new AMSAFile(al);
 +    }
 +  },
 +  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(AlignmentI al)
 +    {
 +      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(AlignmentI al)
 +    {
 +      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(AlignmentI al)
 +    {
 +      return new JSONFile();
 +    }
 +
 +    @Override
 +    public boolean isComplexAlignFile()
 +    {
 +      return true;
 +    }
 +
 +  },
 +  Pileup("PileUp", "pileup", true, true)
 +  {
 +    @Override
 +    public AlignmentFileI getAlignmentFile(String inFile,
 +            DataSourceType sourceType) throws IOException
 +    {
 +      return new PileUpfile(inFile, sourceType);
 +    }    @Override
 +    public AlignmentFileI getAlignmentFile(FileParse source)
 +            throws IOException
 +    {
 +      return new PileUpfile(source);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile(AlignmentI al)
 +    {
 +      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(AlignmentI al)
 +    {
 +      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(AlignmentI al)
 +    {
 +      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(AlignmentI al)
 +    {
 +      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(AlignmentI al)
 +    {
 +      return null; // todo is this called?
 +    }
 +
 +  },
 +  Features("GFF or Jalview features", "gff2,gff3", true, false)
 +  {
 +    @Override
 +    public AlignmentFileI getAlignmentFile(String inFile,
 +            DataSourceType sourceType) throws IOException
 +    {
 +      return new FeaturesFile(true, inFile, sourceType);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile(FileParse source)
 +            throws IOException
 +    {
 +      return new FeaturesFile(source);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile(AlignmentI al)
 +    {
 +      return new FeaturesFile();
 +    }
 +  },
 +  PDB("PDB", "pdb,ent", true, false)
 +  {
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile(String inFile,
 +            DataSourceType sourceType) throws IOException
 +    {
 +      // TODO obtain config value from preference settings.
 +      // Set value to 'true' to test PDB processing with Jmol: JAL-1213
 +      boolean isParseWithJMOL = StructureImportSettings
 +              .getDefaultStructureFileFormat() != PDBEntry.Type.PDB;
 +      if (isParseWithJMOL)
 +      {
-         return new JmolParser(
-                 StructureImportSettings.isVisibleChainAnnotation(),
-                 StructureImportSettings.isProcessSecondaryStructure(),
-                 StructureImportSettings.isExternalSecondaryStructure(),
-                 inFile,
-                 sourceType);
++        return new JmolParser(inFile, sourceType);
 +      }
 +      else
 +      {
 +        StructureImportSettings.setShowSeqFeatures(true);
 +        return new MCview.PDBfile(
 +                StructureImportSettings.isVisibleChainAnnotation(),
 +                StructureImportSettings.isProcessSecondaryStructure(),
 +                StructureImportSettings.isExternalSecondaryStructure(),
 +                inFile,
 +                sourceType);
 +      }
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile(FileParse source)
 +            throws IOException
 +    {
 +      boolean isParseWithJMOL = StructureImportSettings
 +              .getDefaultStructureFileFormat() != PDBEntry.Type.PDB;
 +      if (isParseWithJMOL)
 +      {
-         return new JmolParser(
-                 StructureImportSettings.isVisibleChainAnnotation(),
-                 StructureImportSettings.isProcessSecondaryStructure(),
-                 StructureImportSettings.isExternalSecondaryStructure(),
-                 source);
++        return new JmolParser(source);
 +      }
 +      else
 +      {
 +        StructureImportSettings.setShowSeqFeatures(true);
 +        return new MCview.PDBfile(
 +                StructureImportSettings.isVisibleChainAnnotation(),
 +                StructureImportSettings.isProcessSecondaryStructure(),
 +                StructureImportSettings.isExternalSecondaryStructure(),
 +                source);
 +      }
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile(AlignmentI al)
 +    {
 +      return new JmolParser(); // todo or null?
 +    }
 +  },
 +  MMCif("mmCIF", "cif", true, false)
 +  {
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile(String inFile,
 +            DataSourceType sourceType) throws IOException
 +    {
-       return new JmolParser(
-               StructureImportSettings.isVisibleChainAnnotation(),
-               StructureImportSettings.isProcessSecondaryStructure(),
-               StructureImportSettings.isExternalSecondaryStructure(),
-               inFile, sourceType);
++      return new JmolParser(inFile, sourceType);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile(FileParse source)
 +            throws IOException
 +    {
-       return new JmolParser(
-               StructureImportSettings.isVisibleChainAnnotation(),
-               StructureImportSettings.isProcessSecondaryStructure(),
-               StructureImportSettings.isExternalSecondaryStructure(),
-               source);
++      return new JmolParser(source);
 +    }
 +
 +    @Override
 +    public AlignmentFileI getAlignmentFile(AlignmentI al)
 +    {
 +      return new JmolParser(); // todo or null?
 +    }
 +  },
 +  Jalview("Jalview", "jar,jvp", true, true)
 +  {
 +
 +    @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(AlignmentI al)
 +    {
 +      return null;
 +    }
 +
 +    @Override
 +    public boolean isTextFormat()
 +    {
 +      return false;
 +    }
 +  };
 +
 +  /**
 +   * A lookup map of enums by upper-cased name
 +   */
 +  private static Map<String, FileFormat> names;
 +  static
 +  {
 +    names = new HashMap<String, FileFormat>();
 +    for (FileFormat format : FileFormat.values())
 +    {
 +      names.put(format.toString().toUpperCase(), format);
 +    }
 +  }
 +
 +  private boolean writable;
 +
 +  private boolean readable;
 +
 +  private String extensions;
 +
 +  private String name;
 +
 +  /**
 +   * Answers a list of writeable file formats (as string, corresponding to the
 +   * toString() and forName() methods)
 +   * 
 +   * @return
 +   */
 +  public static List<String> getWritableFormats(boolean textOnly)
 +  {
 +    List<String> l = new ArrayList<String>();
 +    for (FileFormatI ff : values())
 +    {
 +      if (ff.isWritable() && (!textOnly || ff.isTextFormat()))
 +      {
 +        l.add(ff.toString());
 +      }
 +    }
 +    return l;
 +  }
 +
 +  /**
 +   * Answers a list of readable file formats (as string, corresponding to the
 +   * toString() and forName() methods)
 +   * 
 +   * @return
 +   */
 +  public static List<String> getReadableFormats()
 +  {
 +    List<String> l = new ArrayList<String>();
 +    for (FileFormatI ff : values())
 +    {
 +      if (ff.isReadable())
 +      {
 +        l.add(ff.toString());
 +      }
 +    }
 +    return l;
 +  }
 +
 +  @Override
 +  public boolean isComplexAlignFile()
 +  {
 +    return false;
 +  }
 +
 +  /**
 +   * Returns the file format with the given name, or null if format is null or
 +   * invalid. Unlike valueOf(), this is not case-sensitive, to be kind to
 +   * writers of javascript.
 +   * 
 +   * @param format
 +   * @return
 +   */
 +  public static FileFormatI forName(String format)
 +  {
 +    // or could store format.getShortDescription().toUpperCase()
 +    // in order to decouple 'given name' from enum name
 +    return format == null ? null : names.get(format.toUpperCase());
 +  }
 +
 +  @Override
 +  public boolean isReadable()
 +  {
 +    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;
 +  }
 +
 +  @Override
 +  public String toString()
 +  {
 +    return name;
 +  }
 +
 +  @Override
 +  public AlignmentFileI getAlignmentFile()
 +  {
 +    return getAlignmentFile((AlignmentI) null);
 +  }
 +
 +  @Override
 +  public boolean isTextFormat()
 +  {
 +    return true;
 +  }
 +}
--/*
-- * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
-- * Copyright (C) $$Year-Rel$$ The Jalview Authors
-- * 
-- * This file is part of Jalview.
-- * 
-- * Jalview is free software: you can redistribute it and/or
-- * modify it under the terms of the GNU General Public License 
-- * as published by the Free Software Foundation, either version 3
-- * of the License, or (at your option) any later version.
-- *  
-- * Jalview is distributed in the hope that it will be useful, but 
-- * WITHOUT ANY WARRANTY; without even the implied warranty 
-- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
-- * PURPOSE.  See the GNU General Public License for more details.
-- * 
-- * You should have received a copy of the GNU General Public License
-- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
-- * The Jalview Authors are detailed in the 'AUTHORS' file.
-- */
  package jalview.io;
  
- import jalview.bin.Cache;
- import jalview.datamodel.AlignmentI;
- import jalview.datamodel.SequenceI;
- import jalview.gui.AlignViewport;
+ import jalview.api.AlignExportSettingI;
+ import jalview.datamodel.AlignmentExportData;
+ import jalview.exceptions.NoFileSelectedException;
  import jalview.gui.AlignmentPanel;
- import jalview.gui.FeatureRenderer;
- import jalview.gui.SequenceRenderer;
- import jalview.util.BrowserLauncher;
- import jalview.util.ImageMaker;
+ import jalview.gui.IProgressIndicator;
  import jalview.util.MessageManager;
  
- import java.awt.Color;
- import java.awt.Font;
- import java.io.FileWriter;
- import java.io.PrintWriter;
+ import java.io.BufferedReader;
+ import java.io.File;
+ import java.io.IOException;
+ import java.io.InputStreamReader;
+ import java.net.URL;
+ import java.util.Objects;
  
- public class HTMLOutput
 -
+ public abstract class HTMLOutput implements Runnable
  {
-   AlignViewport av;
+   protected AlignmentPanel ap;
  
-   SequenceRenderer sr;
+   protected long pSessionId;
  
-   jalview.renderer.seqfeatures.FeatureRenderer fr;
+   protected IProgressIndicator pIndicator;
  
-   Color color;
+   protected File generatedFile;
  
-   public HTMLOutput(AlignmentPanel ap, SequenceRenderer sr,
-           FeatureRenderer fr1)
+   public HTMLOutput(AlignmentPanel ap)
    {
-     this.av = ap.av;
-     this.sr = sr;
-     fr = new FeatureRenderer(ap);
-     fr.transferSettings(fr1);
-     JalviewFileChooser chooser = new JalviewFileChooser(
-             Cache.getProperty("LAST_DIRECTORY"), ImageMaker.HTML_EXTENSION,
-             "HTML files", "HTML files");
-     chooser.setFileView(new JalviewFileView());
-     chooser.setDialogTitle(MessageManager.getString("label.save_as_html"));
-     chooser.setToolTipText(MessageManager.getString("action.save"));
-     int value = chooser.showSaveDialog(null);
-     if (value == JalviewFileChooser.APPROVE_OPTION)
+     if (ap != null)
      {
-       String choice = chooser.getSelectedFile().getPath();
-       Cache.setProperty("LAST_DIRECTORY", chooser
-               .getSelectedFile().getParent());
-       try
-       {
-         PrintWriter out = new PrintWriter(new FileWriter(choice));
-         out.println("<HTML>");
-         out.println("<style type=\"text/css\">");
-         out.println("<!--");
-         out.print("td {font-family: \"" + av.getFont().getFamily()
-                 + "\", \"" + av.getFont().getName() + "\", mono; "
-                 + "font-size: " + av.getFont().getSize() + "px; ");
-         if (av.getFont().getStyle() == Font.BOLD)
-         {
-           out.print("font-weight: BOLD; ");
-         }
-         if (av.getFont().getStyle() == Font.ITALIC)
-         {
-           out.print("font-style: italic; ");
-         }
-         out.println("text-align: center; }");
-         out.println("-->");
-         out.println("</style>");
-         out.println("<BODY>");
-         if (av.getWrapAlignment())
-         {
-           drawWrappedAlignment(out);
-         }
-         else
-         {
-           drawUnwrappedAlignment(out);
-         }
-         out.println("\n</body>\n</html>");
-         out.close();
-         BrowserLauncher.openURL("file:///" + choice);
-       } catch (Exception ex)
-       {
-         ex.printStackTrace();
-       }
+       this.ap = ap;
+       this.pIndicator = ap.alignFrame;
      }
    }
  
-   void drawUnwrappedAlignment(PrintWriter out)
+   public String getBioJSONData()
    {
-     out.println("<table border=\"1\"><tr><td>\n");
-     out.println("<table border=\"0\"  cellpadding=\"0\" cellspacing=\"0\">\n");
-     // ////////////
-     SequenceI seq;
-     AlignmentI alignment = av.getAlignment();
-     // draws the top row, the measure rule
-     out.println("<tr><td colspan=\"6\"></td>");
-     int i = 0;
+     return getBioJSONData(null);
+   }
  
-     for (i = 10; i < (alignment.getWidth() - 10); i += 10)
+   public String getBioJSONData(AlignExportSettingI exportSettings)
+   {
+     if (!isEmbedData())
      {
-       out.println("<td colspan=\"9\">" + i + "<br>|</td><td></td>");
+       return null;
      }
-     out.println("<td colspan=\"3\"></td><td colspan=\"3\">" + i
-             + "<br>|</td>");
-     out.println("</tr>");
-     for (i = 0; i < alignment.getHeight(); i++)
+     if (exportSettings == null)
      {
-       seq = alignment.getSequenceAt(i);
-       String id = seq.getDisplayId(av.getShowJVSuffix());
-       out.println("<tr><td nowrap>" + id + "&nbsp;&nbsp;</td>");
-       for (int res = 0; res < seq.getLength(); res++)
+       exportSettings = new AlignExportSettingI()
        {
-         if (!jalview.util.Comparison.isGap(seq.getCharAt(res)))
+         @Override
+         public boolean isExportHiddenSequences()
          {
-           color = sr.getResidueBoxColour(seq, res);
+           return true;
+         }
  
-           color = fr.findFeatureColour(color, seq, res);
+         @Override
+         public boolean isExportHiddenColumns()
+         {
+           return true;
          }
-         else
+         @Override
+         public boolean isExportAnnotations()
          {
-           color = Color.white;
+           return true;
          }
  
-         if (color.getRGB() < -1)
+         @Override
+         public boolean isExportFeatures()
          {
-           out.println("<td bgcolor=\"#"
-                   + jalview.util.Format.getHexString(color) + "\">"
-                   + seq.getCharAt(res) + "</td>");
+           return true;
          }
-         else
+         @Override
+         public boolean isExportGroups()
          {
-           out.println("<td>" + seq.getCharAt(res) + "</td>");
+           return true;
          }
-       }
  
-       out.println("</tr>");
+         @Override
+         public boolean isCancelled()
+         {
+           return false;
+         }
+       };
      }
-     // ////////////
-     out.println("</table>");
-     out.println("</td></tr></table>");
+     AlignmentExportData exportData = jalview.gui.AlignFrame
 -            .getAlignmentForExport(JSONFile.FILE_DESC,
++            .getAlignmentForExport(FileFormat.Json,
+                     ap.getAlignViewport(), exportSettings);
+     String bioJSON = new FormatAdapter(ap, exportData.getSettings())
 -            .formatSequences(JSONFile.FILE_DESC, exportData.getAlignment(),
++            .formatSequences(FileFormat.Json, exportData.getAlignment(),
+                     exportData.getOmitHidden(), exportData
+                             .getStartEndPostions(), ap.getAlignViewport()
+                             .getColumnSelection());
+     return bioJSON;
    }
  
-   void drawWrappedAlignment(PrintWriter out)
+   /**
+    * Read a template file content as string
+    * 
+    * @param file
+    *          - the file to be read
+    * @return File content as String
+    * @throws IOException
+    */
+   public static String readFileAsString(File file) throws IOException
    {
-     // //////////////////////////////////
-     // / How many sequences and residues can we fit on a printable page?
-     AlignmentI al = av.getAlignment();
-     SequenceI seq;
-     String r;
-     String g;
-     String b;
-     out.println("<table border=\"1\"><tr><td>\n");
-     out.println("<table border=\"0\"  cellpadding=\"0\" cellspacing=\"0\">\n");
-     for (int startRes = 0; startRes < al.getWidth(); startRes += av
-             .getWrappedWidth())
+     InputStreamReader isReader = null;
+     BufferedReader buffReader = null;
+     StringBuilder sb = new StringBuilder();
+     Objects.requireNonNull(file, "File must not be null!");
+     @SuppressWarnings("deprecation")
+     URL url = file.toURL();
+     if (url != null)
      {
-       int endRes = startRes + av.getWrappedWidth();
-       if (endRes > al.getWidth())
-       {
-         endRes = al.getWidth();
-       }
-       if (av.getScaleAboveWrapped())
+       try
        {
-         out.println("<tr>");
-         if (av.getScaleLeftWrapped())
+         isReader = new InputStreamReader(url.openStream());
+         buffReader = new BufferedReader(isReader);
+         String line;
+         String lineSeparator = System.getProperty("line.separator");
+         while ((line = buffReader.readLine()) != null)
          {
-           out.println("<td colspan=\"7\">&nbsp;</td>");
+           sb.append(line).append(lineSeparator);
          }
-         else
-         {
-           out.println("<td colspan=\"6\">&nbsp;</td>");
-         }
-         for (int i = startRes + 10; i < endRes; i += 10)
-         {
-           out.println("<td colspan=\"9\">" + i + "<br>|</td><td></td>");
-         }
-         out.println("</tr>");
-       }
-       int startPos, endPos;
-       for (int s = 0; s < al.getHeight(); s++)
+   
+       } catch (Exception ex)
        {
-         out.println("<tr>");
-         seq = al.getSequenceAt(s);
-         startPos = seq.findPosition(startRes);
-         endPos = seq.findPosition(endRes) - 1;
-         String id = seq.getDisplayId(av.getShowJVSuffix());
-         out.println("<td nowrap>" + id + "&nbsp;&nbsp;</td>");
-         if (av.getScaleLeftWrapped())
-         {
-           if (startPos > seq.getEnd() || endPos == 0)
-           {
-             out.println("<td nowrap>&nbsp;</td>");
-           }
-           else
-           {
-             out.println("<td nowrap>" + startPos + "&nbsp;&nbsp;</td>");
-           }
-         }
-         for (int res = startRes; res < endRes; res++)
-         {
-           if (!jalview.util.Comparison.isGap(seq.getCharAt(res)))
-           {
-             color = sr.getResidueBoxColour(seq, res);
-             color = fr.findFeatureColour(color, seq, res);
-           }
-           else
-           {
-             color = Color.white;
-           }
-           if (color.getRGB() < -1)
-           {
-             out.println("<td bgcolor=\"#"
-                     + jalview.util.Format.getHexString(color) + "\">"
-                     + seq.getCharAt(res) + "</td>");
-           }
-           else
-           {
-             out.println("<td>" + seq.getCharAt(res) + "</td>");
-           }
-         }
-         if (av.getScaleRightWrapped()
-                 && endRes < startRes + av.getWrappedWidth())
+         ex.printStackTrace();
+       } finally
+       {
+         if (isReader != null)
          {
-           out.println("<td colspan=\""
-                   + (startRes + av.getWrappedWidth() - endRes) + "\">"
-                   + "&nbsp;&nbsp;</td>");
+           isReader.close();
          }
-         if (av.getScaleRightWrapped() && startPos < endPos)
+   
+         if (buffReader != null)
          {
-           out.println("<td nowrap>&nbsp;" + endPos + "&nbsp;&nbsp;</td>");
+           buffReader.close();
          }
-         out.println("</tr>");
-       }
-       if (endRes < al.getWidth())
-       {
-         out.println("<tr><td height=\"5\"></td></tr>");
        }
      }
-     out.println("</table>");
-     out.println("</table>");
+     return sb.toString();
    }
  
    public static String getImageMapHTML()
                      + "initToolTips(); //--></script>\n");
  
    }
- }
+   public String getOutputFile() throws NoFileSelectedException
+   {
+     String selectedFile = null;
+     if (pIndicator != null && !isHeadless())
+     {
+       pIndicator.setProgressBar(MessageManager.formatMessage(
+               "status.waiting_for_user_to_select_output_file", "HTML"),
+               pSessionId);
+     }
+     JalviewFileChooser jvFileChooser = new JalviewFileChooser(
+             jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
+             new String[] { "html" }, new String[] { "HTML files" },
+             "HTML files");
+     jvFileChooser.setFileView(new JalviewFileView());
+     jvFileChooser.setDialogTitle(MessageManager
+             .getString("label.save_as_html"));
+     jvFileChooser.setToolTipText(MessageManager.getString("action.save"));
+     int fileChooserOpt = jvFileChooser.showSaveDialog(null);
+     if (fileChooserOpt == JalviewFileChooser.APPROVE_OPTION)
+     {
+       jalview.bin.Cache.setProperty("LAST_DIRECTORY", jvFileChooser
+               .getSelectedFile().getParent());
+       selectedFile = jvFileChooser.getSelectedFile().getPath();
+     }
+     else
+     {
+       throw new NoFileSelectedException("No file was selected.");
+     }
+     return selectedFile;
+   }
+   protected void setProgressMessage(String message)
+   {
+     if (pIndicator != null && !isHeadless())
+     {
+       pIndicator.setProgressBar(message, pSessionId);
+     }
+     else
+     {
+       System.out.println(message);
+     }
+   }
+   /**
+    * Answers true if HTML export is invoke in headless mode or false otherwise
+    * 
+    * @return
+    */
+   protected boolean isHeadless()
+   {
+     return System.getProperty("java.awt.headless") != null
+             && System.getProperty("java.awt.headless").equals("true");
+   }
+   /**
+    * This method provides implementation of consistent behaviour which should
+    * occur before a HTML file export. It MUST be called at the start of the
+    * exportHTML() method implementation.
+    */
+   protected void exportStarted()
+   {
+     pSessionId = System.currentTimeMillis();
+   }
+   /**
+    * This method provides implementation of consistent behaviour which should
+    * occur after a HTML file export. It MUST be called at the end of the
+    * exportHTML() method implementation.
+    */
+   protected void exportCompleted()
+   {
+     if (isLaunchInBrowserAfterExport() && !isHeadless())
+     {
+       try
+       {
+         jalview.util.BrowserLauncher
+                 .openURL("file:///" + getExportedFile());
+       } catch (IOException e)
+       {
+         e.printStackTrace();
+       }
+     }
+   }
+   /**
+    * if this answers true then BioJSON data will be embedded to the exported
+    * HTML file otherwise it won't be embedded.
+    * 
+    * @return
+    */
+   public abstract boolean isEmbedData();
+   /**
+    * if this answers true then the generated HTML file is opened for viewing in
+    * a browser after its generation otherwise it won't be opened in a browser
+    * 
+    * @return
+    */
+   public abstract boolean isLaunchInBrowserAfterExport();
+   /**
+    * handle to the generated HTML file
+    * 
+    * @return
+    */
+   public abstract File getExportedFile();
+   /**
+    * This is the main method to handle the HTML generation.
+    * 
+    * @param outputFile
+    *          the file path of the generated HTML
+    */
+   public abstract void exportHTML(String outputFile);
 -}
++}
Simple merge
@@@ -53,6 -53,6 +53,7 @@@ public class JalviewFileView extends Fi
      alignSuffix.put("sto", "Stockholm File");
    }
  
++  @Override
    public String getTypeDescription(File f)
    {
      String extension = getExtension(f);
@@@ -69,6 -69,6 +70,7 @@@
      return type;
    }
  
++  @Override
    public Icon getIcon(File f)
    {
      String extension = getExtension(f);
Simple merge
Simple merge
Simple merge
@@@ -48,10 -72,11 +72,12 @@@ public abstract class StructureFile ext
  
    private Vector<PDBChain> chains;
  
+   private boolean pdbIdAvailable;
 -  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
        Class cl = Class.forName("jalview.ext.jmol.JmolParser");
        if (cl != null)
        {
-         final Constructor constructor = cl.getConstructor(new Class[] {
-             boolean.class, boolean.class, boolean.class, FileParse.class });
-         final Object[] args = new Object[] { visibleChainAnnotation,
-             predictSecondaryStructure, externalSecondaryStructure,
-             new FileParse(getDataName(), dataSourceType) };
 -        final Constructor constructor = cl
 -                .getConstructor(new Class[] { FileParse.class });
 -        final Object[] args = new Object[] { new FileParse(getDataName(),
 -                type) };
++        final Constructor constructor = cl.getConstructor(new Class[] {FileParse.class });
++        final Object[] args = new Object[] { new FileParse(getDataName(), dataSourceType) };
  
          StructureImportSettings.setShowSeqFeatures(false);
          StructureImportSettings.setVisibleChainAnnotation(false);
Simple merge
@@@ -322,12 -323,11 +323,11 @@@ public class StructureSelectionManage
     * @return null or the structure data parsed as a pdb file
     */
    synchronized public StructureFile setMapping(SequenceI[] sequence,
-           String[] targetChains, String pdbFile, DataSourceType paste)
 -          String[] targetChains, String pdbFile, String protocol)
++          String[] targetChains, String pdbFile, DataSourceType protocol)
    {
-     return setMapping(true, sequence, targetChains, pdbFile, paste);
+     return setMapping(true, sequence, targetChains, pdbFile, protocol);
    }
  
    /**
     * create sequence structure mappings between each sequence and the given
     * pdbFile (retrieved via the given protocol).
     */
    synchronized public StructureFile setMapping(boolean forStructureView,
            SequenceI[] sequenceArray, String[] targetChainIds,
-           String pdbFile,
-  DataSourceType sourceType)
 -          String pdbFile, String protocol)
++          String pdbFile, DataSourceType sourceType)
    {
      /*
       * There will be better ways of doing this in the future, for now we'll use
      boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
      try
      {
-       boolean isParseWithJMOL = StructureImportSettings
-               .getDefaultPDBFileParser().equalsIgnoreCase(
-                       StructureImportSettings.StructureParser.JMOL_PARSER
-                               .toString());
-       if (isParseWithJMOL || (pdbFile != null && isCIFFile(pdbFile)))
-       {
-         pdb = new JmolParser(addTempFacAnnot, parseSecStr,
-                 secStructServices, pdbFile, sourceType);
-       }
-       else
-       {
-         pdb = new PDBfile(addTempFacAnnot, parseSecStr, secStructServices,
-                 pdbFile, sourceType);
-       }
 -      pdb = new JmolParser(pdbFile, protocol);
++      pdb = new JmolParser(pdbFile, sourceType);
  
        if (pdb.getId() != null && pdb.getId().trim().length() > 0
-               && sourceType == DataSourceType.FILE)
 -              && AppletFormatAdapter.FILE.equals(protocol))
++              && DataSourceType.FILE == sourceType)
        {
          registerPDBFile(pdb.getId().trim(), pdbFile);
        }
          pdbFile = "INLINE" + pdb.getId();
        }
  
--      ArrayList<StructureMapping> seqToStrucMapping = new ArrayList<StructureMapping>();
-       if (isMapUsingSIFTs)
++      List<StructureMapping> seqToStrucMapping = new ArrayList<StructureMapping>();
+       if (isMapUsingSIFTs && seq.isProtein())
        {
          setProgressBar(null);
          setProgressBar(MessageManager
          }
          else
          {
--          ArrayList<StructureMapping> foundSiftsMappings = new ArrayList<StructureMapping>();
++          List<StructureMapping> foundSiftsMappings = new ArrayList<StructureMapping>();
            for (PDBChain chain : pdb.getChains())
            {
              try
@@@ -124,11 -132,11 +133,11 @@@ public abstract class AAStructureBindin
     * @param pdbentry
     * @param sequenceIs
     * @param chains
-    * @param protocol2
+    * @param protocol
     */
    public AAStructureBindingModel(StructureSelectionManager ssm,
-           PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String[][] chains,
-           DataSourceType protocol2)
+           PDBEntry[] pdbentry, SequenceI[][] sequenceIs,
 -          String protocol)
++          DataSourceType protocol)
    {
      this.ssm = ssm;
      this.sequence = sequenceIs;
Simple merge
Simple merge
@@@ -121,14 -119,13 +121,13 @@@ abstract public class Pfam extends Xfa
      // retrieved.
      startQuery();
      AlignmentI rcds = new FormatAdapter().readFile(getXFAMURL()
 -            + queries.trim().toUpperCase(), jalview.io.FormatAdapter.URL,
 -            "STH");
 +            + queries.trim().toUpperCase(), DataSourceType.URL,
 +            FileFormat.Stockholm);
      for (int s = 0, sNum = rcds.getHeight(); s < sNum; s++)
      {
-       rcds.getSequenceAt(s).addDBRef(
- new DBRefEntry(DBRefSource.PFAM,
-               // getDbSource(),
-                       getDbVersion(), queries.trim().toUpperCase()));
+       rcds.getSequenceAt(s).addDBRef(new DBRefEntry(DBRefSource.PFAM,
+       // getDbSource(),
+               getDbVersion(), queries.trim().toUpperCase()));
        if (!getDbSource().equals(DBRefSource.PFAM))
        { // add the specific ref too
          rcds.getSequenceAt(s).addDBRef(
@@@ -32,7 -33,8 +33,8 @@@ import jalview.datamodel.AlignmentI
  import jalview.datamodel.PDBEntry;
  import jalview.datamodel.Sequence;
  import jalview.datamodel.SequenceI;
 -import jalview.io.AppletFormatAdapter;
 +import jalview.io.DataSourceType;
+ import jalview.structure.StructureImportSettings;
  
  import java.io.IOException;
  import java.util.List;
@@@ -307,4 -310,19 +310,17 @@@ public class PDBfileTes
      pf.addAnnotations(al);
      return al.getAlignmentAnnotation();
    }
 -  // @formatter:on
 -
+   @BeforeMethod(alwaysRun = true)
+   public void setUp()
+   {
+     Cache.loadProperties("test/jalview/io/testProps.jvprops");
+     Cache.applicationProperties.setProperty("STRUCT_FROM_PDB",
+             Boolean.TRUE.toString());
+     Cache.applicationProperties.setProperty("ADD_TEMPFACT_ANN",
+             Boolean.TRUE.toString());
+     Cache.applicationProperties.setProperty("ADD_SS_ANN",
+             Boolean.TRUE.toString());
+     StructureImportSettings.setDefaultStructureFileFormat("PDB");
+   }
  }
Simple merge
index 0000000,91ceb82..4fdafde
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,309 +1,309 @@@
+ /*
+  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+  * Copyright (C) $$Year-Rel$$ The Jalview Authors
+  * 
+  * This file is part of Jalview.
+  * 
+  * Jalview is free software: you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License 
+  * as published by the Free Software Foundation, either version 3
+  * of the License, or (at your option) any later version.
+  *  
+  * Jalview is distributed in the hope that it will be useful, but 
+  * WITHOUT ANY WARRANTY; without even the implied warranty 
+  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+  * PURPOSE.  See the GNU General Public License for more details.
+  * 
+  * You should have received a copy of the GNU General Public License
+  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+  * The Jalview Authors are detailed in the 'AUTHORS' file.
+  */
+ package jalview.analysis;
+ import static org.testng.Assert.assertEquals;
+ import static org.testng.Assert.assertSame;
+ import static org.testng.Assert.assertTrue;
+ import jalview.datamodel.Alignment;
+ import jalview.datamodel.AlignmentI;
+ import jalview.datamodel.SearchResultMatchI;
+ import jalview.datamodel.SearchResultsI;
+ import jalview.datamodel.Sequence;
+ import jalview.gui.AlignFrame;
++import jalview.io.DataSourceType;
+ import jalview.io.FileLoader;
 -import jalview.io.FormatAdapter;
+ import java.util.List;
+ import org.testng.annotations.BeforeClass;
+ import org.testng.annotations.Test;
+ public class FinderTest
+ {
+   private AlignFrame af;
+   private AlignmentI al;
+   @BeforeClass(groups = "Functional")
+   public void setUp()
+   {
+     String seqData = "seq1 ABCD--EF-GHI\n" + "seq2 A--BCDefHI\n"
+             + "seq3 --bcdEFH\n" + "seq4 aa---aMMMMMaaa\n";
+     af = new FileLoader().LoadFileWaitTillLoaded(seqData,
 -            FormatAdapter.PASTE);
++            DataSourceType.PASTE);
+     al = af.getViewport().getAlignment();
+   }
+   /**
+    * Test for find all matches of a regular expression
+    */
+   @Test(groups = "Functional")
+   public void testFindAll_regex()
+   {
+     Finder f = new Finder(al, null);
+     f.setFindAll(true);
+     f.find("E.H"); // 'E, any character, H'
+     // should match seq2 efH and seq3 EFH
+     SearchResultsI sr = f.getSearchResults();
+     assertEquals(sr.getSize(), 2);
+     List<SearchResultMatchI> matches = sr.getResults();
+     assertSame(al.getSequenceAt(1), matches.get(0).getSequence());
+     assertSame(al.getSequenceAt(2), matches.get(1).getSequence());
+     assertEquals(matches.get(0).getStart(), 5);
+     assertEquals(matches.get(0).getEnd(), 7);
+     assertEquals(matches.get(1).getStart(), 4);
+     assertEquals(matches.get(1).getEnd(), 6);
+   }
+   /**
+    * Test for (undocumented) find residue by position
+    */
+   @Test(groups = "Functional")
+   public void testFind_residueNumber()
+   {
+     Finder f = new Finder(al, null);
+     f.setFindAll(true);
+     f.find("9");
+     // seq1 and seq4 have 9 residues; no match in other sequences
+     SearchResultsI sr = f.getSearchResults();
+     assertEquals(sr.getSize(), 2);
+     List<SearchResultMatchI> matches = sr.getResults();
+     assertSame(al.getSequenceAt(0), matches.get(0).getSequence());
+     assertSame(al.getSequenceAt(3), matches.get(1).getSequence());
+     assertEquals(matches.get(0).getStart(), 9);
+     assertEquals(matches.get(0).getEnd(), 9);
+     assertEquals(matches.get(1).getStart(), 9);
+     assertEquals(matches.get(1).getEnd(), 9);
+   }
+   /**
+    * Test for find next action
+    */
+   @Test(groups = "Functional")
+   public void testFindNext()
+   {
+     /*
+      * start at second sequence; resIndex of -1
+      * means sequence id / description is searched
+      */
+     Finder f = new Finder(al, null, 1, -1);
+     f.find("e"); // matches id
+     assertTrue(f.getSearchResults().isEmpty());
+     assertEquals(f.getIdMatch().size(), 1);
+     assertSame(f.getIdMatch().get(0), al.getSequenceAt(1));
+     // resIndex is now 0 - for use in next find next
+     assertEquals(f.getResIndex(), 0);
+     f = new Finder(al, null, 1, 0);
+     f.find("e"); // matches in sequence
+     assertTrue(f.getIdMatch().isEmpty());
+     assertEquals(f.getSearchResults().getSize(), 1);
+     List<SearchResultMatchI> matches = f.getSearchResults().getResults();
+     assertEquals(matches.get(0).getStart(), 5);
+     assertEquals(matches.get(0).getEnd(), 5);
+     assertSame(matches.get(0).getSequence(), al.getSequenceAt(1));
+     // still in the second sequence
+     assertEquals(f.getSeqIndex(), 1);
+     // next residue position to search from is 5
+     // (used as base 0 by RegEx so the same as 6 if base 1)
+     assertEquals(f.getResIndex(), 5);
+     // find next from end of sequence - finds next sequence id
+     f = new Finder(al, null, 1, 5);
+     f.find("e");
+     assertEquals(f.getIdMatch().size(), 1);
+     assertSame(f.getIdMatch().get(0), al.getSequenceAt(2));
+   }
+   /**
+    * Test for matching within sequence descriptions
+    */
+   @Test(groups = "Functional")
+   public void testFindAll_inDescription()
+   {
+     AlignmentI al2 = new Alignment(al);
+     al2.getSequenceAt(0).setDescription("BRAF");
+     al2.getSequenceAt(1).setDescription("braf");
+     Finder f = new Finder(al2, null);
+     f.setFindAll(true);
+     f.setIncludeDescription(true);
+     f.find("rAF");
+     assertEquals(f.getIdMatch().size(), 2);
+     assertSame(f.getIdMatch().get(0), al2.getSequenceAt(0));
+     assertSame(f.getIdMatch().get(1), al2.getSequenceAt(1));
+     assertTrue(f.getSearchResults().isEmpty());
+     /*
+      * case sensitive
+      */
+     f = new Finder(al2, null);
+     f.setFindAll(true);
+     f.setCaseSensitive(true);
+     f.setIncludeDescription(true);
+     f.find("RAF");
+     assertEquals(f.getIdMatch().size(), 1);
+     assertSame(f.getIdMatch().get(0), al2.getSequenceAt(0));
+     assertTrue(f.getSearchResults().isEmpty());
+     /*
+      * match sequence id, description and sequence!
+      */
+     al2.getSequenceAt(0).setDescription("the efh sequence");
+     al2.getSequenceAt(0).setName("mouseEFHkinase");
+     al2.getSequenceAt(1).setName("humanEFHkinase");
+     f = new Finder(al2, null);
+     f.setFindAll(true);
+     f.setIncludeDescription(true);
+     /*
+      * sequence matches should have no duplicates
+      */
+     f.find("EFH");
+     assertEquals(f.getIdMatch().size(), 2);
+     assertSame(f.getIdMatch().get(0), al2.getSequenceAt(0));
+     assertSame(f.getIdMatch().get(1), al2.getSequenceAt(1));
+     assertEquals(f.getSearchResults().getSize(), 2);
+     SearchResultMatchI match = f.getSearchResults().getResults().get(0);
+     assertSame(al2.getSequenceAt(1), match.getSequence());
+     assertEquals(5, match.getStart());
+     assertEquals(7, match.getEnd());
+     match = f.getSearchResults().getResults().get(1);
+     assertSame(al2.getSequenceAt(2), match.getSequence());
+     assertEquals(4, match.getStart());
+     assertEquals(6, match.getEnd());
+   }
+   /**
+    * Test for matching within sequence ids
+    */
+   @Test(groups = "Functional")
+   public void testFindAll_sequenceIds()
+   {
+     Finder f = new Finder(al, null);
+     f.setFindAll(true);
+     /*
+      * case insensitive
+      */
+     f.find("SEQ1");
+     assertEquals(f.getIdMatch().size(), 1);
+     assertSame(f.getIdMatch().get(0), al.getSequenceAt(0));
+     assertTrue(f.getSearchResults().isEmpty());
+     /*
+      * case sensitive
+      */
+     f = new Finder(al, null);
+     f.setFindAll(true);
+     f.setCaseSensitive(true);
+     f.find("SEQ1");
+     assertTrue(f.getSearchResults().isEmpty());
+     /*
+      * match both sequence id and sequence
+      */
+     AlignmentI al2 = new Alignment(al);
+     al2.addSequence(new Sequence("aBz", "xyzabZpqrAbZ"));
+     f = new Finder(al2, null);
+     f.setFindAll(true);
+     f.find("ABZ");
+     assertEquals(f.getIdMatch().size(), 1);
+     assertSame(f.getIdMatch().get(0), al2.getSequenceAt(4));
+     assertEquals(f.getSearchResults().getSize(), 2);
+     SearchResultMatchI match = f.getSearchResults().getResults().get(0);
+     assertSame(al2.getSequenceAt(4), match.getSequence());
+     assertEquals(4, match.getStart());
+     assertEquals(6, match.getEnd());
+     match = f.getSearchResults().getResults().get(1);
+     assertSame(al2.getSequenceAt(4), match.getSequence());
+     assertEquals(10, match.getStart());
+     assertEquals(12, match.getEnd());
+   }
+   /**
+    * Test finding all matches of a sequence pattern in an alignment
+    */
+   @Test(groups = "Functional")
+   public void testFindAll_simpleMatch()
+   {
+     Finder f = new Finder(al, null);
+     f.setFindAll(true);
+     /*
+      * case insensitive first
+      */
+     f.find("EfH");
+     SearchResultsI searchResults = f.getSearchResults();
+     assertEquals(searchResults.getSize(), 2);
+     SearchResultMatchI match = searchResults.getResults().get(0);
+     assertSame(al.getSequenceAt(1), match.getSequence());
+     assertEquals(5, match.getStart());
+     assertEquals(7, match.getEnd());
+     match = searchResults.getResults().get(1);
+     assertSame(al.getSequenceAt(2), match.getSequence());
+     assertEquals(4, match.getStart());
+     assertEquals(6, match.getEnd());
+     /*
+      * case sensitive
+      */
+     f = new Finder(al, null);
+     f.setFindAll(true);
+     f.setCaseSensitive(true);
+     f.find("BC");
+     searchResults = f.getSearchResults();
+     assertEquals(searchResults.getSize(), 2);
+     match = searchResults.getResults().get(0);
+     assertSame(al.getSequenceAt(0), match.getSequence());
+     assertEquals(2, match.getStart());
+     assertEquals(3, match.getEnd());
+     match = searchResults.getResults().get(1);
+     assertSame(al.getSequenceAt(1), match.getSequence());
+     assertEquals(2, match.getStart());
+     assertEquals(3, match.getEnd());
+   }
+   /**
+    * Test for JAL-2302 to verify that sub-matches are not included in a find all
+    * result
+    */
+   @Test(groups = "Functional")
+   public void testFind_maximalResultOnly()
+   {
+     Finder f = new Finder(al, null);
+     f.setFindAll(true);
+     f.find("M+");
+     SearchResultsI searchResults = f.getSearchResults();
+     assertEquals(searchResults.getSize(), 1);
+     SearchResultMatchI match = searchResults.getResults().get(0);
+     assertSame(al.getSequenceAt(3), match.getSequence());
+     assertEquals(4, match.getStart()); // dataset sequence positions
+     assertEquals(8, match.getEnd()); // base 1
+   }
+ }
@@@ -24,9 -24,11 +24,11 @@@ import jalview.datamodel.AlignmentI
  import jalview.datamodel.SequenceFeature;
  import jalview.datamodel.SequenceI;
  import jalview.gui.AlignFrame;
 +import jalview.io.DataSourceType;
  import jalview.io.FileLoader;
 -import jalview.io.FormatAdapter;
  
+ import java.util.Arrays;
  import org.testng.Assert;
  import org.testng.annotations.Test;
  
@@@ -135,4 -139,45 +139,45 @@@ public class FeatureScoreModelTes
                + "(" + s + ") should still be distinct from FER1_MAIZE (3)");
      }
    }
+   /**
+    * Check findFeatureAt doesn't return contact features except at contact
+    * points TODO:move to under the FeatureRendererModel test suite
+    */
+   @Test(groups = { "Functional" })
+   public void testFindFeatureAt_PointFeature() throws Exception
+   {
+     String alignment = "a CCCCCCGGGGGGCCCCCC\n" + "b CCCCCCGGGGGGCCCCCC\n"
+             + "c CCCCCCGGGGGGCCCCCC\n";
+     AlignFrame af = new jalview.io.FileLoader(false)
 -            .LoadFileWaitTillLoaded(alignment, FormatAdapter.PASTE);
++            .LoadFileWaitTillLoaded(alignment, DataSourceType.PASTE);
+     SequenceI aseq = af.getViewport().getAlignment().getSequenceAt(0);
+     SequenceFeature sf = null;
+     sf = new SequenceFeature("disulphide bond", "", 2, 5, Float.NaN, "");
+     aseq.addSequenceFeature(sf);
+     Assert.assertTrue(sf.isContactFeature());
+     af.refreshFeatureUI(true);
+     af.getFeatureRenderer().setAllVisible(Arrays.asList("disulphide bond"));
+     Assert.assertEquals(af.getFeatureRenderer().getDisplayedFeatureTypes()
+             .size(), 1, "Should be just one feature type displayed");
+     // step through and check for pointwise feature presence/absence
+     Assert.assertEquals(af.getFeatureRenderer().findFeaturesAtRes(aseq, 1)
+             .size(), 0);
+     // step through and check for pointwise feature presence/absence
+     Assert.assertEquals(af.getFeatureRenderer().findFeaturesAtRes(aseq, 2)
+             .size(), 1);
+     // step through and check for pointwise feature presence/absence
+     Assert.assertEquals(af.getFeatureRenderer().findFeaturesAtRes(aseq, 3)
+             .size(), 0);
+     // step through and check for pointwise feature presence/absence
+     Assert.assertEquals(af.getFeatureRenderer().findFeaturesAtRes(aseq, 4)
+             .size(), 0);
+     // step through and check for pointwise feature presence/absence
+     Assert.assertEquals(af.getFeatureRenderer().findFeaturesAtRes(aseq, 5)
+             .size(), 1);
+     // step through and check for pointwise feature presence/absence
+     Assert.assertEquals(af.getFeatureRenderer().findFeaturesAtRes(aseq, 6)
+             .size(), 0);
+   }
  }
@@@ -7,7 -31,11 +31,11 @@@ import jalview.datamodel.Sequence
  import jalview.datamodel.SequenceFeature;
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
+ import jalview.gui.AlignFrame;
++import jalview.io.DataSourceType;
+ import jalview.io.FileLoader;
 -import jalview.io.FormatAdapter;
  
+ import java.util.Arrays;
  import java.util.BitSet;
  
  import org.testng.annotations.Test;
@@@ -97,4 -152,53 +152,53 @@@ public class AlignViewControllerTes
      assertEquals(0, seqCount);
      assertEquals(0, bs.cardinality());
    }
+   /**
+    * shameless copy of test data from findFeature for testing mark columns from
+    * highlight
+    */
+   @Test(groups = "Functional")
+   public void testSelectColumnsWithHighlight()
+   {
+     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+             "seq1 aMMMaaaaaaaaaaaaaaaa\n" + "seq2 aaaMMMMMMMaaaaaaaaaa\n"
+                     + "seq3 aaaaaaaaaaMMMMMaaaaa\n"
 -                    + "seq4 aaaaaaaaaaaaaaaaaaaa\n", FormatAdapter.PASTE);
++                    + "seq4 aaaaaaaaaaaaaaaaaaaa\n", DataSourceType.PASTE);
+     SearchResultsI sr = new SearchResults();
+     SequenceI[] sqs = af.getViewport().getAlignment().getSequencesArray();
+     SequenceI seq1 = sqs[0];
+     SequenceI seq2 = sqs[1];
+     SequenceI seq3 = sqs[2];
+     SequenceI seq4 = sqs[3];
+     /*
+      * features start/end are base 1
+      */
+     sr.addResult(seq1, 2, 4);
+     sr.addResult(seq2, 4, 10);
+     sr.addResult(seq3, 11, 15);
+     /*
+      *  test Match/Find works first
+      */
+     Finder f = new Finder(af.getViewport().getAlignment(), null);
+     f.setFindAll(true);
+     f.setCaseSensitive(true);
+     f.find("M+");
+     assertEquals(
+             "Finder found different set of results to manually created SearchResults",
+             sr, f.getSearchResults());
+     /*
+      * now check simple mark columns from find operation
+      */
+     af.getViewport().setSearchResults(sr);
+     AlignViewControllerI avc = af.avc;
+     avc.markHighlightedColumns(false, false, false);
+     assertTrue("Didn't select highlighted columns", Arrays.deepEquals(af
+             .getViewport().getColumnSelection().getSelectedRanges()
+             .toArray(), new int[][] { { 1, 14 } }));
+   }
  }
@@@ -27,9 -27,8 +27,10 @@@ import static org.testng.AssertJUnit.as
  import static org.testng.AssertJUnit.assertSame;
  import static org.testng.AssertJUnit.assertTrue;
  
+ import jalview.datamodel.AlignedCodonFrame.SequenceToSequenceMapping;
 -import jalview.io.AppletFormatAdapter;
 +import jalview.io.DataSourceType;
 +import jalview.io.FileFormat;
 +import jalview.io.FileFormatI;
  import jalview.io.FormatAdapter;
  import jalview.util.MapList;
  
@@@ -125,14 -144,13 +144,13 @@@ public class EnsemblSeqProxyTes
    }
  
    @Test(dataProvider = "ens_seqs", suiteName = "live")
-   public void testGetOneSeqs(EnsemblRestClient proxy, String sq, String fastasq)
-           throws Exception
+   public void testGetOneSeqs(EnsemblRestClient proxy, String sq,
+           String fastasq) throws Exception
    {
      FileParse fp = proxy.getSequenceReader(Arrays
-             .asList(new String[]
-     { sq }));
+             .asList(new String[] { sq }));
      SequenceI[] sqs = new FastaFile(fp).getSeqsAsArray();
 -    FastaFile trueRes = new FastaFile(fastasq, AppletFormatAdapter.PASTE);
 +    FastaFile trueRes = new FastaFile(fastasq, DataSourceType.PASTE);
      SequenceI[] trueSqs = trueRes.getSeqsAsArray();
      Assert.assertEquals(sqs.length, trueSqs.length,
              "Different number of sequences retrieved for query " + sq);
@@@ -112,9 -117,8 +116,8 @@@ public class JmolParserTes
      for (String pdbStr : testFile)
      {
        PDBfile mctest = new PDBfile(false, false, false, pdbStr,
 -              AppletFormatAdapter.FILE);
 -      JmolParser jtest = new JmolParser(pdbStr, AppletFormatAdapter.FILE);
 +              DataSourceType.FILE);
-       JmolParser jtest = new JmolParser(false, false, false, pdbStr,
-               DataSourceType.FILE);
++      JmolParser jtest = new JmolParser(pdbStr, DataSourceType.FILE);
        Vector<SequenceI> seqs = jtest.getSeqs(), mcseqs = mctest.getSeqs();
  
        assertTrue(
    public void testParse_missingResidues() throws Exception
    {
      PDBfile mctest = new PDBfile(false, false, false,
 -            pastePDBDataWithChainBreak, AppletFormatAdapter.PASTE);
 -    JmolParser jtest = new JmolParser(pastePDBDataWithChainBreak,
 -            AppletFormatAdapter.PASTE);
 +            pastePDBDataWithChainBreak, DataSourceType.PASTE);
-     boolean annotFromStructure = false;
-     boolean localSecondaryStruct = false;
-     boolean serviceSecondaryStruct = false;
-     JmolParser jtest = new JmolParser(annotFromStructure,
-             localSecondaryStruct, serviceSecondaryStruct,
-             pastePDBDataWithChainBreak, DataSourceType.PASTE);
++    JmolParser jtest = new JmolParser(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,
+     JmolParser jtest = new JmolParser(pdbWithAltLoc,
 -            AppletFormatAdapter.PASTE);
 +            DataSourceType.PASTE);
      Vector<SequenceI> seqs = jtest.getSeqs();
      Vector<SequenceI> mcseqs = mctest.getSeqs();
-   
      assertEquals("Failed to find 1 sequence\n", 1, seqs.size());
      assertEquals("Failed to find 1 sequence\n", 1, mcseqs.size());
      assertEquals("ALC", seqs.get(0).getSequenceAsString());
      assertEquals('H', structCode[4]);
      assertEquals('E', structCode[5]);
    }
+   @Test(groups = "Functional")
+   public void testLocalPDBId() throws Exception
+   {
+     JmolParser structureData;
+     /*
+      * reads a local structure
+      */
+     structureData = new JmolParser("examples/testdata/localstruct.pdb",
 -            AppletFormatAdapter.FILE);
++            DataSourceType.FILE);
+     assertNotNull(structureData);
+     /*
+      * local structure files should yield a false ID based on the filename
+      */
+     assertNotNull(structureData.getId());
+     assertEquals(structureData.getId(), "localstruct.pdb");
+     assertNotNull(structureData.getSeqs());
+     /*
+      * the ID is also the group for features derived from structure data 
+      */
+     assertNotNull(structureData.getSeqs().get(0).getSequenceFeatures()[0].featureGroup);
+     assertEquals(
+             structureData.getSeqs().get(0).getSequenceFeatures()[0].featureGroup,
+             "localstruct.pdb");
+   }
  }
@@@ -51,10 -71,9 +71,8 @@@ public class JmolVsJalviewPDBParserEndT
        JmolParser jtest = null;
        try
        {
--        mctest = new PDBfile(false, false, false, testFile,
-                 DataSourceType.FILE);
-         jtest = new JmolParser(false, false, false, testFile,
-                 DataSourceType.FILE);
 -                AppletFormatAdapter.FILE);
 -        jtest = new JmolParser(testFile, AppletFormatAdapter.FILE);
++        mctest = new PDBfile(false, false, false, testFile, DataSourceType.FILE);
++        jtest = new JmolParser(testFile, DataSourceType.FILE);
        } catch (IOException e)
        {
          System.err.println("Exception thrown while parsing : " + pdbStr);
@@@ -26,15 -26,23 +26,23 @@@ import static org.testng.AssertJUnit.as
  import static org.testng.AssertJUnit.assertSame;
  import static org.testng.AssertJUnit.assertTrue;
  
+ import jalview.bin.Cache;
+ import jalview.bin.Jalview;
  import jalview.datamodel.AlignedCodonFrame;
  import jalview.datamodel.Alignment;
+ import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.AlignmentI;
+ import jalview.datamodel.Annotation;
  import jalview.datamodel.PDBEntry;
  import jalview.datamodel.PDBEntry.Type;
+ import jalview.datamodel.SearchResults;
+ import jalview.datamodel.SearchResultsI;
  import jalview.datamodel.Sequence;
  import jalview.datamodel.SequenceI;
 +import jalview.io.DataSourceType;
  import jalview.io.FileLoader;
 -import jalview.io.FormatAdapter;
+ import jalview.schemes.ColourSchemeI;
+ import jalview.schemes.PIDColourScheme;
  import jalview.structure.StructureSelectionManager;
  import jalview.util.MapList;
  
@@@ -296,4 -304,73 +304,73 @@@ public class AlignViewportTes
      assertTrue(ssmMappings.contains(acf2));
      assertFalse(ssmMappings.contains(acf3));
    }
+   /**
+    * Test for JAL-1306 - conservation thread should run even when only Quality
+    * (and not Conservation) is enabled in Preferences
+    */
+   @Test(groups = { "Functional" })
+   public void testUpdateConservation_qualityOnly()
+   {
+     Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS",
+             Boolean.TRUE.toString());
+     Cache.applicationProperties.setProperty("SHOW_QUALITY",
+             Boolean.TRUE.toString());
+     Cache.applicationProperties.setProperty("SHOW_CONSERVATION",
+             Boolean.FALSE.toString());
+     Cache.applicationProperties.setProperty("SHOW_IDENTITY",
+             Boolean.FALSE.toString());
+     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
 -            "examples/uniref50.fa", FormatAdapter.FILE);
++            "examples/uniref50.fa", DataSourceType.FILE);
+     AlignmentAnnotation[] anns = af.viewport.getAlignment()
+             .getAlignmentAnnotation();
+     assertNotNull("No annotations found", anns);
+     assertEquals("More than one annotation found", 1, anns.length);
+     assertTrue("Annotation is not Quality",
+             anns[0].description.startsWith("Alignment Quality"));
+     Annotation[] annotations = anns[0].annotations;
+     assertNotNull("Quality annotations are null", annotations);
+     assertNotNull("Quality in column 1 is null", annotations[0]);
+     assertTrue("No quality value in column 1", annotations[0].value > 10f);
+   }
+   @Test(groups = { "Functional" })
+   public void testSetGlobalColourScheme()
+   {
+     /*
+      * test for JAL-2283 don't inadvertently turn on colour by conservation
+      */
+     Cache.applicationProperties.setProperty("DEFAULT_COLOUR_PROT", "NONE");
+     Cache.applicationProperties.setProperty("SHOW_CONSERVATION",
+             Boolean.TRUE.toString());
+     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
 -            "examples/uniref50.fa", FormatAdapter.FILE);
++            "examples/uniref50.fa", DataSourceType.FILE);
+     ColourSchemeI cs = new PIDColourScheme();
+     af.getViewport().setGlobalColourScheme(cs);
+     assertFalse(cs.conservationApplied());
+   }
+   @Test(groups = { "Functional" })
+   public void testSetGetHasSearchResults()
+   {
+     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
 -            "examples/uniref50.fa", FormatAdapter.FILE);
++            "examples/uniref50.fa", DataSourceType.FILE);
+     SearchResultsI sr = new SearchResults();
+     SequenceI s1 = af.getViewport().getAlignment().getSequenceAt(0);
+     // create arbitrary range on first sequence
+     sr.addResult(s1, s1.getStart() + 10, s1.getStart() + 15);
+     // test set
+     af.getViewport().setSearchResults(sr);
+     // has -> true
+     assertTrue(af.getViewport().hasSearchResults());
+     // get == original
+     assertEquals(sr, af.getViewport().getSearchResults());
+     // set(null) results in has -> false
+     af.getViewport().setSearchResults(null);
+     assertFalse(af.getViewport().hasSearchResults());
+   }
  }
@@@ -27,9 -29,11 +29,12 @@@ import static org.testng.AssertJUnit.as
  import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.Annotation;
+ 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.io.FileFormat;
  import jalview.io.FormatAdapter;
  import jalview.util.MessageManager;
  
index 0000000,c55ddd9..070fa68
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,573 +1,574 @@@
+ /*
+  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+  * Copyright (C) $$Year-Rel$$ The Jalview Authors
+  * 
+  * This file is part of Jalview.
+  * 
+  * Jalview is free software: you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License 
+  * as published by the Free Software Foundation, either version 3
+  * of the License, or (at your option) any later version.
+  *  
+  * Jalview is distributed in the hope that it will be useful, but 
+  * WITHOUT ANY WARRANTY; without even the implied warranty 
+  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+  * PURPOSE.  See the GNU General Public License for more details.
+  * 
+  * You should have received a copy of the GNU General Public License
+  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+  * The Jalview Authors are detailed in the 'AUTHORS' file.
+  */
+ package jalview.io;
+ import jalview.analysis.CrossRef;
+ import jalview.api.AlignmentViewPanel;
+ import jalview.datamodel.AlignedCodonFrame;
+ import jalview.datamodel.AlignmentI;
+ import jalview.datamodel.AlignmentTest;
+ import jalview.datamodel.SequenceI;
+ import jalview.gui.AlignFrame;
+ import jalview.gui.CrossRefAction;
+ import jalview.gui.Desktop;
+ import jalview.gui.Jalview2XML;
+ import java.io.File;
+ import java.io.IOException;
+ import java.util.ArrayList;
+ import java.util.HashMap;
+ import java.util.List;
+ import org.testng.Assert;
+ import org.testng.annotations.Test;
+ @Test(singleThreaded = true)
+ public class CrossRef2xmlTests extends Jalview2xmlBase
+ {
+   /**
+    * test store and recovery of all reachable cross refs from all reachable
+    * crossrefs for one or more fetched db refs. Currently, this test has a known
+    * failure case.
+    * 
+    * @throws Exception
+    */
+   @Test(groups = { "Operational" }, enabled = true)
+   public void testRetrieveAndShowCrossref() throws Exception
+   {
+     List<String> failedDBRetr = new ArrayList<String>();
+     List<String> failedXrefMenuItems = new ArrayList<String>();
+     List<String> failedProjectRecoveries = new ArrayList<String>();
+     // for every set of db queries
+     // retrieve db query
+     // verify presence of expected xrefs
+     // show xrefs - verify expected type of frame is shown for each xref
+     // show xrefs again
+     // - verify original -> xref -> xref(original) recovers frame containing at
+     // least the first retrieved sequence
+     // store
+     // 1. whole project
+     // 2. individual frames
+     // 3. load each one back and verify
+     // . aligned sequences (.toString() )
+     // . xrefs (.toString() )
+     // . codonframes
+     //
+     //
+     HashMap<String, String> dbtoviewBit = new HashMap<String, String>();
+     List<String> keyseq = new ArrayList<String>();
+     HashMap<String, File> savedProjects = new HashMap<String, File>();
+     for (String[] did : new String[][] { { "ENSEMBL", "ENSG00000157764" },
+     { "UNIPROT", "P01731" } })
+     {
+       // pass counters - 0 - first pass, 1 means retrieve project rather than
+       // perform action
+       int pass1 = 0, pass2 = 0, pass3 = 0;
+       // each do loop performs two iterations in the first outer loop pass, but
+       // only performs one iteration on the second outer loop
+       // ie. pass 1 = 0 {pass 2= 0 { pass 3 = 0,1 }, pass 2=1 { pass 3 = 0 }}, 1
+       // { pass 2 = 0 { pass 3 = 0 } }
+       do
+       {
+         String first = did[0] + " " + did[1];
+         AlignFrame af = null;
+         boolean dna;
+         AlignmentI retral;
+         AlignmentI dataset;
+         SequenceI[] seqs;
+         List<String> ptypes = null;
+         if (pass1 == 0)
+         {
+           // retrieve dbref
+           List<AlignFrame> afs = jalview.gui.SequenceFetcher.fetchAndShow(
+                   did[0], did[1]);
+           if (afs.size() == 0)
+           {
+             failedDBRetr.add("Didn't retrieve " + first);
+             break;
+           }
+           keyseq.add(first);
+           af = afs.get(0);
+           // verify references for retrieved data
+           AlignmentTest.assertAlignmentDatasetRefs(af.getViewport()
+                   .getAlignment(), "Pass (" + pass1 + "," + pass2 + ","
+                   + pass3 + "): Fetch " + first + ":");
+           assertDatasetIsNormalisedKnownDefect(af.getViewport()
+                   .getAlignment(), "Pass (" + pass1 + "," + pass2 + ","
+                   + pass3 + "): Fetch " + first + ":");
+           dna = af.getViewport().getAlignment().isNucleotide();
+           retral = af.getViewport().getAlignment();
+           dataset = retral.getDataset();
+           seqs = retral.getSequencesArray();
+         }
+         else
+         {
+           Desktop.instance.closeAll_actionPerformed(null);
+           // recover stored project
+           af = new FileLoader(false).LoadFileWaitTillLoaded(savedProjects
 -                  .get(first).toString(), FormatAdapter.FILE);
++                  .get(first).toString(), DataSourceType.FILE);
+           System.out.println("Recovered view for '" + first + "' from '"
+                   + savedProjects.get(first).toString() + "'");
+           dna = af.getViewport().getAlignment().isNucleotide();
+           retral = af.getViewport().getAlignment();
+           dataset = retral.getDataset();
+           seqs = retral.getSequencesArray();
+           // verify references for recovered data
+           AlignmentTest.assertAlignmentDatasetRefs(af.getViewport()
+                   .getAlignment(), "Pass (" + pass1 + "," + pass2 + ","
+                   + pass3 + "): Recover " + first + ":");
+           assertDatasetIsNormalisedKnownDefect(af.getViewport()
+                   .getAlignment(), "Pass (" + pass1 + "," + pass2 + ","
+                   + pass3 + "): Recover " + first + ":");
+         }
+         // store project on first pass, compare next pass
+         stringify(dbtoviewBit, savedProjects, first, af.alignPanel);
+         ptypes = (seqs == null || seqs.length == 0) ? null : new CrossRef(
+                 seqs, dataset).findXrefSourcesForSequences(dna);
+         // start of pass2: retrieve each cross-ref for fetched or restored
+         // project.
+         do // first cross ref and recover crossref loop
+         {
+           for (String db : ptypes)
+           {
+             // counter for splitframe views retrieved via crossref
+             int firstcr_ap = 0;
+             // build next key so we an retrieve all views
+             String nextxref = first + " -> " + db + "{" + firstcr_ap + "}";
+             // perform crossref action, or retrieve stored project
+             List<AlignmentViewPanel> cra_views = new ArrayList<AlignmentViewPanel>();
+             CrossRefAction cra = null;
+             if (pass2 == 0)
+             { // retrieve and show cross-refs in this thread
+               cra = new CrossRefAction(af, seqs, dna, db);
+               cra.run();
+               if (cra.getXrefViews().size() == 0)
+               {
+                 failedXrefMenuItems.add("No crossrefs retrieved for "
+                         + first + " -> " + db);
+                 continue;
+               }
+               cra_views = cra.getXrefViews();
+               assertNucleotide(cra_views.get(0),
+                       "Nucleotide panel included proteins for " + first
+                               + " -> " + db);
+               assertProtein(cra_views.get(1),
+                       "Protein panel included nucleotides for " + first
+                               + " -> " + db);
+             }
+             else
+             {
+               Desktop.instance.closeAll_actionPerformed(null);
+               pass3 = 0;
+               // recover stored project
+               File storedProject = savedProjects.get(nextxref);
+               if (storedProject == null)
+               {
+                 failedProjectRecoveries.add("Failed to store a view for '"
+                         + nextxref + "'");
+                 continue;
+               }
+               // recover stored project
+               AlignFrame af2 = new FileLoader(false)
+                       .LoadFileWaitTillLoaded(savedProjects.get(nextxref)
 -                              .toString(), FormatAdapter.FILE);
++                              .toString(), DataSourceType.FILE);
+               System.out.println("Recovered view for '" + nextxref
+                       + "' from '" + savedProjects.get(nextxref).toString()
+                       + "'");
+               // gymnastics to recover the alignPanel/Complementary alignPanel
+               if (af2.getViewport().isNucleotide())
+               {
+                 // top view, then bottom
+                 cra_views.add(af2.getViewport().getAlignPanel());
+                 cra_views.add(((jalview.gui.AlignViewport) af2
+                         .getViewport().getCodingComplement())
+                         .getAlignPanel());
+               }
+               else
+               {
+                 // bottom view, then top
+                 cra_views.add(((jalview.gui.AlignViewport) af2
+                         .getViewport().getCodingComplement())
+                         .getAlignPanel());
+                 cra_views.add(af2.getViewport().getAlignPanel());
+               }
+             }
+             HashMap<String, List<String>> xrptypes = new HashMap<String, List<String>>();
+             // first save/verify views.
+             for (AlignmentViewPanel avp : cra_views)
+             {
+               nextxref = first + " -> " + db + "{" + firstcr_ap++ + "}";
+               // verify references for this panel
+               AlignmentTest.assertAlignmentDatasetRefs(avp.getAlignment(),
+                       "Pass (" + pass1 + "," + pass2 + "," + pass3
+                               + "): before start of pass3: " + nextxref
+                               + ":");
+               assertDatasetIsNormalisedKnownDefect(avp.getAlignment(),
+                       "Pass (" + pass1 + "," + pass2 + "," + pass3
+                               + "): before start of pass3: " + nextxref
+                               + ":");
+               SequenceI[] xrseqs = avp.getAlignment().getSequencesArray();
+               List<String> _xrptypes = (seqs == null || seqs.length == 0) ? null
+                       : new CrossRef(xrseqs, dataset)
+                               .findXrefSourcesForSequences(avp
+                                       .getAlignViewport().isNucleotide());
+               stringify(dbtoviewBit, savedProjects, nextxref, avp);
+               xrptypes.put(nextxref, _xrptypes);
+             }
+             // now do the second xref pass starting from either saved or just
+             // recovered split pane, in sequence
+             do // retrieve second set of cross refs or recover and verify
+             {
+               firstcr_ap = 0;
+               for (AlignmentViewPanel avp : cra_views)
+               {
+                 nextxref = first + " -> " + db + "{" + firstcr_ap++ + "}";
+                 for (String xrefdb : xrptypes.get(nextxref))
+                 {
+                   List<AlignmentViewPanel> cra_views2 = new ArrayList<AlignmentViewPanel>();
+                   int q = 0;
+                   String nextnextxref = nextxref + " -> " + xrefdb + "{"
+                           + q + "}";
+                   if (pass3 == 0)
+                   {
+                     SequenceI[] xrseqs = avp.getAlignment()
+                             .getSequencesArray();
+                     AlignFrame nextaf = Desktop.getAlignFrameFor(avp
+                             .getAlignViewport());
+                     cra = new CrossRefAction(nextaf, xrseqs, avp
+                             .getAlignViewport().isNucleotide(), xrefdb);
+                     cra.run();
+                     if (cra.getXrefViews().size() == 0)
+                     {
+                       failedXrefMenuItems
+                               .add("No crossrefs retrieved for '"
+                                       + nextxref + "' to " + xrefdb
+                                       + " via '" + nextaf.getTitle() + "'");
+                       continue;
+                     }
+                     cra_views2 = cra.getXrefViews();
+                     assertNucleotide(cra_views2.get(0),
+                             "Nucleotide panel included proteins for '"
+                                     + nextxref + "' to " + xrefdb
+                                     + " via '" + nextaf.getTitle() + "'");
+                     assertProtein(cra_views2.get(1),
+                             "Protein panel included nucleotides for '"
+                                     + nextxref + "' to " + xrefdb
+                                     + " via '" + nextaf.getTitle() + "'");
+                   }
+                   else
+                   {
+                     Desktop.instance.closeAll_actionPerformed(null);
+                     // recover stored project
+                     File storedProject = savedProjects.get(nextnextxref);
+                     if (storedProject == null)
+                     {
+                       failedProjectRecoveries
+                               .add("Failed to store a view for '"
+                                       + nextnextxref + "'");
+                       continue;
+                     }
+                     AlignFrame af2 = new FileLoader(false)
+                             .LoadFileWaitTillLoaded(
+                                     savedProjects.get(nextnextxref)
 -                                            .toString(), FormatAdapter.FILE);
++                                            .toString(),
++                                    DataSourceType.FILE);
+                     System.out.println("Recovered view for '"
+                             + nextnextxref + "' from '"
+                             + savedProjects.get(nextnextxref).toString()
+                             + "'");
+                     // gymnastics to recover the alignPanel/Complementary
+                     // alignPanel
+                     if (af2.getViewport().isNucleotide())
+                     {
+                       // top view, then bottom
+                       cra_views2.add(af2.getViewport().getAlignPanel());
+                       cra_views2.add(((jalview.gui.AlignViewport) af2
+                               .getViewport().getCodingComplement())
+                               .getAlignPanel());
+                     }
+                     else
+                     {
+                       // bottom view, then top
+                       cra_views2.add(((jalview.gui.AlignViewport) af2
+                               .getViewport().getCodingComplement())
+                               .getAlignPanel());
+                       cra_views2.add(af2.getViewport().getAlignPanel());
+                     }
+                     Assert.assertEquals(cra_views2.size(), 2);
+                     Assert.assertNotNull(cra_views2.get(0));
+                     Assert.assertNotNull(cra_views2.get(1));
+                   }
+                   for (AlignmentViewPanel nextavp : cra_views2)
+                   {
+                     nextnextxref = nextxref + " -> " + xrefdb + "{" + q++
+                             + "}";
+                     // verify references for this panel
+                     AlignmentTest.assertAlignmentDatasetRefs(
+                             nextavp.getAlignment(), "" + "Pass (" + pass1
+                                     + "," + pass2 + "): For "
+                                     + nextnextxref + ":");
+                     assertDatasetIsNormalisedKnownDefect(
+                             nextavp.getAlignment(), "" + "Pass (" + pass1
+                                     + "," + pass2 + "): For "
+                                     + nextnextxref + ":");
+                     stringify(dbtoviewBit, savedProjects, nextnextxref,
+                             nextavp);
+                     keyseq.add(nextnextxref);
+                   }
+                 } // end of loop around showing all xrefdb for crossrf2
+               } // end of loop around all viewpanels from crossrf1
+             } while (pass2 == 2 && pass3++ < 2);
+             // fetchdb->crossref1->crossref-2->verify for xrefs we
+             // either loop twice when pass2=0, or just once when pass2=1
+             // (recovered project from previous crossref)
+           } // end of loop over db-xrefs for crossref-2
+           // fetchdb-->crossref1
+           // for each xref we try to retrieve xref, store and verify when
+           // pass1=0, or just retrieve and verify when pass1=1
+         } while (pass1 == 1 && pass2++ < 2);
+         // fetchdb
+         // for each ref we
+         // loop twice: first, do the retrieve, second recover from saved project
+         // increment pass counters, so we repeat traversal starting from the
+         // oldest saved project first.
+         if (pass1 == 0)
+         {
+           // verify stored projects for first set of cross references
+           pass1 = 1;
+           // and verify cross-references retrieved from stored projects
+           pass2 = 0;
+           pass3 = 0;
+         }
+         else
+         {
+           pass1++;
+         }
+       } while (pass1 < 3);
+     }
+     if (failedXrefMenuItems.size() > 0)
+     {
+       for (String s : failedXrefMenuItems)
+       {
+         System.err.println(s);
+       }
+       Assert.fail("Faulty xref menu (" + failedXrefMenuItems.size()
+               + " counts)");
+     }
+     if (failedProjectRecoveries.size() > 0)
+     {
+       for (String s : failedProjectRecoveries)
+       {
+         System.err.println(s);
+       }
+       Assert.fail("Didn't recover projects for some retrievals (did they retrieve ?) ("
+               + failedProjectRecoveries.size() + " counts)");
+     }
+     if (failedDBRetr.size() > 0)
+     {
+       for (String s : failedProjectRecoveries)
+       {
+         System.err.println(s);
+       }
+       Assert.fail("Didn't retrieve some db refs for checking cross-refs ("
+               + failedDBRetr.size() + " counts)");
+     }
+   }
+   /**
+    * wrapper to trap known defect for AH002001 testcase
+    * 
+    * @param alignment
+    * @param string
+    */
+   private void assertDatasetIsNormalisedKnownDefect(AlignmentI al,
+           String message)
+   {
+     try
+     {
+       AlignmentTest.assertDatasetIsNormalised(al, message);
+     } catch (AssertionError ae)
+     {
+       if (!ae.getMessage().endsWith("EMBL|AH002001"))
+       {
+         throw ae;
+       }
+       else
+       {
+         System.out
+                 .println("Ignored exception for known defect: JAL-2179 : "
+                         + message);
+       }
+     }
+   }
+   private void assertProtein(AlignmentViewPanel alignmentViewPanel,
+           String message)
+   {
+     assertType(true, alignmentViewPanel, message);
+   }
+   private void assertNucleotide(AlignmentViewPanel alignmentViewPanel,
+           String message)
+   {
+     assertType(false, alignmentViewPanel, message);
+   }
+   private void assertType(boolean expectProtein,
+           AlignmentViewPanel alignmentViewPanel, String message)
+   {
+     List<SequenceI> nonType = new ArrayList<SequenceI>();
+     for (SequenceI sq : alignmentViewPanel.getAlignViewport()
+             .getAlignment().getSequences())
+     {
+       if (sq.isProtein() != expectProtein)
+       {
+         nonType.add(sq);
+       }
+     }
+     if (nonType.size() > 0)
+     {
+       Assert.fail(message + " [ "
+               + (expectProtein ? "nucleotides were " : "proteins were ")
+               + nonType.toString() + " ]");
+     }
+   }
+   /**
+    * first time called, record strings derived from alignment and
+    * alignedcodonframes, and save view to a project file. Second time called,
+    * compare strings to existing ones. org.testng.Assert.assertTrue on
+    * stringmatch
+    * 
+    * @param dbtoviewBit
+    *          map between xrefpath and view string
+    * @param savedProjects
+    *          - map from xrefpath to saved project filename (createTempFile)
+    * @param xrefpath
+    *          - xrefpath - unique ID for this context (composed of sequence of
+    *          db-fetch/cross-ref actions preceeding state)
+    * @param avp
+    *          - viewpanel to store (for viewpanels in splitframe, the same
+    *          project should be written for both panels, only one needs
+    *          recovering for comparison on the next stringify call, but each
+    *          viewpanel needs to be called with a distinct xrefpath to ensure
+    *          each one's strings are compared)
+    */
+   private void stringify(HashMap<String, String> dbtoviewBit,
+           HashMap<String, File> savedProjects, String xrefpath,
+           AlignmentViewPanel avp)
+   {
+     if (savedProjects != null)
+     {
+       if (savedProjects.get(xrefpath) == null)
+       {
+         // write a project file for this view. On the second pass, this will be
+         // recovered and cross-references verified
+         try
+         {
+           File prfile = File.createTempFile("crossRefTest", ".jvp");
+           AlignFrame af = Desktop.getAlignFrameFor(avp.getAlignViewport());
+           new Jalview2XML(false).saveAlignment(af, prfile.toString(),
+                   af.getTitle());
+           System.out.println("Written view from '" + xrefpath + "' as '"
+                   + prfile.getAbsolutePath() + "'");
+           savedProjects.put(xrefpath, prfile);
+         } catch (IOException q)
+         {
+           Assert.fail("Unexpected IO Exception", q);
+         }
+       }
+       else
+       {
+         System.out.println("Stringify check on view from '" + xrefpath
+                 + "' [ possibly retrieved from '"
+                 + savedProjects.get(xrefpath).getAbsolutePath() + "' ]");
+       }
+     }
+     StringBuilder sbr = new StringBuilder();
+     sbr.append(avp.getAlignment().toString());
+     sbr.append("\n");
+     sbr.append("<End of alignment>");
+     sbr.append("\n");
+     sbr.append(avp.getAlignment().getDataset());
+     sbr.append("\n");
+     sbr.append("<End of dataset>");
+     sbr.append("\n");
+     int p = 0;
+     if (avp.getAlignment().getCodonFrames() != null)
+     {
+       for (AlignedCodonFrame ac : avp.getAlignment().getCodonFrames())
+       {
+         sbr.append("<AlignedCodonFrame " + p++ + ">");
+         sbr.append("\n");
+         sbr.append(ac.toString());
+         sbr.append("\n");
+       }
+     }
+     String dbt = dbtoviewBit.get(xrefpath);
+     if (dbt == null)
+     {
+       dbtoviewBit.put(xrefpath, sbr.toString());
+     }
+     else
+     {
+       Assert.assertEquals(sbr.toString(), dbt, "stringify mismatch for "
+               + xrefpath);
+     }
+   }
+ }
Simple merge
Simple merge
@@@ -119,9 -68,9 +68,9 @@@ public class Jalview2xmlTests extends J
      String inFile = "examples/RF00031_folded.stk";
      String tfile = File.createTempFile("JalviewTest", ".jvp")
              .getAbsolutePath();
-     AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
 -    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
 -            FormatAdapter.FILE);
 -    assertNotNull("Didn't read input file " + inFile, af);
++    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
 +            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);
      af.rnahelicesColour_actionPerformed(null);
              "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,
-             DataSourceType.FILE);
 -    af = new FileLoader().LoadFileWaitTillLoaded(tfile, FormatAdapter.FILE);
 -    assertNotNull("Failed to import new project", af);
++    af = new FileLoader().LoadFileWaitTillLoaded(tfile, DataSourceType.FILE);
 +    assertTrue("Failed to import new project", af != null);
      int newdsann = countDsAnn(af.getViewport());
      assertTrue(
              "Differing numbers of dataset sequence annotation\nOriginally "
      String inFile = "examples/uniref50.fa", inAnnot = "examples/uniref50.score_ascii";
      String tfile = File.createTempFile("JalviewTest", ".jvp")
              .getAbsolutePath();
-     AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
 -    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
 -            FormatAdapter.FILE);
++    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
 +            inFile, DataSourceType.FILE);
-     assertTrue("Didn't read input file " + inFile, af != null);
+     assertNotNull("Didn't read input file " + inFile, af);
 -    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()
-                     .equals(jalview.schemes.TCoffeeColourScheme.class));
-     assertTrue(
-             "Recognise T-Coffee score from string",
-             jalview.schemes.ColourSchemeProperty.getColour(af.getViewport()
-                     .getAlignment(),
-                     jalview.schemes.ColourSchemeProperty.getColourName(af
-                             .getViewport().getGlobalColourScheme())) != null);
+     assertSame("Didn't set T-coffee colourscheme", af.getViewport()
+             .getGlobalColourScheme().getClass(), TCoffeeColourScheme.class);
+     assertNotNull("Recognise T-Coffee score from string",
 -            jalview.schemes.ColourSchemeProperty.getColour(af.getViewport()
++            ColourSchemeProperty.getColour(af.getViewport()
+                     .getAlignment(), ColourSchemeProperty.getColourName(af
+                     .getViewport().getGlobalColourScheme())));
  
      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,
 -    af = new FileLoader().LoadFileWaitTillLoaded(tfile, FormatAdapter.FILE);
++    af = new FileLoader().LoadFileWaitTillLoaded(tfile,
 +            DataSourceType.FILE);
-     assertTrue("Failed to import new project", af != null);
-     assertTrue(
-             "Didn't set T-coffee colourscheme for imported project.",
-             af.getViewport().getGlobalColourScheme().getClass()
-                     .equals(jalview.schemes.TCoffeeColourScheme.class));
+     assertNotNull("Failed to import new project", af);
+     assertSame("Didn't set T-coffee colourscheme for imported project.", af
+             .getViewport().getGlobalColourScheme().getClass(),
+             TCoffeeColourScheme.class);
      System.out
              .println("T-Coffee score shading successfully recovered from project.");
    }
      String inFile = "examples/uniref50.fa", inAnnot = "examples/testdata/uniref50_iupred.jva";
      String tfile = File.createTempFile("JalviewTest", ".jvp")
              .getAbsolutePath();
-     AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
-             inFile, DataSourceType.FILE);
-     assertTrue("Didn't read input file " + inFile, af != null);
 -    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
 -            FormatAdapter.FILE);
++    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile, DataSourceType.FILE);
+     assertNotNull("Didn't read input file " + inFile, af);
 -    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,
-             DataSourceType.FILE);
 -    af = new FileLoader().LoadFileWaitTillLoaded(tfile, FormatAdapter.FILE);
 -    assertNotNull("Failed to import new project", af);
++    af = new FileLoader().LoadFileWaitTillLoaded(tfile, 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(
+     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);
+     assertNotNull("Didn't read in the example file correctly.", af);
      assertTrue("Didn't gather the views in the example file.",
              Desktop.getAlignFrames().length == 1 + origCount);
  
    @Test(groups = { "Functional" })
    public void viewRefPdbAnnotation() throws Exception
    {
-     // 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(
+     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);
+     assertNotNull("Didn't read in the example file correctly.", af);
      AlignmentViewPanel sps = null;
      for (AlignmentViewPanel ap : af.alignPanel.alignFrame.getAlignPanels())
      {
    @Test(groups = { "Functional" })
    public void testCopyViewSettings() throws Exception
    {
-     AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
+     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);
+     assertNotNull("Didn't read in the example file correctly.", af);
      AlignmentViewPanel sps = null, groups = null;
      for (AlignmentViewPanel ap : af.alignPanel.alignFrame.getAlignPanels())
      {
    {
      Desktop.instance.closeAll_actionPerformed(null);
  
-     AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
+     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);
      Assert.assertEquals(Desktop.getAlignFrames().length, 1);
      String afid = af.getViewport().getSequenceSetId();
  
      {
        Assert.assertEquals(Desktop.getAlignFrames().length, 0);
      }
-     af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
 -    af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
 -            FormatAdapter.FILE);
++    af = new FileLoader().LoadFileWaitTillLoaded(
 +            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);
+     assertNotNull("Didn't read in the example file correctly.", af);
      String afid = af.getViewport().getSequenceSetId();
  
      // remember reference sequence for each panel
    {
      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);
+     assertNotNull("Didn't read in the example file correctly.", af);
      String afid = af.getViewport().getSequenceSetId();
      // make a second view of the alignment
      af.newView_actionPerformed(null);
      {
        Assert.assertEquals(Desktop.getAlignFrames().length, 0);
      }
 -
 -    af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
 -            FormatAdapter.FILE);
 +  
 +    af = new FileLoader().LoadFileWaitTillLoaded(
 +            tfile.getAbsolutePath(), DataSourceType.FILE);
      afid = af.getViewport().getSequenceSetId();
-   
      for (AlignmentViewPanel ap : Desktop.getAlignmentPanels(afid))
      {
        String viewName = ap.getViewName();
      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);
+     assertNotNull("Didn't read in the example file correctly.", af);
      String afid = af.getViewport().getSequenceSetId();
  
      AlignmentPanel[] alignPanels = Desktop.getAlignmentPanels(afid);
Simple merge
@@@ -9,24 -29,22 +29,22 @@@ import org.testng.annotations.Test
  
  public class PfamFormatInputTest
  {
-   @Test
+   @Test(groups = "Functional")
    public void testPfamFormatNoLimits() throws IOException
    {
-     AlignmentI al = new jalview.io.AppletFormatAdapter().readFile("ASEQ"
+     AlignmentI al = new AppletFormatAdapter().readFile("ASEQ"
 -            + '\t' + "...--FFAFAFF--", AppletFormatAdapter.PASTE, "PFAM");
 +            + '\t' + "...--FFAFAFF--", DataSourceType.PASTE,
 +            FileFormat.Pfam);
      Assert.assertEquals(1, al.getHeight(), "Wrong number of sequences");
      Assert.assertTrue(al.hasValidSequence(),
              "Didn't extract limits from PFAM ID");
    }
  
-   @Test
+   @Test(groups = "Functional")
    public void testPfamFormatValidLimits() throws IOException
    {
-     AlignmentI al = new jalview.io.AppletFormatAdapter().readFile(
-             "ASEQ/15-25" + '\t' + "...--FFAFAFF--",
-  DataSourceType.PASTE,
-             FileFormat.Pfam);
 -    AlignmentI al = new AppletFormatAdapter().readFile(
 -            "ASEQ/15-25" + '\t' + "...--FFAFAFF--",
 -            AppletFormatAdapter.PASTE, "PFAM");
++    AlignmentI al = new AppletFormatAdapter().readFile("ASEQ/15-25" + '\t'
++            + "...--FFAFAFF--", DataSourceType.PASTE, FileFormat.Pfam);
      Assert.assertEquals(1, al.getHeight(), "Wrong number of sequences");
      Assert.assertTrue(al.hasValidSequence(),
              "Didn't extract limits from PFAM ID");
Simple merge
@@@ -69,7 -68,7 +69,7 @@@ public class Mappin
        StructureSelectionManager ssm = new jalview.structure.StructureSelectionManager();
        StructureFile pmap = ssm.setMapping(true, new SequenceI[] { uprot },
                new String[] { "A" }, "test/jalview/ext/jmol/1QCF.pdb",
-               jalview.io.DataSourceType.FILE);
 -              jalview.io.FormatAdapter.FILE);
++              DataSourceType.FILE);
        assertTrue(pmap != null);
        SequenceI protseq = pmap.getSeqsAsArray()[0];
        AlignmentAnnotation pstra = protseq
      // Associate the 1GAQ pdb file with the subsequence 'imported' from another
      // source
      StructureFile pde = ssm.setMapping(true, new SequenceI[] { sq },
 -            new String[] { "A" }, inFile = "examples/1gaq.txt",
 -            jalview.io.FormatAdapter.FILE);
 +            new String[]
-     { "A" }, inFile = "examples/1gaq.txt", jalview.io.DataSourceType.FILE);
++    { "A" }, inFile = "examples/1gaq.txt", DataSourceType.FILE);
      assertTrue("PDB File couldn't be found", pde != null);
      StructureMapping[] mp = ssm.getMapping(inFile);
      assertTrue("No mappings made.", mp != null && mp.length > 0);
      StructureSelectionManager ssm = new jalview.structure.StructureSelectionManager();
      StructureFile pmap = ssm.setMapping(true, new SequenceI[] { newseq },
              new String[] { null }, "examples/3W5V.pdb",
-             jalview.io.DataSourceType.FILE);
 -            jalview.io.FormatAdapter.FILE);
++            DataSourceType.FILE);
      if (pmap == null)
      {
        AssertJUnit.fail("Couldn't make a mapping for 3W5V to FER1_MAIZE");
      StructureImportSettings.setShowSeqFeatures(true);
      AlignFrame ref = new FileLoader(false)
              .LoadFileWaitTillLoaded("test/jalview/ext/jmol/1QCF.pdb",
-                     jalview.io.DataSourceType.FILE);
 -                    jalview.io.FormatAdapter.FILE);
++                    DataSourceType.FILE);
      SequenceI refseq = ref.getViewport().getAlignment().getSequenceAt(0);
      SequenceI newseq = new Sequence(refseq.getName() + "Copy",
              refseq.getSequenceAsString());
      ssm.setAddTempFacAnnot(true);
      StructureFile pmap = ssm.setMapping(true, new SequenceI[] { newseq },
              new String[] { null }, "test/jalview/ext/jmol/1QCF.pdb",
-             jalview.io.DataSourceType.FILE);
 -            jalview.io.FormatAdapter.FILE);
++            DataSourceType.FILE);
      assertTrue(pmap != null);
      assertEquals("Original and copied sequence of different lengths.",
              refseq.getLength(), newseq.getLength());
@@@ -97,14 -110,14 +110,14 @@@ public class AAStructureBindingModelTes
      seqs[2] = new SequenceI[] { seq3 };
      StructureSelectionManager ssm = new StructureSelectionManager();
  
-     ssm.setMapping(new SequenceI[] { seq1 }, null, PDB_1,
+     ssm.setMapping(new SequenceI[] { seq1a, seq1b }, null, PDB_1,
 -            AppletFormatAdapter.PASTE);
 +            DataSourceType.PASTE);
      ssm.setMapping(new SequenceI[] { seq2 }, null, PDB_2,
 -            AppletFormatAdapter.PASTE);
 +            DataSourceType.PASTE);
      ssm.setMapping(new SequenceI[] { seq3 }, null, PDB_3,
 -            AppletFormatAdapter.PASTE);
 +            DataSourceType.PASTE);
  
-     testee = new AAStructureBindingModel(ssm, pdbFiles, seqs, chains, null)
+     testee = new AAStructureBindingModel(ssm, pdbFiles, seqs, null)
      {
        @Override
        public String[] getPdbFile()
@@@ -471,4 -472,34 +472,34 @@@ public class SiftsClientTes
      Assert.assertEquals(entityD.getEntityId(), "D");
  
    }
+   @Test(groups = { "Functional" })
+   public void getEntityByMostOptimalMatchedIdTest2()
+   {
+     // This test is for a SIFTS file in which entity A should map to chain P for
+     // the given PDB Id. All the other chains shouldn't be mapped as there are
+     // no SIFTS entity records for them.
+     SiftsClient siftsClientX = null;
+     PDBfile pdbFile;
+     try
+     {
+       pdbFile = new PDBfile(false, false, false,
 -              "test/jalview/io/3ucu.cif", AppletFormatAdapter.FILE);
++              "test/jalview/io/3ucu.cif", DataSourceType.FILE);
+       siftsClientX = new SiftsClient(pdbFile);
+     } catch (Exception e)
+     {
+       e.printStackTrace();
+     }
+     Entity entityA = siftsClientX.getEntityByMostOptimalMatchedId("P");
+     Entity entityP = siftsClientX.getEntityByMostOptimalMatchedId("A");
+     Entity entityR = siftsClientX.getEntityByMostOptimalMatchedId("R");
+     Assert.assertEquals(entityA.getEntityId(), "A");
+     Assert.assertNotEquals(entityR, "A");
+     Assert.assertNotEquals(entityP, "A");
+     Assert.assertNotEquals(entityR, "R");
+     Assert.assertNotEquals(entityP, "P");
+     Assert.assertNull(entityR);
+     Assert.assertNull(entityP);
+   }
  }