Clover icon

jalviewX

  1. Project Clover database Wed Oct 31 2018 15:13:58 GMT
  2. Package ext.edu.ucsf.rbvi.strucviz2

File ChimeraManager.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart1.png
53% of files have more coverage

Code metrics

96
269
41
1
904
644
108
0.4
6.56
41
2.63

Classes

Class Line # Actions
ChimeraManager 61 269 108 383
0.0566502475.7%
 

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 jalview.ws.HttpClientUtils;
36   
37    import java.awt.Color;
38    import java.io.BufferedReader;
39    import java.io.File;
40    import java.io.IOException;
41    import java.io.InputStream;
42    import java.io.InputStreamReader;
43    import java.nio.file.Paths;
44    import java.util.ArrayList;
45    import java.util.Collection;
46    import java.util.HashMap;
47    import java.util.List;
48    import java.util.Map;
49   
50    import org.apache.http.NameValuePair;
51    import org.apache.http.message.BasicNameValuePair;
52    import org.slf4j.Logger;
53    import org.slf4j.LoggerFactory;
54   
55    import ext.edu.ucsf.rbvi.strucviz2.StructureManager.ModelType;
56    import ext.edu.ucsf.rbvi.strucviz2.port.ListenerThreads;
57   
58    /**
59    * This object maintains the Chimera communication information.
60    */
 
61    public class ChimeraManager
62    {
63    private static final int REST_REPLY_TIMEOUT_MS = 15000;
64   
65    private static final int CONNECTION_TIMEOUT_MS = 100;
66   
67    private static final boolean debug = false;
68   
69    private int chimeraRestPort;
70   
71    private Process chimera;
72   
73    private ListenerThreads chimeraListenerThread;
74   
75    private Map<Integer, ChimeraModel> currentModelsMap;
76   
77    private Logger logger = LoggerFactory
78    .getLogger(ext.edu.ucsf.rbvi.strucviz2.ChimeraManager.class);
79   
80    private StructureManager structureManager;
81   
 
82  2 toggle public ChimeraManager(StructureManager structureManager)
83    {
84  2 this.structureManager = structureManager;
85  2 chimera = null;
86  2 chimeraListenerThread = null;
87  2 currentModelsMap = new HashMap<>();
88   
89    }
90   
 
91  0 toggle public List<ChimeraModel> getChimeraModels(String modelName)
92    {
93  0 List<ChimeraModel> models = getChimeraModels(modelName,
94    ModelType.PDB_MODEL);
95  0 models.addAll(getChimeraModels(modelName, ModelType.SMILES));
96  0 return models;
97    }
98   
 
99  0 toggle public List<ChimeraModel> getChimeraModels(String modelName,
100    ModelType modelType)
101    {
102  0 List<ChimeraModel> models = new ArrayList<>();
103  0 for (ChimeraModel model : currentModelsMap.values())
104    {
105  0 if (modelName.equals(model.getModelName())
106    && modelType.equals(model.getModelType()))
107    {
108  0 models.add(model);
109    }
110    }
111  0 return models;
112    }
113   
 
114  0 toggle public Map<String, List<ChimeraModel>> getChimeraModelsMap()
115    {
116  0 Map<String, List<ChimeraModel>> models = new HashMap<>();
117  0 for (ChimeraModel model : currentModelsMap.values())
118    {
119  0 String modelName = model.getModelName();
120  0 if (!models.containsKey(modelName))
121    {
122  0 models.put(modelName, new ArrayList<ChimeraModel>());
123    }
124  0 if (!models.get(modelName).contains(model))
125    {
126  0 models.get(modelName).add(model);
127    }
128    }
129  0 return models;
130    }
131   
 
132  0 toggle public ChimeraModel getChimeraModel(Integer modelNumber,
133    Integer subModelNumber)
134    {
135  0 Integer key = ChimUtils.makeModelKey(modelNumber, subModelNumber);
136  0 if (currentModelsMap.containsKey(key))
137    {
138  0 return currentModelsMap.get(key);
139    }
140  0 return null;
141    }
142   
 
143  0 toggle public ChimeraModel getChimeraModel()
144    {
145  0 return currentModelsMap.values().iterator().next();
146    }
147   
 
148  0 toggle public Collection<ChimeraModel> getChimeraModels()
149    {
150    // this method is invoked by the model navigator dialog
151  0 return currentModelsMap.values();
152    }
153   
 
154  0 toggle public int getChimeraModelsCount(boolean smiles)
155    {
156    // this method is invokes by the model navigator dialog
157  0 int counter = currentModelsMap.size();
158  0 if (smiles)
159    {
160  0 return counter;
161    }
162   
163  0 for (ChimeraModel model : currentModelsMap.values())
164    {
165  0 if (model.getModelType() == ModelType.SMILES)
166    {
167  0 counter--;
168    }
169    }
170  0 return counter;
171    }
172   
 
173  0 toggle public boolean hasChimeraModel(Integer modelNubmer)
174    {
175  0 return hasChimeraModel(modelNubmer, 0);
176    }
177   
 
178  0 toggle public boolean hasChimeraModel(Integer modelNubmer, Integer subModelNumber)
179    {
180  0 return currentModelsMap.containsKey(ChimUtils.makeModelKey(modelNubmer,
181    subModelNumber));
182    }
183   
 
184  0 toggle public void addChimeraModel(Integer modelNumber, Integer subModelNumber,
185    ChimeraModel model)
186    {
187  0 currentModelsMap.put(
188    ChimUtils.makeModelKey(modelNumber, subModelNumber), model);
189    }
190   
 
191  0 toggle public void removeChimeraModel(Integer modelNumber, Integer subModelNumber)
192    {
193  0 int modelKey = ChimUtils.makeModelKey(modelNumber, subModelNumber);
194  0 if (currentModelsMap.containsKey(modelKey))
195    {
196  0 currentModelsMap.remove(modelKey);
197    }
198    }
199   
 
200  0 toggle public List<ChimeraModel> openModel(String modelPath, ModelType type)
201    {
202  0 return openModel(modelPath, getFileNameFromPath(modelPath), type);
203    }
204   
205    /**
206    * Overloaded method to allow Jalview to pass in a model name.
207    *
208    * @param modelPath
209    * @param modelName
210    * @param type
211    * @return
212    */
 
213  0 toggle public List<ChimeraModel> openModel(String modelPath, String modelName,
214    ModelType type)
215    {
216  0 logger.info("chimera open " + modelPath);
217    // stopListening();
218  0 List<ChimeraModel> modelList = getModelList();
219  0 List<String> response = null;
220    // TODO: [Optional] Handle modbase models
221  0 if (type == ModelType.MODBASE_MODEL)
222    {
223  0 response = sendChimeraCommand("open modbase:" + modelPath, true);
224    // } else if (type == ModelType.SMILES) {
225    // response = sendChimeraCommand("open smiles:" + modelName, true);
226    // modelName = "smiles:" + modelName;
227    }
228    else
229    {
230  0 response = sendChimeraCommand("open " + modelPath, true);
231    }
232  0 if (response == null)
233    {
234    // something went wrong
235  0 logger.warn("Could not open " + modelPath);
236  0 return null;
237    }
238   
239    // patch for Jalview - set model name in Chimera
240    // TODO: find a variant that works for sub-models
241  0 for (ChimeraModel newModel : getModelList())
242    {
243  0 if (!modelList.contains(newModel))
244    {
245  0 newModel.setModelName(modelName);
246  0 sendChimeraCommand(
247    "setattr M name " + modelName + " #"
248    + newModel.getModelNumber(), false);
249  0 modelList.add(newModel);
250    }
251    }
252   
253    // assign color and residues to open models
254  0 for (ChimeraModel chimeraModel : modelList)
255    {
256    // get model color
257  0 Color modelColor = getModelColor(chimeraModel);
258  0 if (modelColor != null)
259    {
260  0 chimeraModel.setModelColor(modelColor);
261    }
262   
263    // Get our properties (default color scheme, etc.)
264    // Make the molecule look decent
265    // chimeraSend("repr stick "+newModel.toSpec());
266   
267    // Create the information we need for the navigator
268  0 if (type != ModelType.SMILES)
269    {
270  0 addResidues(chimeraModel);
271    }
272    }
273   
274  0 sendChimeraCommand("focus", false);
275    // startListening(); // see ChimeraListener
276  0 return modelList;
277    }
278   
279    /**
280    * Refactored method to extract the last (or only) element delimited by file
281    * path separator.
282    *
283    * @param modelPath
284    * @return
285    */
 
286  0 toggle private String getFileNameFromPath(String modelPath)
287    {
288  0 String modelName = modelPath;
289  0 if (modelPath == null)
290    {
291  0 return null;
292    }
293    // TODO: [Optional] Convert path to name in a better way
294  0 if (modelPath.lastIndexOf(File.separator) > 0)
295    {
296  0 modelName = modelPath
297    .substring(modelPath.lastIndexOf(File.separator) + 1);
298    }
299  0 else if (modelPath.lastIndexOf("/") > 0)
300    {
301  0 modelName = modelPath.substring(modelPath.lastIndexOf("/") + 1);
302    }
303  0 return modelName;
304    }
305   
 
306  0 toggle public void closeModel(ChimeraModel model)
307    {
308    // int model = structure.modelNumber();
309    // int subModel = structure.subModelNumber();
310    // Integer modelKey = makeModelKey(model, subModel);
311  0 stopListening();
312  0 logger.info("chimera close model " + model.getModelName());
313  0 if (currentModelsMap.containsKey(ChimUtils.makeModelKey(
314    model.getModelNumber(), model.getSubModelNumber())))
315    {
316  0 sendChimeraCommand("close " + model.toSpec(), false);
317    // currentModelNamesMap.remove(model.getModelName());
318  0 currentModelsMap.remove(ChimUtils.makeModelKey(
319    model.getModelNumber(), model.getSubModelNumber()));
320    // selectionList.remove(chimeraModel);
321    }
322    else
323    {
324  0 logger.warn("Could not find model " + model.getModelName()
325    + " to close.");
326    }
327  0 startListening();
328    }
329   
 
330  0 toggle public void startListening()
331    {
332  0 sendChimeraCommand("listen start models; listen start selection", false);
333    }
334   
 
335  0 toggle public void stopListening()
336    {
337  0 sendChimeraCommand("listen stop models ; listen stop selection ", false);
338    }
339   
340    /**
341    * Tell Chimera we are listening on the given URI
342    *
343    * @param uri
344    */
 
345  0 toggle public void startListening(String uri)
346    {
347  0 sendChimeraCommand("listen start models url " + uri
348    + ";listen start select prefix SelectionChanged url " + uri,
349    false);
350    }
351   
352    /**
353    * Select something in Chimera
354    *
355    * @param command
356    * the selection command to pass to Chimera
357    */
 
358  0 toggle public void select(String command)
359    {
360  0 sendChimeraCommand("listen stop selection; " + command
361    + "; listen start selection", false);
362    }
363   
 
364  0 toggle public void focus()
365    {
366  0 sendChimeraCommand("focus", false);
367    }
368   
 
369  0 toggle public void clearOnChimeraExit()
370    {
371  0 chimera = null;
372  0 currentModelsMap.clear();
373  0 this.chimeraRestPort = 0;
374  0 structureManager.clearOnChimeraExit();
375    }
376   
 
377  0 toggle public void exitChimera()
378    {
379  0 if (isChimeraLaunched() && chimera != null)
380    {
381  0 sendChimeraCommand("stop really", false);
382  0 try
383    {
384    // TODO is this too violent? could it force close the process
385    // before it has done an orderly shutdown?
386  0 chimera.destroy();
387    } catch (Exception ex)
388    {
389    // ignore
390    }
391    }
392  0 clearOnChimeraExit();
393    }
394   
 
395  0 toggle public Map<Integer, ChimeraModel> getSelectedModels()
396    {
397  0 Map<Integer, ChimeraModel> selectedModelsMap = new HashMap<>();
398  0 List<String> chimeraReply = sendChimeraCommand(
399    "list selection level molecule", true);
400  0 if (chimeraReply != null)
401    {
402  0 for (String modelLine : chimeraReply)
403    {
404  0 ChimeraModel chimeraModel = new ChimeraModel(modelLine);
405  0 Integer modelKey = ChimUtils.makeModelKey(
406    chimeraModel.getModelNumber(),
407    chimeraModel.getSubModelNumber());
408  0 selectedModelsMap.put(modelKey, chimeraModel);
409    }
410    }
411  0 return selectedModelsMap;
412    }
413   
414    /**
415    * Sends a 'list selection level residue' command to Chimera and returns the
416    * list of selected atomspecs
417    *
418    * @return
419    */
 
420  0 toggle public List<String> getSelectedResidueSpecs()
421    {
422  0 List<String> selectedResidues = new ArrayList<>();
423  0 List<String> chimeraReply = sendChimeraCommand(
424    "list selection level residue", true);
425  0 if (chimeraReply != null)
426    {
427    /*
428    * expect 0, 1 or more lines of the format
429    * residue id #0:43.A type GLY
430    * where we are only interested in the atomspec #0.43.A
431    */
432  0 for (String inputLine : chimeraReply)
433    {
434  0 String[] inputLineParts = inputLine.split("\\s+");
435  0 if (inputLineParts.length == 5)
436    {
437  0 selectedResidues.add(inputLineParts[2]);
438    }
439    }
440    }
441  0 return selectedResidues;
442    }
443   
 
444  0 toggle public void getSelectedResidues(
445    Map<Integer, ChimeraModel> selectedModelsMap)
446    {
447  0 List<String> chimeraReply = sendChimeraCommand(
448    "list selection level residue", true);
449  0 if (chimeraReply != null)
450    {
451  0 for (String inputLine : chimeraReply)
452    {
453  0 ChimeraResidue r = new ChimeraResidue(inputLine);
454  0 Integer modelKey = ChimUtils.makeModelKey(r.getModelNumber(),
455    r.getSubModelNumber());
456  0 if (selectedModelsMap.containsKey(modelKey))
457    {
458  0 ChimeraModel model = selectedModelsMap.get(modelKey);
459  0 model.addResidue(r);
460    }
461    }
462    }
463    }
464   
465    /**
466    * Return the list of ChimeraModels currently open. Warning: if smiles model
467    * name too long, only part of it with "..." is printed.
468    *
469    *
470    * @return List of ChimeraModel's
471    */
472    // TODO: [Optional] Handle smiles names in a better way in Chimera?
 
473  0 toggle public List<ChimeraModel> getModelList()
474    {
475  0 List<ChimeraModel> modelList = new ArrayList<>();
476  0 List<String> list = sendChimeraCommand("list models type molecule",
477    true);
478  0 if (list != null)
479    {
480  0 for (String modelLine : list)
481    {
482  0 ChimeraModel chimeraModel = new ChimeraModel(modelLine);
483  0 modelList.add(chimeraModel);
484    }
485    }
486  0 return modelList;
487    }
488   
489    /**
490    * Return the list of depiction presets available from within Chimera. Chimera
491    * will return the list as a series of lines with the format: Preset type
492    * number "description"
493    *
494    * @return list of presets
495    */
 
496  0 toggle public List<String> getPresets()
497    {
498  0 ArrayList<String> presetList = new ArrayList<>();
499  0 List<String> output = sendChimeraCommand("preset list", true);
500  0 if (output != null)
501    {
502  0 for (String preset : output)
503    {
504  0 preset = preset.substring(7); // Skip over the "Preset"
505  0 preset = preset.replaceFirst("\"", "(");
506  0 preset = preset.replaceFirst("\"", ")");
507    // string now looks like: type number (description)
508  0 presetList.add(preset);
509    }
510    }
511  0 return presetList;
512    }
513   
 
514  1 toggle public boolean isChimeraLaunched()
515    {
516  1 boolean launched = false;
517  1 if (chimera != null)
518    {
519  0 try
520    {
521  0 chimera.exitValue();
522    // if we get here, process has ended
523    } catch (IllegalThreadStateException e)
524    {
525    // ok - not yet terminated
526  0 launched = true;
527    }
528    }
529  1 return launched;
530    }
531   
532    /**
533    * Launch Chimera, unless an instance linked to this object is already
534    * running. Returns true if chimera is successfully launched, or already
535    * running, else false.
536    *
537    * @param chimeraPaths
538    * @return
539    */
 
540  1 toggle public boolean launchChimera(List<String> chimeraPaths)
541    {
542    // Do nothing if Chimera is already launched
543  1 if (isChimeraLaunched())
544    {
545  0 return true;
546    }
547   
548    // Try to launch Chimera (eventually using one of the possible paths)
549  1 String error = "Error message: ";
550  1 String workingPath = "";
551    // iterate over possible paths for starting Chimera
552  1 for (String chimeraPath : chimeraPaths)
553    {
554  3 try
555    {
556    // ensure symbolic links are resolved
557  3 chimeraPath = Paths.get(chimeraPath).toRealPath().toString();
558  0 File path = new File(chimeraPath);
559    // uncomment the next line to simulate Chimera not installed
560    // path = new File(chimeraPath + "x");
561  0 if (!path.canExecute())
562    {
563  0 error += "File '" + path + "' does not exist.\n";
564  0 continue;
565    }
566  0 List<String> args = new ArrayList<>();
567  0 args.add(chimeraPath);
568    // shows Chimera output window but suppresses REST responses:
569    // args.add("--debug");
570  0 args.add("--start");
571  0 args.add("RESTServer");
572  0 ProcessBuilder pb = new ProcessBuilder(args);
573  0 chimera = pb.start();
574  0 error = "";
575  0 workingPath = chimeraPath;
576  0 break;
577    } catch (Exception e)
578    {
579    // Chimera could not be started using this path
580  3 error += e.getMessage();
581    }
582    }
583    // If no error, then Chimera was launched successfully
584  1 if (error.length() == 0)
585    {
586  0 this.chimeraRestPort = getPortNumber();
587  0 System.out.println("Chimera REST API started on port "
588    + chimeraRestPort);
589    // structureManager.initChimTable();
590  0 structureManager.setChimeraPathProperty(workingPath);
591    // TODO: [Optional] Check Chimera version and show a warning if below 1.8
592    // Ask Chimera to give us updates
593    // startListening(); // later - see ChimeraListener
594  0 return (chimeraRestPort > 0);
595    }
596   
597    // Tell the user that Chimera could not be started because of an error
598  1 logger.warn(error);
599  1 return false;
600    }
601   
602    /**
603    * Read and return the port number returned in the reply to --start RESTServer
604    */
 
605  0 toggle private int getPortNumber()
606    {
607  0 int port = 0;
608  0 InputStream readChan = chimera.getInputStream();
609  0 BufferedReader lineReader = new BufferedReader(new InputStreamReader(
610    readChan));
611  0 StringBuilder responses = new StringBuilder();
612  0 try
613    {
614  0 String response = lineReader.readLine();
615  0 while (response != null)
616    {
617  0 responses.append("\n" + response);
618    // expect: REST server on host 127.0.0.1 port port_number
619  0 if (response.startsWith("REST server"))
620    {
621  0 String[] tokens = response.split(" ");
622  0 if (tokens.length == 7 && "port".equals(tokens[5]))
623    {
624  0 port = Integer.parseInt(tokens[6]);
625  0 break;
626    }
627    }
628  0 response = lineReader.readLine();
629    }
630    } catch (Exception e)
631    {
632  0 logger.error("Failed to get REST port number from " + responses
633    + ": " + e.getMessage());
634    } finally
635    {
636  0 try
637    {
638  0 lineReader.close();
639    } catch (IOException e2)
640    {
641    }
642    }
643  0 if (port == 0)
644    {
645  0 System.err
646    .println("Failed to start Chimera with REST service, response was: "
647    + responses);
648    }
649  0 logger.info("Chimera REST service listening on port " + chimeraRestPort);
650  0 return port;
651    }
652   
653    /**
654    * Determine the color that Chimera is using for this model.
655    *
656    * @param model
657    * the ChimeraModel we want to get the Color for
658    * @return the default model Color for this model in Chimera
659    */
 
660  0 toggle public Color getModelColor(ChimeraModel model)
661    {
662  0 List<String> colorLines = sendChimeraCommand(
663    "list model spec " + model.toSpec() + " attribute color", true);
664  0 if (colorLines == null || colorLines.size() == 0)
665    {
666  0 return null;
667    }
668  0 return ChimUtils.parseModelColor(colorLines.get(0));
669    }
670   
671    /**
672    *
673    * Get information about the residues associated with a model. This uses the
674    * Chimera listr command. We don't return the resulting residues, but we add
675    * the residues to the model.
676    *
677    * @param model
678    * the ChimeraModel to get residue information for
679    *
680    */
 
681  0 toggle public void addResidues(ChimeraModel model)
682    {
683  0 int modelNumber = model.getModelNumber();
684  0 int subModelNumber = model.getSubModelNumber();
685    // Get the list -- it will be in the reply log
686  0 List<String> reply = sendChimeraCommand(
687    "list residues spec " + model.toSpec(), true);
688  0 if (reply == null)
689    {
690  0 return;
691    }
692  0 for (String inputLine : reply)
693    {
694  0 ChimeraResidue r = new ChimeraResidue(inputLine);
695  0 if (r.getModelNumber() == modelNumber
696    || r.getSubModelNumber() == subModelNumber)
697    {
698  0 model.addResidue(r);
699    }
700    }
701    }
702   
 
703  0 toggle public List<String> getAttrList()
704    {
705  0 List<String> attributes = new ArrayList<>();
706  0 final List<String> reply = sendChimeraCommand("list resattr", true);
707  0 if (reply != null)
708    {
709  0 for (String inputLine : reply)
710    {
711  0 String[] lineParts = inputLine.split("\\s");
712  0 if (lineParts.length == 2 && lineParts[0].equals("resattr"))
713    {
714  0 attributes.add(lineParts[1]);
715    }
716    }
717    }
718  0 return attributes;
719    }
720   
 
721  0 toggle public Map<ChimeraResidue, Object> getAttrValues(String aCommand,
722    ChimeraModel model)
723    {
724  0 Map<ChimeraResidue, Object> values = new HashMap<>();
725  0 final List<String> reply = sendChimeraCommand("list residue spec "
726    + model.toSpec() + " attribute " + aCommand, true);
727  0 if (reply != null)
728    {
729  0 for (String inputLine : reply)
730    {
731  0 String[] lineParts = inputLine.split("\\s");
732  0 if (lineParts.length == 5)
733    {
734  0 ChimeraResidue residue = ChimUtils
735    .getResidue(lineParts[2], model);
736  0 String value = lineParts[4];
737  0 if (residue != null)
738    {
739  0 if (value.equals("None"))
740    {
741  0 continue;
742    }
743  0 if (value.equals("True") || value.equals("False"))
744    {
745  0 values.put(residue, Boolean.valueOf(value));
746  0 continue;
747    }
748  0 try
749    {
750  0 Double doubleValue = Double.valueOf(value);
751  0 values.put(residue, doubleValue);
752    } catch (NumberFormatException ex)
753    {
754  0 values.put(residue, value);
755    }
756    }
757    }
758    }
759    }
760  0 return values;
761    }
762   
763    private volatile boolean busy = false;
764   
765    /**
766    * Send a command to Chimera.
767    *
768    * @param command
769    * Command string to be send.
770    * @param reply
771    * Flag indicating whether the method should return the reply from
772    * Chimera or not.
773    * @return List of Strings corresponding to the lines in the Chimera reply or
774    * <code>null</code>.
775    */
 
776  0 toggle public List<String> sendChimeraCommand(String command, boolean reply)
777    {
778    // System.out.println("chimeradebug>> " + command);
779  0 if (!isChimeraLaunched() || command == null
780    || "".equals(command.trim()))
781    {
782  0 return null;
783    }
784    // TODO do we need a maximum wait time before aborting?
785  0 while (busy)
786    {
787  0 try
788    {
789  0 Thread.sleep(25);
790    } catch (InterruptedException q)
791    {
792    }
793    }
794  0 busy = true;
795  0 long startTime = System.currentTimeMillis();
796  0 try
797    {
798  0 return sendRestCommand(command);
799    } finally
800    {
801    /*
802    * Make sure busy flag is reset come what may!
803    */
804  0 busy = false;
805  0 if (debug)
806    {
807  0 System.out.println("Chimera command took "
808    + (System.currentTimeMillis() - startTime) + "ms: "
809    + command);
810    }
811   
812    }
813    }
814   
815    /**
816    * Sends the command to Chimera's REST API, and returns any response lines.
817    *
818    * @param command
819    * @return
820    */
 
821  0 toggle protected List<String> sendRestCommand(String command)
822    {
823  0 String restUrl = "http://127.0.0.1:" + this.chimeraRestPort + "/run";
824  0 List<NameValuePair> commands = new ArrayList<>(1);
825  0 commands.add(new BasicNameValuePair("command", command));
826   
827  0 List<String> reply = new ArrayList<>();
828  0 BufferedReader response = null;
829  0 try
830    {
831  0 response = HttpClientUtils.doHttpUrlPost(restUrl, commands, CONNECTION_TIMEOUT_MS,
832    REST_REPLY_TIMEOUT_MS);
833  0 String line = "";
834  0 while ((line = response.readLine()) != null)
835    {
836  0 reply.add(line);
837    }
838    } catch (Exception e)
839    {
840  0 logger.error("REST call '" + command + "' failed: " + e.getMessage());
841    } finally
842    {
843  0 if (response != null)
844    {
845  0 try
846    {
847  0 response.close();
848    } catch (IOException e)
849    {
850    }
851    }
852    }
853  0 return reply;
854    }
855   
856    /**
857    * Send a command to stdin of Chimera process, and optionally read any
858    * responses.
859    *
860    * @param command
861    * @param readReply
862    * @return
863    */
 
864  0 toggle protected List<String> sendStdinCommand(String command, boolean readReply)
865    {
866  0 chimeraListenerThread.clearResponse(command);
867  0 String text = command.concat("\n");
868  0 try
869    {
870    // send the command
871  0 chimera.getOutputStream().write(text.getBytes());
872  0 chimera.getOutputStream().flush();
873    } catch (IOException e)
874    {
875    // logger.info("Unable to execute command: " + text);
876    // logger.info("Exiting...");
877  0 logger.warn("Unable to execute command: " + text);
878  0 logger.warn("Exiting...");
879  0 clearOnChimeraExit();
880  0 return null;
881    }
882  0 if (!readReply)
883    {
884  0 return null;
885    }
886  0 List<String> rsp = chimeraListenerThread.getResponse(command);
887  0 return rsp;
888    }
889   
 
890  0 toggle public StructureManager getStructureManager()
891    {
892  0 return structureManager;
893    }
894   
 
895  0 toggle public boolean isBusy()
896    {
897  0 return busy;
898    }
899   
 
900  0 toggle public Process getChimeraProcess()
901    {
902  0 return chimera;
903    }
904    }