Clover icon

Coverage Report

  1. Project Clover database Wed Nov 13 2024 16:21:17 GMT
  2. Package ext.edu.ucsf.rbvi.strucviz2

File StructureManager.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart0.png
59% of files have more coverage

Code metrics

62
167
32
2
1,106
463
69
0.41
5.22
16
2.16

Classes

Class Line # Actions
StructureManager 61 167 69
0.00%
StructureManager.ModelType 101 0 0
-1.0 -
 

Contributing tests

No tests hitting this source file were found.

Source view

1    /* vim: set ts=2: */
2    /**
3    * Copyright (c) 2006 The Regents of the University of California.
4    * All rights reserved.
5    *
6    * Redistribution and use in source and binary forms, with or without
7    * modification, are permitted provided that the following conditions
8    * are met:
9    * 1. Redistributions of source code must retain the above copyright
10    * notice, this list of conditions, and the following disclaimer.
11    * 2. Redistributions in binary form must reproduce the above
12    * copyright notice, this list of conditions, and the following
13    * disclaimer in the documentation and/or other materials provided
14    * with the distribution.
15    * 3. Redistributions must acknowledge that this software was
16    * originally developed by the UCSF Computer Graphics Laboratory
17    * under support by the NIH National Center for Research Resources,
18    * grant P41-RR01081.
19    *
20    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
21    * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22    * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23    * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS BE LIABLE
24    * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
26    * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
27    * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28    * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
29    * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30    * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31    *
32    */
33    package ext.edu.ucsf.rbvi.strucviz2;
34   
35    import java.io.File;
36    import java.io.IOException;
37    import java.util.ArrayList;
38    import java.util.Arrays;
39    import java.util.Collection;
40    import java.util.HashMap;
41    import java.util.HashSet;
42    import java.util.List;
43    import java.util.Locale;
44    import java.util.Map;
45    import java.util.Properties;
46    import java.util.Set;
47   
48    import org.slf4j.Logger;
49    import org.slf4j.LoggerFactory;
50   
51    import jalview.bin.Cache;
52    import jalview.gui.Preferences;
53    import jalview.util.FileUtils;
54    import jalview.util.Platform;
55   
56    /**
57    * This object maintains the relationship between Chimera objects and Cytoscape
58    * objects.
59    */
60   
 
61    public class StructureManager
62    {
63    /*
64    * Version numbers to build Windows installation paths for
65    * Chimera https://www.cgl.ucsf.edu/chimera/download.html
66    * ChimeraX http://www.rbvi.ucsf.edu/chimerax/download.html#release
67    * https://www.rbvi.ucsf.edu/trac/ChimeraX/wiki/ChangeLog
68    * These are a fallback for Jalview users who don't save path in Preferences;
69    * these will need to be updated as new versions are released;
70    * deliberately not 'final' (so modifiable using Groovy).
71    *
72    * May 2020: 1.14 is Chimera latest, anticipating a few more...
73    * 0.93 is ChimeraX latest, 1.0 expected soon
74    */
75    private static String[] CHIMERA_VERSIONS = new String[] { "1.18",
76    "1.17.4", "1.17.3", "1.17.2", "1.17.1", "1.17", "1.16.2", "1.16.1",
77    "1.16", "1.15.2", "1.15.1", "1.15", "1.14.2", "1.14.1", "1.14",
78    "1.13.1", "1.13", "1.12.2", "1.12.1", "1.12", "1.11.2", "1.11.2",
79    "1.11.1", "1.11" };
80   
81    private static String[] CHIMERA_VERSIONS_BLACKLIST = new String[] {};
82   
83    // Missing 1.1 as this has known bug see JAL-2422
84    private static String[] CHIMERAX_VERSIONS = new String[] { "1.7", "1.6.1",
85    "1.6", "1.5", "1.4", "1.3", "1.2.5", "1.0", "0.93", "0.92", "0.91",
86    "0.9" };
87   
88    private static String[] CHIMERAX_VERSIONS_BLACKLIST = new String[] {
89    "1.1" };
90   
91    static final String[] defaultStructureKeys = { "Structure", "pdb",
92    "pdbFileName", "PDB ID", "structure", "biopax.xref.PDB", "pdb_ids",
93    "ModelName", "ModelNumber" };
94   
95    static final String[] defaultChemStructKeys = { "Smiles", "smiles",
96    "SMILES" };
97   
98    static final String[] defaultResidueKeys = { "FunctionalResidues",
99    "ResidueList", "Residues" };
100   
 
101    public enum ModelType
102    {
103    PDB_MODEL, MODBASE_MODEL, SMILES
104    };
105   
106    public static Properties pathProps;
107   
108    private String chimeraCommandAttr = "ChimeraCommand";
109   
110    private String chimeraOutputTable = "ChimeraTable";
111   
112    private String chimeraOutputAttr = "ChimeraOutput";
113   
114    private boolean haveGUI = true;
115   
116    private ChimeraManager chimeraManager = null;
117   
118    static private List<ChimeraStructuralObject> chimSelectionList;
119   
120    private boolean ignoreCySelection = false;
121   
122    private File configurationDirectory = null;
123   
124    private static Logger logger = LoggerFactory
125    .getLogger(ext.edu.ucsf.rbvi.strucviz2.StructureManager.class);
126   
 
127  0 toggle public StructureManager(boolean haveGUI)
128    {
129  0 this.haveGUI = haveGUI;
130    // Create the Chimera interface
131  0 chimeraManager = new ChimeraManager(this);
132  0 chimSelectionList = new ArrayList<>();
133  0 pathProps = new Properties();
134    }
135   
 
136  0 toggle public ChimeraManager getChimeraManager()
137    {
138  0 return chimeraManager;
139    }
140   
 
141  0 toggle public boolean openStructures(Collection<List<String>> chimObjNames,
142    ModelType type)
143    {
144    // new models
145  0 Map<String, List<ChimeraModel>> newModels = new HashMap<>();
146  0 if (chimObjNames.size() > 0)
147    {
148  0 List<String> names = chimObjNames.iterator().next();
149  0 if (names == null)
150    {
151  0 return false;
152    }
153  0 for (String chimObjName : names)
154    {
155    // get or open the corresponding models if they already exist
156  0 List<ChimeraModel> currentModels = chimeraManager
157    .getChimeraModels(chimObjName, type);
158  0 if (currentModels.size() == 0)
159    {
160    // open and return models
161  0 currentModels = chimeraManager.openModel(chimObjName, type);
162  0 if (currentModels == null)
163    {
164    // failed to open model, continue with next
165  0 continue;
166    }
167    // if (type == ModelType.SMILES) {
168    // newModels.put("smiles:" + chimObjName, currentModels);
169    // } else {
170  0 newModels.put(chimObjName, currentModels);
171    // }
172    // for each model
173  0 for (ChimeraModel currentModel : currentModels)
174    {
175    // if not RIN then associate new model with the Cytoscape
176    // node
177    // if (!currentChimMap.containsKey(currentModel)) {
178    // currentChimMap.put(currentModel, new HashSet<CyIdentifiable>());
179    // }
180    }
181    }
182    }
183    }
184    else
185    {
186  0 return false;
187    }
188    // update dialog
189    // if (mnDialog != null) {
190    // mnDialog.modelChanged();
191    // }
192    // aTask.associate();
193  0 return true;
194   
195    }
196   
197    // TODO: [Release] Handle case where one network is associated with two models
198    // that are opened
199    // at the same time
200    /*
201    * public boolean openStructures(CyNetwork network, Map<CyIdentifiable,
202    * List<String>> chimObjNames, ModelType type) { if
203    * (!chimeraManager.isChimeraLaunched() &&
204    * !chimeraManager.launchChimera(getChimeraPaths(network))) {
205    * logger.error("Chimera could not be launched."); return false; } else if
206    * (chimObjNames.size() == 0) { return false; } else if (network == null) {
207    * return openStructures(chimObjNames.values(), type); }
208    *
209    * // potential rins Set<CyNetwork> potentialRINs = new HashSet<CyNetwork>();
210    * // attributes List<String> attrsFound = new ArrayList<String>();
211    * attrsFound.
212    * addAll(CytoUtils.getMatchingAttributes(network.getDefaultNodeTable(),
213    * getCurrentStructureKeys(network)));
214    * attrsFound.addAll(CytoUtils.getMatchingAttributes
215    * (network.getDefaultNodeTable(), getCurrentChemStructKeys(network))); // new
216    * models Map<String, List<ChimeraModel>> newModels = new HashMap<String,
217    * List<ChimeraModel>>(); // for each node that has an associated structure
218    * for (CyIdentifiable cyObj : chimObjNames.keySet()) { // get possible res
219    * specs List<String> specsFound = null; if (cyObj instanceof CyNode) {
220    * specsFound = ChimUtils.getResidueKeys(network.getDefaultNodeTable(), cyObj,
221    * attrsFound); } // save node to track its selection and mapping to chimera
222    * objects if (!currentCyMap.containsKey(cyObj)) { currentCyMap.put(cyObj, new
223    * HashSet<ChimeraStructuralObject>()); } // save node to network mapping to
224    * keep track of selection events if (!networkMap.containsKey(cyObj)) {
225    * networkMap.put(cyObj, new HashSet<CyNetwork>()); }
226    * networkMap.get(cyObj).add(network); // for each structure that has to be
227    * opened for (String chimObjName : chimObjNames.get(cyObj)) { // get or open
228    * the corresponding models if they already exist List<ChimeraModel>
229    * currentModels = chimeraManager.getChimeraModels(chimObjName, type); if
230    * (currentModels.size() == 0) { // open and return models currentModels =
231    * chimeraManager.openModel(chimObjName, type); if (currentModels == null) {
232    * // failed to open model, continue with next continue; } // if (type ==
233    * ModelType.SMILES) { // newModels.put("smiles:" + chimObjName,
234    * currentModels); // } else { newModels.put(chimObjName, currentModels); // }
235    * // for each model for (ChimeraModel currentModel : currentModels) { //
236    * check if it is a RIN boolean foundRIN = false; if
237    * (currentModel.getModelType().equals(ModelType.PDB_MODEL)) { // go through
238    * all node annotations and check if any of them is a residue // or a chain if
239    * (cyObj instanceof CyNode && network.containsNode((CyNode) cyObj) &&
240    * specsFound != null && specsFound.size() > 0) { for (String resSpec :
241    * specsFound) { ChimeraStructuralObject res =
242    * ChimUtils.fromAttribute(resSpec, chimeraManager); if (res != null && (res
243    * instanceof ChimeraResidue || res instanceof ChimeraChain)) { // if so,
244    * assume it might be a RIN potentialRINs.add(network); foundRIN = true;
245    * break; } } } else if (cyObj instanceof CyNetwork) { // if cyObj is a
246    * network, check for residue/chain annotations in an // arbitrary node
247    * CyNetwork rinNet = (CyNetwork) cyObj; if (rinNet.getNodeList().size() > 0)
248    * { specsFound = ChimUtils.getResidueKeys( rinNet.getDefaultNodeTable(),
249    * rinNet.getNodeList().get(0), attrsFound); for (String resSpec : specsFound)
250    * { ChimeraStructuralObject res = ChimUtils.fromAttribute( resSpec,
251    * chimeraManager); if (res != null && (res instanceof ChimeraResidue || res
252    * instanceof ChimeraChain)) { potentialRINs.add(network); foundRIN = true;
253    * break; } } } } } if (foundRIN) { continue; } // if not RIN then associate
254    * new model with the Cytoscape // node if
255    * (!currentChimMap.containsKey(currentModel)) {
256    * currentChimMap.put(currentModel, new HashSet<CyIdentifiable>()); } String
257    * cyObjName = network.getRow(cyObj).get(CyNetwork.NAME, String.class); if
258    * (cyObjName != null && cyObjName.endsWith(currentModel.getModelName())) { //
259    * it is a modbase model, associate directly
260    * currentCyMap.get(cyObj).add(currentModel);
261    * currentChimMap.get(currentModel).add(cyObj);
262    * currentModel.addCyObject(cyObj, network); } else if (specsFound != null &&
263    * specsFound.size() > 0) { for (String resSpec : specsFound) {
264    * ChimeraStructuralObject specModel = ChimUtils.fromAttribute( resSpec,
265    * chimeraManager); if (specModel == null &&
266    * resSpec.equals(currentModel.getModelName())) { specModel =
267    * chimeraManager.getChimeraModel( currentModel.getModelNumber(),
268    * currentModel.getSubModelNumber()); } if (specModel != null &&
269    * currentModel.toSpec().equals(specModel.toSpec()) ||
270    * currentModel.getModelName().equals("smiles:" + resSpec)) {
271    * currentCyMap.get(cyObj).add(currentModel);
272    * currentChimMap.get(currentModel).add(cyObj);
273    * currentModel.addCyObject(cyObj, network);
274    * currentModel.setFuncResidues(ChimUtils.parseFuncRes(
275    * getResidueList(network, cyObj), chimObjName)); } } } } } } } // networks
276    * that contain nodes associated to newly opened models // this will usually
277    * be of length 1 for (CyNetwork net : potentialRINs) {
278    * addStructureNetwork(net); } // update dialog if (mnDialog != null) {
279    * mnDialog.modelChanged(); } aTask.associate(); return true; }
280    */
 
281  0 toggle public void closeStructures(Set<String> chimObjNames)
282    {
283    // for each cytoscape object and chimera model pair
284  0 for (String modelName : chimObjNames)
285    {
286  0 List<ChimeraModel> models = chimeraManager
287    .getChimeraModels(modelName);
288  0 for (ChimeraModel model : models)
289    {
290  0 closeModel(model);
291    }
292    }
293    // if (mnDialog != null) {
294    // mnDialog.modelChanged();
295    // }
296    }
297   
298    // TODO: [Optional] Can we make a screenshot of a single molecule?
 
299  0 toggle public File saveChimeraImage()
300    {
301  0 File tmpFile = null;
302  0 try
303    {
304    // Create the temp file name
305  0 tmpFile = File.createTempFile("structureViz", ".png");
306  0 chimeraManager.sendChimeraCommand("set bgTransparency", false);
307  0 chimeraManager.sendChimeraCommand(
308    "copy file " + tmpFile.getAbsolutePath() + " png", true);
309  0 chimeraManager.sendChimeraCommand("unset bgTransparency", false);
310    } catch (IOException ioe)
311    {
312    // Log error
313  0 logger.error("Error writing image", ioe);
314    }
315  0 return tmpFile;
316    }
317   
 
318  0 toggle public void closeModel(ChimeraModel model)
319    {
320    // close model in Chimera
321  0 chimeraManager.closeModel(model);
322    // remove all associations
323    // if (currentChimMap.containsKey(model)) {
324    // for (CyIdentifiable cyObj : model.getCyObjects().keySet()) {
325    // if (cyObj == null) {
326    // continue;
327    // } else if (currentCyMap.containsKey(cyObj)) {
328    // currentCyMap.get(cyObj).remove(model);
329    // } else if (cyObj instanceof CyNetwork) {
330    // for (ChimeraResidue residue : model.getResidues()) {
331    // if (currentChimMap.containsKey(residue)) {
332    // for (CyIdentifiable cyObjRes : currentChimMap.get(residue)) {
333    // if (currentCyMap.containsKey(cyObjRes)) {
334    // currentCyMap.get(cyObjRes).remove(residue);
335    // }
336    // }
337    // currentChimMap.remove(residue);
338    // }
339    // }
340    // }
341    // }
342    // currentChimMap.remove(model);
343    // }
344    }
345   
346    // public void addStructureNetwork(CyNetwork rin) {
347    // if (rin == null) {
348    // return;
349    // }
350    // ChimeraModel model = null;
351    // // the network is not added to the model in the currentChimMap
352    // List<String> attrsFound =
353    // CytoUtils.getMatchingAttributes(rin.getDefaultNodeTable(),
354    // getCurrentStructureKeys(rin));
355    // for (CyNode node : rin.getNodeList()) {
356    // if (!networkMap.containsKey(node)) {
357    // networkMap.put(node, new HashSet<CyNetwork>());
358    // }
359    // networkMap.get(node).add(rin);
360    // List<String> specsFound =
361    // ChimUtils.getResidueKeys(rin.getDefaultNodeTable(), node,
362    // attrsFound);
363    // for (String residueSpec : specsFound) {
364    // // if (!rin.getRow(node).isSet(ChimUtils.RESIDUE_ATTR)) {
365    // // continue;
366    // // }
367    // // String residueSpec = rin.getRow(node).get(ChimUtils.RESIDUE_ATTR,
368    // String.class);
369    // ChimeraStructuralObject chimObj = ChimUtils.fromAttribute(residueSpec,
370    // chimeraManager);
371    // // chimObj.getChimeraModel().addCyObject(node, rin);
372    // if (chimObj == null || chimObj instanceof ChimeraModel) {
373    // continue;
374    // }
375    // model = chimObj.getChimeraModel();
376    // if (!currentCyMap.containsKey(node)) {
377    // currentCyMap.put(node, new HashSet<ChimeraStructuralObject>());
378    // }
379    // currentCyMap.get(node).add(chimObj);
380    // if (!currentChimMap.containsKey(chimObj)) {
381    // currentChimMap.put(chimObj, new HashSet<CyIdentifiable>());
382    // }
383    // currentChimMap.get(chimObj).add(node);
384    // }
385    // }
386    // if (model != null) {
387    // model.addCyObject(rin, rin);
388    // if (!currentCyMap.containsKey(rin)) {
389    // currentCyMap.put(rin, new HashSet<ChimeraStructuralObject>());
390    // }
391    // currentCyMap.get(rin).add(model);
392    // }
393    // }
394   
 
395  0 toggle public void exitChimera()
396    {
397    // // exit chimera, invokes clearOnExitChimera
398    // if (mnDialog != null) {
399    // mnDialog.setVisible(false);
400    // mnDialog = null;
401    // }
402    // if (alDialog != null) {
403    // alDialog.setVisible(false);
404    // }
405  0 chimeraManager.exitChimera();
406    }
407   
408    // invoked by ChimeraManager whenever Chimera exits
 
409  0 toggle public void clearOnChimeraExit()
410    {
411    // // clear structures
412    // currentCyMap.clear();
413    // currentChimMap.clear();
414    // networkMap.clear();
415  0 chimSelectionList.clear();
416    // if (chimTable != null) {
417    // ((CyTableManager)
418    // getService(CyTableManager.class)).deleteTable(chimTable.getSUID());
419    // }
420    // if (mnDialog != null) {
421    // if (mnDialog.isVisible()) {
422    // mnDialog.lostChimera();
423    // mnDialog.setVisible(false);
424    // }
425    // mnDialog = null;
426    // if (alDialog != null) {
427    // alDialog.setVisible(false);
428    // }
429    // }
430    }
431   
432    // We need to do this in two passes since some parts of a structure might be
433    // selected and some might not. Our selection model (unfortunately) only
434    // tells
435    // us that something has changed, not what...
 
436  0 toggle public void updateCytoscapeSelection()
437    {
438    // List<ChimeraStructuralObject> selectedChimObj
439  0 ignoreCySelection = true;
440    // System.out.println("update Cytoscape selection");
441    // find all possibly selected Cytoscape objects and unselect them
442    // Set<CyNetwork> networks = new HashSet<CyNetwork>();
443    // for (CyIdentifiable currentCyObj : currentCyMap.keySet()) {
444    // if (!networkMap.containsKey(currentCyObj)) {
445    // continue;
446    // }
447    // Set<CyNetwork> currentCyNetworks = networkMap.get(currentCyObj);
448    // if (currentCyNetworks == null || currentCyNetworks.size() == 0) {
449    //
450    // continue;
451    // }
452    // for (CyNetwork network : currentCyNetworks) {
453    // if ((currentCyObj instanceof CyNode && network.containsNode((CyNode)
454    // currentCyObj))
455    // || (currentCyObj instanceof CyEdge && network
456    // .containsEdge((CyEdge) currentCyObj))) {
457    // network.getRow(currentCyObj).set(CyNetwork.SELECTED, false);
458    // networks.add(network);
459    // }
460    // }
461    // }
462    //
463    // // select only those associated with selected Chimera objects
464    // Set<CyIdentifiable> currentCyObjs = new HashSet<CyIdentifiable>();
465    // for (ChimeraStructuralObject chimObj : chimSelectionList) {
466    // ChimeraModel currentSelModel = chimObj.getChimeraModel();
467    // if (currentChimMap.containsKey(currentSelModel)) {
468    // currentCyObjs.addAll(currentChimMap.get(currentSelModel));
469    // }
470    // if (currentChimMap.containsKey(chimObj)) {
471    // currentCyObjs.addAll(currentChimMap.get(chimObj));
472    // }
473    // // System.out.println(chimObj.toSpec() + ": " +
474    // // currentCyObjs.size());
475    // }
476    // for (CyIdentifiable cyObj : currentCyObjs) {
477    // // System.out.println(cyObj.toString());
478    // if (cyObj == null || !networkMap.containsKey(cyObj)) {
479    // continue;
480    // }
481    // Set<CyNetwork> currentCyNetworks = networkMap.get(cyObj);
482    // if (currentCyNetworks == null || currentCyNetworks.size() == 0) {
483    // continue;
484    // }
485    // for (CyNetwork network : currentCyNetworks) {
486    // if ((cyObj instanceof CyNode && network.containsNode((CyNode) cyObj))
487    // || (cyObj instanceof CyEdge && network.containsEdge((CyEdge) cyObj))) {
488    // network.getRow(cyObj).set(CyNetwork.SELECTED, true);
489    // networks.add(network);
490    // }
491    // }
492    // }
493    //
494    // CyNetworkViewManager cyNetViewManager = (CyNetworkViewManager)
495    // getService(CyNetworkViewManager.class);
496    // // Update network views
497    // for (CyNetwork network : networks) {
498    // Collection<CyNetworkView> views =
499    // cyNetViewManager.getNetworkViews(network);
500    // for (CyNetworkView view : views) {
501    // view.updateView();
502    // }
503    // }
504  0 ignoreCySelection = false;
505    }
506   
 
507  0 toggle public void cytoscapeSelectionChanged(Map<Long, Boolean> selectedRows)
508    {
509    // if (ignoreCySelection || currentCyMap.size() == 0) {
510    // return;
511    // }
512    // // clearSelectionList();
513    // // System.out.println("cytoscape selection changed");
514    // // iterate over all cy objects with associated models
515    // for (CyIdentifiable cyObj : currentCyMap.keySet()) {
516    // if (cyObj instanceof CyNetwork ||
517    // !selectedRows.containsKey(cyObj.getSUID())) {
518    // continue;
519    // }
520    // for (ChimeraStructuralObject chimObj : currentCyMap.get(cyObj)) {
521    // if (selectedRows.get(cyObj.getSUID())) {
522    // addChimSelection(chimObj);
523    // if (chimObj instanceof ChimeraResidue) {
524    // if (chimObj.getChimeraModel().isSelected()) {
525    // removeChimSelection(chimObj.getChimeraModel());
526    // } else if (chimObj.getChimeraModel()
527    // .getChain(((ChimeraResidue) chimObj).getChainId()).isSelected()) {
528    // removeChimSelection(chimObj.getChimeraModel().getChain(
529    // ((ChimeraResidue) chimObj).getChainId()));
530    // }
531    // }
532    // } else {
533    // removeChimSelection(chimObj);
534    // if (chimObj.hasSelectedChildren() && chimObj instanceof ChimeraModel) {
535    // for (ChimeraResidue residue : ((ChimeraModel) chimObj)
536    // .getSelectedResidues()) {
537    // removeChimSelection(residue);
538    // }
539    // }
540    // }
541    // }
542    // }
543    // System.out.println("selection list: " + getChimSelectionCount());
544  0 updateChimeraSelection();
545  0 selectionChanged();
546    }
547   
548    // Save models in a HashMap/Set for better performance?
 
549  0 toggle public void updateChimeraSelection()
550    {
551    // System.out.println("update Chimera selection");
552  0 String selSpec = "";
553  0 for (int i = 0; i < chimSelectionList.size(); i++)
554    {
555  0 ChimeraStructuralObject nodeInfo = chimSelectionList.get(i);
556    // we do not care about the model anymore
557  0 selSpec = selSpec.concat(nodeInfo.toSpec());
558  0 if (i < chimSelectionList.size() - 1)
559    {
560  0 selSpec.concat("|");
561    }
562    }
563  0 if (selSpec.length() > 0)
564    {
565  0 chimeraManager.select("sel " + selSpec);
566    }
567    else
568    {
569  0 chimeraManager.select("~sel");
570    }
571    }
572   
573    /**
574    * This is called by the selectionListener to let us know that the user has
575    * changed their selection in Chimera. We need to go back to Chimera to find
576    * out what is currently selected and update our list.
577    */
 
578  0 toggle public void chimeraSelectionChanged()
579    {
580    // System.out.println("Chimera selection changed");
581  0 clearSelectionList();
582    // Execute the command to get the list of models with selections
583  0 Map<Integer, ChimeraModel> selectedModelsMap = chimeraManager
584    .getSelectedModels();
585    // Now get the residue-level data
586  0 chimeraManager.getSelectedResidues(selectedModelsMap);
587    // Get the selected objects
588  0 try
589    {
590  0 for (ChimeraModel selectedModel : selectedModelsMap.values())
591    {
592  0 int modelNumber = selectedModel.getModelNumber();
593  0 int subModelNumber = selectedModel.getSubModelNumber();
594    // Get the corresponding "real" model
595  0 if (chimeraManager.hasChimeraModel(modelNumber, subModelNumber))
596    {
597  0 ChimeraModel dataModel = chimeraManager
598    .getChimeraModel(modelNumber, subModelNumber);
599  0 if (dataModel.getResidueCount() == selectedModel.getResidueCount()
600    || dataModel
601    .getModelType() == StructureManager.ModelType.SMILES)
602    {
603    // Select the entire model
604  0 addChimSelection(dataModel);
605    // dataModel.setSelected(true);
606    }
607    else
608    {
609  0 for (ChimeraChain selectedChain : selectedModel.getChains())
610    {
611  0 ChimeraChain dataChain = dataModel
612    .getChain(selectedChain.getChainId());
613  0 if (selectedChain.getResidueCount() == dataChain
614    .getResidueCount())
615    {
616  0 addChimSelection(dataChain);
617    // dataChain.setSelected(true);
618    }
619    // else {
620    // Need to select individual residues
621  0 for (ChimeraResidue res : selectedChain.getResidues())
622    {
623  0 String residueIndex = res.getIndex();
624  0 ChimeraResidue residue = dataChain.getResidue(residueIndex);
625  0 if (residue == null)
626    {
627  0 continue;
628    }
629  0 addChimSelection(residue);
630    // residue.setSelected(true);
631    } // resIter.hasNext
632    // }
633    } // chainIter.hasNext()
634    }
635    }
636    } // modelIter.hasNext()
637    } catch (Exception ex)
638    {
639  0 logger.warn("Could not update selection", ex);
640    }
641    // System.out.println("selection list: " + getChimSelectionCount());
642    // Finally, update the navigator panel
643  0 selectionChanged();
644  0 updateCytoscapeSelection();
645    }
646   
 
647  0 toggle public void selectFunctResidues(Collection<ChimeraModel> models)
648    {
649  0 clearSelectionList();
650  0 for (ChimeraModel model : models)
651    {
652  0 for (ChimeraResidue residue : model.getFuncResidues())
653    {
654  0 addChimSelection(residue);
655    }
656    }
657  0 updateChimeraSelection();
658  0 updateCytoscapeSelection();
659  0 selectionChanged();
660    }
661   
662    // public void selectFunctResidues(CyNode node, CyNetwork network) {
663    // clearSelectionList();
664    // if (currentCyMap.containsKey(node)) {
665    // Set<ChimeraStructuralObject> chimObjects = currentCyMap.get(node);
666    // for (ChimeraStructuralObject obj : chimObjects) {
667    // if (obj instanceof ChimeraModel) {
668    // ChimeraModel model = (ChimeraModel) obj;
669    // for (ChimeraResidue residue : model.getFuncResidues()) {
670    // addChimSelection(residue);
671    // }
672    // }
673    // }
674    // }
675    // updateChimeraSelection();
676    // updateCytoscapeSelection();
677    // selectionChanged();
678    // }
679   
 
680  0 toggle public List<ChimeraStructuralObject> getChimSelectionList()
681    {
682  0 return chimSelectionList;
683    }
684   
 
685  0 toggle public int getChimSelectionCount()
686    {
687  0 return chimSelectionList.size();
688    }
689   
690    /**
691    * Add a selection to the selection list. This is called primarily by the
692    * Model Navigator Dialog to keep the selections in sync
693    *
694    * @param selectionToAdd
695    * the selection to add to our list
696    */
 
697  0 toggle public void addChimSelection(ChimeraStructuralObject selectionToAdd)
698    {
699  0 if (selectionToAdd != null
700    && !chimSelectionList.contains(selectionToAdd))
701    {
702  0 chimSelectionList.add(selectionToAdd);
703  0 selectionToAdd.setSelected(true);
704    }
705    }
706   
707    /**
708    * Remove a selection from the selection list. This is called primarily by the
709    * Model Navigator Dialog to keep the selections in sync
710    *
711    * @param selectionToRemove
712    * the selection to remove from our list
713    */
 
714  0 toggle public void removeChimSelection(ChimeraStructuralObject selectionToRemove)
715    {
716  0 if (selectionToRemove != null
717    && chimSelectionList.contains(selectionToRemove))
718    {
719  0 chimSelectionList.remove(selectionToRemove);
720  0 selectionToRemove.setSelected(false);
721    }
722    }
723   
724    /**
725    * Clear the list of selected objects
726    */
 
727  0 toggle public void clearSelectionList()
728    {
729  0 for (ChimeraStructuralObject cso : chimSelectionList)
730    {
731  0 if (cso != null)
732    {
733  0 cso.setSelected(false);
734    }
735    }
736  0 chimSelectionList.clear();
737    }
738   
739    /**
740    * Associate a new network with the corresponding Chimera objects.
741    *
742    * @param network
743    */
744   
745    /**
746    * Dump and refresh all of our model/chain/residue info
747    */
 
748  0 toggle public void updateModels()
749    {
750    // Stop all of our listeners while we try to handle this
751  0 chimeraManager.stopListening();
752   
753    // Get all of the open models
754  0 List<ChimeraModel> newModelList = chimeraManager.getModelList();
755   
756    // Match them up -- assume that the model #'s haven't changed
757  0 for (ChimeraModel newModel : newModelList)
758    {
759    // Get the color (for our navigator)
760  0 newModel.setModelColor(chimeraManager.getModelColor(newModel));
761   
762    // Get our model info
763  0 int modelNumber = newModel.getModelNumber();
764  0 int subModelNumber = newModel.getSubModelNumber();
765   
766    // If we already know about this model number, get the Structure,
767    // which tells us about the associated CyNode
768  0 if (chimeraManager.hasChimeraModel(modelNumber, subModelNumber))
769    {
770  0 ChimeraModel oldModel = chimeraManager.getChimeraModel(modelNumber,
771    subModelNumber);
772  0 chimeraManager.removeChimeraModel(modelNumber, subModelNumber);
773  0 newModel.setModelType(oldModel.getModelType());
774  0 if (oldModel.getModelType() == ModelType.SMILES)
775    {
776  0 newModel.setModelName(oldModel.getModelName());
777    }
778    // re-assign associations to cytoscape objects
779    // Map<CyIdentifiable, CyNetwork> oldModelCyObjs =
780    // oldModel.getCyObjects();
781    // for (CyIdentifiable cyObj : oldModelCyObjs.keySet()) {
782    // // add cy objects to the new model
783    // newModel.addCyObject(cyObj, oldModelCyObjs.get(cyObj));
784    // if (currentCyMap.containsKey(cyObj)) {
785    // currentCyMap.get(cyObj).add(newModel);
786    // if (currentCyMap.get(cyObj).contains(oldModel)) {
787    // currentCyMap.get(cyObj).remove(oldModel);
788    // }
789    // }
790    // }
791    // // add new model to the chimera objects map and remove old model
792    // if (currentChimMap.containsKey(oldModel)) {
793    // currentChimMap.put(newModel, currentChimMap.get(oldModel));
794    // currentChimMap.remove(oldModel);
795    // }
796    }
797    // add new model to ChimeraManager
798  0 chimeraManager.addChimeraModel(modelNumber, subModelNumber, newModel);
799   
800    // Get the residue information
801  0 if (newModel.getModelType() != ModelType.SMILES)
802    {
803  0 chimeraManager.addResidues(newModel);
804    }
805    // for (CyIdentifiable cyObj : newModel.getCyObjects().keySet()) {
806    // if (cyObj != null && cyObj instanceof CyNetwork) {
807    // addStructureNetwork((CyNetwork) cyObj);
808    // } else if (cyObj != null && cyObj instanceof CyNode) {
809    // newModel.setFuncResidues(ChimUtils.parseFuncRes(
810    // getResidueList(newModel.getCyObjects().get(cyObj), cyObj),
811    // newModel.getModelName()));
812    // }
813    // }
814    }
815   
816    // associate all models with any node or network
817    // aTask.associate();
818   
819    // Restart all of our listeners
820  0 chimeraManager.startListening();
821    // Done
822    }
823   
 
824  0 toggle public void launchModelNavigatorDialog()
825    {
826    // TODO: [Optional] Use haveGUI flag
827    // if (!haveGUI) {
828    // return;
829    // }
830    // if (mnDialog == null) {
831    // CySwingApplication cyApplication = (CySwingApplication)
832    // getService(CySwingApplication.class);
833    // mnDialog = new ModelNavigatorDialog(cyApplication.getJFrame(), this);
834    // mnDialog.pack();
835    // }
836    // mnDialog.setVisible(true);
837    }
838   
 
839  0 toggle public boolean isMNDialogOpen()
840    {
841    // if (mnDialog != null && mnDialog.isVisible()) {
842    // return true;
843    // }
844  0 return false;
845    }
846   
847    /**
848    * Invoked by the listener thread.
849    */
 
850  0 toggle public void modelChanged()
851    {
852    // if (mnDialog != null) {
853    // mnDialog.modelChanged();
854    // }
855    }
856   
857    /**
858    * Inform our interface that the selection has changed
859    */
 
860  0 toggle public void selectionChanged()
861    {
862    // if (mnDialog != null) {
863    // // System.out.println("update dialog selection");
864    // mnDialog.updateSelection(new
865    // ArrayList<ChimeraStructuralObject>(chimSelectionList));
866    // }
867    }
868   
 
869  0 toggle public void launchAlignDialog(boolean useChains)
870    {
871    // TODO: [Optional] Use haveGUI flag
872    // Sometimes it does not appear in Windows
873    // if (!haveGUI) {
874    // return;
875    // }
876    // if (alDialog != null) {
877    // alDialog.setVisible(false);
878    // alDialog.dispose();
879    // }
880    // System.out.println("launch align dialog");
881  0 List<ChimeraStructuralObject> chimObjectList = new ArrayList<>();
882  0 for (ChimeraModel model : chimeraManager.getChimeraModels())
883    {
884  0 if (useChains)
885    {
886  0 for (ChimeraChain chain : model.getChains())
887    {
888  0 chimObjectList.add(chain);
889    }
890    }
891    else
892    {
893  0 chimObjectList.add(model);
894    }
895    }
896    // Bring up the dialog
897    // CySwingApplication cyApplication = (CySwingApplication)
898    // getService(CySwingApplication.class);
899    // alDialog = new AlignStructuresDialog(cyApplication.getJFrame(), this,
900    // chimObjectList);
901    // alDialog.pack();
902    // alDialog.setVisible(true);
903    }
904   
 
905  0 toggle public List<String> getAllStructureKeys()
906    {
907  0 return Arrays.asList(defaultStructureKeys);
908    }
909   
 
910  0 toggle public List<String> getAllChemStructKeys()
911    {
912  0 return Arrays.asList(defaultChemStructKeys);
913    }
914   
 
915  0 toggle public List<String> getAllResidueKeys()
916    {
917  0 return Arrays.asList(defaultResidueKeys);
918    }
919   
 
920  0 toggle public List<String> getAllChimeraResidueAttributes()
921    {
922  0 List<String> attributes = new ArrayList<>();
923    // attributes.addAll(rinManager.getResAttrs());
924  0 attributes.addAll(chimeraManager.getAttrList());
925  0 return attributes;
926    }
927   
928    StructureSettings defaultSettings = null;
929   
930    // TODO: [Optional] Change priority of Chimera paths
 
931  0 toggle public static List<String> getChimeraPaths(boolean isChimeraX)
932    {
933  0 List<String> pathList = new ArrayList<>();
934   
935    // if no network is available and the settings have been modified by the
936    // user, check for a
937    // path to chimera
938    //
939    // For Jalview, Preferences/Cache plays this role instead
940    // if (defaultSettings != null)
941    // {
942    // String defaultPath = defaultSettings.getChimeraPath();
943    // if (defaultPath != null && !defaultPath.equals(""))
944    // {
945    // pathList.add(defaultPath);
946    // return pathList;
947    // }
948    // }
949   
950  0 String userPath = Cache
951  0 .getDefault(isChimeraX ? Preferences.CHIMERAX_PATH
952    : Preferences.CHIMERA_PATH, null);
953   
954    /*
955    * paths are based on getChimeraPaths() in
956    * Chimera:
957    * https://github.com/RBVI/structureViz2/blob/master/src/main/java/edu/ucsf/rbvi/structureViz2/internal/model/StructureManager.java
958    * ChimeraX:
959    * https://github.com/RBVI/structureVizX/blob/master/src/main/java/edu/ucsf/rbvi/structureVizX/internal/model/StructureManager.java
960    */
961  0 String chimera = isChimeraX ? "ChimeraX" : "Chimera";
962  0 String chimeraExe = isChimeraX ? "ChimeraX" : "chimera";
963  0 String[] versionsBlacklist = isChimeraX ? CHIMERAX_VERSIONS_BLACKLIST
964    : CHIMERA_VERSIONS_BLACKLIST;
965  0 String[] versionsWhitelist = isChimeraX ? CHIMERAX_VERSIONS
966    : CHIMERA_VERSIONS;
967   
968    /*
969    * Jalview addition: check if path set in user preferences
970    */
971  0 if (userPath != null)
972    {
973    // in macos, deal with the user selecting the .app folder
974  0 boolean adjusted = false;
975  0 if (Platform.isMac() && userPath.endsWith((".app")))
976    {
977  0 String possiblePath = String.format("%s/Contents/MacOS/%s",
978    userPath, chimeraExe);
979  0 if (new File(possiblePath).exists())
980    {
981  0 pathList.add(possiblePath);
982  0 adjusted = true;
983    }
984    }
985  0 if (!adjusted)
986    {
987  0 pathList.add(userPath);
988    }
989    }
990   
991    // Add default installation paths
992  0 if (Platform.isLinux())
993    {
994    // ChimeraX .deb and .rpm packages put symbolic link from
995    // /usr/bin/chimerax
996  0 pathList.add(String.format("/usr/bin/%s",
997    chimeraExe.toLowerCase(Locale.ROOT)));
998  0 pathList.add(String.format("/usr/bin/%s", chimeraExe));
999   
1000  0 pathList.add(String.format("/usr/local/bin/%s",
1001    chimeraExe.toLowerCase(Locale.ROOT)));
1002  0 pathList.add(String.format("/usr/local/bin/%s", chimeraExe));
1003   
1004    // these paths also used by .deb and .rpm
1005  0 pathList.add(String.format("/usr/lib/ucsf-%s/bin/%s",
1006    chimera.toLowerCase(Locale.ROOT), chimeraExe));
1007  0 pathList.add(String.format("/usr/libexec/UCSF-%s/bin/%s", chimera,
1008    chimeraExe));
1009   
1010  0 pathList.add(String.format("/usr/local/chimera/bin/%s", chimeraExe));
1011   
1012    // user home paths
1013  0 pathList.add(
1014    String.format("%s/bin/%s", System.getProperty("user.home"),
1015    chimeraExe.toLowerCase(Locale.ROOT)));
1016  0 pathList.add(String.format("%s/bin/%s",
1017    System.getProperty("user.home"), chimeraExe));
1018  0 pathList.add(String.format("%s/opt/bin/%s",
1019    System.getProperty("user.home"),
1020    chimeraExe.toLowerCase(Locale.ROOT)));
1021  0 pathList.add(String.format("%s/opt/bin/%s",
1022    System.getProperty("user.home"), chimeraExe));
1023  0 pathList.add(String.format("%s/local/bin/%s",
1024    System.getProperty("user.home"),
1025    chimeraExe.toLowerCase(Locale.ROOT)));
1026  0 pathList.add(String.format("%s/local/bin/%s",
1027    System.getProperty("user.home"), chimeraExe));
1028    }
1029  0 else if (Platform.isWin())
1030    {
1031  0 String[] templates = new String[] {
1032    "%s\\" + chimera + "%s\\bin\\" + chimeraExe + ".exe" };
1033  0 String[] roots = new String[] { "\\Program Files",
1034    "C:\\Program Files", "\\Program Files (x86)",
1035    "C:\\Program Files (x86)", String.format("%s\\AppData\\Local",
1036    System.getProperty("user.home")) };
1037  0 String versionSeparator = " ";
1038  0 for (File f : FileUtils.getMatchingVersionedFiles(templates, roots,
1039    null, versionsBlacklist, versionSeparator, true))
1040    {
1041  0 pathList.add(f.getPath());
1042    }
1043    }
1044  0 else if (Platform.isMac())
1045    {
1046  0 Set<File> installedChimera = new HashSet<>();
1047  0 String[] templates = new String[] {
1048    "%s/" + chimera + "%s.app/Contents/MacOS/" + chimeraExe };
1049  0 String[] roots = new String[] { "/Applications", String
1050    .format("%s/Applications", System.getProperty("user.home")) };
1051  0 String versionSeparator = "-";
1052  0 for (File f : FileUtils.getMatchingVersionedFiles(templates, roots,
1053    null, versionsBlacklist, versionSeparator, true))
1054    {
1055  0 pathList.add(f.getPath());
1056    }
1057    }
1058  0 return pathList;
1059    }
1060   
 
1061  0 toggle public void setChimeraPathProperty(String path)
1062    {
1063    // CytoUtils.setDefaultChimeraPath(registrar, chimeraPropertyName,
1064    // chimeraPathPropertyKey,
1065    // path);
1066    }
1067   
 
1068  0 toggle public void setStructureSettings(StructureSettings structureSettings)
1069    {
1070  0 this.defaultSettings = structureSettings;
1071    }
1072   
 
1073  0 toggle public String getCurrentChimeraPath(Object object)
1074    {
1075  0 if (defaultSettings != null)
1076    {
1077  0 return defaultSettings.getChimeraPath();
1078    }
1079    else
1080    {
1081  0 return "";
1082    }
1083    }
1084   
1085    // public void initChimTable() {
1086    // CyTableManager manager = (CyTableManager) getService(CyTableManager.class);
1087    // CyTableFactory factory = (CyTableFactory) getService(CyTableFactory.class);
1088    // for (CyTable table : manager.getGlobalTables()) {
1089    // if (table.getTitle().equals(chimeraOutputTable)) {
1090    // manager.deleteTable(table.getSUID());
1091    // }
1092    // }
1093    // chimTable = factory.createTable(chimeraOutputTable, chimeraCommandAttr,
1094    // String.class,
1095    // false, true);
1096    // manager.addTable(chimTable);
1097    // if (chimTable.getColumn(chimeraOutputAttr) == null) {
1098    // chimTable.createListColumn(chimeraOutputAttr, String.class, false);
1099    // }
1100    // }
1101   
1102    // public void addChimReply(String command, List<String> reply) {
1103    // chimTable.getRow(command).set(chimeraOutputAttr, reply);
1104    // }
1105   
1106    }