1 package ext.edu.ucsf.rbvi.strucviz2;
4 import java.util.ArrayList;
5 import java.util.HashMap;
9 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory;
12 import ext.edu.ucsf.rbvi.strucviz2.StructureManager.ModelType;
14 public abstract class ChimUtils
17 private static Logger logger = LoggerFactory.getLogger(ChimUtils.class);
19 static int MAX_SUB_MODELS = 1000;
21 public static final HashMap<String, String> aaNames;
23 public static String RESIDUE_ATTR = "ChimeraResidue";
25 public static String RINALYZER_ATTR = "RINalyzerResidue";
27 public static String DEFAULT_STRUCTURE_KEY = "pdbFileName";
30 * Parse the model number returned by Chimera and return the int value
32 // invoked by the ChimeraModel constructor
33 // line = model id #0 type Molecule name 1ert
34 public static int[] parseModelNumber(String inputLine)
36 int hash = inputLine.indexOf('#');
37 int space = inputLine.indexOf(' ', hash);
38 int decimal = inputLine.substring(hash + 1, space).indexOf('.');
39 // model number is between hash+1 and space
41 int subModelNumber = 0;
46 subModelNumber = Integer.parseInt(inputLine.substring(decimal
48 space = decimal + hash + 1;
50 modelNumber = Integer.parseInt(inputLine.substring(hash + 1, space));
53 logger.warn("Unexpected return from Chimera: " + inputLine, e);
55 return new int[] { modelNumber, subModelNumber };
59 * Parse the model number returned by Chimera and return the int value
61 // invoked by openModel in ChimeraManager
62 // line: #1, chain A: hiv-1 protease
63 public static int[] parseOpenedModelNumber(String inputLine)
65 int hash = inputLine.indexOf('#');
66 int space = inputLine.indexOf(',', hash);
67 int decimal = inputLine.substring(hash + 1, space).indexOf('.');
68 // model number is between hash+1 and space
70 int subModelNumber = 0;
75 subModelNumber = Integer.parseInt(inputLine.substring(decimal
77 space = decimal + hash + 1;
79 modelNumber = Integer.parseInt(inputLine.substring(hash + 1, space));
82 logger.warn("Unexpected return from Chimera: " + inputLine, e);
84 return new int[] { modelNumber, subModelNumber };
88 * Parse the model identifier returned by Chimera and return the String value
90 // invoked by the ChimeraModel constructor
91 // line = model id #0 type Molecule name 1ert
92 public static String parseModelName(String inputLine)
94 int start = inputLine.indexOf("name ");
97 // Might get a quoted string (don't understand why, but there you have it)
98 if (inputLine.startsWith("\"", start + 5))
100 start += 6; // Skip over the first quote
101 int end = inputLine.lastIndexOf('"');
104 return inputLine.substring(start, end);
107 return inputLine.substring(start);
111 return inputLine.substring(start + 5);
115 public static Color parseModelColor(String inputLine)
119 int colorStart = inputLine.indexOf("color ");
120 String colorString = inputLine.substring(colorStart + 6);
121 String[] rgbStrings = colorString.split(",");
122 float[] rgbValues = new float[4];
123 for (int i = 0; i < rgbStrings.length; i++)
125 Float f = new Float(rgbStrings[i]);
126 rgbValues[i] = f.floatValue();
128 if (rgbStrings.length == 4)
130 return new Color(rgbValues[0], rgbValues[1], rgbValues[2],
135 return new Color(rgbValues[0], rgbValues[1], rgbValues[2]);
137 } catch (Exception ex)
139 logger.warn("Unexpected return from Chimera: " + inputLine, ex);
145 * Create the key to use for forming the model/submodel key into the modelHash
150 * the submodel number
151 * @return the model key as an Integer
153 public static Integer makeModelKey(int model, int subModel)
155 return new Integer(model * MAX_SUB_MODELS + subModel);
158 // invoked by the getResdiue (parseConnectivityReplies in
159 // CreateStructureNetworkTask)
160 // atomSpec = #0:1.A or #1:96.B@N
161 public static ChimeraModel getModel(String atomSpec,
162 ChimeraManager chimeraManager)
164 // System.out.println("getting model for "+atomSpec);
165 String[] split = atomSpec.split(":");
166 // No model specified....
167 if (split[0].length() == 0)
169 logger.info("Unexpected return from Chimera: " + atomSpec);
172 // System.out.println("model = "+split[0].substring(1));
177 String[] subSplit = split[0].substring(1).split("\\.");
178 if (subSplit.length > 0)
179 model = Integer.parseInt(subSplit[0]);
181 model = Integer.parseInt(split[0].substring(1));
183 if (subSplit.length > 1)
184 submodel = Integer.parseInt(subSplit[1]);
185 } catch (Exception e)
188 logger.warn("Unexpected return from Chimera: " + atomSpec, e);
190 return chimeraManager.getChimeraModel(model, submodel);
193 // invoked by the parseConnectivityReplies in CreateStructureNetworkTask
194 // atomSpec = #0:1.A or #1:96.B@N
195 public static ChimeraResidue getResidue(String atomSpec,
196 ChimeraManager chimeraManager)
198 // System.out.println("Getting residue from: "+atomSpec);
199 ChimeraModel model = getModel(atomSpec, chimeraManager); // Get the model
202 model = chimeraManager.getChimeraModel();
204 return getResidue(atomSpec, model);
207 // invoked by the getResdiue (parseConnectivityReplies in
208 // CreateStructureNetworkTask)
209 // atomSpec = #0:1.A or #1:96.B@N
210 public static ChimeraResidue getResidue(String atomSpec,
213 // System.out.println("Getting residue from: "+atomSpec);
214 String[] split = atomSpec.split(":|@");
216 // Split into residue and chain
217 String[] residueChain = split[1].split("\\.");
219 if (residueChain[0].length() == 0)
221 logger.info("Unexpected return from Chimera: " + atomSpec);
225 if (residueChain.length == 2 && residueChain[1].length() > 0)
227 ChimeraChain chain = model.getChain(residueChain[1]);
228 return chain.getResidue(residueChain[0]);
230 return model.getResidue("_", residueChain[0]);
233 public static ChimeraChain getChain(String atomSpec, ChimeraModel model)
235 String[] split = atomSpec.split(":|@");
237 // Split into residue and chain
238 String[] residueChain = split[1].split("\\.");
239 if (residueChain.length == 1)
241 logger.info("Unexpected return from Chimera: " + atomSpec);
244 return model.getChain(residueChain[1]);
247 public static String getAtomName(String atomSpec)
249 String[] split = atomSpec.split("@");
250 if (split.length > 1)
257 public static boolean isBackbone(String atom)
259 if (atom.equals("C") || atom.equals("CA") || atom.equals("N")
260 || atom.equals("O") || atom.equals("H"))
265 public static String getIntSubtype(String node, String atom)
267 String[] split = node.split("#| ");
269 if (split.length == 2)
271 resType = split[0].trim().toUpperCase();
273 else if (split.length == 3)
275 resType = split[1].trim().toUpperCase();
277 if (resType.equalsIgnoreCase("HOH") || resType.equalsIgnoreCase("WAT"))
281 else if (aaNames.containsKey(resType))
283 if (atom.equals("C") || atom.equals("CA") || atom.equals("N")
284 || atom.equals("O") || atom.equals("H"))
299 public static String[] getResKeyParts(String resKey)
301 // [pdbID[.modelNo]#][residueID][.chainID]
302 // pdbID := 4-character code | "URL" | "path"
303 String[] resKeyParts = new String[4];
304 String[] split = resKey.split("#");
305 String resChain = null;
306 // if no "#" then it is either only a pdb id or a residue or a chain
307 if (split.length == 1)
309 // pdb id without model
310 if (resKey.length() == 4 && resKey.indexOf("\\.") < 0)
312 parseModelID(resKey, resKeyParts);
315 else if (resKey.startsWith("\""))
317 parseModelID(resKey, resKeyParts);
319 // chain and residue or model and number
322 String[] splitSplit = resKey.split("\\.");
323 if (splitSplit.length == 1)
325 // only a chain or a residue
333 Integer.parseInt(splitSplit[1]);
334 parseModelID(resKey, resKeyParts);
335 } catch (NumberFormatException ex)
343 else if (split.length == 2)
345 // model and residue+chain
346 parseModelID(split[0], resKeyParts);
351 // model string with "#"
352 // TODO: [Optional] Are there more possibilities?
353 parseModelID(resKey.substring(0, resKey.lastIndexOf("#")),
355 resChain = resKey.substring(resKey.lastIndexOf("#") + 1,
358 if (resChain != null)
360 // System.out.println(resChain);
361 String[] resChainSplit = resChain.split("\\.");
362 if (resChainSplit.length == 1)
364 // TODO: [Optional] Find a better way to distinguish between chain and
366 // if only one character and not an int, probably a chain
367 if (resChainSplit[0].length() == 1)
371 Integer.parseInt(resChainSplit[0]);
372 resKeyParts[3] = resChainSplit[0];
373 } catch (NumberFormatException ex)
375 resKeyParts[2] = resChainSplit[0];
380 resKeyParts[3] = resChainSplit[0];
383 else if (resChainSplit.length == 2)
385 resKeyParts[2] = resChainSplit[0];
386 resKeyParts[3] = resChainSplit[1];
391 logger.info("Could not parse residue identifier: " + resKey);
394 // String print = "";
395 // for (int i = 0; i < resKeyParts.length; i++) {
396 // if (resKeyParts[i] == null) {
397 // print += i + ": null\t";
399 // print += i + ": " + resKeyParts[i] + ";";
402 // System.out.println(print);
406 public static void parseModelID(String modelID, String[] resKeyParts)
408 if (modelID.startsWith("\""))
410 if (modelID.endsWith("\""))
412 resKeyParts[0] = modelID.substring(1, modelID.length() - 1);
419 Integer.parseInt(modelID.substring(modelID.lastIndexOf("\"") + 2,
421 resKeyParts[0] = modelID.substring(0,
422 modelID.lastIndexOf("\"") - 1);
423 resKeyParts[1] = modelID.substring(modelID.lastIndexOf("\"") + 2,
425 } catch (NumberFormatException ex)
427 resKeyParts[0] = modelID.substring(1);
433 String[] modelIDNo = modelID.split("\\.");
434 if (modelIDNo.length == 1)
436 resKeyParts[0] = modelIDNo[0];
438 else if (modelIDNo.length == 2)
442 Integer.parseInt(modelIDNo[1]);
443 resKeyParts[0] = modelIDNo[0];
444 resKeyParts[1] = modelIDNo[1];
445 } catch (NumberFormatException ex)
447 resKeyParts[0] = modelID;
452 logger.info("Could not parse model identifier: " + modelID);
458 * This method takes a Cytoscape attribute specification
459 * ([structure#][residue][.chainID]) and returns the lowest-level object
460 * referenced by the spec. For example, if the spec is "1tkk", this method
461 * will return a ChimeraModel. If the spec is ".A", it will return a
465 * the specification string
466 * @param chimeraManager
467 * the Chimera object we're currently using
468 * @return a ChimeraStructuralObject of the lowest type
470 public static ChimeraStructuralObject fromAttributeOld(String attrSpec,
471 ChimeraManager chimeraManager)
473 if (attrSpec == null || attrSpec.indexOf(',') > 0
474 || attrSpec.indexOf('-') > 0)
476 // No support for either lists or ranges
477 logger.warn("No support for identifier: " + attrSpec);
481 String residue = null;
485 ChimeraModel chimeraModel = null;
486 ChimeraChain chimeraChain = null;
487 ChimeraResidue chimeraResidue = null;
489 // System.out.println("Getting object from attribute: "+attrSpec);
492 String[] split = attrSpec.split("#");
493 String resChain = null;
494 if (split.length == 1)
499 else if (split.length == 2)
507 // model string with "#"
508 model = attrSpec.substring(0, attrSpec.lastIndexOf("#"));
509 resChain = attrSpec.substring(attrSpec.lastIndexOf("#") + 1,
512 if (resChain != null)
514 String[] resChainSplit = resChain.split("\\.");
515 if (resChainSplit.length == 1)
517 residue = resChainSplit[0];
519 else if (resChainSplit.length == 2)
521 residue = resChainSplit[0];
522 chain = resChainSplit[1];
527 logger.warn("No support for identifier: " + attrSpec);
531 // if (split.length == 1) {
533 // residue = split[0];
534 // } else if (split.length == 3) {
535 // // We have all three
537 // residue = split[1];
539 // } else if (split.length == 2 && attrSpec.indexOf('#') > 0) {
540 // // Model and Residue
542 // residue = split[1];
544 // // Residue and Chain
545 // residue = split[0];
549 // System.out.println("model = " + model + " chain = " + chain +
554 List<ChimeraModel> models = chimeraManager.getChimeraModels(model,
555 ModelType.PDB_MODEL);
556 if (models.size() == 1)
558 chimeraModel = models.get(0);
564 chimeraModel = chimeraManager.getChimeraModel(
565 Integer.valueOf(model), 0);
566 } catch (NumberFormatException ex)
572 if (chimeraModel == null)
574 chimeraModel = chimeraManager.getChimeraModel();
576 // System.out.println("ChimeraModel = " + chimeraModel);
580 chimeraChain = chimeraModel.getChain(chain);
581 // System.out.println("ChimeraChain = " + chimeraChain);
585 if (chimeraChain != null)
587 chimeraResidue = chimeraChain.getResidue(residue);
591 chimeraResidue = chimeraModel.getResidue("_", residue);
593 // System.out.println("ChimeraResidue = " + chimeraResidue);
596 if (chimeraResidue != null)
597 return chimeraResidue;
599 if (chimeraChain != null)
602 if (chimeraModel != null)
605 } catch (Exception ex)
607 logger.warn("Could not parse residue identifier: " + attrSpec, ex);
612 public static ChimeraStructuralObject fromAttribute(String attrSpec,
613 ChimeraManager chimeraManager)
615 // TODO: Make sure it is OK to remove this: || attrSpec.indexOf('-') > 0
616 if (attrSpec == null || attrSpec.indexOf(',') > 0)
618 // No support for either lists or ranges
619 // System.out.println("No support for identifier: " + attrSpec);
620 logger.warn("No support for identifier: " + attrSpec);
623 String[] modelIDNoResChain = getResKeyParts(attrSpec);
625 ChimeraModel chimeraModel = null;
626 ChimeraChain chimeraChain = null;
627 ChimeraResidue chimeraResidue = null;
629 // System.out.println("Getting object from attribute: "+attrSpec);
632 if (modelIDNoResChain[0] != null)
634 String modelID = modelIDNoResChain[0];
635 List<ChimeraModel> models = chimeraManager.getChimeraModels(
636 modelID, ModelType.PDB_MODEL);
637 if (models.size() == 1)
638 { // usual case with only one model
639 chimeraModel = models.get(0);
641 else if (models.size() > 1 && modelIDNoResChain[1] != null)
643 // there are several submodels
646 int modelNo = Integer.valueOf(modelIDNoResChain[1]);
647 for (ChimeraModel model : models)
649 if (model.getSubModelNumber() == modelNo)
651 chimeraModel = model;
655 } catch (NumberFormatException ex)
662 // TODO: [Optional] What is this doing?
665 chimeraModel = chimeraManager.getChimeraModel(
666 Integer.valueOf(modelID), 0);
667 } catch (NumberFormatException ex)
673 if (chimeraModel == null)
675 // TODO: [Optional] Find a better way to handle this case
676 // If no model can be matched, continue
677 // System.out.println("No matching model could be find for " +
680 // chimeraModel = chimeraManager.getChimeraModel();
681 // logger.warn("No matching model could be find for " + attrSpec +
683 // + chimeraModel.toSpec());
685 // System.out.println("ChimeraModel = " + chimeraModel);
687 if (modelIDNoResChain[3] != null)
689 chimeraChain = chimeraModel.getChain(modelIDNoResChain[3]);
690 // System.out.println("ChimeraChain = " + chimeraChain);
692 if (modelIDNoResChain[2] != null)
694 String residue = modelIDNoResChain[2];
695 if (chimeraChain != null)
697 chimeraResidue = chimeraChain.getResidue(residue);
699 else if (chimeraModel.getChain("_") != null)
701 chimeraResidue = chimeraModel.getResidue("_", residue);
703 else if (chimeraModel.getChainCount() == 1)
705 chimeraResidue = chimeraModel.getResidue(chimeraModel
706 .getChainNames().iterator().next(), residue);
708 // System.out.println("ChimeraResidue = " + chimeraResidue);
711 if (chimeraResidue != null)
712 return chimeraResidue;
714 if (chimeraChain != null)
717 if (chimeraModel != null)
720 } catch (Exception ex)
722 // System.out.println("Could not parse chimera identifier: " +
723 // attrSpec+"("+ex.getMessage()+")");
724 logger.warn("Could not parse chimera identifier: " + attrSpec, ex);
730 * Search for structure references in the residue list
733 * the list of residues
734 * @return a concatenated list of structures encoded in the list
736 public static String findStructures(String residueList)
738 if (residueList == null)
740 String[] residues = residueList.split(",");
741 Map<String, String> structureNameMap = new HashMap<String, String>();
742 for (int i = 0; i < residues.length; i++)
744 String[] components = residues[i].split("#");
745 if (components.length > 1)
747 structureNameMap.put(components[0], components[1]);
750 if (structureNameMap.isEmpty())
753 String structure = null;
754 for (String struct : structureNameMap.keySet())
756 if (structure == null)
757 structure = new String();
759 structure = structure.concat(",");
760 structure = structure.concat(struct);
765 // invoked by openStructures in StructureManager
766 public static List<String> parseFuncRes(List<String> residueNames,
769 List<String> resRanges = new ArrayList<String>();
770 for (int i = 0; i < residueNames.size(); i++)
772 String residue = residueNames.get(i);
773 // Parse out the structure, if there is one
774 String[] components = residue.split("#");
775 if (components.length > 1 && !modelName.equals(components[0]))
779 else if (components.length > 1)
781 residue = components[1];
783 else if (components.length == 1)
785 residue = components[0];
787 // Check to see if we have a range-spec
788 String resRange = "";
789 if (residue == null || residue.equals("") || residue.length() == 0)
793 String[] range = residue.split("-", 2);
795 for (int res = 0; res < range.length; res++)
799 resRange = resRange.concat("-");
800 if (chain != null && range[res].indexOf('.') == -1)
801 range[res] = range[res].concat("." + chain);
804 if (res == 0 && range.length >= 2 && range[res].indexOf('.') > 0)
806 // This is a range spec with the leading residue containing a chain
808 String[] resChain = range[res].split("\\.");
810 range[res] = resChain[0];
812 // Fix weird SFLD syntax...
813 if (range[res].indexOf('|') > 0
814 && Character.isDigit(range[res].charAt(0)))
816 int offset = range[res].indexOf('|');
817 String str = range[res].substring(offset + 1)
818 + range[res].substring(0, offset);
822 // Convert to legal atom-spec
823 if (Character.isDigit(range[res].charAt(0)))
825 resRange = resRange.concat(range[res]);
827 else if (Character.isDigit(range[res].charAt(1)))
829 resRange = resRange.concat(range[res].substring(1));
831 else if (range[res].charAt(0) == '.')
833 // Do we have a chain spec?
834 resRange = resRange.concat(range[res]);
838 resRange = resRange.concat(range[res].substring(3));
841 if (!resRanges.contains(resRange))
843 resRanges.add(resRange);
851 aaNames = new HashMap<String, String>();
852 aaNames.put("ALA", "A Ala Alanine N[C@@H](C)C(O)=O");
853 aaNames.put("ARG", "R Arg Arginine N[C@@H](CCCNC(N)=N)C(O)=O");
854 aaNames.put("ASN", "N Asn Asparagine N[C@@H](CC(N)=O)C(O)=O");
855 aaNames.put("ASP", "D Asp Aspartic_acid N[C@@H](CC(O)=O)C(O)=O");
856 aaNames.put("CYS", "C Cys Cysteine N[C@@H](CS)C(O)=O");
857 aaNames.put("GLN", "Q Gln Glutamine N[C@H](C(O)=O)CCC(N)=O");
858 aaNames.put("GLU", "E Glu Glumatic_acid N[C@H](C(O)=O)CCC(O)=O");
859 aaNames.put("GLY", "G Gly Glycine NCC(O)=O");
860 aaNames.put("HIS", "H His Histidine N[C@@H](CC1=CN=CN1)C(O)=O");
861 aaNames.put("ILE", "I Ile Isoleucine N[C@]([C@H](C)CC)([H])C(O)=O");
862 aaNames.put("LEU", "L Leu Leucine N[C@](CC(C)C)([H])C(O)=O");
863 aaNames.put("LYS", "K Lys Lysine N[C@](CCCCN)([H])C(O)=O");
864 aaNames.put("DLY", "K Dly D-Lysine NCCCC[C@@H](N)C(O)=O");
865 aaNames.put("MET", "M Met Methionine N[C@](CCSC)([H])C(O)=O");
866 aaNames.put("PHE", "F Phe Phenylalanine N[C@](CC1=CC=CC=C1)([H])C(O)=O");
867 aaNames.put("PRO", "P Pro Proline OC([C@@]1([H])NCCC1)=O");
868 aaNames.put("SER", "S Ser Serine OC[C@](C(O)=O)([H])N");
869 aaNames.put("THR", "T Thr Threonine O[C@H](C)[C@](C(O)=O)([H])N");
871 "W Trp Tryptophan N[C@@]([H])(CC1=CN([H])C2=C1C=CC=C2)C(O)=O");
872 aaNames.put("TYR", "Y Tyr Tyrosine N[C@@](C(O)=O)([H])CC1=CC=C(O)C=C1");
873 aaNames.put("VAL", "V Val Valine N[C@@](C(O)=O)([H])C(C)C");
874 aaNames.put("ASX", "B Asx Aspartic_acid_or_Asparagine");
875 aaNames.put("GLX", "Z Glx Glutamine_or_Glutamic_acid");
876 aaNames.put("XAA", "X Xaa Any_or_unknown_amino_acid");
877 aaNames.put("HOH", "HOH HOH Water [H]O[H]");
881 * Convert the amino acid type to a full name
884 * the residue type to convert
885 * @return the full name of the residue
887 public static String toFullName(String aaType)
889 if (!aaNames.containsKey(aaType))
891 String[] ids = ((String) aaNames.get(aaType)).split(" ");
892 return ids[2].replace('_', ' ');
896 * Convert the amino acid type to a single letter
899 * the residue type to convert
900 * @return the single letter representation of the residue
902 public static String toSingleLetter(String aaType)
904 if (!aaNames.containsKey(aaType))
906 String[] ids = ((String) aaNames.get(aaType)).split(" ");
911 * Convert the amino acid type to three letters
914 * the residue type to convert
915 * @return the three letter representation of the residue
917 public static String toThreeLetter(String aaType)
919 if (!aaNames.containsKey(aaType))
921 String[] ids = ((String) aaNames.get(aaType)).split(" ");
926 * Convert the amino acid type to its SMILES string
929 * the residue type to convert
930 * @return the SMILES representation of the residue
932 public static String toSMILES(String aaType)
934 if (!aaNames.containsKey(aaType))
936 String[] ids = ((String) aaNames.get(aaType)).split(" ");
942 public static String getAlignName(ChimeraStructuralObject chimObj)
944 String name = chimObj.getChimeraModel().toString();
945 if (chimObj instanceof ChimeraChain)
947 name = ((ChimeraChain) chimObj).toString() + " [" + name + "]";