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 // line: Model 0 (filename)
64 public static int[] parseOpenedModelNumber(String inputLine)
66 int hash = inputLine.indexOf('#');
70 hash = inputLine.indexOf("Model");
75 space = inputLine.indexOf(' ', hash + 1);
79 space = inputLine.indexOf(',', hash);
82 int decimal = inputLine.substring(hash + 1, space).indexOf('.');
83 // model number is between hash+1 and space
85 int subModelNumber = 0;
90 subModelNumber = Integer.parseInt(inputLine.substring(decimal
92 space = decimal + hash + 1;
94 modelNumber = Integer.parseInt(inputLine.substring(hash + 1, space));
97 logger.warn("Unexpected return from Chimera: " + inputLine, e);
99 return new int[] { modelNumber, subModelNumber };
103 * Parse the model identifier returned by Chimera and return the String value
105 // invoked by the ChimeraModel constructor
106 // line = model id #0 type Molecule name 1ert
107 public static String parseModelName(String inputLine)
109 int start = inputLine.indexOf("name ");
114 // Might get a quoted string (don't understand why, but there you have it)
115 if (inputLine.startsWith("\"", start + 5))
117 start += 6; // Skip over the first quote
118 int end = inputLine.lastIndexOf('"');
121 return inputLine.substring(start, end);
125 return inputLine.substring(start);
130 return inputLine.substring(start + 5);
134 public static Color parseModelColor(String inputLine)
138 int colorStart = inputLine.indexOf("color ");
139 String colorString = inputLine.substring(colorStart + 6);
140 String[] rgbStrings = colorString.split(",");
141 float[] rgbValues = new float[4];
142 for (int i = 0; i < rgbStrings.length; i++)
144 Float f = new Float(rgbStrings[i]);
145 rgbValues[i] = f.floatValue();
147 if (rgbStrings.length == 4)
149 return new Color(rgbValues[0], rgbValues[1], rgbValues[2],
154 return new Color(rgbValues[0], rgbValues[1], rgbValues[2]);
156 } catch (Exception ex)
158 logger.warn("Unexpected return from Chimera: " + inputLine, ex);
164 * Create the key to use for forming the model/submodel key into the modelHash
169 * the submodel number
170 * @return the model key as an Integer
172 public static Integer makeModelKey(int model, int subModel)
174 return new Integer(model * MAX_SUB_MODELS + subModel);
177 // invoked by the getResdiue (parseConnectivityReplies in
178 // CreateStructureNetworkTask)
179 // atomSpec = #0:1.A or #1:96.B@N
180 public static ChimeraModel getModel(String atomSpec,
181 ChimeraManager chimeraManager)
183 // System.out.println("getting model for "+atomSpec);
184 String[] split = atomSpec.split(":");
185 // No model specified....
186 if (split[0].length() == 0)
188 logger.info("Unexpected return from Chimera: " + atomSpec);
191 // System.out.println("model = "+split[0].substring(1));
196 String[] subSplit = split[0].substring(1).split("\\.");
197 if (subSplit.length > 0)
199 model = Integer.parseInt(subSplit[0]);
203 model = Integer.parseInt(split[0].substring(1));
206 if (subSplit.length > 1)
208 submodel = Integer.parseInt(subSplit[1]);
210 } catch (Exception e)
213 logger.warn("Unexpected return from Chimera: " + atomSpec, e);
215 return chimeraManager.getChimeraModel(model, submodel);
218 // invoked by the parseConnectivityReplies in CreateStructureNetworkTask
219 // atomSpec = #0:1.A or #1:96.B@N
220 public static ChimeraResidue getResidue(String atomSpec,
221 ChimeraManager chimeraManager)
223 // System.out.println("Getting residue from: "+atomSpec);
224 ChimeraModel model = getModel(atomSpec, chimeraManager); // Get the model
227 model = chimeraManager.getChimeraModel();
229 return getResidue(atomSpec, model);
232 // invoked by the getResdiue (parseConnectivityReplies in
233 // CreateStructureNetworkTask)
234 // atomSpec = #0:1.A or #1:96.B@N
235 public static ChimeraResidue getResidue(String atomSpec,
238 // System.out.println("Getting residue from: "+atomSpec);
239 String[] split = atomSpec.split(":|@");
241 // Split into residue and chain
242 String[] residueChain = split[1].split("\\.");
244 if (residueChain[0].length() == 0)
246 logger.info("Unexpected return from Chimera: " + atomSpec);
250 if (residueChain.length == 2 && residueChain[1].length() > 0)
252 ChimeraChain chain = model.getChain(residueChain[1]);
253 return chain.getResidue(residueChain[0]);
255 return model.getResidue("_", residueChain[0]);
258 public static ChimeraChain getChain(String atomSpec, ChimeraModel model)
260 String[] split = atomSpec.split(":|@");
262 // Split into residue and chain
263 String[] residueChain = split[1].split("\\.");
264 if (residueChain.length == 1)
266 logger.info("Unexpected return from Chimera: " + atomSpec);
269 return model.getChain(residueChain[1]);
272 public static String getAtomName(String atomSpec)
274 String[] split = atomSpec.split("@");
275 if (split.length > 1)
282 public static boolean isBackbone(String atom)
284 if (atom.equals("C") || atom.equals("CA") || atom.equals("N")
285 || atom.equals("O") || atom.equals("H"))
292 public static String getIntSubtype(String node, String atom)
294 String[] split = node.split("#| ");
296 if (split.length == 2)
298 resType = split[0].trim().toUpperCase();
300 else if (split.length == 3)
302 resType = split[1].trim().toUpperCase();
304 if (resType.equalsIgnoreCase("HOH") || resType.equalsIgnoreCase("WAT"))
308 else if (aaNames.containsKey(resType))
310 if (atom.equals("C") || atom.equals("CA") || atom.equals("N")
311 || atom.equals("O") || atom.equals("H"))
326 public static String[] getResKeyParts(String resKey)
328 // [pdbID[.modelNo]#][residueID][.chainID]
329 // pdbID := 4-character code | "URL" | "path"
330 String[] resKeyParts = new String[4];
331 String[] split = resKey.split("#");
332 String resChain = null;
333 // if no "#" then it is either only a pdb id or a residue or a chain
334 if (split.length == 1)
336 // pdb id without model
337 if (resKey.length() == 4 && resKey.indexOf("\\.") < 0)
339 parseModelID(resKey, resKeyParts);
342 else if (resKey.startsWith("\""))
344 parseModelID(resKey, resKeyParts);
346 // chain and residue or model and number
349 String[] splitSplit = resKey.split("\\.");
350 if (splitSplit.length == 1)
352 // only a chain or a residue
360 Integer.parseInt(splitSplit[1]);
361 parseModelID(resKey, resKeyParts);
362 } catch (NumberFormatException ex)
370 else if (split.length == 2)
372 // model and residue+chain
373 parseModelID(split[0], resKeyParts);
378 // model string with "#"
379 // TODO: [Optional] Are there more possibilities?
380 parseModelID(resKey.substring(0, resKey.lastIndexOf("#")),
382 resChain = resKey.substring(resKey.lastIndexOf("#") + 1,
385 if (resChain != null)
387 // System.out.println(resChain);
388 String[] resChainSplit = resChain.split("\\.");
389 if (resChainSplit.length == 1)
391 // TODO: [Optional] Find a better way to distinguish between chain and
393 // if only one character and not an int, probably a chain
394 if (resChainSplit[0].length() == 1)
398 Integer.parseInt(resChainSplit[0]);
399 resKeyParts[3] = resChainSplit[0];
400 } catch (NumberFormatException ex)
402 resKeyParts[2] = resChainSplit[0];
407 resKeyParts[3] = resChainSplit[0];
410 else if (resChainSplit.length == 2)
412 resKeyParts[2] = resChainSplit[0];
413 resKeyParts[3] = resChainSplit[1];
418 logger.info("Could not parse residue identifier: " + resKey);
421 // String print = "";
422 // for (int i = 0; i < resKeyParts.length; i++) {
423 // if (resKeyParts[i] == null) {
424 // print += i + ": null\t";
426 // print += i + ": " + resKeyParts[i] + ";";
429 // System.out.println(print);
433 public static void parseModelID(String modelID, String[] resKeyParts)
435 if (modelID.startsWith("\""))
437 if (modelID.endsWith("\""))
439 resKeyParts[0] = modelID.substring(1, modelID.length() - 1);
446 Integer.parseInt(modelID.substring(modelID.lastIndexOf("\"") + 2,
448 resKeyParts[0] = modelID.substring(0,
449 modelID.lastIndexOf("\"") - 1);
450 resKeyParts[1] = modelID.substring(modelID.lastIndexOf("\"") + 2,
452 } catch (NumberFormatException ex)
454 resKeyParts[0] = modelID.substring(1);
460 String[] modelIDNo = modelID.split("\\.");
461 if (modelIDNo.length == 1)
463 resKeyParts[0] = modelIDNo[0];
465 else if (modelIDNo.length == 2)
469 Integer.parseInt(modelIDNo[1]);
470 resKeyParts[0] = modelIDNo[0];
471 resKeyParts[1] = modelIDNo[1];
472 } catch (NumberFormatException ex)
474 resKeyParts[0] = modelID;
479 // length > 1, so we probably have a file name with "." in it
480 logger.info("Could not parse model identifier: " + modelID);
481 resKeyParts[0] = modelID;
487 * This method takes a Cytoscape attribute specification
488 * ([structure#][residue][.chainID]) and returns the lowest-level object
489 * referenced by the spec. For example, if the spec is "1tkk", this method
490 * will return a ChimeraModel. If the spec is ".A", it will return a
494 * the specification string
495 * @param chimeraManager
496 * the Chimera object we're currently using
497 * @return a ChimeraStructuralObject of the lowest type
499 public static ChimeraStructuralObject fromAttributeOld(String attrSpec,
500 ChimeraManager chimeraManager)
502 if (attrSpec == null || attrSpec.indexOf(',') > 0
503 || attrSpec.indexOf('-') > 0)
505 // No support for either lists or ranges
506 logger.warn("No support for identifier: " + attrSpec);
510 String residue = null;
514 ChimeraModel chimeraModel = null;
515 ChimeraChain chimeraChain = null;
516 ChimeraResidue chimeraResidue = null;
518 // System.out.println("Getting object from attribute: "+attrSpec);
521 String[] split = attrSpec.split("#");
522 String resChain = null;
523 if (split.length == 1)
528 else if (split.length == 2)
536 // model string with "#"
537 model = attrSpec.substring(0, attrSpec.lastIndexOf("#"));
538 resChain = attrSpec.substring(attrSpec.lastIndexOf("#") + 1,
541 if (resChain != null)
543 String[] resChainSplit = resChain.split("\\.");
544 if (resChainSplit.length == 1)
546 residue = resChainSplit[0];
548 else if (resChainSplit.length == 2)
550 residue = resChainSplit[0];
551 chain = resChainSplit[1];
556 logger.warn("No support for identifier: " + attrSpec);
560 // if (split.length == 1) {
562 // residue = split[0];
563 // } else if (split.length == 3) {
564 // // We have all three
566 // residue = split[1];
568 // } else if (split.length == 2 && attrSpec.indexOf('#') > 0) {
569 // // Model and Residue
571 // residue = split[1];
573 // // Residue and Chain
574 // residue = split[0];
578 // System.out.println("model = " + model + " chain = " + chain +
579 // " residue = " + residue);
582 List<ChimeraModel> models = chimeraManager.getChimeraModels(model,
583 ModelType.PDB_MODEL);
584 if (models.size() == 1)
586 chimeraModel = models.get(0);
592 chimeraModel = chimeraManager.getChimeraModel(
593 Integer.valueOf(model), 0);
594 } catch (NumberFormatException ex)
600 if (chimeraModel == null)
602 chimeraModel = chimeraManager.getChimeraModel();
604 // System.out.println("ChimeraModel = " + chimeraModel);
608 chimeraChain = chimeraModel.getChain(chain);
609 // System.out.println("ChimeraChain = " + chimeraChain);
613 if (chimeraChain != null)
615 chimeraResidue = chimeraChain.getResidue(residue);
619 chimeraResidue = chimeraModel.getResidue("_", residue);
621 // System.out.println("ChimeraResidue = " + chimeraResidue);
624 if (chimeraResidue != null)
626 return chimeraResidue;
629 if (chimeraChain != null)
634 if (chimeraModel != null)
639 } catch (Exception ex)
641 logger.warn("Could not parse residue identifier: " + attrSpec, ex);
646 public static ChimeraStructuralObject fromAttribute(String attrSpec,
647 ChimeraManager chimeraManager)
649 // TODO: Make sure it is OK to remove this: || attrSpec.indexOf('-') > 0
650 if (attrSpec == null || attrSpec.indexOf(',') > 0)
652 // No support for either lists or ranges
653 // System.out.println("No support for identifier: " + attrSpec);
654 logger.warn("No support for identifier: " + attrSpec);
657 String[] modelIDNoResChain = getResKeyParts(attrSpec);
659 ChimeraModel chimeraModel = null;
660 ChimeraChain chimeraChain = null;
661 ChimeraResidue chimeraResidue = null;
663 // System.out.println("Getting object from attribute: "+attrSpec);
666 if (modelIDNoResChain[0] != null)
668 String modelID = modelIDNoResChain[0];
669 List<ChimeraModel> models = chimeraManager.getChimeraModels(
670 modelID, ModelType.PDB_MODEL);
671 if (models.size() == 1)
672 { // usual case with only one model
673 chimeraModel = models.get(0);
675 else if (models.size() > 1 && modelIDNoResChain[1] != null)
677 // there are several submodels
680 int modelNo = Integer.valueOf(modelIDNoResChain[1]);
681 for (ChimeraModel model : models)
683 if (model.getSubModelNumber() == modelNo)
685 chimeraModel = model;
689 } catch (NumberFormatException ex)
696 // TODO: [Optional] What is this doing?
699 chimeraModel = chimeraManager.getChimeraModel(
700 Integer.valueOf(modelID), 0);
701 } catch (NumberFormatException ex)
707 if (chimeraModel == null)
709 // TODO: [Optional] Find a better way to handle this case
710 // If no model can be matched, continue
711 // System.out.println("No matching model could be find for " +
714 // chimeraModel = chimeraManager.getChimeraModel();
715 // logger.warn("No matching model could be find for " + attrSpec +
717 // + chimeraModel.toSpec());
719 // System.out.println("ChimeraModel = " + chimeraModel);
721 if (modelIDNoResChain[3] != null)
723 chimeraChain = chimeraModel.getChain(modelIDNoResChain[3]);
724 // System.out.println("ChimeraChain = " + chimeraChain);
726 if (modelIDNoResChain[2] != null)
728 String residue = modelIDNoResChain[2];
729 if (chimeraChain != null)
731 chimeraResidue = chimeraChain.getResidue(residue);
733 else if (chimeraModel.getChain("_") != null)
735 chimeraResidue = chimeraModel.getResidue("_", residue);
737 else if (chimeraModel.getChainCount() == 1)
739 chimeraResidue = chimeraModel.getResidue(chimeraModel
740 .getChainNames().iterator().next(), residue);
742 // System.out.println("ChimeraResidue = " + chimeraResidue);
745 if (chimeraResidue != null)
747 return chimeraResidue;
750 if (chimeraChain != null)
755 if (chimeraModel != null)
760 } catch (Exception ex)
762 // System.out.println("Could not parse chimera identifier: " +
763 // attrSpec+"("+ex.getMessage()+")");
764 logger.warn("Could not parse chimera identifier: " + attrSpec, ex);
770 * Search for structure references in the residue list
773 * the list of residues
774 * @return a concatenated list of structures encoded in the list
776 public static String findStructures(String residueList)
778 if (residueList == null)
782 String[] residues = residueList.split(",");
783 Map<String, String> structureNameMap = new HashMap<String, String>();
784 for (int i = 0; i < residues.length; i++)
786 String[] components = residues[i].split("#");
787 if (components.length > 1)
789 structureNameMap.put(components[0], components[1]);
792 if (structureNameMap.isEmpty())
797 String structure = null;
798 for (String struct : structureNameMap.keySet())
800 if (structure == null)
802 structure = new String();
806 structure = structure.concat(",");
808 structure = structure.concat(struct);
813 // invoked by openStructures in StructureManager
814 public static List<String> parseFuncRes(List<String> residueNames,
817 List<String> resRanges = new ArrayList<String>();
818 for (int i = 0; i < residueNames.size(); i++)
820 String residue = residueNames.get(i);
821 // Parse out the structure, if there is one
822 String[] components = residue.split("#");
823 if (components.length > 1 && !modelName.equals(components[0]))
827 else if (components.length > 1)
829 residue = components[1];
831 else if (components.length == 1)
833 residue = components[0];
835 // Check to see if we have a range-spec
836 String resRange = "";
837 if (residue == null || residue.equals("") || residue.length() == 0)
841 String[] range = residue.split("-", 2);
843 for (int res = 0; res < range.length; res++)
847 resRange = resRange.concat("-");
848 if (chain != null && range[res].indexOf('.') == -1)
850 range[res] = range[res].concat("." + chain);
854 if (res == 0 && range.length >= 2 && range[res].indexOf('.') > 0)
856 // This is a range spec with the leading residue containing a chain
858 String[] resChain = range[res].split("\\.");
860 range[res] = resChain[0];
862 // Fix weird SFLD syntax...
863 if (range[res].indexOf('|') > 0
864 && Character.isDigit(range[res].charAt(0)))
866 int offset = range[res].indexOf('|');
867 String str = range[res].substring(offset + 1)
868 + range[res].substring(0, offset);
872 // Convert to legal atom-spec
873 if (Character.isDigit(range[res].charAt(0)))
875 resRange = resRange.concat(range[res]);
877 else if (Character.isDigit(range[res].charAt(1)))
879 resRange = resRange.concat(range[res].substring(1));
881 else if (range[res].charAt(0) == '.')
883 // Do we have a chain spec?
884 resRange = resRange.concat(range[res]);
888 resRange = resRange.concat(range[res].substring(3));
891 if (!resRanges.contains(resRange))
893 resRanges.add(resRange);
901 aaNames = new HashMap<String, String>();
902 aaNames.put("ALA", "A Ala Alanine N[C@@H](C)C(O)=O");
903 aaNames.put("ARG", "R Arg Arginine N[C@@H](CCCNC(N)=N)C(O)=O");
904 aaNames.put("ASN", "N Asn Asparagine N[C@@H](CC(N)=O)C(O)=O");
905 aaNames.put("ASP", "D Asp Aspartic_acid N[C@@H](CC(O)=O)C(O)=O");
906 aaNames.put("CYS", "C Cys Cysteine N[C@@H](CS)C(O)=O");
907 aaNames.put("GLN", "Q Gln Glutamine N[C@H](C(O)=O)CCC(N)=O");
908 aaNames.put("GLU", "E Glu Glumatic_acid N[C@H](C(O)=O)CCC(O)=O");
909 aaNames.put("GLY", "G Gly Glycine NCC(O)=O");
910 aaNames.put("HIS", "H His Histidine N[C@@H](CC1=CN=CN1)C(O)=O");
911 aaNames.put("ILE", "I Ile Isoleucine N[C@]([C@H](C)CC)([H])C(O)=O");
912 aaNames.put("LEU", "L Leu Leucine N[C@](CC(C)C)([H])C(O)=O");
913 aaNames.put("LYS", "K Lys Lysine N[C@](CCCCN)([H])C(O)=O");
914 aaNames.put("DLY", "K Dly D-Lysine NCCCC[C@@H](N)C(O)=O");
915 aaNames.put("MET", "M Met Methionine N[C@](CCSC)([H])C(O)=O");
916 aaNames.put("PHE", "F Phe Phenylalanine N[C@](CC1=CC=CC=C1)([H])C(O)=O");
917 aaNames.put("PRO", "P Pro Proline OC([C@@]1([H])NCCC1)=O");
918 aaNames.put("SER", "S Ser Serine OC[C@](C(O)=O)([H])N");
919 aaNames.put("THR", "T Thr Threonine O[C@H](C)[C@](C(O)=O)([H])N");
921 "W Trp Tryptophan N[C@@]([H])(CC1=CN([H])C2=C1C=CC=C2)C(O)=O");
922 aaNames.put("TYR", "Y Tyr Tyrosine N[C@@](C(O)=O)([H])CC1=CC=C(O)C=C1");
923 aaNames.put("VAL", "V Val Valine N[C@@](C(O)=O)([H])C(C)C");
924 aaNames.put("ASX", "B Asx Aspartic_acid_or_Asparagine");
925 aaNames.put("GLX", "Z Glx Glutamine_or_Glutamic_acid");
926 aaNames.put("XAA", "X Xaa Any_or_unknown_amino_acid");
927 aaNames.put("HOH", "HOH HOH Water [H]O[H]");
931 * Convert the amino acid type to a full name
934 * the residue type to convert
935 * @return the full name of the residue
937 public static String toFullName(String aaType)
939 if (!aaNames.containsKey(aaType))
943 String[] ids = aaNames.get(aaType).split(" ");
944 return ids[2].replace('_', ' ');
948 * Convert the amino acid type to a single letter
951 * the residue type to convert
952 * @return the single letter representation of the residue
954 public static String toSingleLetter(String aaType)
956 if (!aaNames.containsKey(aaType))
960 String[] ids = aaNames.get(aaType).split(" ");
965 * Convert the amino acid type to three letters
968 * the residue type to convert
969 * @return the three letter representation of the residue
971 public static String toThreeLetter(String aaType)
973 if (!aaNames.containsKey(aaType))
977 String[] ids = aaNames.get(aaType).split(" ");
982 * Convert the amino acid type to its SMILES string
985 * the residue type to convert
986 * @return the SMILES representation of the residue
988 public static String toSMILES(String aaType)
990 if (!aaNames.containsKey(aaType))
994 String[] ids = aaNames.get(aaType).split(" ");
1002 public static String getAlignName(ChimeraStructuralObject chimObj)
1004 String name = chimObj.getChimeraModel().toString();
1005 if (chimObj instanceof ChimeraChain)
1007 name = ((ChimeraChain) chimObj).toString() + " [" + name + "]";