Merge branch 'develop' into spike/matrix_annot
authorJim Procter <jprocter@issues.jalview.org>
Fri, 10 Feb 2017 18:46:15 +0000 (18:46 +0000)
committerJim Procter <jprocter@issues.jalview.org>
Fri, 10 Feb 2017 18:46:15 +0000 (18:46 +0000)
1  2 
src/jalview/api/AlignViewportI.java
src/jalview/datamodel/Alignment.java
src/jalview/datamodel/AlignmentI.java
src/jalview/io/AppletFormatAdapter.java
src/jalview/renderer/AnnotationRenderer.java
src/jalview/viewmodel/AlignmentViewport.java

@@@ -26,12 -26,12 +26,13 @@@ import jalview.datamodel.AlignmentI
  import jalview.datamodel.AlignmentView;
  import jalview.datamodel.CigarArray;
  import jalview.datamodel.ColumnSelection;
 +import jalview.datamodel.ContactListI;
  import jalview.datamodel.ProfilesI;
  import jalview.datamodel.SearchResultsI;
  import jalview.datamodel.SequenceCollectionI;
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
+ import jalview.renderer.ResidueShaderI;
  import jalview.schemes.ColourSchemeI;
  
  import java.awt.Color;
@@@ -80,6 -80,14 +81,14 @@@ public interface AlignViewportI extend
  
    ColourSchemeI getGlobalColourScheme();
  
+   /**
+    * Returns an object that describes colouring (including any thresholding or
+    * fading) of the alignment
+    * 
+    * @return
+    */
+   ResidueShaderI getResidueShading();
    AlignmentI getAlignment();
  
    ColumnSelection getColumnSelection();
  
    /**
     * 
-    * @return the alignment annotatino row for the structure consensus
+    * @return the alignment annotation row for the structure consensus
     *         calculation
     */
    AlignmentAnnotation getAlignmentStrucConsensusAnnotation();
    void setRnaStructureConsensusHash(Hashtable[] hStrucConsensus);
  
    /**
-    * set global colourscheme
+    * Sets the colour scheme for the background alignment (as distinct from
+    * sub-groups, which may have their own colour schemes). A null value is used
+    * for no residue colour (white).
     * 
-    * @param rhc
+    * @param cs
     */
-   void setGlobalColourScheme(ColourSchemeI rhc);
+   void setGlobalColourScheme(ColourSchemeI cs);
  
    Map<SequenceI, SequenceCollectionI> getHiddenRepSequences();
  
     * @return search results or null
     */
    SearchResultsI getSearchResults();
 +
 +  ContactListI getContactList(AlignmentAnnotation _aa, int column);
  }
@@@ -30,7 -30,6 +30,7 @@@ import jalview.util.MessageManager
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.Enumeration;
 +import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Hashtable;
  import java.util.List;
@@@ -55,11 -54,7 +55,7 @@@ public class Alignment implements Align
  
    protected char gapCharacter = '-';
  
-   protected int type = NUCLEOTIDE;
-   public static final int PROTEIN = 0;
-   public static final int NUCLEOTIDE = 1;
+   private boolean nucleotide = true;
  
    public boolean hasRNAStructure = false;
  
      hiddenSequences = new HiddenSequences(this);
      codonFrameList = new ArrayList<AlignedCodonFrame>();
  
-     if (Comparison.isNucleotide(seqs))
-     {
-       type = NUCLEOTIDE;
-     }
-     else
-     {
-       type = PROTEIN;
-     }
+     nucleotide = Comparison.isNucleotide(seqs);
  
      sequences = Collections.synchronizedList(new ArrayList<SequenceI>());
  
    }
  
    /**
-    * Adds a sequence to the alignment. Recalculates maxLength and size.
+    * Adds a sequence to the alignment. Recalculates maxLength and size. Note
+    * this currently does not recalculate whether or not the alignment is
+    * nucleotide, so mixed alignments may have undefined behaviour.
     * 
     * @param snew
     */
     * @see jalview.datamodel.AlignmentI#findGroup(jalview.datamodel.SequenceI)
     */
    @Override
-   public SequenceGroup findGroup(SequenceI s)
+   public SequenceGroup findGroup(SequenceI seq, int position)
    {
      synchronized (groups)
      {
-       for (int i = 0; i < this.groups.size(); i++)
+       for (SequenceGroup sg : groups)
        {
-         SequenceGroup sg = groups.get(i);
-         if (sg.getSequences(null).contains(s))
+         if (sg.getSequences(null).contains(seq))
          {
-           return sg;
+           if (position >= sg.getStartRes() && position <= sg.getEndRes())
+           {
+             return sg;
+           }
          }
        }
      }
    }
  
    @Override
-   public void setNucleotide(boolean b)
-   {
-     if (b)
-     {
-       type = NUCLEOTIDE;
-     }
-     else
-     {
-       type = PROTEIN;
-     }
-   }
-   @Override
    public boolean isNucleotide()
    {
-     if (type == NUCLEOTIDE)
-     {
-       return true;
-     }
-     else
-     {
-       return false;
-     }
+     return nucleotide;
    }
  
    @Override
            String calcId, boolean autoCalc, SequenceI seqRef,
            SequenceGroup groupRef)
    {
-     assert (name != null);
      if (annotations != null)
      {
        for (AlignmentAnnotation annot : getAlignmentAnnotation())
    @Override
    public Iterable<AlignmentAnnotation> findAnnotation(String calcId)
    {
-     ArrayList<AlignmentAnnotation> aa = new ArrayList<AlignmentAnnotation>();
-     for (AlignmentAnnotation a : getAlignmentAnnotation())
+     List<AlignmentAnnotation> aa = new ArrayList<AlignmentAnnotation>();
+     AlignmentAnnotation[] alignmentAnnotation = getAlignmentAnnotation();
+     if (alignmentAnnotation != null)
      {
-       if (a.getCalcId() == calcId
-               || (a.getCalcId() != null && calcId != null && a.getCalcId()
-                       .equals(calcId)))
+       for (AlignmentAnnotation a : alignmentAnnotation)
        {
-         aa.add(a);
+         if (a.getCalcId() == calcId
+                 || (a.getCalcId() != null && calcId != null && a
+                         .getCalcId().equals(calcId)))
+         {
+           aa.add(a);
+         }
        }
      }
      return aa;
      }
      return new int[] { startPos, endPos };
    }
 +
 +  Map<Object, ContactMatrixI> contactmaps = new HashMap<Object, ContactMatrixI>();
 +  @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);
 +    addAnnotation(aa);
 +    return aa;
 +  }
  }
@@@ -156,15 -156,16 +156,16 @@@ public interface AlignmentI extends Ann
    int findIndex(SequenceI s);
  
    /**
-    * Finds group that given sequence is part of.
+    * Returns the first group (in the order in which groups were added) that
+    * includes the given sequence and aligned position (base 0), or null if none
+    * found
     * 
-    * @param s
-    *          Sequence in alignment.
+    * @param seq
+    * @param position
     * 
-    * @return First group found for sequence. WARNING : Sequences may be members
-    *         of several groups. This method is incomplete.
+    * @return
     */
-   SequenceGroup findGroup(SequenceI s);
+   SequenceGroup findGroup(SequenceI seq, int position);
  
    /**
     * Finds all groups that a given sequence is part of.
    char getGapCharacter();
  
    /**
-    * Test for all nucleotide alignment
-    * 
-    * @return true if alignment is nucleotide sequence
-    */
-   boolean isNucleotide();
-   /**
     * Test if alignment contains RNA structure
     * 
     * @return true if RNA structure AligmnentAnnotation was added to alignment
    boolean hasRNAStructure();
  
    /**
-    * Set alignment to be a nucleotide sequence
-    * 
-    */
-   void setNucleotide(boolean b);
-   /**
     * Get the associated dataset for the alignment.
     * 
     * @return Alignment containing dataset sequences or null of this is a
     * @return
     */
    public int[] getVisibleStartAndEndIndex(List<int[]> hiddenCols);
 +
 +  /**
 +   * resolve a contact list instance (if any) associated with the annotation row
 +   * and column position
 +   * 
 +   * @param _aa
 +   * @param column
 +   * @return
 +   */
 +  ContactListI getContactListFor(AlignmentAnnotation _aa, int column);
 +
 +  AlignmentAnnotation addContactList(ContactMatrixI cm);
  }
@@@ -26,7 -26,6 +26,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;
@@@ -65,7 -64,7 +65,7 @@@ public class AppletFormatAdapte
     */
    boolean serviceSecondaryStruct = false;
  
-   private AlignmentFileI alignFile = null;
+   private AlignmentFileReaderI alignFile = null;
  
    String inFile;
  
  
    public static String INVALID_CHARACTERS = "Contains invalid characters";
  
-   public static String SUPPORTED_FORMATS = "Formats currently supported are\n"
-           + prettyPrint(FileFormat.getReadableFormats());
+   /**
+    * Returns an error message with a list of supported readable file formats
+    * 
+    * @return
+    */
+   public static String getSupportedFormats()
+   {
+     return "Formats currently supported are\n"
+           + prettyPrint(FileFormats.getInstance().getReadableFormats());
+   }
    public AppletFormatAdapter()
    {
    }
          }
          else
          {
-           // todo is MCview parsing obsolete yet?
+           // todo is MCview parsing obsolete yet? JAL-2120
            StructureImportSettings.setShowSeqFeatures(true);
            alignFile = new MCview.PDBfile(annotFromStructure,
                    localSecondaryStruct, serviceSecondaryStruct, inFile,
        }
        else
        {
-         alignFile = fileFormat.getAlignmentFile(inFile, sourceType);
+         // alignFile = fileFormat.getAlignmentFile(inFile, sourceType);
+         alignFile = fileFormat.getReader(new FileParse(inFile,
+                 sourceType));
        }
        return buildAlignmentFromFile();
      } catch (Exception e)
          throw new IOException(e.getMessage());
        }
      }
-     throw new FileFormatException(SUPPORTED_FORMATS);
+     throw new FileFormatException(getSupportedFormats());
    }
  
    /**
        }
        else
        {
-         alignFile = format.getAlignmentFile(source);
+         alignFile = format.getReader(source);
        }
  
        return buildAlignmentFromFile();
        }
  
        // If we get to this stage, the format was not supported
-       throw new FileFormatException(SUPPORTED_FORMATS);
+       throw new FileFormatException(getSupportedFormats());
      }
    }
  
  
      alignFile.addGroups(al);
  
 +    al.addContactList(new SeqDistanceContactMatrix(al.getWidth()));
 +
      return al;
    }
  
    {
      try
      {
-       AlignmentFileI afile = format.getAlignmentFile(alignment);
+       AlignmentFileWriterI afile = format.getWriter(alignment);
  
        afile.setNewlineString(newline);
        afile.setExportSettings(exportSettings);
        return afileresp;
      } catch (Exception e)
      {
-       System.err.println("Failed to write alignment as a '" + format
+       System.err.println("Failed to write alignment as a '"
+               + format.getName()
                + "' file\n");
        e.printStackTrace();
      }
      return null;
    }
  
-   public AlignmentFileI getAlignFile()
+   public AlignmentFileReaderI getAlignFile()
    {
      return alignFile;
    }
@@@ -29,10 -29,10 +29,12 @@@ import jalview.datamodel.AlignmentAnnot
  import jalview.datamodel.Annotation;
  import jalview.datamodel.ColumnSelection;
  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;
+ import jalview.schemes.ZappoColourScheme;
  import jalview.util.Platform;
  
  import java.awt.BasicStroke;
@@@ -72,7 -72,7 +74,7 @@@ public class AnnotationRendere
    boolean av_renderHistogram = true, av_renderProfile = true,
            av_normaliseProfile = false;
  
-   ColourSchemeI profcolour = null;
+   ResidueShaderI profcolour = null;
  
    private ColumnSelection columnSelection;
  
      hStrucConsensus = null;
      fadedImage = null;
      annotationPanel = null;
 +    rendererFactoryI = null;
    }
  
    void drawStemAnnot(Graphics g, Annotation[] row_annotations, int lastSSX,
        useClip = false;
      }
  
 +    rendererFactoryI = AnnotationRendererFactory.getRendererFactory();
      updateFromAlignViewport(av);
    }
  
      av_renderHistogram = av.isShowConsensusHistogram();
      av_renderProfile = av.isShowSequenceLogo();
      av_normaliseProfile = av.isNormaliseSequenceLogo();
-     profcolour = av.getGlobalColourScheme();
-     if (profcolour == null)
+     profcolour = av.getResidueShading();
+     if (profcolour == null || profcolour.getColourScheme() == null)
      {
-       // Set the default colour for sequence logo if the alignnent has no
-       // colourscheme set
-       profcolour = av.getAlignment().isNucleotide() ? new jalview.schemes.NucleotideColourScheme()
-               : new jalview.schemes.ZappoColourScheme();
+       /*
+        * Use default colour for sequence logo if 
+        * the alignment has no colourscheme set
+        * (would like to use user preference but n/a for applet)
+        */
+       ColourSchemeI col = av.getAlignment().isNucleotide() ? new NucleotideColourScheme()
+               : new ZappoColourScheme();
+       profcolour = new ResidueShader(col);
      }
      columnSelection = av.getColumnSelection();
      hconsensus = av.getSequenceConsensusHash();
          }
          else
          {
 -          return AAFrequency.extractProfile(
 -hconsensus.get(column),
 +          return AAFrequency.extractProfile(hconsensus.get(column),
                    av_ignoreGapsConsensus);
          }
        }
  
    boolean rna = false;
  
 +  private AnnotationRendererFactoryI rendererFactoryI;
 +
    /**
     * Render the annotation rows associated with an alignment.
     * 
                      row.graphMin, row.graphMax, y, renderHistogram,
                      renderProfile, normaliseProfile);
            }
 +          else
 +          {
 +            AnnotationRowRendererI renderer = rendererFactoryI
 +                    .getRendererFor(row);
 +            if (renderer != null)
 +            {
 +              renderer.renderRow(g, charWidth, charHeight,
 +                      hasHiddenColumns, av, 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
@@@ -35,7 -35,6 +35,7 @@@ import jalview.datamodel.AlignmentView
  import jalview.datamodel.Annotation;
  import jalview.datamodel.CigarArray;
  import jalview.datamodel.ColumnSelection;
 +import jalview.datamodel.ContactListI;
  import jalview.datamodel.HiddenSequences;
  import jalview.datamodel.ProfilesI;
  import jalview.datamodel.SearchResultsI;
@@@ -43,9 -42,9 +43,9 @@@ import jalview.datamodel.Sequence
  import jalview.datamodel.SequenceCollectionI;
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
- import jalview.schemes.Blosum62ColourScheme;
+ import jalview.renderer.ResidueShader;
+ import jalview.renderer.ResidueShaderI;
  import jalview.schemes.ColourSchemeI;
- import jalview.schemes.PIDColourScheme;
  import jalview.structure.CommandListener;
  import jalview.structure.StructureSelectionManager;
  import jalview.structure.VamsasSource;
@@@ -598,7 -597,7 +598,7 @@@ public abstract class AlignmentViewpor
  
    protected boolean ignoreGapsInConsensusCalculation = false;
  
-   protected ColourSchemeI globalColourScheme = null;
+   protected ResidueShaderI residueShading;
  
    @Override
    public void setGlobalColourScheme(ColourSchemeI cs)
      // TODO: logic refactored from AlignFrame changeColour -
      // TODO: autorecalc stuff should be changed to rely on the worker system
      // check to see if we should implement a changeColour(cs) method rather than
-     // put th logic in here
+     // put the logic in here
      // - means that caller decides if they want to just modify state and defer
      // calculation till later or to do all calculations in thread.
      // via changecolour
-     globalColourScheme = cs;
-     boolean recalc = false;
+     /*
+      * only instantiate alignment colouring once, thereafter update it;
+      * this means that any conservation or PID threshold settings
+      * persist when the alignment colour scheme is changed
+      */
+     if (residueShading == null)
+     {
+       residueShading = new ResidueShader(viewStyle);
+     }
+     residueShading.setColourScheme(cs);
+     // TODO: do threshold and increment belong in ViewStyle or ResidueShader?
+     // ...problem: groups need these, but do not currently have a ViewStyle
      if (cs != null)
      {
-       recalc = getConservationSelected();
-       if (getAbovePIDThreshold() || cs instanceof PIDColourScheme
-               || cs instanceof Blosum62ColourScheme)
-       {
-         recalc = true;
-         cs.setThreshold(viewStyle.getThreshold(),
-                 ignoreGapsInConsensusCalculation);
-       }
-       else
+       if (getConservationSelected())
        {
-         cs.setThreshold(0, ignoreGapsInConsensusCalculation);
+         residueShading.setConservation(hconservation);
        }
-       if (recalc)
-       {
-         cs.setConsensus(hconsensus);
-         cs.setConservation(hconservation);
-       }
-       cs.setConservationApplied(getConservationSelected());
-       cs.alignmentChanged(alignment, hiddenRepSequences);
+       residueShading.alignmentChanged(alignment, hiddenRepSequences);
      }
+     /*
+      * if 'apply colour to all groups' is selected... do so
+      * (but don't transfer any colour threshold settings to groups)
+      */
      if (getColourAppliesToAllGroups())
      {
        for (SequenceGroup sg : getAlignment().getGroups())
        {
-         if (cs == null)
-         {
-           sg.cs = null;
-           continue;
-         }
-         sg.cs = cs.applyTo(sg, getHiddenRepSequences());
-         sg.setConsPercGaps(ConsPercGaps);
-         if (getAbovePIDThreshold() || cs instanceof PIDColourScheme
-                 || cs instanceof Blosum62ColourScheme)
-         {
-           sg.cs.setThreshold(viewStyle.getThreshold(),
-                   isIgnoreGapsConsensus());
-           recalc = true;
-         }
-         else
+         /*
+          * retain any colour thresholds per group while
+          * changing choice of colour scheme (JAL-2386)
+          */
+         sg.setColourScheme(cs);
+         if (cs != null)
          {
-           sg.cs.setThreshold(0, isIgnoreGapsConsensus());
-         }
-         if (getConservationSelected())
-         {
-           sg.cs.setConservationApplied(true);
-           recalc = true;
-         }
-         else
-         {
-           sg.cs.setConservation(null);
-           // sg.cs.setThreshold(0, getIgnoreGapsConsensus());
-         }
-         if (recalc)
-         {
-           sg.recalcConservation();
-         }
-         else
-         {
-           sg.cs.alignmentChanged(sg, hiddenRepSequences);
+           sg.getGroupColourScheme()
+                   .alignmentChanged(sg, hiddenRepSequences);
          }
        }
      }
    @Override
    public ColourSchemeI getGlobalColourScheme()
    {
-     return globalColourScheme;
+     return residueShading == null ? null : residueShading
+             .getColourScheme();
+   }
+   @Override
+   public ResidueShaderI getResidueShading()
+   {
+     return residueShading;
    }
  
    protected AlignmentAnnotation consensus;
      hconsensus = null;
      hcomplementConsensus = null;
      // colour scheme may hold reference to consensus
-     globalColourScheme = null;
+     residueShading = null;
      // TODO remove listeners from changeSupport?
      changeSupport = null;
      setAlignment(null);
    }
  
    /**
-    * Set the selection group for this window.
+    * Set the selection group for this window. Also sets the current alignment as
+    * the context for the group, if it does not already have one.
     * 
     * @param sg
     *          - group holding references to sequences in this alignment view
    public void setSelectionGroup(SequenceGroup sg)
    {
      selectionGroup = sg;
+     if (sg != null && sg.getContext() == null)
+     {
+       sg.setContext(alignment);
+     }
    }
  
    public void setHiddenColumns(ColumnSelection colsel)
      if (ap != null)
      {
        updateConsensus(ap);
-       if (globalColourScheme != null)
+       if (residueShading != null)
        {
-         globalColourScheme.setThreshold(globalColourScheme.getThreshold(),
+         residueShading.setThreshold(residueShading.getThreshold(),
                  ignoreGapsInConsensusCalculation);
        }
      }
        selectionGroup.setEndRes(alWidth - 1);
      }
  
-     resetAllColourSchemes();
+     updateAllColourSchemes();
      calculator.restartWorkers();
      // alignment.adjustSequenceAnnotations();
    }
    /**
     * reset scope and do calculations for all applied colourschemes on alignment
     */
-   void resetAllColourSchemes()
+   void updateAllColourSchemes()
    {
-     ColourSchemeI cs = globalColourScheme;
-     if (cs != null)
+     ResidueShaderI rs = residueShading;
+     if (rs != null)
      {
-       cs.alignmentChanged(alignment, hiddenRepSequences);
+       rs.alignmentChanged(alignment, hiddenRepSequences);
  
-       cs.setConsensus(hconsensus);
-       if (cs.conservationApplied())
+       rs.setConsensus(hconsensus);
+       if (rs.conservationApplied())
        {
-         cs.setConservation(Conservation.calculateConservation("All",
+         rs.setConservation(Conservation.calculateConservation("All",
                  alignment.getSequences(), 0, alignment.getWidth(), false,
                  getConsPercGaps(), false));
        }
    public void setViewStyle(ViewStyleI settingsForView)
    {
      viewStyle = new ViewStyle(settingsForView);
+     if (residueShading != null)
+     {
+       residueShading.setConservationApplied(settingsForView
+               .isConservationColourSelected());
+     }
    }
  
    @Override
    {
      return searchResults;
    }
 +
 +  @Override
 +  public ContactListI getContactList(AlignmentAnnotation _aa, int column)
 +  {
 +    return alignment.getContactListFor(_aa, column);
 +  }
  }