JAL-2422 basic proof of concept of ChimeraX opened/coloured by Jalview
[jalview.git] / src / jalview / ext / rbvi / chimera / AtomSpecModel.java
index f3c9c1e..a72844e 100644 (file)
@@ -1,3 +1,23 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
 package jalview.ext.rbvi.chimera;
 
 import jalview.util.IntRangeComparator;
@@ -26,7 +46,7 @@ import java.util.TreeMap;
  * </ul>
  * 
  * <pre>
- * @see http://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/frameatom_spec.html
+ * &#64;see http://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/frameatom_spec.html
  * </pre>
  */
 public class AtomSpecModel
@@ -38,7 +58,7 @@ public class AtomSpecModel
    */
   public AtomSpecModel()
   {
-    atomSpec = new TreeMap<Integer, Map<String, List<int[]>>>();
+    atomSpec = new TreeMap<>();
   }
 
   /**
@@ -57,7 +77,7 @@ public class AtomSpecModel
     Map<String, List<int[]>> modelData = atomSpec.get(model);
     if (modelData == null)
     {
-      atomSpec.put(model, modelData = new TreeMap<String, List<int[]>>());
+      atomSpec.put(model, modelData = new TreeMap<>());
     }
 
     /*
@@ -66,7 +86,7 @@ public class AtomSpecModel
     List<int[]> chainData = modelData.get(chain);
     if (chainData == null)
     {
-      chainData = new ArrayList<int[]>();
+      chainData = new ArrayList<>();
       modelData.put(chain, chainData);
     }
 
@@ -100,7 +120,7 @@ public class AtomSpecModel
 
       for (String chain : modelData.keySet())
       {
-        chain = chain.trim();
+        chain = " ".equals(chain) ? chain : chain.trim();
 
         List<int[]> rangeList = modelData.get(chain);
 
@@ -130,7 +150,8 @@ public class AtomSpecModel
             /*
              * we have a break so append the last range
              */
-            appendRange(sb, start, end, chain, firstPositionForModel);
+            appendRange(sb, start, end, chain, firstPositionForModel,
+                    false);
             firstPositionForModel = false;
             start = range[0];
             end = range[1];
@@ -142,7 +163,7 @@ public class AtomSpecModel
          */
         if (!rangeList.isEmpty())
         {
-          appendRange(sb, start, end, chain, firstPositionForModel);
+          appendRange(sb, start, end, chain, firstPositionForModel, false);
           firstPositionForModel = false;
         }
       }
@@ -158,7 +179,7 @@ public class AtomSpecModel
    * @param firstPositionForModel
    */
   protected void appendRange(StringBuilder sb, int start, int end,
-          String chain, boolean firstPositionForModel)
+          String chain, boolean firstPositionForModel, boolean isChimeraX)
   {
     if (!firstPositionForModel)
     {
@@ -172,9 +193,89 @@ public class AtomSpecModel
     {
       sb.append(start).append("-").append(end);
     }
-    if (chain.length() > 0)
+
+    if (!isChimeraX)
+    {
+      sb.append(".");
+      if (!" ".equals(chain))
+      {
+        sb.append(chain);
+      }
+    }
+  }
+
+  /**
+   * Returns the range(s) formatted as a ChimeraX atomspec, for example
+   * <p>
+   * #1/A:2-20,30-40/B:10-20|#2/A:12-30
+   * 
+   * @return
+   */
+  public String getAtomSpecX()
+  {
+    StringBuilder sb = new StringBuilder(128);
+    boolean firstModel = true;
+    for (Integer model : atomSpec.keySet())
     {
-      sb.append(".").append(chain);
+      if (!firstModel)
+      {
+        sb.append("|");
+      }
+      firstModel = false;
+      sb.append("#").append(model);
+  
+      final Map<String, List<int[]>> modelData = atomSpec.get(model);
+  
+      for (String chain : modelData.keySet())
+      {
+        boolean firstPositionForChain = true;
+        chain = " ".equals(chain) ? chain : chain.trim();
+        sb.append("/").append(chain).append(":");
+        List<int[]> rangeList = modelData.get(chain);
+  
+        /*
+         * sort ranges into ascending start position order
+         */
+        Collections.sort(rangeList, IntRangeComparator.ASCENDING);
+  
+        int start = rangeList.isEmpty() ? 0 : rangeList.get(0)[0];
+        int end = rangeList.isEmpty() ? 0 : rangeList.get(0)[1];
+  
+        Iterator<int[]> iterator = rangeList.iterator();
+        while (iterator.hasNext())
+        {
+          int[] range = iterator.next();
+          if (range[0] <= end + 1)
+          {
+            /*
+             * range overlaps or is contiguous with the last one
+             * - so just extend the end position, and carry on
+             * (unless this is the last in the list)
+             */
+            end = Math.max(end, range[1]);
+          }
+          else
+          {
+            /*
+             * we have a break so append the last range
+             */
+            appendRange(sb, start, end, chain, firstPositionForChain, true);
+            start = range[0];
+            end = range[1];
+            firstPositionForChain = false;
+          }
+        }
+  
+        /*
+         * and append the last range
+         */
+        if (!rangeList.isEmpty())
+        {
+          appendRange(sb, start, end, chain, firstPositionForChain, true);
+        }
+        firstPositionForChain = false;
+      }
     }
+    return sb.toString();
   }
 }