JAL-1622 fix bug computing superposition + associated refactorings
[jalview.git] / src / jalview / structures / models / AAStructureBindingModel.java
index 298e82c..86c9a21 100644 (file)
@@ -5,10 +5,12 @@ import java.util.Arrays;
 import java.util.List;
 
 import jalview.api.StructureSelectionManagerProvider;
+import jalview.datamodel.AlignmentI;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
 import jalview.structure.AtomSpec;
 import jalview.structure.StructureListener;
+import jalview.structure.StructureMapping;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.Comparison;
 import jalview.util.MessageManager;
@@ -51,6 +53,35 @@ public abstract class AAStructureBindingModel extends
   private boolean nucleotide;
 
   /**
+   * Data bean class to simplify parameterisation in superposeStructures
+   */
+  protected class SuperposeData
+  {
+    /**
+     * Constructor with alignment width argument
+     * 
+     * @param width
+     */
+    public SuperposeData(int width)
+    {
+      pdbResNo = new int[width];
+    }
+
+    public String filename;
+
+    public String pdbId;
+
+    public String chain = "";
+
+    public boolean isRna;
+
+    /*
+     * The pdb residue number (if any) mapped to each column of the alignment
+     */
+    public int[] pdbResNo;
+  }
+
+  /**
    * Constructor
    * 
    * @param ssm
@@ -388,6 +419,14 @@ public abstract class AAStructureBindingModel extends
     }
   }
 
+  @Override
+  public abstract void highlightAtoms(List<AtomSpec> atoms);
+
+  protected boolean isNucleotide()
+  {
+    return this.nucleotide;
+  }
+
   /**
    * Returns a readable description of all mappings for the wrapped pdbfile to
    * any mapped sequences
@@ -412,24 +451,154 @@ public abstract class AAStructureBindingModel extends
     return sb.toString();
   }
 
-  @Override
-  public void highlightAtoms(List<AtomSpec> atoms)
+  /**
+   * Returns the mapped structure position for a given aligned column of a given
+   * sequence, or -1 if the column is gapped, beyond the end of the sequence, or
+   * not mapped to structure.
+   * 
+   * @param seq
+   * @param alignedPos
+   * @param mapping
+   * @return
+   */
+  protected int getMappedPosition(SequenceI seq, int alignedPos,
+          StructureMapping mapping)
   {
-    if (atoms != null)
+    if (alignedPos >= seq.getLength())
     {
-      for (AtomSpec atom : atoms)
+      return -1;
+    }
+
+    if (Comparison.isGap(seq.getCharAt(alignedPos)))
+    {
+      return -1;
+    }
+    int seqPos = seq.findPosition(alignedPos);
+    int pos = mapping.getPDBResNum(seqPos);
+    return pos;
+  }
+
+  /**
+   * Helper method to identify residues that can participate in a structure
+   * superposition command. For each structure, identify a sequence in the
+   * alignment which is mapped to the structure. Identify non-gapped columns in
+   * the sequence which have a mapping to a residue in the structure. Returns
+   * the index of the first structure that has a mapping to the alignment.
+   * 
+   * @param alignment
+   *          the sequence alignment which is the basis of structure
+   *          superposition
+   * @param matched
+   *          an array of booleans, indexed by alignment column, where true
+   *          indicates that every structure has a mapped residue present in the
+   *          column (so the column can participate in structure alignment)
+   * @param structures
+   *          an array of data beans corresponding to pdb file index
+   * @return
+   */
+  protected int findSuperposableResidues(AlignmentI alignment,
+          boolean[] matched, SuperposeData[] structures)
+  {
+    int refStructure = -1;
+    String[] files = getPdbFile();
+    for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
+    {
+      StructureMapping[] mappings = getSsm().getMapping(files[pdbfnum]);
+      int lastPos = -1;
+
+      /*
+       * Find the first mapped sequence (if any) for this PDB entry which is in
+       * the alignment
+       */
+      final int seqCountForPdbFile = getSequence()[pdbfnum].length;
+      for (int s = 0; s < seqCountForPdbFile; s++)
       {
-        highlightAtom(atom.getAtomIndex(), atom.getPdbResNum(),
-                atom.getChain(), atom.getPdbFile());
+        for (StructureMapping mapping : mappings)
+        {
+          final SequenceI theSequence = getSequence()[pdbfnum][s];
+          if (mapping.getSequence() == theSequence
+                  && alignment.findIndex(theSequence) > -1)
+          {
+            if (refStructure < 0)
+            {
+              refStructure = pdbfnum;
+            }
+            for (int r = 0; r < matched.length; r++)
+            {
+              if (!matched[r])
+              {
+                continue;
+              }
+              int pos = getMappedPosition(theSequence, r, mapping);
+              if (pos < 1 || pos == lastPos)
+              {
+                matched[r] = false;
+                continue;
+              }
+              lastPos = pos;
+              structures[pdbfnum].pdbResNo[r] = pos;
+            }
+            String chain = mapping.getChain();
+            if (chain != null && chain.trim().length() > 0)
+            {
+              structures[pdbfnum].chain = chain;
+            }
+            structures[pdbfnum].pdbId = mapping.getPdbId();
+            structures[pdbfnum].isRna = theSequence.getRNA() != null;
+            // move on to next pdb file
+            s = seqCountForPdbFile;
+            break;
+          }
+        }
       }
     }
+    return refStructure;
   }
 
-  protected abstract void highlightAtom(int atomIndex, int pdbResNum,
-          String chain, String pdbFile);
-
-  protected boolean isNucleotide()
+  /**
+   * Returns true if the structure viewer has loaded all of the files of
+   * interest (identified by the file mapping having been set up), or false if
+   * any are still not loaded after a timeout interval.
+   * 
+   * @param files
+   */
+  protected boolean waitForFileLoad(String[] files)
   {
-    return this.nucleotide;
+    /*
+     * give up after 10 secs plus 1 sec per file
+     */
+    long starttime = System.currentTimeMillis();
+    long endTime = 10000 + 1000 * files.length + starttime;
+    String notLoaded = null;
+
+    boolean waiting = true;
+    while (waiting && System.currentTimeMillis() < endTime)
+    {
+      waiting = false;
+      for (String file : files)
+      {
+        notLoaded = file;
+        try
+        {
+          StructureMapping[] sm = getSsm().getMapping(file);
+          if (sm == null || sm.length == 0)
+          {
+            waiting = true;
+          }
+        } catch (Throwable x)
+        {
+          waiting = true;
+        }
+      }
+    }
+
+    if (waiting)
+    {
+      System.err
+              .println("Timed out waiting for structure viewer to load file "
+                      + notLoaded);
+      return false;
+    }
+    return true;
   }
 }
\ No newline at end of file