JAL-1333 Chimera specific code extracted from the StructureViz library for opening...
[jalview.git] / src / ext / edu / ucsf / rbvi / strucviz2 / ChimUtils.java
1 package ext.edu.ucsf.rbvi.strucviz2;
2
3 import java.awt.Color;
4 import java.util.ArrayList;
5 import java.util.HashMap;
6 import java.util.List;
7 import java.util.Map;
8
9 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory;
11
12 import ext.edu.ucsf.rbvi.strucviz2.StructureManager.ModelType;
13
14
15 public abstract class ChimUtils {
16
17         private static Logger logger = LoggerFactory
18                         .getLogger(ChimUtils.class);
19
20         static int MAX_SUB_MODELS = 1000;
21
22         public static final HashMap<String, String> aaNames;
23
24         public static String RESIDUE_ATTR = "ChimeraResidue";
25         public static String RINALYZER_ATTR = "RINalyzerResidue";
26         public static String DEFAULT_STRUCTURE_KEY = "pdbFileName";
27
28         /**
29          * Parse the model number returned by Chimera and return the int value
30          */
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
38                 int modelNumber = -1;
39                 int subModelNumber = 0;
40                 try {
41                         if (decimal > 0) {
42                                 subModelNumber = Integer.parseInt(inputLine.substring(decimal + hash + 2, space));
43                                 space = decimal + hash + 1;
44                         }
45                         modelNumber = Integer.parseInt(inputLine.substring(hash + 1, space));
46                 } catch (Exception e) {
47                         logger.warn("Unexpected return from Chimera: " + inputLine, e);
48                 }
49                 return new int[] { modelNumber, subModelNumber };
50         }
51
52         /**
53          * Parse the model number returned by Chimera and return the int value
54          */
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
62                 int modelNumber = -1;
63                 int subModelNumber = 0;
64                 try {
65                         if (decimal > 0) {
66                                 subModelNumber = Integer.parseInt(inputLine.substring(decimal + hash + 2, space));
67                                 space = decimal + hash + 1;
68                         }
69                         modelNumber = Integer.parseInt(inputLine.substring(hash + 1, space));
70                 } catch (Exception e) {
71                         logger.warn("Unexpected return from Chimera: " + inputLine, e);
72                 }
73                 return new int[] { modelNumber, subModelNumber };
74         }
75
76         /**
77          * Parse the model identifier returned by Chimera and return the String value
78          */
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 ");
83                 if (start < 0)
84                         return null;
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('"');
89                         if (end >= 1) {
90                                 return inputLine.substring(start, end);
91                         } else
92                                 return inputLine.substring(start);
93                 } else {
94                         return inputLine.substring(start + 5);
95                 }
96         }
97
98         public static Color parseModelColor(String inputLine) {
99                 try {
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();
107                         }
108                         if (rgbStrings.length == 4) {
109                                 return new Color(rgbValues[0], rgbValues[1], rgbValues[2], rgbValues[3]);
110                         } else {
111                                 return new Color(rgbValues[0], rgbValues[1], rgbValues[2]);
112                         }
113                 } catch (Exception ex) {
114                         logger.warn("Unexpected return from Chimera: " + inputLine, ex);
115                 }
116                 return Color.white;
117         }
118
119         /**
120          * Create the key to use for forming the model/submodel key into the modelHash
121          * 
122          * @param model
123          *            the model number
124          * @param subModel
125          *            the submodel number
126          * @return the model key as an Integer
127          */
128         public static Integer makeModelKey(int model, int subModel) {
129                 return new Integer(model * MAX_SUB_MODELS + subModel);
130         }
131
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);
140                         return null;
141                 }
142                 // System.out.println("model = "+split[0].substring(1));
143                 int model = 0;
144                 int submodel = 0;
145                 try {
146                         String[] subSplit = split[0].substring(1).split("\\.");
147                         if (subSplit.length > 0)
148                                 model = Integer.parseInt(subSplit[0]);
149                         else
150                                 model = Integer.parseInt(split[0].substring(1));
151
152                         if (subSplit.length > 1)
153                                 submodel = Integer.parseInt(subSplit[1]);
154                 } catch (Exception e) {
155                         // ignore
156                         logger.warn("Unexpected return from Chimera: " + atomSpec, e);
157                 }
158                 return chimeraManager.getChimeraModel(model, submodel);
159         }
160
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
166                 if (model == null) {
167                         model = chimeraManager.getChimeraModel();
168                 }
169                 return getResidue(atomSpec, model);
170         }
171
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(":|@");
177
178                 // Split into residue and chain
179                 String[] residueChain = split[1].split("\\.");
180
181                 if (residueChain[0].length() == 0) {
182                         logger.info("Unexpected return from Chimera: " + atomSpec);
183                         return null;
184                 }
185
186                 if (residueChain.length == 2 && residueChain[1].length() > 0) {
187                         ChimeraChain chain = model.getChain(residueChain[1]);
188                         return chain.getResidue(residueChain[0]);
189                 }
190                 return model.getResidue("_", residueChain[0]);
191         }
192
193         public static ChimeraChain getChain(String atomSpec, ChimeraModel model) {
194                 String[] split = atomSpec.split(":|@");
195
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);
200                         return null;
201                 }
202                 return model.getChain(residueChain[1]);
203         }
204
205         public static String getAtomName(String atomSpec) {
206                 String[] split = atomSpec.split("@");
207                 if (split.length > 1) {
208                         return split[1];
209                 }
210                 return atomSpec;
211         }
212
213         public static boolean isBackbone(String atom) {
214                 if (atom.equals("C") || atom.equals("CA") || atom.equals("N") || atom.equals("O")
215                                 || atom.equals("H"))
216                         return true;
217                 return false;
218         }
219
220         public static String getIntSubtype(String node, String atom) {
221                 String[] split = node.split("#| ");
222                 String resType = "";
223                 if (split.length == 2) {
224                         resType = split[0].trim().toUpperCase();
225                 } else if (split.length == 3) {
226                         resType = split[1].trim().toUpperCase();
227                 }
228                 if (resType.equalsIgnoreCase("HOH") || resType.equalsIgnoreCase("WAT")) {
229                         return "water";
230                 } else if (aaNames.containsKey(resType)) {
231                         if (atom.equals("C") || atom.equals("CA") || atom.equals("N") || atom.equals("O")
232                                         || atom.equals("H")) {
233                                 return "mc";
234                         } else {
235                                 return "sc";
236                         }
237                 } else {
238                         return "other";
239                 }
240         }
241
242
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);
254                         }
255                         // pdb link or file
256                         else if (resKey.startsWith("\"")) {
257                                 parseModelID(resKey, resKeyParts);
258                         }
259                         // chain and residue or model and number
260                         else {
261                                 String[] splitSplit = resKey.split("\\.");
262                                 if (splitSplit.length == 1) {
263                                         // only a chain or a residue
264                                         resChain = resKey;
265                                 } else {
266                                         try {
267                                                 // pdb with a model
268                                                 Integer.parseInt(splitSplit[1]);
269                                                 parseModelID(resKey, resKeyParts);
270                                         } catch (NumberFormatException ex) {
271                                                 // residue and chain
272                                                 resChain = resKey;
273                                         }
274                                 }
275                         }
276                 } else if (split.length == 2) {
277                         // model and residue+chain
278                         parseModelID(split[0], resKeyParts);
279                         resChain = split[1];
280                 } else {
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());
285                 }
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) {
293                                         try {
294                                                 Integer.parseInt(resChainSplit[0]);
295                                                 resKeyParts[3] = resChainSplit[0];
296                                         } catch (NumberFormatException ex) {
297                                                 resKeyParts[2] = resChainSplit[0];
298                                         }
299                                 } else {
300                                         resKeyParts[3] = resChainSplit[0];
301                                 }
302                         } else if (resChainSplit.length == 2) {
303                                 resKeyParts[2] = resChainSplit[0];
304                                 resKeyParts[3] = resChainSplit[1];
305                         } else {
306                                 // too many dots?
307                                 logger.info("Could not parse residue identifier: " + resKey);
308                         }
309                 }
310                 // String print = "";
311                 // for (int i = 0; i < resKeyParts.length; i++) {
312                 // if (resKeyParts[i] == null) {
313                 // print += i + ": null\t";
314                 // } else {
315                 // print += i + ": " + resKeyParts[i] + ";";
316                 // }
317                 // }
318                 // System.out.println(print);
319                 return resKeyParts;
320         }
321
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);
326                                 return;
327                         } else {
328                                 try {
329                                         Integer.parseInt(modelID.substring(modelID.lastIndexOf("\"") + 2,
330                                                         modelID.length()));
331                                         resKeyParts[0] = modelID.substring(0, modelID.lastIndexOf("\"") - 1);
332                                         resKeyParts[1] = modelID.substring(modelID.lastIndexOf("\"") + 2,
333                                                         modelID.length());
334                                 } catch (NumberFormatException ex) {
335                                         resKeyParts[0] = modelID.substring(1);
336                                 }
337                         }
338                 } else {
339                         String[] modelIDNo = modelID.split("\\.");
340                         if (modelIDNo.length == 1) {
341                                 resKeyParts[0] = modelIDNo[0];
342                         } else if (modelIDNo.length == 2) {
343                                 try {
344                                         Integer.parseInt(modelIDNo[1]);
345                                         resKeyParts[0] = modelIDNo[0];
346                                         resKeyParts[1] = modelIDNo[1];
347                                 } catch (NumberFormatException ex) {
348                                         resKeyParts[0] = modelID;
349                                 }
350                         } else {
351                                 logger.info("Could not parse model identifier: " + modelID);
352                         }
353                 }
354         }
355
356         /**
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,
360          * etc.
361          * 
362          * @param attrSpec
363          *            the specification string
364          * @param chimeraManager
365          *            the Chimera object we're currently using
366          * @return a ChimeraStructuralObject of the lowest type
367          */
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);
373                         return null;
374                 }
375
376                 String residue = null;
377                 String model = null;
378                 String chain = null;
379
380                 ChimeraModel chimeraModel = null;
381                 ChimeraChain chimeraChain = null;
382                 ChimeraResidue chimeraResidue = null;
383
384                 // System.out.println("Getting object from attribute: "+attrSpec);
385                 try {
386                         String[] split = attrSpec.split("#");
387                         String resChain = null;
388                         if (split.length == 1) {
389                                 // no model
390                                 resChain = split[0];
391                         } else if (split.length == 2) {
392                                 // model and rest
393                                 model = split[0];
394                                 resChain = split[1];
395                         } else {
396                                 // model string with "#"
397                                 model = attrSpec.substring(0, attrSpec.lastIndexOf("#"));
398                                 resChain = attrSpec.substring(attrSpec.lastIndexOf("#") + 1, attrSpec.length());
399                         }
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];
407                                 } else {
408                                         // too many dots?
409                                         logger.warn("No support for identifier: " + attrSpec);
410                                 }
411                         }
412
413                         // if (split.length == 1) {
414                         // // No model
415                         // residue = split[0];
416                         // } else if (split.length == 3) {
417                         // // We have all three
418                         // model = split[0];
419                         // residue = split[1];
420                         // chain = split[2];
421                         // } else if (split.length == 2 && attrSpec.indexOf('#') > 0) {
422                         // // Model and Residue
423                         // model = split[0];
424                         // residue = split[1];
425                         // } else {
426                         // // Residue and Chain
427                         // residue = split[0];
428                         // chain = split[1];
429                         // }
430
431                         // System.out.println("model = " + model + " chain = " + chain + " residue = " +
432                         // residue);
433                         if (model != null) {
434                                 List<ChimeraModel> models = chimeraManager.getChimeraModels(model,
435                                                 ModelType.PDB_MODEL);
436                                 if (models.size() == 1) {
437                                         chimeraModel = models.get(0);
438                                 } else {
439                                         try {
440                                                 chimeraModel = chimeraManager.getChimeraModel(Integer.valueOf(model), 0);
441                                         } catch (NumberFormatException ex) {
442                                                 // ignore
443                                         }
444                                 }
445                         }
446                         if (chimeraModel == null) {
447                                 chimeraModel = chimeraManager.getChimeraModel();
448                         }
449                         // System.out.println("ChimeraModel = " + chimeraModel);
450
451                         if (chain != null) {
452                                 chimeraChain = chimeraModel.getChain(chain);
453                                 // System.out.println("ChimeraChain = " + chimeraChain);
454                         }
455                         if (residue != null) {
456                                 if (chimeraChain != null) {
457                                         chimeraResidue = chimeraChain.getResidue(residue);
458                                 } else {
459                                         chimeraResidue = chimeraModel.getResidue("_", residue);
460                                 }
461                                 // System.out.println("ChimeraResidue = " + chimeraResidue);
462                         }
463
464                         if (chimeraResidue != null)
465                                 return chimeraResidue;
466
467                         if (chimeraChain != null)
468                                 return chimeraChain;
469
470                         if (chimeraModel != null)
471                                 return chimeraModel;
472
473                 } catch (Exception ex) {
474                         logger.warn("Could not parse residue identifier: " + attrSpec, ex);
475                 }
476                 return null;
477         }
478
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);
486                         return null;
487                 }
488                 String[] modelIDNoResChain = getResKeyParts(attrSpec);
489
490                 ChimeraModel chimeraModel = null;
491                 ChimeraChain chimeraChain = null;
492                 ChimeraResidue chimeraResidue = null;
493
494                 // System.out.println("Getting object from attribute: "+attrSpec);
495                 try {
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
504                                         try {
505                                                 int modelNo = Integer.valueOf(modelIDNoResChain[1]);
506                                                 for (ChimeraModel model : models) {
507                                                         if (model.getSubModelNumber() == modelNo) {
508                                                                 chimeraModel = model;
509                                                                 break;
510                                                         }
511                                                 }
512                                         } catch (NumberFormatException ex) {
513                                                 // ignore
514                                         }
515                                 } else {
516                                         // TODO: [Optional] What is this doing?
517                                         try {
518                                                 chimeraModel = chimeraManager.getChimeraModel(Integer.valueOf(modelID), 0);
519                                         } catch (NumberFormatException ex) {
520                                                 // ignore
521                                         }
522                                 }
523                         }
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);
528                                 return null;
529                                 // chimeraModel = chimeraManager.getChimeraModel();
530                                 // logger.warn("No matching model could be find for " + attrSpec + ". Trying with "
531                                 // + chimeraModel.toSpec());
532                         }
533                         // System.out.println("ChimeraModel = " + chimeraModel);
534
535                         if (modelIDNoResChain[3] != null) {
536                                 chimeraChain = chimeraModel.getChain(modelIDNoResChain[3]);
537                                 // System.out.println("ChimeraChain = " + chimeraChain);
538                         }
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);
548                                 }
549                                 // System.out.println("ChimeraResidue = " + chimeraResidue);
550                         }
551
552                         if (chimeraResidue != null)
553                                 return chimeraResidue;
554
555                         if (chimeraChain != null)
556                                 return chimeraChain;
557
558                         if (chimeraModel != null)
559                                 return chimeraModel;
560
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);
565                 }
566                 return null;
567         }
568
569         /**
570          * Search for structure references in the residue list
571          * 
572          * @param residueList
573          *            the list of residues
574          * @return a concatenated list of structures encoded in the list
575          */
576         public static String findStructures(String residueList) {
577                 if (residueList == null)
578                         return 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]);
585                         }
586                 }
587                 if (structureNameMap.isEmpty())
588                         return null;
589
590                 String structure = null;
591                 for (String struct : structureNameMap.keySet()) {
592                         if (structure == null)
593                                 structure = new String();
594                         else
595                                 structure = structure.concat(",");
596                         structure = structure.concat(struct);
597                 }
598                 return structure;
599         }
600
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])) {
609                                 continue;
610                         } else if (components.length > 1) {
611                                 residue = components[1];
612                         } else if (components.length == 1) {
613                                 residue = components[0];
614                         }
615                         // Check to see if we have a range-spec
616                         String resRange = "";
617                         if (residue == null || residue.equals("") || residue.length() == 0) {
618                                 continue;
619                         }
620                         String[] range = residue.split("-", 2);
621                         String chain = null;
622                         for (int res = 0; res < range.length; res++) {
623                                 if (res == 1) {
624                                         resRange = resRange.concat("-");
625                                         if (chain != null && range[res].indexOf('.') == -1)
626                                                 range[res] = range[res].concat("." + chain);
627                                 }
628
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("\\.");
632                                         chain = resChain[1];
633                                         range[res] = resChain[0];
634                                 }
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);
639                                         range[res] = str;
640                                 }
641
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]);
650                                 } else {
651                                         resRange = resRange.concat(range[res].substring(3));
652                                 }
653                         }
654                         if (!resRanges.contains(resRange)) {
655                                 resRanges.add(resRange);
656                         }
657                 }
658                 return resRanges;
659         }
660
661         static {
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]");
688         }
689
690         /**
691          * Convert the amino acid type to a full name
692          * 
693          * @param aaType
694          *            the residue type to convert
695          * @return the full name of the residue
696          */
697         public static String toFullName(String aaType) {
698                 if (!aaNames.containsKey(aaType))
699                         return aaType;
700                 String[] ids = ((String) aaNames.get(aaType)).split(" ");
701                 return ids[2].replace('_', ' ');
702         }
703
704         /**
705          * Convert the amino acid type to a single letter
706          * 
707          * @param aaType
708          *            the residue type to convert
709          * @return the single letter representation of the residue
710          */
711         public static String toSingleLetter(String aaType) {
712                 if (!aaNames.containsKey(aaType))
713                         return aaType;
714                 String[] ids = ((String) aaNames.get(aaType)).split(" ");
715                 return ids[0];
716         }
717
718         /**
719          * Convert the amino acid type to three letters
720          * 
721          * @param aaType
722          *            the residue type to convert
723          * @return the three letter representation of the residue
724          */
725         public static String toThreeLetter(String aaType) {
726                 if (!aaNames.containsKey(aaType))
727                         return aaType;
728                 String[] ids = ((String) aaNames.get(aaType)).split(" ");
729                 return ids[1];
730         }
731
732         /**
733          * Convert the amino acid type to its SMILES string
734          * 
735          * @param aaType
736          *            the residue type to convert
737          * @return the SMILES representation of the residue
738          */
739         public static String toSMILES(String aaType) {
740                 if (!aaNames.containsKey(aaType))
741                         return null;
742                 String[] ids = ((String) aaNames.get(aaType)).split(" ");
743                 if (ids.length < 4)
744                         return null;
745                 return ids[3];
746         }
747
748         public static String getAlignName(ChimeraStructuralObject chimObj) {
749                 String name = chimObj.getChimeraModel().toString();
750                 if (chimObj instanceof ChimeraChain) {
751                         name = ((ChimeraChain) chimObj).toString() + " [" + name + "]";
752                 }
753                 return name;
754         }
755 }