1 package ext.edu.ucsf.rbvi.strucviz2;
5 import java.io.IOException;
6 import java.util.ArrayList;
7 import java.util.Collection;
8 import java.util.HashMap;
12 import org.slf4j.Logger;
13 import org.slf4j.LoggerFactory;
15 import ext.edu.ucsf.rbvi.strucviz2.StructureManager.ModelType;
16 import ext.edu.ucsf.rbvi.strucviz2.port.ListenerThreads;
19 * This object maintains the Chimera communication information.
21 public class ChimeraManager
24 static private Process chimera;
26 static private ListenerThreads chimeraListenerThreads;
28 static private Map<Integer, ChimeraModel> currentModelsMap;
30 private static Logger logger = LoggerFactory
31 .getLogger(ext.edu.ucsf.rbvi.strucviz2.ChimeraManager.class);
33 private StructureManager structureManager;
35 public ChimeraManager(StructureManager structureManager)
37 this.structureManager = structureManager;
39 chimeraListenerThreads = null;
40 currentModelsMap = new HashMap<Integer, ChimeraModel>();
44 public List<ChimeraModel> getChimeraModels(String modelName)
46 List<ChimeraModel> models = getChimeraModels(modelName,
48 models.addAll(getChimeraModels(modelName, ModelType.SMILES));
52 public List<ChimeraModel> getChimeraModels(String modelName,
55 List<ChimeraModel> models = new ArrayList<ChimeraModel>();
56 for (ChimeraModel model : currentModelsMap.values())
58 if (modelName.equals(model.getModelName())
59 && modelType.equals(model.getModelType()))
67 public Map<String, List<ChimeraModel>> getChimeraModelsMap()
69 Map<String, List<ChimeraModel>> models = new HashMap<String, List<ChimeraModel>>();
70 for (ChimeraModel model : currentModelsMap.values())
72 String modelName = model.getModelName();
73 if (!models.containsKey(modelName))
75 models.put(modelName, new ArrayList<ChimeraModel>());
77 if (!models.get(modelName).contains(model))
79 models.get(modelName).add(model);
85 public ChimeraModel getChimeraModel(Integer modelNumber,
86 Integer subModelNumber)
88 Integer key = ChimUtils.makeModelKey(modelNumber, subModelNumber);
89 if (currentModelsMap.containsKey(key))
91 return currentModelsMap.get(key);
96 public ChimeraModel getChimeraModel()
98 return currentModelsMap.values().iterator().next();
101 public Collection<ChimeraModel> getChimeraModels()
103 // this method is invoked by the model navigator dialog
104 return currentModelsMap.values();
107 public int getChimeraModelsCount(boolean smiles)
109 // this method is invokes by the model navigator dialog
110 int counter = currentModelsMap.size();
116 for (ChimeraModel model : currentModelsMap.values())
118 if (model.getModelType() == ModelType.SMILES)
126 public boolean hasChimeraModel(Integer modelNubmer)
128 return hasChimeraModel(modelNubmer, 0);
131 public boolean hasChimeraModel(Integer modelNubmer, Integer subModelNumber)
133 return currentModelsMap.containsKey(ChimUtils.makeModelKey(modelNubmer,
137 public void addChimeraModel(Integer modelNumber, Integer subModelNumber,
140 currentModelsMap.put(
141 ChimUtils.makeModelKey(modelNumber, subModelNumber), model);
144 public void removeChimeraModel(Integer modelNumber, Integer subModelNumber)
146 int modelKey = ChimUtils.makeModelKey(modelNumber, subModelNumber);
147 if (currentModelsMap.containsKey(modelKey))
149 currentModelsMap.remove(modelKey);
153 public List<ChimeraModel> openModel(String modelPath, ModelType type)
155 logger.info("chimera open " + modelPath);
157 List<String> response = null;
158 // TODO: [Optional] Handle modbase models
159 if (type == ModelType.MODBASE_MODEL)
161 response = sendChimeraCommand("open modbase:" + modelPath, true);
162 // } else if (type == ModelType.SMILES) {
163 // response = sendChimeraCommand("open smiles:" + modelName, true);
164 // modelName = "smiles:" + modelName;
168 response = sendChimeraCommand("open " + modelPath, true);
170 if (response == null)
172 // something went wrong
173 logger.warn("Could not open " + modelPath);
176 List<ChimeraModel> models = new ArrayList<ChimeraModel>();
177 int[] modelNumbers = null;
178 if (type == ModelType.PDB_MODEL)
180 for (String line : response)
182 if (line.startsWith("#"))
184 modelNumbers = ChimUtils.parseOpenedModelNumber(line);
185 if (modelNumbers != null)
187 int modelNumber = ChimUtils.makeModelKey(modelNumbers[0],
189 if (currentModelsMap.containsKey(modelNumber))
193 String modelName = modelPath;
194 // TODO: [Optional] Convert path to name in a better way
195 if (modelPath.lastIndexOf(File.separator) > 0)
197 modelName = modelPath.substring(modelPath
198 .lastIndexOf(File.separator) + 1);
200 else if (modelPath.lastIndexOf("/") > 0)
202 modelName = modelPath
203 .substring(modelPath.lastIndexOf("/") + 1);
205 ChimeraModel newModel = new ChimeraModel(modelName, type,
206 modelNumbers[0], modelNumbers[1]);
207 currentModelsMap.put(modelNumber, newModel);
208 models.add(newModel);
216 // TODO: [Optional] Open smiles from file would fail. Do we need it?
217 // If parsing fails, iterate over all open models to get the right one
218 List<ChimeraModel> openModels = getModelList();
219 for (ChimeraModel openModel : openModels)
221 String openModelName = openModel.getModelName();
222 if (openModelName.endsWith("..."))
224 openModelName = openModelName.substring(0,
225 openModelName.length() - 3);
227 if (modelPath.startsWith(openModelName))
229 openModel.setModelName(modelPath);
230 int modelNumber = ChimUtils
231 .makeModelKey(openModel.getModelNumber(),
232 openModel.getSubModelNumber());
233 if (!currentModelsMap.containsKey(modelNumber))
235 currentModelsMap.put(modelNumber, openModel);
236 models.add(openModel);
242 // assign color and residues to open models
243 for (ChimeraModel newModel : models)
246 Color modelColor = getModelColor(newModel);
247 if (modelColor != null)
249 newModel.setModelColor(modelColor);
252 // Get our properties (default color scheme, etc.)
253 // Make the molecule look decent
254 // chimeraSend("repr stick "+newModel.toSpec());
256 // Create the information we need for the navigator
257 if (type != ModelType.SMILES)
259 addResidues(newModel);
263 sendChimeraCommand("focus", false);
268 public void closeModel(ChimeraModel model)
270 // int model = structure.modelNumber();
271 // int subModel = structure.subModelNumber();
272 // Integer modelKey = makeModelKey(model, subModel);
274 logger.info("chimera close model " + model.getModelName());
275 if (currentModelsMap.containsKey(ChimUtils.makeModelKey(
276 model.getModelNumber(), model.getSubModelNumber())))
278 sendChimeraCommand("close " + model.toSpec(), false);
279 // currentModelNamesMap.remove(model.getModelName());
280 currentModelsMap.remove(ChimUtils.makeModelKey(
281 model.getModelNumber(), model.getSubModelNumber()));
282 // selectionList.remove(chimeraModel);
286 logger.warn("Could not find model " + model.getModelName()
292 public void startListening()
294 sendChimeraCommand("listen start models; listen start select", false);
297 public void stopListening()
299 sendChimeraCommand("listen stop models; listen stop select", false);
303 * Select something in Chimera
306 * the selection command to pass to Chimera
308 public void select(String command)
310 sendChimeraCommand("listen stop select; " + command
311 + "; listen start select", false);
316 sendChimeraCommand("focus", false);
319 public void clearOnChimeraExit()
322 currentModelsMap.clear();
323 chimeraListenerThreads = null;
324 structureManager.clearOnChimeraExit();
327 public void exitChimera()
329 if (isChimeraLaunched() && chimera != null)
331 sendChimeraCommand("stop really", false);
335 } catch (Exception ex)
340 clearOnChimeraExit();
343 public Map<Integer, ChimeraModel> getSelectedModels()
345 Map<Integer, ChimeraModel> selectedModelsMap = new HashMap<Integer, ChimeraModel>();
346 List<String> chimeraReply = sendChimeraCommand(
347 "list selection level molecule", true);
348 if (chimeraReply != null)
350 for (String modelLine : chimeraReply)
352 ChimeraModel chimeraModel = new ChimeraModel(modelLine);
353 Integer modelKey = ChimUtils.makeModelKey(
354 chimeraModel.getModelNumber(),
355 chimeraModel.getSubModelNumber());
356 selectedModelsMap.put(modelKey, chimeraModel);
359 return selectedModelsMap;
362 public List<String> getSelectedResidueSpecs()
364 List<String> selectedResidues = new ArrayList<String>();
365 List<String> chimeraReply = sendChimeraCommand(
366 "list selection level residue", true);
367 if (chimeraReply != null)
369 for (String inputLine : chimeraReply)
371 String[] inputLineParts = inputLine.split("\\s+");
372 if (inputLineParts.length == 5)
374 selectedResidues.add(inputLineParts[2]);
378 return selectedResidues;
381 public void getSelectedResidues(
382 Map<Integer, ChimeraModel> selectedModelsMap)
384 List<String> chimeraReply = sendChimeraCommand(
385 "list selection level residue", true);
386 if (chimeraReply != null)
388 for (String inputLine : chimeraReply)
390 ChimeraResidue r = new ChimeraResidue(inputLine);
391 Integer modelKey = ChimUtils.makeModelKey(r.getModelNumber(),
392 r.getSubModelNumber());
393 if (selectedModelsMap.containsKey(modelKey))
395 ChimeraModel model = selectedModelsMap.get(modelKey);
403 * Return the list of ChimeraModels currently open. Warning: if smiles model
404 * name too long, only part of it with "..." is printed.
407 * @return List of ChimeraModel's
409 // TODO: [Optional] Handle smiles names in a better way in Chimera?
410 public List<ChimeraModel> getModelList()
412 List<ChimeraModel> modelList = new ArrayList<ChimeraModel>();
413 List<String> list = sendChimeraCommand("list models type molecule",
417 for (String modelLine : list)
419 ChimeraModel chimeraModel = new ChimeraModel(modelLine);
420 modelList.add(chimeraModel);
427 * Return the list of depiction presets available from within Chimera. Chimera
428 * will return the list as a series of lines with the format: Preset type
429 * number "description"
431 * @return list of presets
433 public List<String> getPresets()
435 ArrayList<String> presetList = new ArrayList<String>();
436 List<String> output = sendChimeraCommand("preset list", true);
439 for (String preset : output)
441 preset = preset.substring(7); // Skip over the "Preset"
442 preset = preset.replaceFirst("\"", "(");
443 preset = preset.replaceFirst("\"", ")");
444 // string now looks like: type number (description)
445 presetList.add(preset);
451 public boolean isChimeraLaunched()
453 // TODO: [Optional] What is the best way to test if chimera is launched?
455 // sendChimeraCommand("test", true) !=null
463 public boolean launchChimera(List<String> chimeraPaths)
465 // Do nothing if Chimera is already launched
466 if (isChimeraLaunched())
471 // Try to launch Chimera (eventually using one of the possible paths)
472 String error = "Error message: ";
473 String workingPath = "";
474 // iterate over possible paths for starting Chimera
475 for (String chimeraPath : chimeraPaths)
477 File path = new File(chimeraPath);
478 if (!path.canExecute())
480 error += "File '" + path + "' does not exist.\n";
485 List<String> args = new ArrayList<String>();
486 args.add(chimeraPath);
488 args.add("ReadStdin");
489 ProcessBuilder pb = new ProcessBuilder(args);
490 chimera = pb.start();
492 workingPath = chimeraPath;
493 logger.info("Strarting " + chimeraPath);
495 } catch (Exception e)
497 // Chimera could not be started
498 error += e.getMessage();
501 // If no error, then Chimera was launched successfully
502 if (error.length() == 0)
504 // Initialize the listener threads
505 chimeraListenerThreads = new ListenerThreads(chimera,
507 chimeraListenerThreads.start();
508 // structureManager.initChimTable();
509 structureManager.setChimeraPathProperty(workingPath);
510 // TODO: [Optional] Check Chimera version and show a warning if below 1.8
511 // Ask Chimera to give us updates
516 // Tell the user that Chimera could not be started because of an error
522 * Determine the color that Chimera is using for this model.
525 * the ChimeraModel we want to get the Color for
526 * @return the default model Color for this model in Chimera
528 public Color getModelColor(ChimeraModel model)
530 List<String> colorLines = sendChimeraCommand(
531 "list model spec " + model.toSpec() + " attribute color", true);
532 if (colorLines == null || colorLines.size() == 0)
536 return ChimUtils.parseModelColor((String) colorLines.get(0));
541 * Get information about the residues associated with a model. This uses the
542 * Chimera listr command. We don't return the resulting residues, but we add
543 * the residues to the model.
546 * the ChimeraModel to get residue information for
549 public void addResidues(ChimeraModel model)
551 int modelNumber = model.getModelNumber();
552 int subModelNumber = model.getSubModelNumber();
553 // Get the list -- it will be in the reply log
554 List<String> reply = sendChimeraCommand(
555 "list residues spec " + model.toSpec(), true);
560 for (String inputLine : reply)
562 ChimeraResidue r = new ChimeraResidue(inputLine);
563 if (r.getModelNumber() == modelNumber
564 || r.getSubModelNumber() == subModelNumber)
571 public List<String> getAttrList()
573 List<String> attributes = new ArrayList<String>();
574 final List<String> reply = sendChimeraCommand("list resattr", true);
577 for (String inputLine : reply)
579 String[] lineParts = inputLine.split("\\s");
580 if (lineParts.length == 2 && lineParts[0].equals("resattr"))
582 attributes.add(lineParts[1]);
589 public Map<ChimeraResidue, Object> getAttrValues(String aCommand,
592 Map<ChimeraResidue, Object> values = new HashMap<ChimeraResidue, Object>();
593 final List<String> reply = sendChimeraCommand("list residue spec "
594 + model.toSpec() + " attribute " + aCommand, true);
597 for (String inputLine : reply)
599 String[] lineParts = inputLine.split("\\s");
600 if (lineParts.length == 5)
602 ChimeraResidue residue = ChimUtils
603 .getResidue(lineParts[2], model);
604 String value = lineParts[4];
607 if (value.equals("None"))
611 if (value.equals("True") || value.equals("False"))
613 values.put(residue, Boolean.valueOf(value));
618 Double doubleValue = Double.valueOf(value);
619 values.put(residue, doubleValue);
620 } catch (NumberFormatException ex)
622 values.put(residue, value);
632 * Send a command to Chimera.
635 * Command string to be send.
637 * Flag indicating whether the method should return the reply from
639 * @return List of Strings corresponding to the lines in the Chimera reply or
642 public List<String> sendChimeraCommand(String command, boolean reply)
644 if (!isChimeraLaunched())
649 chimeraListenerThreads.clearResponse(command);
650 String text = command.concat("\n");
651 // System.out.println("send command to chimera: " + text);
655 chimera.getOutputStream().write(text.getBytes());
656 chimera.getOutputStream().flush();
657 } catch (IOException e)
659 // logger.info("Unable to execute command: " + text);
660 // logger.info("Exiting...");
661 logger.warn("Unable to execute command: " + text);
662 logger.warn("Exiting...");
663 clearOnChimeraExit();
670 return chimeraListenerThreads.getResponse(command);