JAL-4020 class reformatting
[jalview.git] / src / ext / edu / ucsf / rbvi / strucviz2 / StructureManager.java
index 1208638..f7dba79 100644 (file)
@@ -1,8 +1,37 @@
+/* vim: set ts=2: */
+/**
+ * Copyright (c) 2006 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions, and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions, and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *   3. Redistributions must acknowledge that this software was
+ *      originally developed by the UCSF Computer Graphics Laboratory
+ *      under support by the NIH National Center for Research Resources,
+ *      grant P41-RR01081.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
 package ext.edu.ucsf.rbvi.strucviz2;
 
-import jalview.bin.Cache;
-import jalview.gui.Preferences;
-
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -10,6 +39,7 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
@@ -17,6 +47,9 @@ import java.util.Set;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import jalview.bin.Cache;
+import jalview.gui.Preferences;
+
 /**
  * This object maintains the relationship between Chimera objects and Cytoscape
  * objects.
@@ -24,15 +57,36 @@ import org.slf4j.LoggerFactory;
 
 public class StructureManager
 {
-  static final String[] defaultStructureKeys =
-  { "Structure", "pdb", "pdbFileName", "PDB ID", "structure",
-      "biopax.xref.PDB", "pdb_ids", "ModelName", "ModelNumber" };
+  /*
+   * Version numbers to build Windows installation paths for 
+   * Chimera  https://www.cgl.ucsf.edu/chimera/download.html
+   * ChimeraX http://www.rbvi.ucsf.edu/chimerax/download.html#release
+   *          https://www.rbvi.ucsf.edu/trac/ChimeraX/wiki/ChangeLog
+   * These are a fallback for Jalview users who don't save path in Preferences;
+   * these will need to be updated as new versions are released;
+   * deliberately not 'final' (so modifiable using Groovy).
+   * 
+   * May 2020: 1.14 is Chimera latest, anticipating a few more...
+   * 0.93 is ChimeraX latest, 1.0 expected soon
+   */
+  private static String[] CHIMERA_VERSIONS = new String[] { "1.16.2",
+      "1.16.1", "1.16", "1.15.2", "1.15.1", "1.15", "1.14.2", "1.14.1",
+      "1.14", "1.13.1", "1.13", "1.12.2", "1.12.1", "1.12", "1.11.2",
+      "1.11.2", "1.11.1", "1.11" };
+
+  // Missing 1.1 as this has known bug see JAL-2422
+  private static String[] CHIMERAX_VERSIONS = new String[] { "1.3", "1.2.5",
+      "1.0", "0.93", "0.92", "0.91", "0.9" };
+
+  static final String[] defaultStructureKeys = { "Structure", "pdb",
+      "pdbFileName", "PDB ID", "structure", "biopax.xref.PDB", "pdb_ids",
+      "ModelName", "ModelNumber" };
 
-  static final String[] defaultChemStructKeys =
-  { "Smiles", "smiles", "SMILES" };
+  static final String[] defaultChemStructKeys = { "Smiles", "smiles",
+      "SMILES" };
 
-  static final String[] defaultResidueKeys =
-  { "FunctionalResidues", "ResidueList", "Residues" };
+  static final String[] defaultResidueKeys = { "FunctionalResidues",
+      "ResidueList", "Residues" };
 
   public enum ModelType
   {
@@ -65,7 +119,7 @@ public class StructureManager
     this.haveGUI = haveGUI;
     // Create the Chimera interface
     chimeraManager = new ChimeraManager(this);
-    chimSelectionList = new ArrayList<ChimeraStructuralObject>();
+    chimSelectionList = new ArrayList<>();
     pathProps = new Properties();
   }
 
@@ -78,7 +132,7 @@ public class StructureManager
           ModelType type)
   {
     // new models
-    Map<String, List<ChimeraModel>> newModels = new HashMap<String, List<ChimeraModel>>();
+    Map<String, List<ChimeraModel>> newModels = new HashMap<>();
     if (chimObjNames.size() > 0)
     {
       List<String> names = chimObjNames.iterator().next();
@@ -89,8 +143,8 @@ public class StructureManager
       for (String chimObjName : names)
       {
         // get or open the corresponding models if they already exist
-        List<ChimeraModel> currentModels = chimeraManager.getChimeraModels(
-                chimObjName, type);
+        List<ChimeraModel> currentModels = chimeraManager
+                .getChimeraModels(chimObjName, type);
         if (currentModels.size() == 0)
         {
           // open and return models
@@ -530,11 +584,11 @@ public class StructureManager
         // Get the corresponding "real" model
         if (chimeraManager.hasChimeraModel(modelNumber, subModelNumber))
         {
-          ChimeraModel dataModel = chimeraManager.getChimeraModel(
-                  modelNumber, subModelNumber);
-          if (dataModel.getResidueCount() == selectedModel
-                  .getResidueCount()
-                  || dataModel.getModelType() == StructureManager.ModelType.SMILES)
+          ChimeraModel dataModel = chimeraManager
+                  .getChimeraModel(modelNumber, subModelNumber);
+          if (dataModel.getResidueCount() == selectedModel.getResidueCount()
+                  || dataModel
+                          .getModelType() == StructureManager.ModelType.SMILES)
           {
             // Select the entire model
             addChimSelection(dataModel);
@@ -544,8 +598,8 @@ public class StructureManager
           {
             for (ChimeraChain selectedChain : selectedModel.getChains())
             {
-              ChimeraChain dataChain = dataModel.getChain(selectedChain
-                      .getChainId());
+              ChimeraChain dataChain = dataModel
+                      .getChain(selectedChain.getChainId());
               if (selectedChain.getResidueCount() == dataChain
                       .getResidueCount())
               {
@@ -814,7 +868,7 @@ public class StructureManager
     // alDialog.dispose();
     // }
     // System.out.println("launch align dialog");
-    List<ChimeraStructuralObject> chimObjectList = new ArrayList<ChimeraStructuralObject>();
+    List<ChimeraStructuralObject> chimObjectList = new ArrayList<>();
     for (ChimeraModel model : chimeraManager.getChimeraModels())
     {
       if (useChains)
@@ -855,7 +909,7 @@ public class StructureManager
 
   public List<String> getAllChimeraResidueAttributes()
   {
-    List<String> attributes = new ArrayList<String>();
+    List<String> attributes = new ArrayList<>();
     // attributes.addAll(rinManager.getResAttrs());
     attributes.addAll(chimeraManager.getAttrList());
     return attributes;
@@ -864,48 +918,144 @@ public class StructureManager
   StructureSettings defaultSettings = null;
 
   // TODO: [Optional] Change priority of Chimera paths
-  public List<String> getChimeraPaths()
+  public static List<String> getChimeraPaths(boolean isChimeraX)
   {
-    List<String> pathList = new ArrayList<String>();
+    List<String> pathList = new ArrayList<>();
 
     // if no network is available and the settings have been modified by the
     // user, check for a
     // path to chimera
-    if (defaultSettings != null)
-    {
-      String defaultPath = defaultSettings.getChimeraPath();
-      if (defaultPath != null && !defaultPath.equals(""))
-      {
-        pathList.add(defaultPath);
-        return pathList;
-      }
-    }
+    //
+    // For Jalview, Preferences/Cache plays this role instead
+    // if (defaultSettings != null)
+    // {
+    // String defaultPath = defaultSettings.getChimeraPath();
+    // if (defaultPath != null && !defaultPath.equals(""))
+    // {
+    // pathList.add(defaultPath);
+    // return pathList;
+    // }
+    // }
+
+    String os = System.getProperty("os.name");
+    String userPath = Cache
+            .getDefault(isChimeraX ? Preferences.CHIMERAX_PATH
+                    : Preferences.CHIMERA_PATH, null);
+
+    /*
+     * paths are based on getChimeraPaths() in
+     * Chimera:
+     * https://github.com/RBVI/structureViz2/blob/master/src/main/java/edu/ucsf/rbvi/structureViz2/internal/model/StructureManager.java
+     * ChimeraX:
+     * https://github.com/RBVI/structureVizX/blob/master/src/main/java/edu/ucsf/rbvi/structureVizX/internal/model/StructureManager.java
+     */
+    String chimera = isChimeraX ? "ChimeraX" : "Chimera";
+    String chimeraExe = isChimeraX ? "ChimeraX" : "chimera";
 
     /*
-     * Jalview addition: check if path set in user preferences.
+     * Jalview addition: check if path set in user preferences
      */
-    String userPath = Cache.getDefault(Preferences.CHIMERA_PATH, null);
     if (userPath != null)
     {
-      pathList.add(userPath);
+      // in macos, deal with the user selecting the .app folder
+      boolean adjusted = false;
+      if (os.startsWith("Mac") && userPath.endsWith((".app")))
+      {
+        String possiblePath = String.format("%s/Contents/MacOS/%s",
+                userPath, chimeraExe);
+        if (new File(possiblePath).exists())
+        {
+          pathList.add(possiblePath);
+          adjusted = true;
+        }
+      }
+      if (!adjusted)
+      {
+        pathList.add(userPath);
+      }
     }
 
     // Add default installation paths
-    String os = System.getProperty("os.name");
     if (os.startsWith("Linux"))
     {
-      pathList.add("/usr/local/chimera/bin/chimera");
-      pathList.add("/usr/local/bin/chimera");
-      pathList.add("/usr/bin/chimera");
+      // ChimeraX .deb and .rpm packages put symbolic link from
+      // /usr/bin/chimerax
+      pathList.add(String.format("/usr/bin/%s",
+              chimeraExe.toLowerCase(Locale.ROOT)));
+      pathList.add(String.format("/usr/bin/%s", chimeraExe));
+
+      pathList.add(String.format("/usr/local/bin/%s",
+              chimeraExe.toLowerCase(Locale.ROOT)));
+      pathList.add(String.format("/usr/local/bin/%s", chimeraExe));
+
+      // these paths also used by .deb and .rpm
+      pathList.add(String.format("/usr/lib/ucsf-%s/bin/%s",
+              chimera.toLowerCase(Locale.ROOT), chimeraExe));
+      pathList.add(String.format("/usr/libexec/UCSF-%s/bin/%s", chimera,
+              chimeraExe));
+
+      pathList.add(String.format("/usr/local/chimera/bin/%s", chimeraExe));
+
+      // user home paths
+      pathList.add(
+              String.format("%s/bin/%s", System.getProperty("user.home"),
+                      chimeraExe.toLowerCase(Locale.ROOT)));
+      pathList.add(String.format("%s/bin/%s",
+              System.getProperty("user.home"), chimeraExe));
+      pathList.add(String.format("%s/opt/bin/%s",
+              System.getProperty("user.home"),
+              chimeraExe.toLowerCase(Locale.ROOT)));
+      pathList.add(String.format("%s/opt/bin/%s",
+              System.getProperty("user.home"), chimeraExe));
+      pathList.add(String.format("%s/local/bin/%s",
+              System.getProperty("user.home"),
+              chimeraExe.toLowerCase(Locale.ROOT)));
+      pathList.add(String.format("%s/local/bin/%s",
+              System.getProperty("user.home"), chimeraExe));
     }
     else if (os.startsWith("Windows"))
     {
-      pathList.add("\\Program Files\\Chimera\\bin\\chimera");
-      pathList.add("C:\\Program Files\\Chimera\\bin\\chimera.exe");
+      for (String root : new String[] { "\\Program Files",
+          "C:\\Program Files", "\\Program Files (x86)",
+          "C:\\Program Files (x86)", String.format("%s\\AppData\\Local",
+                  System.getProperty("user.home")) })
+      {
+        String[] candidates = isChimeraX ? CHIMERAX_VERSIONS
+                : CHIMERA_VERSIONS;
+        for (String version : candidates)
+        {
+          // TODO original code doesn't include version in path; which is right?
+          String path = String.format("%s\\%s %s\\bin\\%s", root, chimera,
+                  version, chimeraExe);
+          pathList.add(path);
+          pathList.add(path + ".exe");
+        }
+        // try without a version number too
+        String path = String.format("%s\\%s\\bin\\%s", root, chimera,
+                chimeraExe);
+        pathList.add(path);
+        pathList.add(path + ".exe");
+      }
     }
     else if (os.startsWith("Mac"))
     {
-      pathList.add("/Applications/Chimera.app/Contents/MacOS/chimera");
+      // check for installations with version numbers first
+      String[] candidates = isChimeraX ? CHIMERAX_VERSIONS
+              : CHIMERA_VERSIONS;
+      for (String version : candidates)
+      {
+        pathList.add(
+                String.format("/Applications/%s-%s.app/Contents/MacOS/%s",
+                        chimera, version, chimeraExe));
+        pathList.add(
+                String.format("%s/Applications/%s-%s.app/Contents/MacOS/%s",
+                        System.getProperty("user.home"), chimera, version,
+                        chimeraExe));
+      }
+      pathList.add(String.format("/Applications/%s.app/Contents/MacOS/%s",
+              chimera, chimeraExe));
+      pathList.add(String.format("%s/Applications/%s.app/Contents/MacOS/%s",
+              System.getProperty("user.home"), chimera, chimeraExe));
     }
     return pathList;
   }