Merge branch 'features/r2_11_2_alphafold/JAL-2349_JAL-3855' into develop
authorJim Procter <j.procter@dundee.ac.uk>
Fri, 10 Jun 2022 11:13:11 +0000 (12:13 +0100)
committerJim Procter <j.procter@dundee.ac.uk>
Fri, 10 Jun 2022 11:13:11 +0000 (12:13 +0100)
12 files changed:
1  2 
src/jalview/api/AlignViewportI.java
src/jalview/appletgui/AnnotationPanel.java
src/jalview/datamodel/Alignment.java
src/jalview/datamodel/AlignmentAnnotation.java
src/jalview/gui/AnnotationPanel.java
src/jalview/io/AppletFormatAdapter.java
src/jalview/renderer/AnnotationRenderer.java
src/jalview/viewmodel/AlignmentViewport.java
src/jalview/ws/SequenceFetcher.java
src/jalview/ws/dbsources/EBIAlfaFold.java
test/jalview/gui/SeqPanelTest.java
test/jalview/ws/seqfetcher/DbRefFetcherTest.java

@@@ -27,6 -27,7 +27,7 @@@ import jalview.datamodel.AlignmentExpor
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.AlignmentView;
  import jalview.datamodel.ColumnSelection;
+ import jalview.datamodel.ContactListI;
  import jalview.datamodel.ProfilesI;
  import jalview.datamodel.SearchResultsI;
  import jalview.datamodel.SequenceCollectionI;
@@@ -475,6 -476,8 +476,8 @@@ public interface AlignViewportI extend
     */
    SearchResultsI getSearchResults();
  
+   ContactListI getContactList(AlignmentAnnotation _aa, int column);
    /**
     * Updates view settings with the given font. You may need to call
     * AlignmentPanel.fontChanged to update the layout geometry.
     * regions of the alignment
     * 
     * @param selectedRegionOnly
 -   *                             if true, and the view has a selection region,
 -   *                             then only the intersection of visible columns
 -   *                             with the selection region is returned
 +   *          if true, and the view has a selection region, then only the
 +   *          intersection of visible columns with the selection region is
 +   *          returned
     * @return
     */
    Iterator<int[]> getViewAsVisibleContigs(boolean selectedRegionOnly);
@@@ -99,14 -99,14 +99,14 @@@ public class AnnotationPanel extends Pa
  
    public static int GRAPH_HEIGHT = 40;
  
 -//  boolean MAC = false;
 +  // boolean MAC = false;
  
    public final AnnotationRenderer renderer;
  
    public AnnotationPanel(AlignmentPanel ap)
    {
      new jalview.util.Platform();
 -//    MAC = Platform.isAMac();
 +    // MAC = Platform.isAMac();
      this.ap = ap;
      av = ap.av;
      setLayout(null);
          {
            activeRow = i;
          }
-         else if (aa[i].graph > 0)
+         else if (aa[i].graph != AlignmentAnnotation.NO_GRAPH)
          {
            // Stretch Graph
            graphStretch = i;
@@@ -32,6 -32,7 +32,7 @@@ import java.util.Arrays
  import java.util.BitSet;
  import java.util.Collections;
  import java.util.Enumeration;
+ import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Hashtable;
  import java.util.Iterator;
@@@ -195,7 -196,7 +196,7 @@@ public class Alignment implements Align
    {
      synchronized (sequences)
      {
 -    
 +
        if (i > -1 && i < sequences.size())
        {
          return sequences.get(i);
    public int getWidth()
    {
      int maxLength = -1;
 -  
 +
      for (int i = 0; i < sequences.size(); i++)
      {
        maxLength = Math.max(maxLength, getSequenceAt(i).getLength());
    @Override
    public boolean setHiddenColumns(HiddenColumns cols)
    {
-     boolean changed = cols == null ? hiddenCols != null
-             : !cols.equals(hiddenCols);
-     hiddenCols = cols;
-     return changed;
+   boolean changed = cols == null ? hiddenCols != null
+           : !cols.equals(hiddenCols);
+   hiddenCols = cols;
+   return changed;
    }
    @Override
    public void setupJPredAlignment()
    {
      }
    }
  
+   Map<Object, ContactMatrixI> contactmaps = new HashMap<>();
+   @Override
+   public
+   ContactListI getContactListFor(AlignmentAnnotation _aa, int column)
+   {
+     ContactMatrixI cm = contactmaps.get(_aa.annotationId);
+     if (cm == null)
+     {
+       return null;
+     }
+     return cm.getContactList(column);
+   }
+   @Override
+   public AlignmentAnnotation addContactList(ContactMatrixI cm)
+   {
+     Annotation _aa[] = new Annotation[getWidth()];
+     Annotation dummy = new Annotation(0.0f);
+     for (int i = 0; i < _aa.length; _aa[i++] = dummy)
+     {
+       ;
+     }
+     AlignmentAnnotation aa = new AlignmentAnnotation("Contact Matrix",
+             "Contact Matrix", _aa);
+     aa.graph = AlignmentAnnotation.CUSTOMRENDERER;
+     aa.graphMin = cm.getMin();
+     aa.graphMax = cm.getMax();
+     aa.editable = false;
+     // aa.autoCalculated = true;
+     contactmaps.put(aa.annotationId, cm);
+     // TODO: contact matrices could be intra or inter - more than one refseq possible! 
+     if (cm.hasReferenceSeq())
+     {
+       aa.setSequenceRef(cm.getReferenceSeq());
+     }
+     addAnnotation(aa);
+     return aa;
+   }
  }
   */
  package jalview.datamodel;
  
 -import java.util.Locale;
 -
 -import jalview.analysis.Rna;
 -import jalview.analysis.SecStrConsensus.SimpleBP;
 -import jalview.analysis.WUSSParseException;
 -
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collection;
@@@ -27,14 -33,9 +27,14 @@@ import java.util.Collections
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
 +import java.util.Locale;
  import java.util.Map;
  import java.util.Map.Entry;
  
 +import jalview.analysis.Rna;
 +import jalview.analysis.SecStrConsensus.SimpleBP;
 +import jalview.analysis.WUSSParseException;
 +
  /**
   * DOCUMENT ME!
   * 
@@@ -182,8 -183,7 +182,8 @@@ public class AlignmentAnnotatio
      return rnaSecondaryStructureEquivalent(that, true);
    }
  
 -  public boolean rnaSecondaryStructureEquivalent(AlignmentAnnotation that, boolean compareType)
 +  public boolean rnaSecondaryStructureEquivalent(AlignmentAnnotation that,
 +          boolean compareType)
    {
      SequenceFeature[] thisSfArray = this.getRnaSecondaryStructure();
      SequenceFeature[] thatSfArray = that.getRnaSecondaryStructure();
        return false;
      }
      Arrays.sort(thisSfArray, new SFSortByEnd()); // probably already sorted
 -                                                   // like this
 +                                                 // like this
      Arrays.sort(thatSfArray, new SFSortByEnd()); // probably already sorted
 -                                                   // like this
 -    for (int i=0; i < thisSfArray.length; i++) {
 +                                                 // like this
 +    for (int i = 0; i < thisSfArray.length; i++)
 +    {
        SequenceFeature thisSf = thisSfArray[i];
        SequenceFeature thatSf = thatSfArray[i];
 -      if (compareType) {
 -        if (thisSf.getType() == null || thatSf.getType() == null) {
 -          if (thisSf.getType() == null && thatSf.getType() == null) {
 +      if (compareType)
 +      {
 +        if (thisSf.getType() == null || thatSf.getType() == null)
 +        {
 +          if (thisSf.getType() == null && thatSf.getType() == null)
 +          {
              continue;
 -          } else {
 +          }
 +          else
 +          {
              return false;
            }
          }
 -        if (! thisSf.getType().equals(thatSf.getType())) {
 +        if (!thisSf.getType().equals(thatSf.getType()))
 +        {
            return false;
          }
        }
  
    public static final int LINE_GRAPH = 2;
  
+   public static final int CUSTOMRENDERER = 4;
    public boolean belowAlignment = true;
  
    public SequenceGroup groupRef = null;
          // annotations[i].secondaryStructure + "'");
          // TODO: 2.8.2 should this ss symbol validation check be a function in
          // RNA/ResidueProperties ?
 +        // allow for DSSP extended code:
 +        // https://www.wikidoc.org/index.php/Secondary_structure#The_DSSP_code
 +        // GHITEBS as well as C and X (for missing?)
          if (annotations[i].secondaryStructure == '('
                  || annotations[i].secondaryStructure == '['
                  || annotations[i].secondaryStructure == '<'
                  || annotations[i].secondaryStructure == '{'
                  || annotations[i].secondaryStructure == 'A'
 -                || annotations[i].secondaryStructure == 'B'
 -                || annotations[i].secondaryStructure == 'C'
 +                // || annotations[i].secondaryStructure == 'B'
 +                // || annotations[i].secondaryStructure == 'C'
                  || annotations[i].secondaryStructure == 'D'
                  // || annotations[i].secondaryStructure == 'E' // ambiguous on
                  // its own -- already checked above
                  || annotations[i].secondaryStructure == 'F'
 -                || annotations[i].secondaryStructure == 'G'
 +                // || annotations[i].secondaryStructure == 'G'
                  // || annotations[i].secondaryStructure == 'H' // ambiguous on
                  // its own -- already checked above
 -                || annotations[i].secondaryStructure == 'I'
 +                // || annotations[i].secondaryStructure == 'I'
                  || annotations[i].secondaryStructure == 'J'
                  || annotations[i].secondaryStructure == 'K'
                  || annotations[i].secondaryStructure == 'L'
                  || annotations[i].secondaryStructure == 'P'
                  || annotations[i].secondaryStructure == 'Q'
                  || annotations[i].secondaryStructure == 'R'
 -                || annotations[i].secondaryStructure == 'S'
 -                || annotations[i].secondaryStructure == 'T'
 +                // || annotations[i].secondaryStructure == 'S'
 +                // || annotations[i].secondaryStructure == 'T'
                  || annotations[i].secondaryStructure == 'U'
                  || annotations[i].secondaryStructure == 'V'
                  || annotations[i].secondaryStructure == 'W'
 -                || annotations[i].secondaryStructure == 'X'
 +                // || annotations[i].secondaryStructure == 'X'
                  || annotations[i].secondaryStructure == 'Y'
                  || annotations[i].secondaryStructure == 'Z')
          {
                        : annotations[index + offset].displayCharacter == null
                                || annotations[index
                                        + offset].displayCharacter
 -                                              .length() == 0
 -                                                      ? annotations[index
 -                                                              + offset].secondaryStructure
 -                                                      : annotations[index
 -                                                              + offset].displayCharacter
 -                                                                      .charAt(0));
 +                                      .length() == 0
 +                                              ? annotations[index
 +                                                      + offset].secondaryStructure
 +                                              : annotations[index
 +                                                      + offset].displayCharacter
 +                                                      .charAt(0));
      }
  
      @Override
@@@ -355,8 -355,7 +355,8 @@@ public class AnnotationPanel extends JP
          @Override
          public void colourSelected(Color c)
          {
 -          HiddenColumns hiddenColumns = av.getAlignment().getHiddenColumns();
 +          HiddenColumns hiddenColumns = av.getAlignment()
 +                  .getHiddenColumns();
            for (int index : av.getColumnSelection().getSelected())
            {
              if (hiddenColumns.isVisible(index))
                }
                fAnot[index].colour = c;
              }
 -        }};
 +          }
 +        };
        };
 -      JalviewColourChooser.showColourChooser(this,
 -              title, Color.black, listener);
 +      JalviewColourChooser.showColourChooser(this, title, Color.black,
 +              listener);
      }
      else
      // HELIX, SHEET or STEM
          {
            activeRow = i;
          }
-         else if (aa[i].graph > 0)
+         else if (aa[i].graph != 0)
          {
            /*
             * we have clicked on a resizable graph annotation
    }
  
    private volatile boolean imageFresh = false;
 -  private Rectangle visibleRect = new Rectangle(), clipBounds = new Rectangle();
 +
 +  private Rectangle visibleRect = new Rectangle(),
 +          clipBounds = new Rectangle();
  
    /**
     * DOCUMENT ME!
    @Override
    public void paintComponent(Graphics g)
    {
 -        
 -        // BH: note that this method is generally recommended to 
 -        // call super.paintComponent(g). Otherwise, the children of this
 -        // component will not be rendered. That is not needed here 
 -        // because AnnotationPanel does not have any children. It is
 -        // just a JPanel contained in a JViewPort. 
 +
 +    // BH: note that this method is generally recommended to
 +    // call super.paintComponent(g). Otherwise, the children of this
 +    // component will not be rendered. That is not needed here
 +    // because AnnotationPanel does not have any children. It is
 +    // just a JPanel contained in a JViewPort.
  
      computeVisibleRect(visibleRect);
 -    
 +
      g.setColor(Color.white);
      g.fillRect(0, 0, visibleRect.width, visibleRect.height);
  
      if (image != null)
      {
 -      // BH 2018 optimizing generation of new Rectangle().
 -      if (fastPaint || (visibleRect.width != (clipBounds = g.getClipBounds(clipBounds)).width)
 -            || (visibleRect.height != clipBounds.height))
 +      // BH 2018 optimizing generation of new Rectangle().
 +      if (fastPaint
 +              || (visibleRect.width != (clipBounds = g
 +                      .getClipBounds(clipBounds)).width)
 +              || (visibleRect.height != clipBounds.height))
        {
  
 -        
 -        g.drawImage(image, 0, 0, this);
 +        g.drawImage(image, 0, 0, this);
          fastPaint = false;
          return;
        }
        gg.setColor(Color.white);
        gg.fillRect(0, 0, imgWidth, image.getHeight());
        imageFresh = true;
 -    } else {
 -        gg = (Graphics2D) image.getGraphics();
 +    }
 +    else
 +    {
 +      gg = (Graphics2D) image.getGraphics();
  
      }
 -    
 +
      drawComponent(gg, av.getRanges().getStartRes(),
              av.getRanges().getEndRes() + 1);
      gg.dispose();
  
      Graphics2D gg = (Graphics2D) image.getGraphics();
  
 -    gg.copyArea(0, 0, imgWidth, getHeight(),
 -            -horizontal * av.getCharWidth(), 0);
 -
 -    if (horizontal > 0) // scrollbar pulled right, image to the left
 -    {
 -      transX = (er - sr - horizontal) * av.getCharWidth();
 -      sr = er - horizontal;
 -    }
 -    else if (horizontal < 0)
 -    {
 -      er = sr - horizontal;
 +    if (imgWidth>Math.abs(horizontal*av.getCharWidth())) {
 +      //scroll is less than imgWidth away so can re-use buffered graphics
 +      gg.copyArea(0, 0, imgWidth, getHeight(),
 +              -horizontal * av.getCharWidth(), 0);
 +      
 +      if (horizontal > 0) // scrollbar pulled right, image to the left
 +      {
 +        transX = (er - sr - horizontal) * av.getCharWidth();
 +        sr = er - horizontal;
 +      }
 +      else if (horizontal < 0)
 +      {
 +        er = sr - horizontal;
 +      }
      }
 -
      gg.translate(transX, 0);
  
      drawComponent(gg, sr, er);
      gg.translate(-transX, 0);
  
      gg.dispose();
 -    
 +
      fastPaint = true;
  
      // Call repaint on alignment panel so that repaints from other alignment
      ap = null;
      image = null;
      fadedImage = null;
 -//    gg = null;
 +    // gg = null;
      _mwl = null;
  
      /*
@@@ -28,6 -28,7 +28,7 @@@ import jalview.datamodel.Alignment
  import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.AlignmentView;
+ import jalview.datamodel.SeqDistanceContactMatrix;
  import jalview.datamodel.PDBEntry.Type;
  import jalview.datamodel.SequenceI;
  import jalview.ext.jmol.JmolParser;
@@@ -154,10 -155,9 +155,10 @@@ public class AppletFormatAdapte
    {
      return readFile(null, file, sourceType, fileFormat);
    }
 -  
 -  public AlignmentI readFile(File selectedFile, String file, DataSourceType sourceType,
 -          FileFormatI fileFormat) throws IOException
 +
 +  public AlignmentI readFile(File selectedFile, String file,
 +          DataSourceType sourceType, FileFormatI fileFormat)
 +          throws IOException
    {
  
      this.selectedFile = selectedFile;
          if (isParseWithJMOL)
          {
            // needs a File option
 -          alignFile = new JmolParser(selectedFile == null ? inFile : selectedFile, sourceType);
 +          alignFile = new JmolParser(
 +                  selectedFile == null ? inFile : selectedFile, sourceType);
          }
          else
          {
          ((StructureFile) alignFile).setDbRefType(
                  FileFormat.PDB.equals(fileFormat) ? Type.PDB : Type.MMCIF);
        }
 -      else if (selectedFile != null) {
 -        alignFile = fileFormat.getReader(new FileParse(selectedFile, sourceType));
 -      } else 
 +      else if (selectedFile != null)
 +      {
 +        alignFile = fileFormat
 +                .getReader(new FileParse(selectedFile, sourceType));
 +      }
 +      else
        {
          // alignFile = fileFormat.getAlignmentFile(inFile, sourceType);
          alignFile = fileFormat.getReader(new FileParse(inFile, sourceType));
      return null;
    }
  
 -
    /**
     * Determines the protocol (i.e DataSourceType.{FILE|PASTE|URL}) for the input
     * data
     * 
     * BH 2018 allows File or String, and can return RELATIVE_URL
     *
 -   * @param dataObject File or String
 +   * @param dataObject
 +   *          File or String
     * @return the protocol for the input data
     */
    public static DataSourceType checkProtocol(Object dataObject)
    {
 -    if(dataObject instanceof File)
 +    if (dataObject instanceof File)
      {
        return DataSourceType.FILE;
      }
 -    
 +
      String data = dataObject.toString();
      DataSourceType protocol = DataSourceType.PASTE;
      String ft = data.toLowerCase(Locale.ROOT).trim();
@@@ -30,6 -30,8 +30,8 @@@ import jalview.datamodel.Annotation
  import jalview.datamodel.ColumnSelection;
  import jalview.datamodel.HiddenColumns;
  import jalview.datamodel.ProfilesI;
+ import jalview.renderer.api.AnnotationRendererFactoryI;
+ import jalview.renderer.api.AnnotationRowRendererI;
  import jalview.schemes.ColourSchemeI;
  import jalview.schemes.NucleotideColourScheme;
  import jalview.schemes.ResidueProperties;
@@@ -155,6 -157,7 +157,7 @@@ public class AnnotationRendere
      hStrucConsensus = null;
      fadedImage = null;
      annotationPanel = null;
+     rendererFactoryI = null;
    }
  
    void drawStemAnnot(Graphics g, Annotation[] row_annotations, int lastSSX,
        useClip = false;
      }
  
+     rendererFactoryI = AnnotationRendererFactory.getRendererFactory();
      updateFromAlignViewport(av);
    }
  
      return null;
    }
  
+   boolean rna = false;
+   private AnnotationRendererFactoryI rendererFactoryI;
    /**
     * Render the annotation rows associated with an alignment.
     * 
      boolean validRes = false;
      boolean validEnd = false;
      boolean labelAllCols = false;
 -//    boolean centreColLabels;
 -//    boolean centreColLabelsDef = av.isCentreColumnLabels();
 +    // boolean centreColLabels;
 +    // boolean centreColLabelsDef = av.isCentreColumnLabels();
      boolean scaleColLabel = false;
      final AlignmentAnnotation consensusAnnot = av
              .getAlignmentConsensusAnnotation();
        {
          continue;
        }
 -//      centreColLabels = row.centreColLabels || centreColLabelsDef;
 +      // centreColLabels = row.centreColLabels || centreColLabelsDef;
        labelAllCols = row.showAllColLabels;
        scaleColLabel = row.scaleColLabel;
        lastSS = ' ';
                      row.graphMin, row.graphMax, y, renderHistogram,
                      renderProfile, normaliseProfile);
            }
+           else
+           {
+             AnnotationRowRendererI renderer = rendererFactoryI
+                     .getRendererFor(row);
+             if (renderer != null)
+             {
+               renderer.renderRow(g, charWidth, charHeight,
+                       hasHiddenColumns, av, hiddenColumns, columnSelection,
+                       row, row_annotations, startRes, endRes, row.graphMin,
+                       row.graphMax, y);
+             }
+             if (debugRedraw)
+             {
+               if (renderer == null)
+               {
+                 System.err.println("No renderer found for "
+                         + row.toString());
+               }
+               else
+               {
+                 System.err.println("rendered with "
+                         + renderer.getClass().toString());
+               }
+             }
+           }
          }
        }
        else
@@@ -37,6 -37,7 +37,7 @@@ import jalview.datamodel.AlignmentI
  import jalview.datamodel.AlignmentView;
  import jalview.datamodel.Annotation;
  import jalview.datamodel.ColumnSelection;
+ import jalview.datamodel.ContactListI;
  import jalview.datamodel.HiddenColumns;
  import jalview.datamodel.HiddenSequences;
  import jalview.datamodel.ProfilesI;
@@@ -664,7 -665,8 +665,7 @@@ public abstract class AlignmentViewpor
           * retain any colour thresholds per group while
           * changing choice of colour scheme (JAL-2386)
           */
 -        sg.setColourScheme(
 -                cs == null ? null : cs.getInstance(this, sg));
 +        sg.setColourScheme(cs == null ? null : cs.getInstance(this, sg));
          if (cs != null)
          {
            sg.getGroupColourScheme().alignmentChanged(sg,
          AlignmentAnnotation clone = new AlignmentAnnotation(annot);
          if (selectedOnly && selectionGroup != null)
          {
 -          clone.makeVisibleAnnotation(
 -                  selectionGroup.getStartRes(), selectionGroup.getEndRes(),
 -                  alignment.getHiddenColumns());
 +          clone.makeVisibleAnnotation(selectionGroup.getStartRes(),
 +                  selectionGroup.getEndRes(), alignment.getHiddenColumns());
          }
          else
          {
      return searchResults;
    }
  
+   @Override
+   public ContactListI getContactList(AlignmentAnnotation _aa, int column)
+   {
+     return alignment.getContactListFor(_aa, column);
+   }
    /**
     * get the consensus sequence as displayed under the PID consensus annotation
     * row.
    }
  
    @Override
 -  public AlignmentExportData getAlignExportData(AlignExportSettingsI options)
 +  public AlignmentExportData getAlignExportData(
 +          AlignExportSettingsI options)
    {
      AlignmentI alignmentToExport = null;
      String[] omitHidden = null;
              omitHidden, alignmentStartEnd);
      return ed;
    }
 -  
 +
    /**
     * flag set to indicate if structure views might be out of sync with sequences
     * in the alignment
@@@ -21,6 -21,7 +21,7 @@@
  package jalview.ws;
  
  import jalview.ext.ensembl.EnsemblGene;
+ import jalview.ws.dbsources.EBIAlfaFold;
  import jalview.ws.dbsources.EmblCdsSource;
  import jalview.ws.dbsources.EmblSource;
  import jalview.ws.dbsources.Pdb;
@@@ -61,11 -62,11 +62,12 @@@ public class SequenceFetcher extends AS
      addDBRefSourceImpl(PfamFull.class);
      addDBRefSourceImpl(PfamSeed.class);
      addDBRefSourceImpl(RfamSeed.class);
+     addDBRefSourceImpl(EBIAlfaFold.class);
    }
  
    /**
 -   * return an ordered list of database sources excluding alignment only databases
 +   * return an ordered list of database sources excluding alignment only
 +   * databases
     */
    public String[] getNonAlignmentSources()
    {
  package jalview.ws.dbsources;
  
  import jalview.api.FeatureSettingsModelI;
+ import jalview.bin.Cache;
+ import jalview.bin.Console;
  import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.AlignmentI;
+ import jalview.datamodel.ContactMatrix;
+ import jalview.datamodel.ContactMatrixI;
  import jalview.datamodel.DBRefEntry;
  import jalview.datamodel.DBRefSource;
  import jalview.datamodel.PDBEntry;
@@@ -36,15 -40,23 +40,23 @@@ import jalview.io.FileFormat
  import jalview.io.FileFormatI;
  import jalview.io.FormatAdapter;
  import jalview.io.PDBFeatureSettings;
+ import jalview.javascript.json.JSON;
  import jalview.structure.StructureImportSettings;
  import jalview.util.HttpUtils;
  import jalview.util.MessageManager;
+ import jalview.util.Platform;
+ import jalview.ws.datamodel.alphafold.PAEContactMatrix;
  import jalview.ws.ebi.EBIFetchClient;
  import jalview.ws.utils.UrlDownloadClient;
  
+ import java.io.BufferedReader;
  import java.io.File;
+ import java.io.FileInputStream;
  import java.util.ArrayList;
  import java.util.List;
+ import java.util.Map;
+ import org.jmol.adapter.readers.simple.JSONReader;
  
  import com.stevesoft.pat.Regex;
  
@@@ -84,7 -96,7 +96,7 @@@ public class EBIAlfaFold extends EbiFil
    @Override
    public Regex getAccessionValidator()
    {
 -    Regex validator =  new Regex("(AF-[A-Z]+[0-9]+[A-Z0-9]+-F1)");
 +    Regex validator = new Regex("(AF-[A-Z]+[0-9]+[A-Z0-9]+-F1)");
      validator.setIgnoreCase(true);
      return validator;
    }
      return "https://alphafold.ebi.ac.uk/files/" + id + "-model_v1.cif";
    }
  
+   public static String getAlphaFoldPaeDownloadUrl(String id)
+   {
+     return "https://alphafold.ebi.ac.uk/files/" + id
+             + "-predicted_aligned_error_v1.json";
+   }
    /*
     * (non-Javadoc)
     * 
    {
      return getSequenceRecords(queries, null);
    }
 -  public AlignmentI getSequenceRecords(String queries, String retrievalUrl) throws Exception
 +
 +  public AlignmentI getSequenceRecords(String queries, String retrievalUrl)
 +          throws Exception
    {
      AlignmentI pdbAlignment = null;
      String chain = null;
      if (!isValidReference(id))
      {
        System.err.println(
-               "(AFClient) Ignoring invalid pdb query: '" + id + "'");
+               "(AFClient) Ignoring invalid alphafold query: '" + id + "'");
        stopQuery();
        return null;
      }
      try
      {
        File tmpFile = File.createTempFile(id, ".cif");
+       Console.debug("Retrieving structure file for "+id+" from "+alphaFoldCif);
        UrlDownloadClient.download(alphaFoldCif, tmpFile);
 -      
 +
        // may not need this check ?
        file = tmpFile.getAbsolutePath();
        if (file == null)
          return null;
        }
  
 -      pdbAlignment = importDownloadedStructureFromUrl(alphaFoldCif, tmpFile, id, chain, getDbSource(),getDbVersion());
 -      
 +      pdbAlignment = importDownloadedStructureFromUrl(alphaFoldCif, tmpFile,
 +              id, chain, getDbSource(), getDbVersion());
  
        if (pdbAlignment == null || pdbAlignment.getHeight() < 1)
        {
                  { id, ((chain == null) ? "' '" : chain) }));
        }
  
+       // import PAE as contact matrix - assume this will work if there was a
+       // model
+       File pae = File.createTempFile(id, "pae_json");
+       String paeURL = getAlphaFoldPaeDownloadUrl(id);
+       
+       if (retrievalUrl!=null) {
+         // manufacture the PAE url from a url like ...-model-vN.cif
+         paeURL = retrievalUrl.replace("model","predicted_aligned_error").replace(".cif",".json");
+       }
+       Console.debug("Downloading pae from " + paeURL
+               + " to " + pae.toString() + "");
+       try {
+         UrlDownloadClient.download(paeURL, pae);        
+       if (!importPaeJSONAsContactMatrix(pdbAlignment, pae))
+       {
+         Console.warn("Couln't import contact matrix from " + paeURL
+                 + " (stored in " + pae.toString() + ")");
+       }
+       } catch (Exception pae_ex) {
+         Console.debug("Couldn't download PAE",pae_ex);
+       }
      } catch (Exception ex) // Problem parsing PDB file
      {
        stopQuery();
      return pdbAlignment;
    }
  
+   private boolean importPaeJSONAsContactMatrix(AlignmentI pdbAlignment,
+           File pae) throws Exception
+   {
+     FileInputStream pae_input = new FileInputStream(pae);
+     List<Object> pae_obj = (List<Object>) Platform
+             .parseJSON(pae_input);
+     if (pae_obj == null)
+     {
+       return false;
+     }
+     ContactMatrixI matrix = new PAEContactMatrix(
+             pdbAlignment.getSequenceAt(0), (Map<String, Object>)pae_obj.get(0));
+     pdbAlignment.getSequenceAt(0).addAlignmentAnnotation(pdbAlignment.addContactList(matrix));
+     return true;
+   }
    /**
 -   * general purpose structure importer - designed to yield alignment useful for transfer of annotation to associated sequences
 +   * general purpose structure importer - designed to yield alignment useful for
 +   * transfer of annotation to associated sequences
 +   * 
     * @param alphaFoldCif
     * @param tmpFile
     * @param id
     * @return
     * @throws Exception
     */
 -  public static AlignmentI importDownloadedStructureFromUrl(String alphaFoldCif,
 -          File tmpFile, String id, String chain, String dbSource, String dbVersion) throws Exception
 +  public static AlignmentI importDownloadedStructureFromUrl(
 +          String alphaFoldCif, File tmpFile, String id, String chain,
 +          String dbSource, String dbVersion) throws Exception
    {
      String file = tmpFile.getAbsolutePath();
      // todo get rid of Type and use FileFormatI instead?
              // dbentry.setMap()
              pdbcs.addDBRef(dbentry);
              // update any feature groups
 -            List<SequenceFeature> allsf = pdbcs.getFeatures().getAllFeatures();
 +            List<SequenceFeature> allsf = pdbcs.getFeatures()
 +                    .getAllFeatures();
              List<SequenceFeature> newsf = new ArrayList<SequenceFeature>();
 -            if (allsf!=null && allsf.size()>0)
 +            if (allsf != null && allsf.size() > 0)
              {
 -              for (SequenceFeature f:allsf)
 +              for (SequenceFeature f : allsf)
                {
                  if (file.equals(f.getFeatureGroup()))
                  {
 -                  f = new SequenceFeature(f, f.type, f.begin, f.end, id, f.score);
 +                  f = new SequenceFeature(f, f.type, f.begin, f.end, id,
 +                          f.score);
                  }
                  newsf.add(f);
                }
    @Override
    public String getTestQuery()
    {
-     return "1QIP";
+     return "AF-O15552-F1";
    }
  
    @Override
    public String getDbName()
    {
-     return "PDB"; // getDbSource();
+     return "ALPHAFOLD"; // getDbSource();
    }
  
    @Override
@@@ -65,7 -65,6 +65,7 @@@ public class SeqPanelTes
      JvOptionPane.setInteractiveMode(false);
      JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
    }
 +
    @Test(groups = "Functional")
    public void testSetStatusReturnsNearestResiduePosition()
    {
      AlignmentI visAl = alignFrame.getViewport().getAlignment();
  
      // Test either side of gap
 -    assertEquals(
 -            alignFrame.alignPanel.getSeqPanel().setStatusMessage(
 -                    visAl.getSequenceAt(1), 1, 1), 2);
 +    assertEquals(alignFrame.alignPanel.getSeqPanel()
 +            .setStatusMessage(visAl.getSequenceAt(1), 1, 1), 2);
      assertEquals(((JLabel) PA.getValue(alignFrame, "statusBar")).getText(),
              "Sequence 2 ID: Seq2 Residue: ALA (2)");
 -    assertEquals(
 -            alignFrame.alignPanel.getSeqPanel().setStatusMessage(
 -                    visAl.getSequenceAt(1), 4, 1), 3);
 +    assertEquals(alignFrame.alignPanel.getSeqPanel()
 +            .setStatusMessage(visAl.getSequenceAt(1), 4, 1), 3);
      assertEquals(((JLabel) PA.getValue(alignFrame, "statusBar")).getText(),
              "Sequence 2 ID: Seq2 Residue: GLU (3)");
      // no status message at a gap, returns next residue position to the right
 -    assertEquals(
 -            alignFrame.alignPanel.getSeqPanel().setStatusMessage(
 -                    visAl.getSequenceAt(1), 2, 1), 3);
 +    assertEquals(alignFrame.alignPanel.getSeqPanel()
 +            .setStatusMessage(visAl.getSequenceAt(1), 2, 1), 3);
      assertEquals(((JLabel) PA.getValue(alignFrame, "statusBar")).getText(),
              "Sequence 2 ID: Seq2");
 -    assertEquals(
 -            alignFrame.alignPanel.getSeqPanel().setStatusMessage(
 -                    visAl.getSequenceAt(1), 3, 1), 3);
 +    assertEquals(alignFrame.alignPanel.getSeqPanel()
 +            .setStatusMessage(visAl.getSequenceAt(1), 3, 1), 3);
      assertEquals(((JLabel) PA.getValue(alignFrame, "statusBar")).getText(),
              "Sequence 2 ID: Seq2");
    }
              al.getHeight());
      AlignmentI visAl = alignFrame.getViewport().getAlignment();
  
 -    assertEquals(
 -            alignFrame.alignPanel.getSeqPanel().setStatusMessage(
 -                    visAl.getSequenceAt(1), 1, 1), 2);
 +    assertEquals(alignFrame.alignPanel.getSeqPanel()
 +            .setStatusMessage(visAl.getSequenceAt(1), 1, 1), 2);
      assertEquals(((JLabel) PA.getValue(alignFrame, "statusBar")).getText(),
              "Sequence 2 ID: Seq2 Residue: B (2)");
    }
      assertNull(SeqPanel.getEditStatusMessage(edit));
  
      SequenceI[] seqs = new SequenceI[] { new Sequence("a", "b") };
 -    
 +
      // 1 gap
      edit.addEdit(edit.new Edit(Action.INSERT_GAP, seqs, 1, 1, '-'));
      String expected = MessageManager.formatMessage("label.insert_gap", "1");
    {
      EditCommand edit = new EditCommand(); // empty
      SequenceI[] seqs = new SequenceI[] { new Sequence("a", "b") };
 -    
 +
      // 1 gap inserted, balanced by 1 delete
      Edit e1 = edit.new Edit(Action.INSERT_GAP, seqs, 1, 1, '-');
      edit.addEdit(e1);
      edit.addEdit(e2);
      String expected = MessageManager.formatMessage("label.insert_gap", "1");
      assertEquals(SeqPanel.getEditStatusMessage(edit), expected);
 -  
 +
      // 2 more gaps makes +3
      Edit e3 = edit.new Edit(Action.INSERT_GAP, seqs, 1, 2, '-');
      edit.addEdit(e3);
      edit.addEdit(e4);
      expected = MessageManager.formatMessage("label.insert_gaps", "3");
      assertEquals(SeqPanel.getEditStatusMessage(edit), expected);
 -  
 +
      // 2 deletes makes + 1
      Edit e5 = edit.new Edit(Action.DELETE_GAP, seqs, 1, 2, '-');
      edit.addEdit(e5);
      edit.addEdit(e6);
      expected = MessageManager.formatMessage("label.insert_gap", "1");
      assertEquals(SeqPanel.getEditStatusMessage(edit), expected);
 -  
 +
      // 1 more delete makes 0 - no text
      Edit e7 = edit.new Edit(Action.DELETE_GAP, seqs, 1, 1, '-');
      edit.addEdit(e7);
      edit.addEdit(e8);
      expected = MessageManager.formatMessage("label.insert_gaps", "2");
      assertNull(SeqPanel.getEditStatusMessage(edit));
 -  
 +
      // 1 more delete makes 1 delete
      Edit e9 = edit.new Edit(Action.DELETE_GAP, seqs, 1, 1, '-');
      edit.addEdit(e9);
      edit.addEdit(e10);
      expected = MessageManager.formatMessage("label.delete_gap", "1");
      assertEquals(SeqPanel.getEditStatusMessage(edit), expected);
 -  
 +
      // 2 more deletes makes 3 deletes
      Edit e11 = edit.new Edit(Action.DELETE_GAP, seqs, 1, 2, '-');
      edit.addEdit(e11);
      /*
       * mouse at top left of unwrapped panel
       */
 -    MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y,
 -            0, 0, 0, false, 0);
 +    MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0,
 +            x, y, 0, 0, 0, false, 0);
      MousePos pos = testee.findMousePosition(evt);
      assertEquals(pos.column, 0);
      assertEquals(pos.seqIndex, 0);
      final int charHeight = av.getCharHeight();
      final int charWidth = av.getCharWidth();
      final int alignmentHeight = av.getAlignment().getHeight();
 -    
 +
      // sanity checks:
      assertTrue(charHeight > 0);
      assertTrue(charWidth > 0);
      assertTrue(alignFrame.alignPanel.getSeqPanel().getWidth() > 0);
 -  
 +
      SeqPanel testee = alignFrame.alignPanel.getSeqPanel();
      int x = 0;
      int y = 0;
 -  
 +
      /*
       * mouse at top left of wrapped panel; there is a gap of charHeight
       * above the alignment
       */
 -    MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y,
 -            0, 0, 0, false, 0);
 +    MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0,
 +            x, y, 0, 0, 0, false, 0);
      MousePos pos = testee.findMousePosition(evt);
      assertEquals(pos.column, 0);
      assertEquals(pos.seqIndex, -1); // above sequences
       * cursor at bottom of gap above
       */
      y = charHeight - 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, -1);
      assertEquals(pos.annotationIndex, -1);
       * cursor over top of first sequence
       */
      y = charHeight;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, 0);
      assertEquals(pos.annotationIndex, -1);
       * cursor at bottom of first sequence
       */
      y = 2 * charHeight - 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, 0);
      assertEquals(pos.annotationIndex, -1);
       * cursor at top of second sequence
       */
      y = 2 * charHeight;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, 1);
      assertEquals(pos.annotationIndex, -1);
       * cursor at bottom of second sequence
       */
      y = 3 * charHeight - 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, 1);
      assertEquals(pos.annotationIndex, -1);
       * cursor at bottom of last sequence
       */
      y = charHeight * (1 + alignmentHeight) - 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, alignmentHeight - 1);
      assertEquals(pos.annotationIndex, -1);
       * method reports index of nearest sequence above
       */
      y += 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, alignmentHeight - 1);
      assertEquals(pos.annotationIndex, -1);
       * cursor still in the gap above annotations, now at the bottom of it
       */
      y += SeqCanvas.SEQS_ANNOTATION_GAP - 1; // 3-1 = 2
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, alignmentHeight - 1);
      assertEquals(pos.annotationIndex, -1);
         * cursor at the top of the n'th annotation  
         */
        y += 1;
 -      evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -              false, 0);
 +      evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0,
 +              0, 0, false, 0);
        pos = testee.findMousePosition(evt);
        assertEquals(pos.seqIndex, alignmentHeight - 1);
        assertEquals(pos.annotationIndex, n); // over n'th annotation
         * cursor at the bottom of the n'th annotation  
         */
        y += annotationRows[n].height - 1;
 -      evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -              false, 0);
 +      evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0,
 +              0, 0, false, 0);
        pos = testee.findMousePosition(evt);
        assertEquals(pos.seqIndex, alignmentHeight - 1);
        assertEquals(pos.annotationIndex, n);
       * cursor in gap between wrapped widths  
       */
      y += 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, -1);
      assertEquals(pos.annotationIndex, -1);
       * cursor at bottom of gap between wrapped widths  
       */
      y += charHeight - 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, -1);
      assertEquals(pos.annotationIndex, -1);
       * cursor at top of first sequence, second wrapped width  
       */
      y += 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, 0);
      assertEquals(pos.annotationIndex, -1);
      final int charHeight = av.getCharHeight();
      final int charWidth = av.getCharWidth();
      final int alignmentHeight = av.getAlignment().getHeight();
 -    
 +
      // sanity checks:
      assertTrue(charHeight > 0);
      assertTrue(charWidth > 0);
      assertTrue(alignFrame.alignPanel.getSeqPanel().getWidth() > 0);
 -  
 +
      SeqPanel testee = alignFrame.alignPanel.getSeqPanel();
      int x = 0;
      int y = 0;
 -  
 +
      /*
       * mouse at top left of wrapped panel; there is a gap of charHeight
       * above the alignment
       */
 -    MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y,
 -            0, 0, 0, false, 0);
 +    MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0,
 +            x, y, 0, 0, 0, false, 0);
      MousePos pos = testee.findMousePosition(evt);
      assertEquals(pos.column, 0);
      assertEquals(pos.seqIndex, -1); // above sequences
      assertEquals(pos.annotationIndex, -1);
 -  
 +
      /*
       * cursor at bottom of gap above
       * two charHeights including scale panel
       */
      y = 2 * charHeight - 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, -1);
      assertEquals(pos.annotationIndex, -1);
 -  
 +
      /*
       * cursor over top of first sequence
       */
      y += 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, 0);
      assertEquals(pos.annotationIndex, -1);
 -  
 +
      /*
       * cursor at bottom of first sequence
       */
      y += charHeight - 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, 0);
      assertEquals(pos.annotationIndex, -1);
 -  
 +
      /*
       * cursor at top of second sequence
       */
      y += 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, 1);
      assertEquals(pos.annotationIndex, -1);
 -  
 +
      /*
       * cursor at bottom of second sequence
       */
      y += charHeight - 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, 1);
      assertEquals(pos.annotationIndex, -1);
 -  
 +
      /*
       * cursor at bottom of last sequence
       * (scale + gap + sequences)
       */
      y = charHeight * (2 + alignmentHeight) - 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, alignmentHeight - 1);
      assertEquals(pos.annotationIndex, -1);
 -  
 +
      /*
       * cursor below sequences, in 3-pixel gap above annotations
       */
      y += 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, alignmentHeight - 1);
      assertEquals(pos.annotationIndex, -1);
 -  
 +
      /*
       * cursor still in the gap above annotations, now at the bottom of it
       * method reports index of nearest sequence above  
       */
      y += SeqCanvas.SEQS_ANNOTATION_GAP - 1; // 3-1 = 2
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, alignmentHeight - 1);
      assertEquals(pos.annotationIndex, -1);
 -  
 -    AlignmentAnnotation[] annotationRows = av.getAlignment().getAlignmentAnnotation();
 +
 +    AlignmentAnnotation[] annotationRows = av.getAlignment()
 +            .getAlignmentAnnotation();
      for (int n = 0; n < annotationRows.length; n++)
      {
        /*
         * cursor at the top of the n'th annotation  
         */
        y += 1;
 -      evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -              false, 0);
 +      evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0,
 +              0, 0, false, 0);
        pos = testee.findMousePosition(evt);
        assertEquals(pos.seqIndex, alignmentHeight - 1);
        assertEquals(pos.annotationIndex, n); // over n'th annotation
         * cursor at the bottom of the n'th annotation  
         */
        y += annotationRows[n].height - 1;
 -      evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -              false, 0);
 +      evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0,
 +              0, 0, false, 0);
        pos = testee.findMousePosition(evt);
        SeqCanvas sc = testee.seqCanvas;
        assertEquals(pos.seqIndex, alignmentHeight - 1,
                        sc.wrappedSpaceAboveAlignment));
        assertEquals(pos.annotationIndex, n);
      }
 -  
 +
      /*
       * cursor in gap between wrapped widths  
       */
      y += 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, -1);
      assertEquals(pos.annotationIndex, -1);
 -  
 +
      /*
       * cursor at bottom of gap between wrapped widths  
       */
      y += charHeight - 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, -1);
      assertEquals(pos.annotationIndex, -1);
 -  
 +
      /*
       * cursor at top of scale, second wrapped width  
       */
      y += 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, -1);
      assertEquals(pos.annotationIndex, -1);
       * cursor at bottom of scale, second wrapped width  
       */
      y += charHeight - 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, -1);
      assertEquals(pos.annotationIndex, -1);
       * cursor at top of first sequence, second wrapped width  
       */
      y += 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, 0);
      assertEquals(pos.annotationIndex, -1);
      final int charHeight = av.getCharHeight();
      final int charWidth = av.getCharWidth();
      final int alignmentHeight = av.getAlignment().getHeight();
 -    
 +
      // sanity checks:
      assertTrue(charHeight > 0);
      assertTrue(charWidth > 0);
      assertTrue(alignFrame.alignPanel.getSeqPanel().getWidth() > 0);
 -  
 +
      SeqPanel testee = alignFrame.alignPanel.getSeqPanel();
      int x = 0;
      int y = 0;
 -  
 +
      /*
       * mouse at top left of wrapped panel; there is a gap of charHeight
       * above the alignment
       */
 -    MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y,
 -            0, 0, 0, false, 0);
 +    MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0,
 +            x, y, 0, 0, 0, false, 0);
      MousePos pos = testee.findMousePosition(evt);
      assertEquals(pos.column, 0);
      assertEquals(pos.seqIndex, -1); // above sequences
      assertEquals(pos.annotationIndex, -1);
 -  
 +
      /*
       * cursor over top of first sequence
       */
      y = charHeight;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, 0);
      assertEquals(pos.annotationIndex, -1);
       * cursor at bottom of last sequence
       */
      y = charHeight * (1 + alignmentHeight) - 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, alignmentHeight - 1);
      assertEquals(pos.annotationIndex, -1);
 -  
 +
      /*
       * cursor below sequences, at top of charHeight gap between widths
       */
      y += 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, -1);
      assertEquals(pos.annotationIndex, -1);
 -  
 +
      /*
       * cursor below sequences, at top of charHeight gap between widths
       */
      y += charHeight - 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, -1);
      assertEquals(pos.annotationIndex, -1);
 -  
 +
      /*
       * cursor at the top of the first sequence, second width  
       */
      y += 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, 0);
      assertEquals(pos.annotationIndex, -1);
      /*
       * mouse at top left of unwrapped panel
       */
 -    MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0,
 -            0, 0, 0, false, 0);
 +    MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0,
 +            x, 0, 0, 0, 0, false, 0);
      assertEquals(testee.findColumn(evt), 0);
 -    
 +
      /*
       * not quite one charWidth across
       */
 -    x = charWidth-1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0,
 -            0, 0, 0, false, 0);
 +    x = charWidth - 1;
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0,
 +            0, false, 0);
      assertEquals(testee.findColumn(evt), 0);
  
      /*
       * one charWidth across
       */
      x = charWidth;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0,
 +            0, false, 0);
      assertEquals(testee.findColumn(evt), 1);
  
      /*
       * two charWidths across
       */
      x = 2 * charWidth;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0,
 +            0, false, 0);
      assertEquals(testee.findColumn(evt), 2);
  
      /*
       * limited to last column of seqcanvas
       */
      x = 20000;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0,
 +            0, false, 0);
      SeqCanvas seqCanvas = alignFrame.alignPanel.getSeqPanel().seqCanvas;
      int w = seqCanvas.getWidth();
      // limited to number of whole columns, base 0,
      alignFrame.getViewport().hideColumns(4, 9);
      x = 5 * charWidth + 2;
      // x is in 6th visible column, absolute column 12, or 11 base 0
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0,
 +            0, false, 0);
      assertEquals(testee.findColumn(evt), 11);
    }
  
      final int charWidth = av.getCharWidth();
      assertTrue(charWidth > 0); // sanity check
      assertEquals(av.getRanges().getStartRes(), 0);
 -  
 +
      /*
       * mouse at top left of wrapped panel, no West (left) scale
       */
 -    MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0,
 -            0, 0, 0, false, 0);
 +    MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0,
 +            x, 0, 0, 0, 0, false, 0);
      assertEquals(testee.findColumn(evt), 0);
 -    
 +
      /*
       * not quite one charWidth across
       */
 -    x = charWidth-1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0,
 -            0, 0, 0, false, 0);
 +    x = charWidth - 1;
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0,
 +            0, false, 0);
      assertEquals(testee.findColumn(evt), 0);
 -  
 +
      /*
       * one charWidth across
       */
      x = charWidth;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0,
 +            0, false, 0);
      assertEquals(testee.findColumn(evt), 1);
  
      /*
      int labelWidth = (int) PA.getValue(seqCanvas, "labelWidthWest");
      assertTrue(labelWidth > 0);
      x = labelWidth - 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0,
 +            0, false, 0);
      assertEquals(testee.findColumn(evt), -1);
  
      x = labelWidth;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0,
 +            0, false, 0);
      assertEquals(testee.findColumn(evt), 0);
  
      /*
      int residuesWide = av.getRanges().getViewportWidth();
      assertTrue(residuesWide > 0);
      x = labelWidth + charWidth * residuesWide - 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0,
 +            0, false, 0);
      assertEquals(testee.findColumn(evt), residuesWide - 1);
  
      /*
      assertTrue(residuesWide2 > 0);
      assertTrue(residuesWide2 < residuesWide); // available width reduced
      x += 1; // just over left edge of scale right
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0,
 +            0, false, 0);
      assertEquals(testee.findColumn(evt), -1);
 -    
 +
      // todo add startRes offset, hidden columns
  
    }
 +
    @BeforeClass(alwaysRun = true)
    public static void setUpBeforeClass() throws Exception
    {
        e.printStackTrace();
      }
    }
 +
    @Test(groups = "Functional")
    public void testFindMousePosition_wrapped_scales_longSequence()
    {
      Cache.applicationProperties.setProperty("FONT_STYLE", "0");
      // sequence of 50 bases, doubled 10 times, = 51200 bases
      String dna = "ATGGCCATTGGGCCCAAATTTCCCAAAGGGTTTCCCTGAGGTCAGTCAGA";
 -    for (int i = 0 ; i < 10 ; i++)
 +    for (int i = 0; i < 10; i++)
      {
        dna += dna;
      }
      assertEquals(dna.length(), 51200);
-     AlignFrame alignFrame = new FileLoader().LoadFileWaitTillLoaded(dna,
-             DataSourceType.PASTE);
+     AlignFrame alignFrame = new FileLoader()
+             .LoadFileWaitTillLoaded("dna "+dna, DataSourceType.PASTE);
      SeqPanel testee = alignFrame.alignPanel.getSeqPanel();
      AlignViewport av = alignFrame.getViewport();
      av.setScaleAboveWrapped(true);
      } catch (InterruptedException e)
      {
      }
 -  
 +
      final int charHeight = av.getCharHeight();
      final int charWidth = av.getCharWidth();
      assertEquals(charHeight, 17);
      assertEquals(charWidth, 12);
 -    
 +
      FontMetrics fm = testee.getFontMetrics(av.getFont());
      int labelWidth = fm.stringWidth("00000") + charWidth;
      assertEquals(labelWidth, 57); // 5 x 9 + charWidth
  
      int x = 0;
      int y = 0;
 -  
 +
      /*
       * mouse at top left of wrapped panel; there is a gap of 2 * charHeight
       * above the alignment
       */
 -    MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y,
 -            0, 0, 0, false, 0);
 +    MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0,
 +            x, y, 0, 0, 0, false, 0);
      MousePos pos = testee.findMousePosition(evt);
      assertEquals(pos.column, -1); // over scale left, not an alignment column
      assertEquals(pos.seqIndex, -1); // above sequences
       */
      y += charHeight;
      x = labelWidth;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, -1);
      assertEquals(pos.column, 0);
      assertEquals(pos.annotationIndex, -1);
 -    
 +
      /*
       * cursor over scale left of first sequence
       */
      y += charHeight;
      x = 0;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, 0);
      assertEquals(pos.column, -1);
      assertEquals(pos.annotationIndex, -1);
 -  
 +
      /*
       * cursor over start of first sequence
       */
      x = labelWidth;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, 0);
      assertEquals(pos.column, 0);
      assertEquals(pos.annotationIndex, -1);
 -  
 +
      /*
       * move one character right, to bottom pixel of same row
       */
      x += charWidth;
      y += charHeight - 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, 0);
      assertEquals(pos.column, 1);
 -    
 +
      /*
       * move down one pixel - now in the no man's land between rows
       */
      y += 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, -1);
      assertEquals(pos.column, 1);
 -    
 +
      /*
       * move down two char heights less one pixel - still in the no man's land
       * (scale above + spacer line)
       */
      y += (2 * charHeight - 1);
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, -1);
      assertEquals(pos.column, 1);
 -    
 +
      /*
       * move down one more pixel - now on the next row of the sequence
       */
      y += 1;
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, 0);
      assertEquals(pos.column, 1 + av.getWrappedWidth());
 -    
 +
      /*
       * scroll to near the end of the sequence
       */
      SearchResultsI sr = new SearchResults();
      int scrollTo = dna.length() - 1000;
 -    sr.addResult(av.getAlignment().getSequenceAt(0), scrollTo, scrollTo); 
 +    sr.addResult(av.getAlignment().getSequenceAt(0), scrollTo, scrollTo);
      alignFrame.alignPanel.scrollToPosition(sr);
 -    
 +
      /*
       * place the mouse on the first column of the 6th sequence, and
       * verify that (computed) findMousePosition matches (actual) ViewportRanges
       */
      x = labelWidth;
      y = 17 * charHeight; // 17 = 6 times two header rows and 5 sequence rows
 -    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
 -            false, 0);
 +    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0,
 +            0, false, 0);
      pos = testee.findMousePosition(evt);
      assertEquals(pos.seqIndex, 0);
      int expected = av.getRanges().getStartRes() + 5 * av.getWrappedWidth();
   */
  package jalview.ws.seqfetcher;
  
+ import static org.testng.Assert.assertNotEquals;
  import static org.testng.AssertJUnit.assertEquals;
  import static org.testng.AssertJUnit.assertFalse;
  import static org.testng.AssertJUnit.assertNotNull;
  import static org.testng.AssertJUnit.assertTrue;
  
  import jalview.analysis.CrossRef;
+ import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.DBRefEntry;
  import jalview.datamodel.DBRefSource;
@@@ -37,6 -39,7 +39,7 @@@ import jalview.gui.JvOptionPane
  import jalview.util.DBRefUtils;
  import jalview.ws.DBRefFetcher;
  import jalview.ws.SequenceFetcher;
+ import jalview.ws.dbsources.EBIAlfaFold;
  import jalview.ws.dbsources.Pdb;
  import jalview.ws.dbsources.Uniprot;
  
@@@ -44,6 -47,7 +47,7 @@@ import java.util.ArrayList
  import java.util.Arrays;
  import java.util.List;
  
+ import org.junit.Assert;
  import org.testng.annotations.AfterClass;
  import org.testng.annotations.BeforeClass;
  import org.testng.annotations.Test;
@@@ -79,28 -83,26 +83,28 @@@ public class DbRefFetcherTes
    {
    }
  
 -  @Test(groups= {"Network"})
 +  @Test(groups = { "Network" })
    public void checkUniprotCanonicalFlagSet()
    {
 -    // TODO - mock this  - for moment it is a live request.
 +    // TODO - mock this - for moment it is a live request.
      SequenceI uniprotSeq = new Sequence("FER1_SPIOL",
              "MAATTTTMMGMATTFVPKPQAPPMMAALPSNTGRSLFGLKTGSRGGRMTMAAYKVTLVTPTGNVEFQCPDDV"
 -            + "YILDAAEEEGIDLPYSCRAGSCSSCAGKLKTGSLNQDDQSFLDDDQIDEGWVLTCAAYPVSDVTIETHKEEE"
 -            + "LTA");
 +                    + "YILDAAEEEGIDLPYSCRAGSCSSCAGKLKTGSLNQDDQSFLDDDQIDEGWVLTCAAYPVSDVTIETHKEEE"
 +                    + "LTA");
      DBRefFetcher dbr = new DBRefFetcher(new SequenceI[] { uniprotSeq });
      dbr.fetchDBRefs(true);
      List<DBRefEntry> primRefs = uniprotSeq.getPrimaryDBRefs();
      assertNotNull(primRefs);
 -    assertTrue(primRefs.size()>0);
 -    boolean canonicalUp=false;
 -    for (DBRefEntry ref:primRefs) {
 +    assertTrue(primRefs.size() > 0);
 +    boolean canonicalUp = false;
 +    for (DBRefEntry ref : primRefs)
 +    {
        assertEquals(DBRefSource.UNIPROT, ref.getCanonicalSourceName());
        canonicalUp |= ref.isCanonical();
      }
      assertTrue("No Canonical Uniprot reference detected", canonicalUp);
    }
 +
    /**
     * Tests that standard protein database sources include Uniprot (as the first)
     * and also PDB. (Additional sources are dependent on availability of DAS
    public void testEmblUniprotProductRecovery() throws Exception
    {
      String retrievalId = "V00488";
 -    DbSourceProxy embl = new SequenceFetcher().getSourceProxy(
 -            DBRefSource.EMBL).get(0);
 +    DbSourceProxy embl = new SequenceFetcher()
 +            .getSourceProxy(DBRefSource.EMBL).get(0);
      assertNotNull("Couldn't find the EMBL retrieval client", embl);
      verifyProteinNucleotideXref(retrievalId, embl);
    }
    public void testEmblCDSUniprotProductRecovery() throws Exception
    {
      String retrievalId = "AAH29712";
 -    DbSourceProxy embl = new SequenceFetcher().getSourceProxy(
 -            DBRefSource.EMBLCDS).get(0);
 +    DbSourceProxy embl = new SequenceFetcher()
 +            .getSourceProxy(DBRefSource.EMBLCDS).get(0);
      assertNotNull("Couldn't find the EMBL retrieval client", embl);
      verifyProteinNucleotideXref(retrievalId, embl);
    }
      assertEquals("Didn't retrieve right number of records", 1,
              alsq.getHeight());
      SequenceI seq = alsq.getSequenceAt(0);
 -    assertEquals("Wrong sequence name", embl.getDbSource() + "|"
 -            + retrievalId, seq.getName());
 +    assertEquals("Wrong sequence name",
 +            embl.getDbSource() + "|" + retrievalId, seq.getName());
      List<SequenceFeature> sfs = seq.getSequenceFeatures();
      assertFalse("Sequence features missing", sfs.isEmpty());
 -    assertTrue(
 -            "Feature not CDS",
 -            FeatureProperties.isCodingFeature(embl.getDbSource(),
 - sfs.get(0).getType()));
 +    assertTrue("Feature not CDS", FeatureProperties
 +            .isCodingFeature(embl.getDbSource(), sfs.get(0).getType()));
      assertEquals(embl.getDbSource(), sfs.get(0).getFeatureGroup());
      List<DBRefEntry> dr = DBRefUtils.selectRefs(seq.getDBRefs(),
 -            new String[] { DBRefSource.UNIPROT });
 +            new String[]
 +            { DBRefSource.UNIPROT });
      assertNotNull(dr);
      assertEquals("Expected a single Uniprot cross reference", 1, dr.size());
 -    assertEquals("Expected cross reference map to be one amino acid", dr.get(0)
 -            .getMap().getMappedWidth(), 1);
 -    assertEquals("Expected local reference map to be 3 nucleotides", dr.get(0)
 -            .getMap().getWidth(), 3);
 +    assertEquals("Expected cross reference map to be one amino acid",
 +            dr.get(0).getMap().getMappedWidth(), 1);
 +    assertEquals("Expected local reference map to be 3 nucleotides",
 +            dr.get(0).getMap().getWidth(), 3);
      AlignmentI sprods = new CrossRef(alsq.getSequencesArray(), alsq)
              .findXrefSequences(dr.get(0).getSource(), true);
      assertNotNull(
      assertEquals("Didn't xref right number of records", 1,
              sprods.getHeight());
      SequenceI proteinSeq = sprods.getSequenceAt(0);
 -    assertEquals(proteinSeq.getSequenceAsString(), dr.get(0).getMap().getTo()
 -            .getSequenceAsString());
 +    assertEquals(proteinSeq.getSequenceAsString(),
 +            dr.get(0).getMap().getTo().getSequenceAsString());
      assertEquals(dr.get(0).getSource() + "|" + dr.get(0).getAccessionId(),
              proteinSeq.getName());
    }
+   /**
+    * Tests retrieval of one entry from EMBLCDS. Test is dependent on
+    * availability of network and the EMBLCDS service.
+    * 
+    * @throws Exception
+    */
+   @Test(groups = { "External" })
+   public void testAlphaFoldClien() throws Exception
+   {
+     DbSourceProxy alphafold = new EBIAlfaFold();
+     AlignmentI resp = alphafold.getSequenceRecords(alphafold.getTestQuery());
+     assertNotNull(resp);
+     assertEquals("One sequence only",resp.getHeight(), 1);
+     for (AlignmentAnnotation aa:resp.getAlignmentAnnotation()) {
+       if (aa.graph == AlignmentAnnotation.CUSTOMRENDERER)
+       {
+         assertTrue("Contact map didn't provide valid contact",resp.getContactListFor(aa, 1).getContactAt(1)!=-1d);
+         // test passes
+         return;
+       }
+     }
+     Assert.fail();
+   }
  }