Merge branch 'develop' into features/JAL-2295setChimeraAttributes
[jalview.git] / src / jalview / ext / rbvi / chimera / JalviewChimeraBinding.java
index 54adf87..cc1de6a 100644 (file)
@@ -27,8 +27,8 @@ import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SearchResultMatchI;
 import jalview.datamodel.SearchResults;
-import jalview.datamodel.SearchResults.Match;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.httpserver.AbstractRequestHandler;
@@ -48,6 +48,7 @@ import java.io.PrintWriter;
 import java.net.BindException;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Hashtable;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -61,8 +62,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
 {
   public static final String CHIMERA_FEATURE_GROUP = "Chimera";
 
-  private static final String CHIMERA_FEATURE_PREFIX = "chim_";
-
   // Chimera clause to exclude alternate locations in atom selection
   private static final String NO_ALTLOCS = "&~@.B-Z&~@.2-9";
 
@@ -75,6 +74,10 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
 
   private static final String ALPHACARBON = "CA";
 
+  private List<String> chainNames = new ArrayList<String>();
+
+  private Hashtable<String, String> chainFile = new Hashtable<String, String>();
+  
   /*
    * Object through which we talk to Chimera
    */
@@ -191,14 +194,12 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
    * @param ssm
    * @param pdbentry
    * @param sequenceIs
-   * @param chains
    * @param protocol
    */
   public JalviewChimeraBinding(StructureSelectionManager ssm,
-          PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String[][] chains,
-          String protocol)
+          PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String protocol)
   {
-    super(ssm, pdbentry, sequenceIs, chains, protocol);
+    super(ssm, pdbentry, sequenceIs, protocol);
     viewer = new ChimeraManager(
             new ext.edu.ucsf.rbvi.strucviz2.StructureManager(true));
   }
@@ -229,7 +230,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
    */
   public String getViewerTitle(boolean verbose)
   {
-    return getViewerTitle(CHIMERA_FEATURE_GROUP, verbose);
+    return getViewerTitle("Chimera", verbose);
   }
 
   /**
@@ -248,11 +249,14 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
     boolean first = true;
     for (String chain : toshow)
     {
+      int modelNumber = getModelNoForChain(chain);
+      String showChainCmd = modelNumber == -1 ? "" : modelNumber + ":."
+              + chain.split(":")[1];
       if (!first)
       {
         cmd.append(",");
       }
-      cmd.append(":.").append(chain);
+      cmd.append(showChainCmd);
       first = false;
     }
 
@@ -261,7 +265,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
      * window, but it looks more helpful not to (easier to relate chains to the
      * whole)
      */
-    final String command = "~display #*; ~ribbon #*; ribbon "
+    final String command = "~display #*; ~ribbon #*; ribbon :"
             + cmd.toString();
     sendChimeraCommand(command, false);
   }
@@ -723,30 +727,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
    */
   public abstract void refreshPdbEntries();
 
-  private int getModelNum(String modelFileName)
-  {
-    String[] mfn = getPdbFile();
-    if (mfn == null)
-    {
-      return -1;
-    }
-    for (int i = 0; i < mfn.length; i++)
-    {
-      if (mfn[i].equalsIgnoreCase(modelFileName))
-      {
-        return i;
-      }
-    }
-    return -1;
-  }
-
-  /**
-   * map between index of model filename returned from getPdbFile and the first
-   * index of models from this file in the viewer. Note - this is not trimmed -
-   * use getPdbFile to get number of unique models.
-   */
-  private int _modelFileNameMap[];
-
   // ////////////////////////////////
   // /StructureListener
   @Override
@@ -756,18 +736,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
     {
       return new String[0];
     }
-    // if (modelFileNames == null)
-    // {
-    // Collection<ChimeraModel> chimodels = viewer.getChimeraModels();
-    // _modelFileNameMap = new int[chimodels.size()];
-    // int j = 0;
-    // for (ChimeraModel chimodel : chimodels)
-    // {
-    // String mdlName = chimodel.getModelName();
-    // }
-    // modelFileNames = new String[j];
-    // // System.arraycopy(mset, 0, modelFileNames, 0, j);
-    // }
 
     return chimeraMaps.keySet().toArray(
             modelFileNames = new String[chimeraMaps.size()]);
@@ -1070,27 +1038,10 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
    * 
    * @return
    */
+  @Override
   public List<String> getChainNames()
   {
-    List<String> names = new ArrayList<String>();
-    String[][] allNames = getChains();
-    if (allNames != null)
-    {
-      for (String[] chainsForPdb : allNames)
-      {
-        if (chainsForPdb != null)
-        {
-          for (String chain : chainsForPdb)
-          {
-            if (chain != null && !names.contains(chain))
-            {
-              names.add(chain);
-            }
-          }
-        }
-      }
-    }
-    return names;
+    return chainNames;
   }
 
   /**
@@ -1225,34 +1176,34 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
     String cmd = "list residues attr '" + attName + "'";
     List<String> residues = sendChimeraCommand(cmd, true);
 
-    /*
-     * TODO check if Jalview already has this feature name, if so give it a 
-     * distinguishing prefix e.g. chim_
-     */
-    FeatureRenderer fr = alignmentPanel.getFeatureRenderer();
-    // todo should getRenderOrder be in api.FeatureRenderer?
-    // FIXME this is empty if feature display is turned off
-    List<String> existingFeatures = ((jalview.gui.FeatureRenderer) fr)
-            .getRenderOrder();
-    if (existingFeatures.contains(attName))
+    boolean featureAdded = createFeaturesForAttributes(attName, residues);
+    if (featureAdded)
     {
-      attName = getStructureFeaturePrefix() + attName;
+      alignmentPanel.getFeatureRenderer().featuresAdded();
     }
+  }
 
-    /*
-     * Expect 0, 1 or more reply lines of the format (chi2 is attName):
-     * residue id #0:5.A chi2 -155.000836316 index 5
-     * or
-     * residue id #0:6.A chi3 None
-     * 
-     * We assume here that attributes on structure do not naturally convert
-     * to ranges on sequence, i.e. we just set one feature per mapped position.
-     * 
-     * To conflate positions, would need to first build a map 
-     * Map<String value, Map<Sequence seq, List<Integer position>>>
-     * and then traverse it to find feature ranges.
-     */
+  /**
+   * Create features in Jalview for the given attribute name and structure
+   * residues.
+   * 
+   * <pre>
+   * The residue list should be 0, 1 or more reply lines of the format: 
+   *     residue id #0:5.A isHelix -155.000836316 index 5 
+   * or 
+   *     residue id #0:6.A isHelix None
+   * </pre>
+   * 
+   * @param attName
+   * @param residues
+   * @return
+   */
+  protected boolean createFeaturesForAttributes(String attName,
+          List<String> residues)
+  {
     boolean featureAdded = false;
+    String featureGroup = getViewerFeatureGroup();
+
     for (String residue : residues)
     {
       AtomSpec spec = null;
@@ -1263,10 +1214,16 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
       }
       String atomSpec = tokens[2];
       String attValue = tokens[4];
-      if ("None".equals(attValue))
+
+      /*
+       * ignore 'None' (e.g. for phi) or 'False' (e.g. for isHelix)
+       */
+      if ("None".equalsIgnoreCase(attValue)
+              || "False".equalsIgnoreCase(attValue))
       {
         continue;
       }
+
       try
       {
         spec = AtomSpec.fromChimeraAtomspec(atomSpec);
@@ -1275,10 +1232,27 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
         System.err.println("Problem parsing atomspec " + atomSpec);
         continue;
       }
+
+      String chainId = spec.getChain();
+      String description = attValue;
+      float score = Float.NaN;
+      try
+      {
+        score = Float.valueOf(attValue);
+        description = chainId;
+      } catch (NumberFormatException e)
+      {
+        // was not a float value
+      }
+
       String pdbFile = getPdbFileForModel(spec.getModelNumber());
       spec.setPdbFile(pdbFile);
 
       List<AtomSpec> atoms = Collections.singletonList(spec);
+
+      /*
+       * locate the mapped position in the alignment (if any)
+       */
       SearchResults sr = getSsm()
               .findAlignmentPositionsForStructurePositions(atoms);
 
@@ -1286,43 +1260,19 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
        * expect one matched alignment position, or none 
        * (if the structure position is not mapped)
        */
-      for (Match m : sr.getResults())
+      for (SearchResultMatchI m : sr.getResults())
       {
         SequenceI seq = m.getSequence();
         int start = m.getStart();
         int end = m.getEnd();
-
-        float score = Float.NaN;
-        try
-        {
-          score = Float.valueOf(attValue);
-        } catch (NumberFormatException e)
-        {
-          // was not a float value
-        }
-        String featureGroup = getViewerFeatureGroup();
-        SequenceFeature sf = new SequenceFeature(attName, attValue, start,
-                end, score, featureGroup);
+        SequenceFeature sf = new SequenceFeature(attName, description,
+                start, end, score, featureGroup);
+        // todo: should SequenceFeature have an explicit property for chain?
         // note: repeating the action shouldn't duplicate features
         featureAdded |= seq.addSequenceFeature(sf);
       }
     }
-    if (featureAdded)
-    {
-      fr.featuresAdded();
-    }
-  }
-
-  /**
-   * Answers a 'namespace' prefix to use for features created in Jalview from
-   * attributes in the structure viewer
-   * 
-   * @return
-   */
-  protected String getStructureFeaturePrefix()
-  {
-    // TODO pull up as abstract
-    return CHIMERA_FEATURE_PREFIX;
+    return featureAdded;
   }
 
   /**
@@ -1336,4 +1286,25 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
     // todo pull up to interface
     return CHIMERA_FEATURE_GROUP;
   }
+
+
+  public Hashtable<String, String> getChainFile()
+  {
+    return chainFile;
+  }
+
+  public List<ChimeraModel> getChimeraModelByChain(String chain)
+  {
+    return chimeraMaps.get(chainFile.get(chain));
+  }
+
+  public int getModelNoForChain(String chain)
+  {
+    List<ChimeraModel> foundModels = getChimeraModelByChain(chain);
+    if (foundModels != null && !foundModels.isEmpty())
+    {
+      return foundModels.get(0).getModelNumber();
+    }
+    return -1;
+  }
 }