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;
15 public abstract class ChimUtils {
17 private static Logger logger = LoggerFactory
18 .getLogger(ChimUtils.class);
20 static int MAX_SUB_MODELS = 1000;
22 public static final HashMap<String, String> aaNames;
24 public static String RESIDUE_ATTR = "ChimeraResidue";
25 public static String RINALYZER_ATTR = "RINalyzerResidue";
26 public static String DEFAULT_STRUCTURE_KEY = "pdbFileName";
29 * Parse the model number returned by Chimera and return the int value
31 // invoked by the ChimeraModel constructor
32 // line = model id #0 type Molecule name 1ert
33 public static int[] parseModelNumber(String inputLine) {
34 int hash = inputLine.indexOf('#');
35 int space = inputLine.indexOf(' ', hash);
36 int decimal = inputLine.substring(hash + 1, space).indexOf('.');
37 // model number is between hash+1 and space
39 int subModelNumber = 0;
42 subModelNumber = Integer.parseInt(inputLine.substring(decimal + hash + 2, space));
43 space = decimal + hash + 1;
45 modelNumber = Integer.parseInt(inputLine.substring(hash + 1, space));
46 } catch (Exception e) {
47 logger.warn("Unexpected return from Chimera: " + inputLine, e);
49 return new int[] { modelNumber, subModelNumber };
53 * Parse the model number returned by Chimera and return the int value
55 // invoked by openModel in ChimeraManager
56 // line: #1, chain A: hiv-1 protease
57 public static int[] parseOpenedModelNumber(String inputLine) {
58 int hash = inputLine.indexOf('#');
59 int space = inputLine.indexOf(',', hash);
60 int decimal = inputLine.substring(hash + 1, space).indexOf('.');
61 // model number is between hash+1 and space
63 int subModelNumber = 0;
66 subModelNumber = Integer.parseInt(inputLine.substring(decimal + hash + 2, space));
67 space = decimal + hash + 1;
69 modelNumber = Integer.parseInt(inputLine.substring(hash + 1, space));
70 } catch (Exception e) {
71 logger.warn("Unexpected return from Chimera: " + inputLine, e);
73 return new int[] { modelNumber, subModelNumber };
77 * Parse the model identifier returned by Chimera and return the String value
79 // invoked by the ChimeraModel constructor
80 // line = model id #0 type Molecule name 1ert
81 public static String parseModelName(String inputLine) {
82 int start = inputLine.indexOf("name ");
85 // Might get a quoted string (don't understand why, but there you have it)
86 if (inputLine.startsWith("\"", start + 5)) {
87 start += 6; // Skip over the first quote
88 int end = inputLine.lastIndexOf('"');
90 return inputLine.substring(start, end);
92 return inputLine.substring(start);
94 return inputLine.substring(start + 5);
98 public static Color parseModelColor(String inputLine) {
100 int colorStart = inputLine.indexOf("color ");
101 String colorString = inputLine.substring(colorStart + 6);
102 String[] rgbStrings = colorString.split(",");
103 float[] rgbValues = new float[4];
104 for (int i = 0; i < rgbStrings.length; i++) {
105 Float f = new Float(rgbStrings[i]);
106 rgbValues[i] = f.floatValue();
108 if (rgbStrings.length == 4) {
109 return new Color(rgbValues[0], rgbValues[1], rgbValues[2], rgbValues[3]);
111 return new Color(rgbValues[0], rgbValues[1], rgbValues[2]);
113 } catch (Exception ex) {
114 logger.warn("Unexpected return from Chimera: " + inputLine, ex);
120 * Create the key to use for forming the model/submodel key into the modelHash
125 * the submodel number
126 * @return the model key as an Integer
128 public static Integer makeModelKey(int model, int subModel) {
129 return new Integer(model * MAX_SUB_MODELS + subModel);
132 // invoked by the getResdiue (parseConnectivityReplies in CreateStructureNetworkTask)
133 // atomSpec = #0:1.A or #1:96.B@N
134 public static ChimeraModel getModel(String atomSpec, ChimeraManager chimeraManager) {
135 // System.out.println("getting model for "+atomSpec);
136 String[] split = atomSpec.split(":");
137 // No model specified....
138 if (split[0].length() == 0) {
139 logger.info("Unexpected return from Chimera: " + atomSpec);
142 // System.out.println("model = "+split[0].substring(1));
146 String[] subSplit = split[0].substring(1).split("\\.");
147 if (subSplit.length > 0)
148 model = Integer.parseInt(subSplit[0]);
150 model = Integer.parseInt(split[0].substring(1));
152 if (subSplit.length > 1)
153 submodel = Integer.parseInt(subSplit[1]);
154 } catch (Exception e) {
156 logger.warn("Unexpected return from Chimera: " + atomSpec, e);
158 return chimeraManager.getChimeraModel(model, submodel);
161 // invoked by the parseConnectivityReplies in CreateStructureNetworkTask
162 // atomSpec = #0:1.A or #1:96.B@N
163 public static ChimeraResidue getResidue(String atomSpec, ChimeraManager chimeraManager) {
164 // System.out.println("Getting residue from: "+atomSpec);
165 ChimeraModel model = getModel(atomSpec, chimeraManager); // Get the model
167 model = chimeraManager.getChimeraModel();
169 return getResidue(atomSpec, model);
172 // invoked by the getResdiue (parseConnectivityReplies in CreateStructureNetworkTask)
173 // atomSpec = #0:1.A or #1:96.B@N
174 public static ChimeraResidue getResidue(String atomSpec, ChimeraModel model) {
175 // System.out.println("Getting residue from: "+atomSpec);
176 String[] split = atomSpec.split(":|@");
178 // Split into residue and chain
179 String[] residueChain = split[1].split("\\.");
181 if (residueChain[0].length() == 0) {
182 logger.info("Unexpected return from Chimera: " + atomSpec);
186 if (residueChain.length == 2 && residueChain[1].length() > 0) {
187 ChimeraChain chain = model.getChain(residueChain[1]);
188 return chain.getResidue(residueChain[0]);
190 return model.getResidue("_", residueChain[0]);
193 public static ChimeraChain getChain(String atomSpec, ChimeraModel model) {
194 String[] split = atomSpec.split(":|@");
196 // Split into residue and chain
197 String[] residueChain = split[1].split("\\.");
198 if (residueChain.length == 1) {
199 logger.info("Unexpected return from Chimera: " + atomSpec);
202 return model.getChain(residueChain[1]);
205 public static String getAtomName(String atomSpec) {
206 String[] split = atomSpec.split("@");
207 if (split.length > 1) {
213 public static boolean isBackbone(String atom) {
214 if (atom.equals("C") || atom.equals("CA") || atom.equals("N") || atom.equals("O")
220 public static String getIntSubtype(String node, String atom) {
221 String[] split = node.split("#| ");
223 if (split.length == 2) {
224 resType = split[0].trim().toUpperCase();
225 } else if (split.length == 3) {
226 resType = split[1].trim().toUpperCase();
228 if (resType.equalsIgnoreCase("HOH") || resType.equalsIgnoreCase("WAT")) {
230 } else if (aaNames.containsKey(resType)) {
231 if (atom.equals("C") || atom.equals("CA") || atom.equals("N") || atom.equals("O")
232 || atom.equals("H")) {
243 public static String[] getResKeyParts(String resKey) {
244 // [pdbID[.modelNo]#][residueID][.chainID]
245 // pdbID := 4-character code | "URL" | "path"
246 String[] resKeyParts = new String[4];
247 String[] split = resKey.split("#");
248 String resChain = null;
249 // if no "#" then it is either only a pdb id or a residue or a chain
250 if (split.length == 1) {
251 // pdb id without model
252 if (resKey.length() == 4 && resKey.indexOf("\\.") < 0) {
253 parseModelID(resKey, resKeyParts);
256 else if (resKey.startsWith("\"")) {
257 parseModelID(resKey, resKeyParts);
259 // chain and residue or model and number
261 String[] splitSplit = resKey.split("\\.");
262 if (splitSplit.length == 1) {
263 // only a chain or a residue
268 Integer.parseInt(splitSplit[1]);
269 parseModelID(resKey, resKeyParts);
270 } catch (NumberFormatException ex) {
276 } else if (split.length == 2) {
277 // model and residue+chain
278 parseModelID(split[0], resKeyParts);
281 // model string with "#"
282 // TODO: [Optional] Are there more possibilities?
283 parseModelID(resKey.substring(0, resKey.lastIndexOf("#")), resKeyParts);
284 resChain = resKey.substring(resKey.lastIndexOf("#") + 1, resKey.length());
286 if (resChain != null) {
287 //System.out.println(resChain);
288 String[] resChainSplit = resChain.split("\\.");
289 if (resChainSplit.length == 1) {
290 // TODO: [Optional] Find a better way to distinguish between chain and residue
291 // if only one character and not an int, probably a chain
292 if (resChainSplit[0].length() == 1) {
294 Integer.parseInt(resChainSplit[0]);
295 resKeyParts[3] = resChainSplit[0];
296 } catch (NumberFormatException ex) {
297 resKeyParts[2] = resChainSplit[0];
300 resKeyParts[3] = resChainSplit[0];
302 } else if (resChainSplit.length == 2) {
303 resKeyParts[2] = resChainSplit[0];
304 resKeyParts[3] = resChainSplit[1];
307 logger.info("Could not parse residue identifier: " + resKey);
310 // String print = "";
311 // for (int i = 0; i < resKeyParts.length; i++) {
312 // if (resKeyParts[i] == null) {
313 // print += i + ": null\t";
315 // print += i + ": " + resKeyParts[i] + ";";
318 // System.out.println(print);
322 public static void parseModelID(String modelID, String[] resKeyParts) {
323 if (modelID.startsWith("\"")) {
324 if (modelID.endsWith("\"")) {
325 resKeyParts[0] = modelID.substring(1, modelID.length() - 1);
329 Integer.parseInt(modelID.substring(modelID.lastIndexOf("\"") + 2,
331 resKeyParts[0] = modelID.substring(0, modelID.lastIndexOf("\"") - 1);
332 resKeyParts[1] = modelID.substring(modelID.lastIndexOf("\"") + 2,
334 } catch (NumberFormatException ex) {
335 resKeyParts[0] = modelID.substring(1);
339 String[] modelIDNo = modelID.split("\\.");
340 if (modelIDNo.length == 1) {
341 resKeyParts[0] = modelIDNo[0];
342 } else if (modelIDNo.length == 2) {
344 Integer.parseInt(modelIDNo[1]);
345 resKeyParts[0] = modelIDNo[0];
346 resKeyParts[1] = modelIDNo[1];
347 } catch (NumberFormatException ex) {
348 resKeyParts[0] = modelID;
351 logger.info("Could not parse model identifier: " + modelID);
357 * This method takes a Cytoscape attribute specification ([structure#][residue][.chainID]) and
358 * returns the lowest-level object referenced by the spec. For example, if the spec is "1tkk",
359 * this method will return a ChimeraModel. If the spec is ".A", it will return a ChimeraChain,
363 * the specification string
364 * @param chimeraManager
365 * the Chimera object we're currently using
366 * @return a ChimeraStructuralObject of the lowest type
368 public static ChimeraStructuralObject fromAttributeOld(String attrSpec,
369 ChimeraManager chimeraManager) {
370 if (attrSpec == null || attrSpec.indexOf(',') > 0 || attrSpec.indexOf('-') > 0) {
371 // No support for either lists or ranges
372 logger.warn("No support for identifier: " + attrSpec);
376 String residue = null;
380 ChimeraModel chimeraModel = null;
381 ChimeraChain chimeraChain = null;
382 ChimeraResidue chimeraResidue = null;
384 // System.out.println("Getting object from attribute: "+attrSpec);
386 String[] split = attrSpec.split("#");
387 String resChain = null;
388 if (split.length == 1) {
391 } else if (split.length == 2) {
396 // model string with "#"
397 model = attrSpec.substring(0, attrSpec.lastIndexOf("#"));
398 resChain = attrSpec.substring(attrSpec.lastIndexOf("#") + 1, attrSpec.length());
400 if (resChain != null) {
401 String[] resChainSplit = resChain.split("\\.");
402 if (resChainSplit.length == 1) {
403 residue = resChainSplit[0];
404 } else if (resChainSplit.length == 2) {
405 residue = resChainSplit[0];
406 chain = resChainSplit[1];
409 logger.warn("No support for identifier: " + attrSpec);
413 // if (split.length == 1) {
415 // residue = split[0];
416 // } else if (split.length == 3) {
417 // // We have all three
419 // residue = split[1];
421 // } else if (split.length == 2 && attrSpec.indexOf('#') > 0) {
422 // // Model and Residue
424 // residue = split[1];
426 // // Residue and Chain
427 // residue = split[0];
431 // System.out.println("model = " + model + " chain = " + chain + " residue = " +
434 List<ChimeraModel> models = chimeraManager.getChimeraModels(model,
435 ModelType.PDB_MODEL);
436 if (models.size() == 1) {
437 chimeraModel = models.get(0);
440 chimeraModel = chimeraManager.getChimeraModel(Integer.valueOf(model), 0);
441 } catch (NumberFormatException ex) {
446 if (chimeraModel == null) {
447 chimeraModel = chimeraManager.getChimeraModel();
449 // System.out.println("ChimeraModel = " + chimeraModel);
452 chimeraChain = chimeraModel.getChain(chain);
453 // System.out.println("ChimeraChain = " + chimeraChain);
455 if (residue != null) {
456 if (chimeraChain != null) {
457 chimeraResidue = chimeraChain.getResidue(residue);
459 chimeraResidue = chimeraModel.getResidue("_", residue);
461 // System.out.println("ChimeraResidue = " + chimeraResidue);
464 if (chimeraResidue != null)
465 return chimeraResidue;
467 if (chimeraChain != null)
470 if (chimeraModel != null)
473 } catch (Exception ex) {
474 logger.warn("Could not parse residue identifier: " + attrSpec, ex);
479 public static ChimeraStructuralObject fromAttribute(String attrSpec,
480 ChimeraManager chimeraManager) {
481 // TODO: Make sure it is OK to remove this: || attrSpec.indexOf('-') > 0
482 if (attrSpec == null || attrSpec.indexOf(',') > 0) {
483 // No support for either lists or ranges
484 // System.out.println("No support for identifier: " + attrSpec);
485 logger.warn("No support for identifier: " + attrSpec);
488 String[] modelIDNoResChain = getResKeyParts(attrSpec);
490 ChimeraModel chimeraModel = null;
491 ChimeraChain chimeraChain = null;
492 ChimeraResidue chimeraResidue = null;
494 // System.out.println("Getting object from attribute: "+attrSpec);
496 if (modelIDNoResChain[0] != null) {
497 String modelID = modelIDNoResChain[0];
498 List<ChimeraModel> models = chimeraManager.getChimeraModels(modelID,
499 ModelType.PDB_MODEL);
500 if (models.size() == 1) { // usual case with only one model
501 chimeraModel = models.get(0);
502 } else if (models.size() > 1 && modelIDNoResChain[1] != null) {
503 // there are several submodels
505 int modelNo = Integer.valueOf(modelIDNoResChain[1]);
506 for (ChimeraModel model : models) {
507 if (model.getSubModelNumber() == modelNo) {
508 chimeraModel = model;
512 } catch (NumberFormatException ex) {
516 // TODO: [Optional] What is this doing?
518 chimeraModel = chimeraManager.getChimeraModel(Integer.valueOf(modelID), 0);
519 } catch (NumberFormatException ex) {
524 if (chimeraModel == null) {
525 // TODO: [Optional] Find a better way to handle this case
526 // If no model can be matched, continue
527 // System.out.println("No matching model could be find for " + attrSpec);
529 // chimeraModel = chimeraManager.getChimeraModel();
530 // logger.warn("No matching model could be find for " + attrSpec + ". Trying with "
531 // + chimeraModel.toSpec());
533 // System.out.println("ChimeraModel = " + chimeraModel);
535 if (modelIDNoResChain[3] != null) {
536 chimeraChain = chimeraModel.getChain(modelIDNoResChain[3]);
537 // System.out.println("ChimeraChain = " + chimeraChain);
539 if (modelIDNoResChain[2] != null) {
540 String residue = modelIDNoResChain[2];
541 if (chimeraChain != null) {
542 chimeraResidue = chimeraChain.getResidue(residue);
543 } else if (chimeraModel.getChain("_") != null) {
544 chimeraResidue = chimeraModel.getResidue("_", residue);
545 } else if (chimeraModel.getChainCount() == 1) {
546 chimeraResidue = chimeraModel.getResidue(chimeraModel.getChainNames()
547 .iterator().next(), residue);
549 // System.out.println("ChimeraResidue = " + chimeraResidue);
552 if (chimeraResidue != null)
553 return chimeraResidue;
555 if (chimeraChain != null)
558 if (chimeraModel != null)
561 } catch (Exception ex) {
562 // System.out.println("Could not parse chimera identifier: " +
563 // attrSpec+"("+ex.getMessage()+")");
564 logger.warn("Could not parse chimera identifier: " + attrSpec, ex);
570 * Search for structure references in the residue list
573 * the list of residues
574 * @return a concatenated list of structures encoded in the list
576 public static String findStructures(String residueList) {
577 if (residueList == null)
579 String[] residues = residueList.split(",");
580 Map<String, String> structureNameMap = new HashMap<String, String>();
581 for (int i = 0; i < residues.length; i++) {
582 String[] components = residues[i].split("#");
583 if (components.length > 1) {
584 structureNameMap.put(components[0], components[1]);
587 if (structureNameMap.isEmpty())
590 String structure = null;
591 for (String struct : structureNameMap.keySet()) {
592 if (structure == null)
593 structure = new String();
595 structure = structure.concat(",");
596 structure = structure.concat(struct);
601 // invoked by openStructures in StructureManager
602 public static List<String> parseFuncRes(List<String> residueNames, String modelName) {
603 List<String> resRanges = new ArrayList<String>();
604 for (int i = 0; i < residueNames.size(); i++) {
605 String residue = residueNames.get(i);
606 // Parse out the structure, if there is one
607 String[] components = residue.split("#");
608 if (components.length > 1 && !modelName.equals(components[0])) {
610 } else if (components.length > 1) {
611 residue = components[1];
612 } else if (components.length == 1) {
613 residue = components[0];
615 // Check to see if we have a range-spec
616 String resRange = "";
617 if (residue == null || residue.equals("") || residue.length() == 0) {
620 String[] range = residue.split("-", 2);
622 for (int res = 0; res < range.length; res++) {
624 resRange = resRange.concat("-");
625 if (chain != null && range[res].indexOf('.') == -1)
626 range[res] = range[res].concat("." + chain);
629 if (res == 0 && range.length >= 2 && range[res].indexOf('.') > 0) {
630 // This is a range spec with the leading residue containing a chain spec
631 String[] resChain = range[res].split("\\.");
633 range[res] = resChain[0];
635 // Fix weird SFLD syntax...
636 if (range[res].indexOf('|') > 0 && Character.isDigit(range[res].charAt(0))) {
637 int offset = range[res].indexOf('|');
638 String str = range[res].substring(offset + 1) + range[res].substring(0, offset);
642 // Convert to legal atom-spec
643 if (Character.isDigit(range[res].charAt(0))) {
644 resRange = resRange.concat(range[res]);
645 } else if (Character.isDigit(range[res].charAt(1))) {
646 resRange = resRange.concat(range[res].substring(1));
647 } else if (range[res].charAt(0) == '.') {
648 // Do we have a chain spec?
649 resRange = resRange.concat(range[res]);
651 resRange = resRange.concat(range[res].substring(3));
654 if (!resRanges.contains(resRange)) {
655 resRanges.add(resRange);
662 aaNames = new HashMap<String, String>();
663 aaNames.put("ALA", "A Ala Alanine N[C@@H](C)C(O)=O");
664 aaNames.put("ARG", "R Arg Arginine N[C@@H](CCCNC(N)=N)C(O)=O");
665 aaNames.put("ASN", "N Asn Asparagine N[C@@H](CC(N)=O)C(O)=O");
666 aaNames.put("ASP", "D Asp Aspartic_acid N[C@@H](CC(O)=O)C(O)=O");
667 aaNames.put("CYS", "C Cys Cysteine N[C@@H](CS)C(O)=O");
668 aaNames.put("GLN", "Q Gln Glutamine N[C@H](C(O)=O)CCC(N)=O");
669 aaNames.put("GLU", "E Glu Glumatic_acid N[C@H](C(O)=O)CCC(O)=O");
670 aaNames.put("GLY", "G Gly Glycine NCC(O)=O");
671 aaNames.put("HIS", "H His Histidine N[C@@H](CC1=CN=CN1)C(O)=O");
672 aaNames.put("ILE", "I Ile Isoleucine N[C@]([C@H](C)CC)([H])C(O)=O");
673 aaNames.put("LEU", "L Leu Leucine N[C@](CC(C)C)([H])C(O)=O");
674 aaNames.put("LYS", "K Lys Lysine N[C@](CCCCN)([H])C(O)=O");
675 aaNames.put("DLY", "K Dly D-Lysine NCCCC[C@@H](N)C(O)=O");
676 aaNames.put("MET", "M Met Methionine N[C@](CCSC)([H])C(O)=O");
677 aaNames.put("PHE", "F Phe Phenylalanine N[C@](CC1=CC=CC=C1)([H])C(O)=O");
678 aaNames.put("PRO", "P Pro Proline OC([C@@]1([H])NCCC1)=O");
679 aaNames.put("SER", "S Ser Serine OC[C@](C(O)=O)([H])N");
680 aaNames.put("THR", "T Thr Threonine O[C@H](C)[C@](C(O)=O)([H])N");
681 aaNames.put("TRP", "W Trp Tryptophan N[C@@]([H])(CC1=CN([H])C2=C1C=CC=C2)C(O)=O");
682 aaNames.put("TYR", "Y Tyr Tyrosine N[C@@](C(O)=O)([H])CC1=CC=C(O)C=C1");
683 aaNames.put("VAL", "V Val Valine N[C@@](C(O)=O)([H])C(C)C");
684 aaNames.put("ASX", "B Asx Aspartic_acid_or_Asparagine");
685 aaNames.put("GLX", "Z Glx Glutamine_or_Glutamic_acid");
686 aaNames.put("XAA", "X Xaa Any_or_unknown_amino_acid");
687 aaNames.put("HOH", "HOH HOH Water [H]O[H]");
691 * Convert the amino acid type to a full name
694 * the residue type to convert
695 * @return the full name of the residue
697 public static String toFullName(String aaType) {
698 if (!aaNames.containsKey(aaType))
700 String[] ids = ((String) aaNames.get(aaType)).split(" ");
701 return ids[2].replace('_', ' ');
705 * Convert the amino acid type to a single letter
708 * the residue type to convert
709 * @return the single letter representation of the residue
711 public static String toSingleLetter(String aaType) {
712 if (!aaNames.containsKey(aaType))
714 String[] ids = ((String) aaNames.get(aaType)).split(" ");
719 * Convert the amino acid type to three letters
722 * the residue type to convert
723 * @return the three letter representation of the residue
725 public static String toThreeLetter(String aaType) {
726 if (!aaNames.containsKey(aaType))
728 String[] ids = ((String) aaNames.get(aaType)).split(" ");
733 * Convert the amino acid type to its SMILES string
736 * the residue type to convert
737 * @return the SMILES representation of the residue
739 public static String toSMILES(String aaType) {
740 if (!aaNames.containsKey(aaType))
742 String[] ids = ((String) aaNames.get(aaType)).split(" ");
748 public static String getAlignName(ChimeraStructuralObject chimObj) {
749 String name = chimObj.getChimeraModel().toString();
750 if (chimObj instanceof ChimeraChain) {
751 name = ((ChimeraChain) chimObj).toString() + " [" + name + "]";