Merge branch 'develop' (JAL-4102 2.11.2.6 patch release) into features/r2_11_2_alphaf...
[jalview.git] / src / jalview / structure / StructureSelectionManager.java
index cdb4e0a..24320b5 100644 (file)
  */
 package jalview.structure;
 
  */
 package jalview.structure;
 
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Vector;
+
 import jalview.analysis.AlignSeq;
 import jalview.api.StructureSelectionManagerProvider;
 import jalview.analysis.AlignSeq;
 import jalview.api.StructureSelectionManagerProvider;
+import jalview.bin.Console;
 import jalview.commands.CommandI;
 import jalview.commands.EditCommand;
 import jalview.commands.OrderCommand;
 import jalview.commands.CommandI;
 import jalview.commands.EditCommand;
 import jalview.commands.OrderCommand;
@@ -29,6 +42,7 @@ import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
+import jalview.datamodel.ContiguousI;
 import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SearchResults;
 import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SearchResults;
@@ -41,24 +55,13 @@ import jalview.io.DataSourceType;
 import jalview.io.StructureFile;
 import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
 import jalview.io.StructureFile;
 import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 import jalview.ws.sifts.SiftsClient;
 import jalview.ws.sifts.SiftsException;
 import jalview.ws.sifts.SiftsSettings;
 import jalview.ws.sifts.SiftsClient;
 import jalview.ws.sifts.SiftsException;
 import jalview.ws.sifts.SiftsSettings;
-
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.IdentityHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Vector;
-
-import MCview.Atom;
-import MCview.PDBChain;
-import MCview.PDBfile;
+import mc_view.Atom;
+import mc_view.PDBChain;
+import mc_view.PDBfile;
 
 public class StructureSelectionManager
 {
 
 public class StructureSelectionManager
 {
@@ -319,7 +322,7 @@ public class StructureSelectionManager
    * @return null or the structure data parsed as a pdb file
    */
   synchronized public StructureFile setMapping(SequenceI[] sequence,
    * @return null or the structure data parsed as a pdb file
    */
   synchronized public StructureFile setMapping(SequenceI[] sequence,
-          String[] targetChains, String pdbFile, DataSourceType protocol, 
+          String[] targetChains, String pdbFile, DataSourceType protocol,
           IProgressIndicator progress)
   {
     return computeMapping(true, sequence, targetChains, pdbFile, protocol,
           IProgressIndicator progress)
   {
     return computeMapping(true, sequence, targetChains, pdbFile, protocol,
@@ -376,9 +379,9 @@ public class StructureSelectionManager
    *          mapping operation
    * @return null or the structure data parsed as a pdb file
    */
    *          mapping operation
    * @return null or the structure data parsed as a pdb file
    */
-  synchronized public StructureFile computeMapping(
-          boolean forStructureView, SequenceI[] sequenceArray,
-          String[] targetChainIds, String pdbFile, DataSourceType sourceType,
+  synchronized public StructureFile computeMapping(boolean forStructureView,
+          SequenceI[] sequenceArray, String[] targetChainIds,
+          String pdbFile, DataSourceType sourceType,
           IProgressIndicator progress)
   {
     long progressSessionId = System.currentTimeMillis() * 3;
           IProgressIndicator progress)
   {
     long progressSessionId = System.currentTimeMillis() * 3;
@@ -398,15 +401,31 @@ public class StructureSelectionManager
     {
       // FIXME if sourceType is not null, we've lost data here
       sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
     {
       // FIXME if sourceType is not null, we've lost data here
       sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
-      pdb = new JmolParser(pdbFile, sourceType);
-
+      pdb = new JmolParser(false, pdbFile, sourceType);
+      pdb.addSettings(parseSecStr && processSecondaryStructure,
+              parseSecStr && addTempFacAnnot,
+              parseSecStr && secStructServices);
+      pdb.doParse();
       if (pdb.getId() != null && pdb.getId().trim().length() > 0
               && DataSourceType.FILE == sourceType)
       {
         registerPDBFile(pdb.getId().trim(), pdbFile);
       }
       // if PDBId is unavailable then skip SIFTS mapping execution path
       if (pdb.getId() != null && pdb.getId().trim().length() > 0
               && DataSourceType.FILE == sourceType)
       {
         registerPDBFile(pdb.getId().trim(), pdbFile);
       }
       // if PDBId is unavailable then skip SIFTS mapping execution path
-      isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable();
+      // TODO: JAL-3868 need to know if structure is actually from
+      // PDB (has valid PDB ID and has provenance suggesting it
+      // actually came from PDB)
+      boolean isProtein = false;
+      for (SequenceI s : sequenceArray)
+      {
+        if (s.isProtein())
+        {
+          isProtein = true;
+          break;
+        }
+      }
+      isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable()
+              && !pdb.getId().startsWith("AF-") && isProtein;
 
     } catch (Exception ex)
     {
 
     } catch (Exception ex)
     {
@@ -426,7 +445,8 @@ public class StructureSelectionManager
     } catch (SiftsException e)
     {
       isMapUsingSIFTs = false;
     } catch (SiftsException e)
     {
       isMapUsingSIFTs = false;
-      e.printStackTrace();
+      Console.error("SIFTS mapping failed", e);
+      Console.error("Falling back on Needleman & Wunsch alignment");
       siftsClient = null;
     }
 
       siftsClient = null;
     }
 
@@ -517,9 +537,11 @@ public class StructureSelectionManager
       List<StructureMapping> seqToStrucMapping = new ArrayList<>();
       if (isMapUsingSIFTs && seq.isProtein())
       {
       List<StructureMapping> seqToStrucMapping = new ArrayList<>();
       if (isMapUsingSIFTs && seq.isProtein())
       {
-        if (progress!=null) {
-          progress.setProgressBar(MessageManager
-                .getString("status.obtaining_mapping_with_sifts"),
+        if (progress != null)
+        {
+          progress.setProgressBar(
+                  MessageManager
+                          .getString("status.obtaining_mapping_with_sifts"),
                   progressSessionId);
         }
         jalview.datamodel.Mapping sqmpping = maxAlignseq
                   progressSessionId);
         }
         jalview.datamodel.Mapping sqmpping = maxAlignseq
@@ -533,22 +555,23 @@ public class StructureSelectionManager
                     pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
             seqToStrucMapping.add(siftsMapping);
             maxChain.makeExactMapping(siftsMapping, seq);
                     pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
             seqToStrucMapping.add(siftsMapping);
             maxChain.makeExactMapping(siftsMapping, seq);
-            maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this
-                                                       // "IEA:SIFTS" ?
+            maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS",
+                    pdb.getId().toLowerCase(Locale.ROOT));
             maxChain.transferResidueAnnotation(siftsMapping, null);
             ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
 
           } catch (SiftsException e)
           {
             // fall back to NW alignment
             maxChain.transferResidueAnnotation(siftsMapping, null);
             ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
 
           } catch (SiftsException e)
           {
             // fall back to NW alignment
-            System.err.println(e.getMessage());
+            Console.error(e.getMessage());
             StructureMapping nwMapping = getNWMappings(seq, pdbFile,
                     targetChainId, maxChain, pdb, maxAlignseq);
             seqToStrucMapping.add(nwMapping);
             maxChain.makeExactMapping(maxAlignseq, seq);
             StructureMapping nwMapping = getNWMappings(seq, pdbFile,
                     targetChainId, maxChain, pdb, maxAlignseq);
             seqToStrucMapping.add(nwMapping);
             maxChain.makeExactMapping(maxAlignseq, seq);
-            maxChain.transferRESNUMFeatures(seq, "IEA:Jalview"); // FIXME: is
-                                                                 // this
-                                                        // "IEA:Jalview" ?
+            maxChain.transferRESNUMFeatures(seq, "IEA:Jalview",
+                    pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is
+            // this
+            // "IEA:Jalview" ?
             maxChain.transferResidueAnnotation(nwMapping, sqmpping);
             ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
           }
             maxChain.transferResidueAnnotation(nwMapping, sqmpping);
             ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
           }
@@ -561,17 +584,22 @@ public class StructureSelectionManager
             StructureMapping siftsMapping = null;
             try
             {
             StructureMapping siftsMapping = null;
             try
             {
-              siftsMapping = getStructureMapping(seq,
-                      pdbFile, chain.id, pdb, chain, sqmpping, maxAlignseq,
-                      siftsClient);
+              siftsMapping = getStructureMapping(seq, pdbFile, chain.id,
+                      pdb, chain, sqmpping, maxAlignseq, siftsClient);
               foundSiftsMappings.add(siftsMapping);
               chain.makeExactMapping(siftsMapping, seq);
               foundSiftsMappings.add(siftsMapping);
               chain.makeExactMapping(siftsMapping, seq);
-              chain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this
+              chain.transferRESNUMFeatures(seq, "IEA: SIFTS",
+                      pdb.getId().toLowerCase(Locale.ROOT));// FIXME: is this
               // "IEA:SIFTS" ?
               chain.transferResidueAnnotation(siftsMapping, null);
             } catch (SiftsException e)
             {
               System.err.println(e.getMessage());
               // "IEA:SIFTS" ?
               chain.transferResidueAnnotation(siftsMapping, null);
             } catch (SiftsException e)
             {
               System.err.println(e.getMessage());
+            } catch (Exception e)
+            {
+              System.err.println(
+                      "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
+              System.err.println(e.getMessage());
             }
           }
           if (!foundSiftsMappings.isEmpty())
             }
           }
           if (!foundSiftsMappings.isEmpty())
@@ -584,8 +612,9 @@ public class StructureSelectionManager
             StructureMapping nwMapping = getNWMappings(seq, pdbFile,
                     maxChainId, maxChain, pdb, maxAlignseq);
             seqToStrucMapping.add(nwMapping);
             StructureMapping nwMapping = getNWMappings(seq, pdbFile,
                     maxChainId, maxChain, pdb, maxAlignseq);
             seqToStrucMapping.add(nwMapping);
-            maxChain.transferRESNUMFeatures(seq, null); // FIXME: is this
-                                                        // "IEA:Jalview" ?
+            maxChain.transferRESNUMFeatures(seq, null,
+                    pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is this
+            // "IEA:Jalview" ?
             maxChain.transferResidueAnnotation(nwMapping, sqmpping);
             ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
           }
             maxChain.transferResidueAnnotation(nwMapping, sqmpping);
             ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
           }
@@ -595,8 +624,9 @@ public class StructureSelectionManager
       {
         if (progress != null)
         {
       {
         if (progress != null)
         {
-          progress.setProgressBar(MessageManager
-                                 .getString("status.obtaining_mapping_with_nw_alignment"),
+          progress.setProgressBar(
+                  MessageManager.getString(
+                          "status.obtaining_mapping_with_nw_alignment"),
                   progressSessionId);
         }
         StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
                   progressSessionId);
         }
         StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
@@ -606,7 +636,10 @@ public class StructureSelectionManager
       }
       if (forStructureView)
       {
       }
       if (forStructureView)
       {
-        mappings.addAll(seqToStrucMapping);
+        for (StructureMapping sm : seqToStrucMapping)
+        {
+          addStructureMapping(sm); // not addAll!
+        }
       }
       if (progress != null)
       {
       }
       if (progress != null)
       {
@@ -658,7 +691,10 @@ public class StructureSelectionManager
 
   public void addStructureMapping(StructureMapping sm)
   {
 
   public void addStructureMapping(StructureMapping sm)
   {
-    mappings.add(sm);
+    if (!mappings.contains(sm))
+    {
+      mappings.add(sm);
+    }
   }
 
   /**
   }
 
   /**
@@ -680,7 +716,8 @@ public class StructureSelectionManager
   private StructureMapping getStructureMapping(SequenceI seq,
           String pdbFile, String targetChainId, StructureFile pdb,
           PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
   private StructureMapping getStructureMapping(SequenceI seq,
           String pdbFile, String targetChainId, StructureFile pdb,
           PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
-          AlignSeq maxAlignseq, SiftsClient siftsClient) throws SiftsException
+          AlignSeq maxAlignseq, SiftsClient siftsClient)
+          throws SiftsException
   {
     StructureMapping curChainMapping = siftsClient
             .getSiftsStructureMapping(seq, pdbFile, targetChainId);
   {
     StructureMapping curChainMapping = siftsClient
             .getSiftsStructureMapping(seq, pdbFile, targetChainId);
@@ -746,7 +783,8 @@ public class StructureSelectionManager
     maxChain.makeExactMapping(maxAlignseq, seq);
     jalview.datamodel.Mapping sqmpping = maxAlignseq
             .getMappingFromS1(false);
     maxChain.makeExactMapping(maxAlignseq, seq);
     jalview.datamodel.Mapping sqmpping = maxAlignseq
             .getMappingFromS1(false);
-    maxChain.transferRESNUMFeatures(seq, null);
+    maxChain.transferRESNUMFeatures(seq, null,
+            pdb.getId().toLowerCase(Locale.ROOT));
 
     HashMap<Integer, int[]> mapping = new HashMap<>();
     int resNum = -10000;
 
     HashMap<Integer, int[]> mapping = new HashMap<>();
     int resNum = -10000;
@@ -837,18 +875,65 @@ public class StructureSelectionManager
   }
 
   /**
   }
 
   /**
+   * hack to highlight a range of positions at once on any structure views
+   * 
+   * @param sequenceRef
+   * @param is
+   *          - series of int start-end ranges as positions on sequenceRef
+   * @param i
+   * @param object
+   */
+  public void highlightPositionsOn(SequenceI sequenceRef, int[][] is,
+          Object source)
+  {
+    boolean hasSequenceListeners = handlingVamsasMo
+            || !seqmappings.isEmpty();
+    SearchResultsI results = null;
+    ArrayList<Integer> listOfPositions = new ArrayList<Integer>();
+    for (int[] s_e : is)
+    {
+      for (int p = s_e[0]; p <= s_e[1]; listOfPositions.add(p++))
+        ;
+    }
+    int seqpos[] = new int[listOfPositions.size()];
+    int i = 0;
+    for (Integer p : listOfPositions)
+    {
+      seqpos[i++] = p;
+    }
+
+    for (i = 0; i < listeners.size(); i++)
+    {
+      Object listener = listeners.elementAt(i);
+      if (listener == source)
+      {
+        // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
+        // Temporary fudge with SequenceListener.getVamsasSource()
+        continue;
+      }
+      if (listener instanceof StructureListener)
+      {
+        highlightStructure((StructureListener) listener, sequenceRef,
+                seqpos);
+      }
+
+    }
+  }
+
+  /**
    * Propagate mouseover of a single position in a structure
    * 
    * @param pdbResNum
    * @param chain
    * @param pdbfile
    * Propagate mouseover of a single position in a structure
    * 
    * @param pdbResNum
    * @param chain
    * @param pdbfile
+   * @return
    */
    */
-  public void mouseOverStructure(int pdbResNum, String chain,
+  public String mouseOverStructure(int pdbResNum, String chain,
           String pdbfile)
   {
     AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
     List<AtomSpec> atoms = Collections.singletonList(atomSpec);
           String pdbfile)
   {
     AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
     List<AtomSpec> atoms = Collections.singletonList(atomSpec);
-    mouseOverStructure(atoms);
+    return mouseOverStructure(atoms);
   }
 
   /**
   }
 
   /**
@@ -856,12 +941,12 @@ public class StructureSelectionManager
    * 
    * @param atoms
    */
    * 
    * @param atoms
    */
-  public void mouseOverStructure(List<AtomSpec> atoms)
+  public String mouseOverStructure(List<AtomSpec> atoms)
   {
     if (listeners == null)
     {
       // old or prematurely sent event
   {
     if (listeners == null)
     {
       // old or prematurely sent event
-      return;
+      return null;
     }
     boolean hasSequenceListener = false;
     for (int i = 0; i < listeners.size(); i++)
     }
     boolean hasSequenceListener = false;
     for (int i = 0; i < listeners.size(); i++)
@@ -873,18 +958,24 @@ public class StructureSelectionManager
     }
     if (!hasSequenceListener)
     {
     }
     if (!hasSequenceListener)
     {
-      return;
+      return null;
     }
 
     SearchResultsI results = findAlignmentPositionsForStructurePositions(
             atoms);
     }
 
     SearchResultsI results = findAlignmentPositionsForStructurePositions(
             atoms);
+    String result = null;
     for (Object li : listeners)
     {
       if (li instanceof SequenceListener)
       {
     for (Object li : listeners)
     {
       if (li instanceof SequenceListener)
       {
-        ((SequenceListener) li).highlightSequence(results);
+        String s = ((SequenceListener) li).highlightSequence(results);
+        if (s != null)
+        {
+          result = s;
+        }
       }
     }
       }
     }
+    return result;
   }
 
   /**
   }
 
   /**
@@ -1039,6 +1130,62 @@ public class StructureSelectionManager
     sl.highlightAtoms(atoms);
   }
 
     sl.highlightAtoms(atoms);
   }
 
+  public void highlightStructureRegionsFor(StructureListener sl,
+          SequenceI[] seqs, int... columns)
+  {
+    List<SequenceI> to_highlight = new ArrayList<SequenceI>();
+    for (SequenceI seq : seqs)
+    {
+      if (sl.isListeningFor(seq))
+      {
+        to_highlight.add(seq);
+      }
+    }
+    if (to_highlight.size() == 0)
+    {
+      return;
+    }
+    List<AtomSpec> atoms = new ArrayList<>();
+    for (SequenceI seq : to_highlight)
+    {
+      int atomNo;
+      for (StructureMapping sm : mappings)
+      {
+        if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
+                || (sm.sequence.getDatasetSequence() != null && sm.sequence
+                        .getDatasetSequence() == seq.getDatasetSequence()))
+        {
+
+          for (int i = 0; i < columns.length; i += 2)
+          {
+            ContiguousI positions = seq.findPositions(columns[i] + 1,
+                    columns[i + 1] + 1);
+            if (positions == null)
+            {
+              continue;
+            }
+            for (int index = positions.getBegin(); index <= positions
+                    .getEnd(); index++)
+            {
+
+              atomNo = sm.getAtomNum(index);
+
+              if (atomNo > 0)
+              {
+                atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
+                        sm.getPDBResNum(index), atomNo));
+              }
+            }
+          }
+        }
+      }
+      if (atoms.size() > 0)
+      {
+        sl.highlightAtoms(atoms);
+      }
+    }
+  }
+
   /**
    * true if a mouse over event from an external (ie Vamsas) source is being
    * handled
   /**
    * true if a mouse over event from an external (ie Vamsas) source is being
    * handled
@@ -1154,7 +1301,8 @@ public class StructureSelectionManager
     StringBuilder sb = new StringBuilder(64);
     for (StructureMapping sm : mappings)
     {
     StringBuilder sb = new StringBuilder(64);
     for (StructureMapping sm : mappings)
     {
-      if (sm.pdbfile.equals(pdbfile) && seqs.contains(sm.sequence))
+      if (Platform.pathEquals(sm.pdbfile, pdbfile)
+              && seqs.contains(sm.sequence))
       {
         sb.append(sm.mappingDetails);
         sb.append(NEWLINE);
       {
         sb.append(sm.mappingDetails);
         sb.append(NEWLINE);
@@ -1327,7 +1475,10 @@ public class StructureSelectionManager
         instances.remove(jalviewLite);
         try
         {
         instances.remove(jalviewLite);
         try
         {
-          mnger.finalize();
+          /* bsoares 2019-03-20 finalize deprecated, no apparent external
+           * resources to close
+           */
+          // mnger.finalize();
         } catch (Throwable x)
         {
         }
         } catch (Throwable x)
         {
         }
@@ -1413,4 +1564,34 @@ public class StructureSelectionManager
     return seqmappings;
   }
 
     return seqmappings;
   }
 
+  /**
+   * quick and dirty route to just highlight all structure positions for a range
+   * of columns
+   * 
+   * @param sequencesArray
+   * @param is
+   *          start-end columns on sequencesArray
+   * @param source
+   *          origin parent AlignmentPanel
+   */
+  public void highlightPositionsOnMany(SequenceI[] sequencesArray, int[] is,
+          Object source)
+  {
+    for (int i = 0; i < listeners.size(); i++)
+    {
+      Object listener = listeners.elementAt(i);
+      if (listener == source)
+      {
+        // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
+        // Temporary fudge with SequenceListener.getVamsasSource()
+        continue;
+      }
+      if (listener instanceof StructureListener)
+      {
+        highlightStructureRegionsFor((StructureListener) listener,
+                sequencesArray, is);
+      }
+    }
+  }
+
 }
 }