03f51f7a5ddfb12a8f3cb4732006f4193f50188b
[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 public abstract class ChimUtils
15 {
16
17   private static Logger logger = LoggerFactory.getLogger(ChimUtils.class);
18
19   static int MAX_SUB_MODELS = 1000;
20
21   public static final HashMap<String, String> aaNames;
22
23   public static String RESIDUE_ATTR = "ChimeraResidue";
24
25   public static String RINALYZER_ATTR = "RINalyzerResidue";
26
27   public static String DEFAULT_STRUCTURE_KEY = "pdbFileName";
28
29   /**
30    * Parse the model number returned by Chimera and return the int value
31    */
32   // invoked by the ChimeraModel constructor
33   // line = model id #0 type Molecule name 1ert
34   public static int[] parseModelNumber(String inputLine)
35   {
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
40     int modelNumber = -1;
41     int subModelNumber = 0;
42     try
43     {
44       if (decimal > 0)
45       {
46         subModelNumber = Integer.parseInt(inputLine.substring(decimal
47                 + hash + 2, space));
48         space = decimal + hash + 1;
49       }
50       modelNumber = Integer.parseInt(inputLine.substring(hash + 1, space));
51     } catch (Exception e)
52     {
53       logger.warn("Unexpected return from Chimera: " + inputLine, e);
54     }
55     return new int[] { modelNumber, subModelNumber };
56   }
57
58   /**
59    * Parse the model number returned by Chimera and return the int value
60    */
61   // invoked by openModel in ChimeraManager
62   // line: #1, chain A: hiv-1 protease
63   public static int[] parseOpenedModelNumber(String inputLine)
64   {
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
69     int modelNumber = -1;
70     int subModelNumber = 0;
71     try
72     {
73       if (decimal > 0)
74       {
75         subModelNumber = Integer.parseInt(inputLine.substring(decimal
76                 + hash + 2, space));
77         space = decimal + hash + 1;
78       }
79       modelNumber = Integer.parseInt(inputLine.substring(hash + 1, space));
80     } catch (Exception e)
81     {
82       logger.warn("Unexpected return from Chimera: " + inputLine, e);
83     }
84     return new int[] { modelNumber, subModelNumber };
85   }
86
87   /**
88    * Parse the model identifier returned by Chimera and return the String value
89    */
90   // invoked by the ChimeraModel constructor
91   // line = model id #0 type Molecule name 1ert
92   public static String parseModelName(String inputLine)
93   {
94     int start = inputLine.indexOf("name ");
95     if (start < 0)
96       return null;
97     // Might get a quoted string (don't understand why, but there you have it)
98     if (inputLine.startsWith("\"", start + 5))
99     {
100       start += 6; // Skip over the first quote
101       int end = inputLine.lastIndexOf('"');
102       if (end >= 1)
103       {
104         return inputLine.substring(start, end);
105       }
106       else
107         return inputLine.substring(start);
108     }
109     else
110     {
111       return inputLine.substring(start + 5);
112     }
113   }
114
115   public static Color parseModelColor(String inputLine)
116   {
117     try
118     {
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++)
124       {
125         Float f = new Float(rgbStrings[i]);
126         rgbValues[i] = f.floatValue();
127       }
128       if (rgbStrings.length == 4)
129       {
130         return new Color(rgbValues[0], rgbValues[1], rgbValues[2],
131                 rgbValues[3]);
132       }
133       else
134       {
135         return new Color(rgbValues[0], rgbValues[1], rgbValues[2]);
136       }
137     } catch (Exception ex)
138     {
139       logger.warn("Unexpected return from Chimera: " + inputLine, ex);
140     }
141     return Color.white;
142   }
143
144   /**
145    * Create the key to use for forming the model/submodel key into the modelHash
146    * 
147    * @param model
148    *          the model number
149    * @param subModel
150    *          the submodel number
151    * @return the model key as an Integer
152    */
153   public static Integer makeModelKey(int model, int subModel)
154   {
155     return new Integer(model * MAX_SUB_MODELS + subModel);
156   }
157
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)
163   {
164     // System.out.println("getting model for "+atomSpec);
165     String[] split = atomSpec.split(":");
166     // No model specified....
167     if (split[0].length() == 0)
168     {
169       logger.info("Unexpected return from Chimera: " + atomSpec);
170       return null;
171     }
172     // System.out.println("model = "+split[0].substring(1));
173     int model = 0;
174     int submodel = 0;
175     try
176     {
177       String[] subSplit = split[0].substring(1).split("\\.");
178       if (subSplit.length > 0)
179         model = Integer.parseInt(subSplit[0]);
180       else
181         model = Integer.parseInt(split[0].substring(1));
182
183       if (subSplit.length > 1)
184         submodel = Integer.parseInt(subSplit[1]);
185     } catch (Exception e)
186     {
187       // ignore
188       logger.warn("Unexpected return from Chimera: " + atomSpec, e);
189     }
190     return chimeraManager.getChimeraModel(model, submodel);
191   }
192
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)
197   {
198     // System.out.println("Getting residue from: "+atomSpec);
199     ChimeraModel model = getModel(atomSpec, chimeraManager); // Get the model
200     if (model == null)
201     {
202       model = chimeraManager.getChimeraModel();
203     }
204     return getResidue(atomSpec, model);
205   }
206
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,
211           ChimeraModel model)
212   {
213     // System.out.println("Getting residue from: "+atomSpec);
214     String[] split = atomSpec.split(":|@");
215
216     // Split into residue and chain
217     String[] residueChain = split[1].split("\\.");
218
219     if (residueChain[0].length() == 0)
220     {
221       logger.info("Unexpected return from Chimera: " + atomSpec);
222       return null;
223     }
224
225     if (residueChain.length == 2 && residueChain[1].length() > 0)
226     {
227       ChimeraChain chain = model.getChain(residueChain[1]);
228       return chain.getResidue(residueChain[0]);
229     }
230     return model.getResidue("_", residueChain[0]);
231   }
232
233   public static ChimeraChain getChain(String atomSpec, ChimeraModel model)
234   {
235     String[] split = atomSpec.split(":|@");
236
237     // Split into residue and chain
238     String[] residueChain = split[1].split("\\.");
239     if (residueChain.length == 1)
240     {
241       logger.info("Unexpected return from Chimera: " + atomSpec);
242       return null;
243     }
244     return model.getChain(residueChain[1]);
245   }
246
247   public static String getAtomName(String atomSpec)
248   {
249     String[] split = atomSpec.split("@");
250     if (split.length > 1)
251     {
252       return split[1];
253     }
254     return atomSpec;
255   }
256
257   public static boolean isBackbone(String atom)
258   {
259     if (atom.equals("C") || atom.equals("CA") || atom.equals("N")
260             || atom.equals("O") || atom.equals("H"))
261       return true;
262     return false;
263   }
264
265   public static String getIntSubtype(String node, String atom)
266   {
267     String[] split = node.split("#| ");
268     String resType = "";
269     if (split.length == 2)
270     {
271       resType = split[0].trim().toUpperCase();
272     }
273     else if (split.length == 3)
274     {
275       resType = split[1].trim().toUpperCase();
276     }
277     if (resType.equalsIgnoreCase("HOH") || resType.equalsIgnoreCase("WAT"))
278     {
279       return "water";
280     }
281     else if (aaNames.containsKey(resType))
282     {
283       if (atom.equals("C") || atom.equals("CA") || atom.equals("N")
284               || atom.equals("O") || atom.equals("H"))
285       {
286         return "mc";
287       }
288       else
289       {
290         return "sc";
291       }
292     }
293     else
294     {
295       return "other";
296     }
297   }
298
299   public static String[] getResKeyParts(String resKey)
300   {
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)
308     {
309       // pdb id without model
310       if (resKey.length() == 4 && resKey.indexOf("\\.") < 0)
311       {
312         parseModelID(resKey, resKeyParts);
313       }
314       // pdb link or file
315       else if (resKey.startsWith("\""))
316       {
317         parseModelID(resKey, resKeyParts);
318       }
319       // chain and residue or model and number
320       else
321       {
322         String[] splitSplit = resKey.split("\\.");
323         if (splitSplit.length == 1)
324         {
325           // only a chain or a residue
326           resChain = resKey;
327         }
328         else
329         {
330           try
331           {
332             // pdb with a model
333             Integer.parseInt(splitSplit[1]);
334             parseModelID(resKey, resKeyParts);
335           } catch (NumberFormatException ex)
336           {
337             // residue and chain
338             resChain = resKey;
339           }
340         }
341       }
342     }
343     else if (split.length == 2)
344     {
345       // model and residue+chain
346       parseModelID(split[0], resKeyParts);
347       resChain = split[1];
348     }
349     else
350     {
351       // model string with "#"
352       // TODO: [Optional] Are there more possibilities?
353       parseModelID(resKey.substring(0, resKey.lastIndexOf("#")),
354               resKeyParts);
355       resChain = resKey.substring(resKey.lastIndexOf("#") + 1,
356               resKey.length());
357     }
358     if (resChain != null)
359     {
360       // System.out.println(resChain);
361       String[] resChainSplit = resChain.split("\\.");
362       if (resChainSplit.length == 1)
363       {
364         // TODO: [Optional] Find a better way to distinguish between chain and
365         // residue
366         // if only one character and not an int, probably a chain
367         if (resChainSplit[0].length() == 1)
368         {
369           try
370           {
371             Integer.parseInt(resChainSplit[0]);
372             resKeyParts[3] = resChainSplit[0];
373           } catch (NumberFormatException ex)
374           {
375             resKeyParts[2] = resChainSplit[0];
376           }
377         }
378         else
379         {
380           resKeyParts[3] = resChainSplit[0];
381         }
382       }
383       else if (resChainSplit.length == 2)
384       {
385         resKeyParts[2] = resChainSplit[0];
386         resKeyParts[3] = resChainSplit[1];
387       }
388       else
389       {
390         // too many dots?
391         logger.info("Could not parse residue identifier: " + resKey);
392       }
393     }
394     // String print = "";
395     // for (int i = 0; i < resKeyParts.length; i++) {
396     // if (resKeyParts[i] == null) {
397     // print += i + ": null\t";
398     // } else {
399     // print += i + ": " + resKeyParts[i] + ";";
400     // }
401     // }
402     // System.out.println(print);
403     return resKeyParts;
404   }
405
406   public static void parseModelID(String modelID, String[] resKeyParts)
407   {
408     if (modelID.startsWith("\""))
409     {
410       if (modelID.endsWith("\""))
411       {
412         resKeyParts[0] = modelID.substring(1, modelID.length() - 1);
413         return;
414       }
415       else
416       {
417         try
418         {
419           Integer.parseInt(modelID.substring(modelID.lastIndexOf("\"") + 2,
420                   modelID.length()));
421           resKeyParts[0] = modelID.substring(0,
422                   modelID.lastIndexOf("\"") - 1);
423           resKeyParts[1] = modelID.substring(modelID.lastIndexOf("\"") + 2,
424                   modelID.length());
425         } catch (NumberFormatException ex)
426         {
427           resKeyParts[0] = modelID.substring(1);
428         }
429       }
430     }
431     else
432     {
433       String[] modelIDNo = modelID.split("\\.");
434       if (modelIDNo.length == 1)
435       {
436         resKeyParts[0] = modelIDNo[0];
437       }
438       else if (modelIDNo.length == 2)
439       {
440         try
441         {
442           Integer.parseInt(modelIDNo[1]);
443           resKeyParts[0] = modelIDNo[0];
444           resKeyParts[1] = modelIDNo[1];
445         } catch (NumberFormatException ex)
446         {
447           resKeyParts[0] = modelID;
448         }
449       }
450       else
451       {
452         logger.info("Could not parse model identifier: " + modelID);
453       }
454     }
455   }
456
457   /**
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
462    * ChimeraChain, etc.
463    * 
464    * @param attrSpec
465    *          the specification string
466    * @param chimeraManager
467    *          the Chimera object we're currently using
468    * @return a ChimeraStructuralObject of the lowest type
469    */
470   public static ChimeraStructuralObject fromAttributeOld(String attrSpec,
471           ChimeraManager chimeraManager)
472   {
473     if (attrSpec == null || attrSpec.indexOf(',') > 0
474             || attrSpec.indexOf('-') > 0)
475     {
476       // No support for either lists or ranges
477       logger.warn("No support for identifier: " + attrSpec);
478       return null;
479     }
480
481     String residue = null;
482     String model = null;
483     String chain = null;
484
485     ChimeraModel chimeraModel = null;
486     ChimeraChain chimeraChain = null;
487     ChimeraResidue chimeraResidue = null;
488
489     // System.out.println("Getting object from attribute: "+attrSpec);
490     try
491     {
492       String[] split = attrSpec.split("#");
493       String resChain = null;
494       if (split.length == 1)
495       {
496         // no model
497         resChain = split[0];
498       }
499       else if (split.length == 2)
500       {
501         // model and rest
502         model = split[0];
503         resChain = split[1];
504       }
505       else
506       {
507         // model string with "#"
508         model = attrSpec.substring(0, attrSpec.lastIndexOf("#"));
509         resChain = attrSpec.substring(attrSpec.lastIndexOf("#") + 1,
510                 attrSpec.length());
511       }
512       if (resChain != null)
513       {
514         String[] resChainSplit = resChain.split("\\.");
515         if (resChainSplit.length == 1)
516         {
517           residue = resChainSplit[0];
518         }
519         else if (resChainSplit.length == 2)
520         {
521           residue = resChainSplit[0];
522           chain = resChainSplit[1];
523         }
524         else
525         {
526           // too many dots?
527           logger.warn("No support for identifier: " + attrSpec);
528         }
529       }
530
531       // if (split.length == 1) {
532       // // No model
533       // residue = split[0];
534       // } else if (split.length == 3) {
535       // // We have all three
536       // model = split[0];
537       // residue = split[1];
538       // chain = split[2];
539       // } else if (split.length == 2 && attrSpec.indexOf('#') > 0) {
540       // // Model and Residue
541       // model = split[0];
542       // residue = split[1];
543       // } else {
544       // // Residue and Chain
545       // residue = split[0];
546       // chain = split[1];
547       // }
548
549       // System.out.println("model = " + model + " chain = " + chain +
550       // " residue = " +
551       // residue);
552       if (model != null)
553       {
554         List<ChimeraModel> models = chimeraManager.getChimeraModels(model,
555                 ModelType.PDB_MODEL);
556         if (models.size() == 1)
557         {
558           chimeraModel = models.get(0);
559         }
560         else
561         {
562           try
563           {
564             chimeraModel = chimeraManager.getChimeraModel(
565                     Integer.valueOf(model), 0);
566           } catch (NumberFormatException ex)
567           {
568             // ignore
569           }
570         }
571       }
572       if (chimeraModel == null)
573       {
574         chimeraModel = chimeraManager.getChimeraModel();
575       }
576       // System.out.println("ChimeraModel = " + chimeraModel);
577
578       if (chain != null)
579       {
580         chimeraChain = chimeraModel.getChain(chain);
581         // System.out.println("ChimeraChain = " + chimeraChain);
582       }
583       if (residue != null)
584       {
585         if (chimeraChain != null)
586         {
587           chimeraResidue = chimeraChain.getResidue(residue);
588         }
589         else
590         {
591           chimeraResidue = chimeraModel.getResidue("_", residue);
592         }
593         // System.out.println("ChimeraResidue = " + chimeraResidue);
594       }
595
596       if (chimeraResidue != null)
597         return chimeraResidue;
598
599       if (chimeraChain != null)
600         return chimeraChain;
601
602       if (chimeraModel != null)
603         return chimeraModel;
604
605     } catch (Exception ex)
606     {
607       logger.warn("Could not parse residue identifier: " + attrSpec, ex);
608     }
609     return null;
610   }
611
612   public static ChimeraStructuralObject fromAttribute(String attrSpec,
613           ChimeraManager chimeraManager)
614   {
615     // TODO: Make sure it is OK to remove this: || attrSpec.indexOf('-') > 0
616     if (attrSpec == null || attrSpec.indexOf(',') > 0)
617     {
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);
621       return null;
622     }
623     String[] modelIDNoResChain = getResKeyParts(attrSpec);
624
625     ChimeraModel chimeraModel = null;
626     ChimeraChain chimeraChain = null;
627     ChimeraResidue chimeraResidue = null;
628
629     // System.out.println("Getting object from attribute: "+attrSpec);
630     try
631     {
632       if (modelIDNoResChain[0] != null)
633       {
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);
640         }
641         else if (models.size() > 1 && modelIDNoResChain[1] != null)
642         {
643           // there are several submodels
644           try
645           {
646             int modelNo = Integer.valueOf(modelIDNoResChain[1]);
647             for (ChimeraModel model : models)
648             {
649               if (model.getSubModelNumber() == modelNo)
650               {
651                 chimeraModel = model;
652                 break;
653               }
654             }
655           } catch (NumberFormatException ex)
656           {
657             // ignore
658           }
659         }
660         else
661         {
662           // TODO: [Optional] What is this doing?
663           try
664           {
665             chimeraModel = chimeraManager.getChimeraModel(
666                     Integer.valueOf(modelID), 0);
667           } catch (NumberFormatException ex)
668           {
669             // ignore
670           }
671         }
672       }
673       if (chimeraModel == null)
674       {
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 " +
678         // attrSpec);
679         return null;
680         // chimeraModel = chimeraManager.getChimeraModel();
681         // logger.warn("No matching model could be find for " + attrSpec +
682         // ". Trying with "
683         // + chimeraModel.toSpec());
684       }
685       // System.out.println("ChimeraModel = " + chimeraModel);
686
687       if (modelIDNoResChain[3] != null)
688       {
689         chimeraChain = chimeraModel.getChain(modelIDNoResChain[3]);
690         // System.out.println("ChimeraChain = " + chimeraChain);
691       }
692       if (modelIDNoResChain[2] != null)
693       {
694         String residue = modelIDNoResChain[2];
695         if (chimeraChain != null)
696         {
697           chimeraResidue = chimeraChain.getResidue(residue);
698         }
699         else if (chimeraModel.getChain("_") != null)
700         {
701           chimeraResidue = chimeraModel.getResidue("_", residue);
702         }
703         else if (chimeraModel.getChainCount() == 1)
704         {
705           chimeraResidue = chimeraModel.getResidue(chimeraModel
706                   .getChainNames().iterator().next(), residue);
707         }
708         // System.out.println("ChimeraResidue = " + chimeraResidue);
709       }
710
711       if (chimeraResidue != null)
712         return chimeraResidue;
713
714       if (chimeraChain != null)
715         return chimeraChain;
716
717       if (chimeraModel != null)
718         return chimeraModel;
719
720     } catch (Exception ex)
721     {
722       // System.out.println("Could not parse chimera identifier: " +
723       // attrSpec+"("+ex.getMessage()+")");
724       logger.warn("Could not parse chimera identifier: " + attrSpec, ex);
725     }
726     return null;
727   }
728
729   /**
730    * Search for structure references in the residue list
731    * 
732    * @param residueList
733    *          the list of residues
734    * @return a concatenated list of structures encoded in the list
735    */
736   public static String findStructures(String residueList)
737   {
738     if (residueList == null)
739       return null;
740     String[] residues = residueList.split(",");
741     Map<String, String> structureNameMap = new HashMap<String, String>();
742     for (int i = 0; i < residues.length; i++)
743     {
744       String[] components = residues[i].split("#");
745       if (components.length > 1)
746       {
747         structureNameMap.put(components[0], components[1]);
748       }
749     }
750     if (structureNameMap.isEmpty())
751       return null;
752
753     String structure = null;
754     for (String struct : structureNameMap.keySet())
755     {
756       if (structure == null)
757         structure = new String();
758       else
759         structure = structure.concat(",");
760       structure = structure.concat(struct);
761     }
762     return structure;
763   }
764
765   // invoked by openStructures in StructureManager
766   public static List<String> parseFuncRes(List<String> residueNames,
767           String modelName)
768   {
769     List<String> resRanges = new ArrayList<String>();
770     for (int i = 0; i < residueNames.size(); i++)
771     {
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]))
776       {
777         continue;
778       }
779       else if (components.length > 1)
780       {
781         residue = components[1];
782       }
783       else if (components.length == 1)
784       {
785         residue = components[0];
786       }
787       // Check to see if we have a range-spec
788       String resRange = "";
789       if (residue == null || residue.equals("") || residue.length() == 0)
790       {
791         continue;
792       }
793       String[] range = residue.split("-", 2);
794       String chain = null;
795       for (int res = 0; res < range.length; res++)
796       {
797         if (res == 1)
798         {
799           resRange = resRange.concat("-");
800           if (chain != null && range[res].indexOf('.') == -1)
801             range[res] = range[res].concat("." + chain);
802         }
803
804         if (res == 0 && range.length >= 2 && range[res].indexOf('.') > 0)
805         {
806           // This is a range spec with the leading residue containing a chain
807           // spec
808           String[] resChain = range[res].split("\\.");
809           chain = resChain[1];
810           range[res] = resChain[0];
811         }
812         // Fix weird SFLD syntax...
813         if (range[res].indexOf('|') > 0
814                 && Character.isDigit(range[res].charAt(0)))
815         {
816           int offset = range[res].indexOf('|');
817           String str = range[res].substring(offset + 1)
818                   + range[res].substring(0, offset);
819           range[res] = str;
820         }
821
822         // Convert to legal atom-spec
823         if (Character.isDigit(range[res].charAt(0)))
824         {
825           resRange = resRange.concat(range[res]);
826         }
827         else if (Character.isDigit(range[res].charAt(1)))
828         {
829           resRange = resRange.concat(range[res].substring(1));
830         }
831         else if (range[res].charAt(0) == '.')
832         {
833           // Do we have a chain spec?
834           resRange = resRange.concat(range[res]);
835         }
836         else
837         {
838           resRange = resRange.concat(range[res].substring(3));
839         }
840       }
841       if (!resRanges.contains(resRange))
842       {
843         resRanges.add(resRange);
844       }
845     }
846     return resRanges;
847   }
848
849   static
850   {
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");
870     aaNames.put("TRP",
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]");
878   }
879
880   /**
881    * Convert the amino acid type to a full name
882    * 
883    * @param aaType
884    *          the residue type to convert
885    * @return the full name of the residue
886    */
887   public static String toFullName(String aaType)
888   {
889     if (!aaNames.containsKey(aaType))
890       return aaType;
891     String[] ids = ((String) aaNames.get(aaType)).split(" ");
892     return ids[2].replace('_', ' ');
893   }
894
895   /**
896    * Convert the amino acid type to a single letter
897    * 
898    * @param aaType
899    *          the residue type to convert
900    * @return the single letter representation of the residue
901    */
902   public static String toSingleLetter(String aaType)
903   {
904     if (!aaNames.containsKey(aaType))
905       return aaType;
906     String[] ids = ((String) aaNames.get(aaType)).split(" ");
907     return ids[0];
908   }
909
910   /**
911    * Convert the amino acid type to three letters
912    * 
913    * @param aaType
914    *          the residue type to convert
915    * @return the three letter representation of the residue
916    */
917   public static String toThreeLetter(String aaType)
918   {
919     if (!aaNames.containsKey(aaType))
920       return aaType;
921     String[] ids = ((String) aaNames.get(aaType)).split(" ");
922     return ids[1];
923   }
924
925   /**
926    * Convert the amino acid type to its SMILES string
927    * 
928    * @param aaType
929    *          the residue type to convert
930    * @return the SMILES representation of the residue
931    */
932   public static String toSMILES(String aaType)
933   {
934     if (!aaNames.containsKey(aaType))
935       return null;
936     String[] ids = ((String) aaNames.get(aaType)).split(" ");
937     if (ids.length < 4)
938       return null;
939     return ids[3];
940   }
941
942   public static String getAlignName(ChimeraStructuralObject chimObj)
943   {
944     String name = chimObj.getChimeraModel().toString();
945     if (chimObj instanceof ChimeraChain)
946     {
947       name = ((ChimeraChain) chimObj).toString() + " [" + name + "]";
948     }
949     return name;
950   }
951 }