95221d2bec39ea2b837a4ef6b4c9a680c32a7843
[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   // line: Model 0 (filename)
64   public static int[] parseOpenedModelNumber(String inputLine)
65   {
66     int hash = inputLine.indexOf('#');
67     int space = -1;
68     if (hash == (-1))
69     {
70       hash = inputLine.indexOf("Model");
71       if (hash >= 0)
72       {
73         hash = hash + 5;
74       }
75       space = inputLine.indexOf(' ', hash + 1);
76     }
77     else
78     {
79       space = inputLine.indexOf(',', hash);
80     }
81
82     int decimal = inputLine.substring(hash + 1, space).indexOf('.');
83     // model number is between hash+1 and space
84     int modelNumber = -1;
85     int subModelNumber = 0;
86     try
87     {
88       if (decimal > 0)
89       {
90         subModelNumber = Integer.parseInt(inputLine.substring(decimal
91                 + hash + 2, space));
92         space = decimal + hash + 1;
93       }
94       modelNumber = Integer.parseInt(inputLine.substring(hash + 1, space));
95     } catch (Exception e)
96     {
97       logger.warn("Unexpected return from Chimera: " + inputLine, e);
98     }
99     return new int[] { modelNumber, subModelNumber };
100   }
101
102   /**
103    * Parse the model identifier returned by Chimera and return the String value
104    */
105   // invoked by the ChimeraModel constructor
106   // line = model id #0 type Molecule name 1ert
107   public static String parseModelName(String inputLine)
108   {
109     int start = inputLine.indexOf("name ");
110     if (start < 0)
111     {
112       return null;
113     }
114     // Might get a quoted string (don't understand why, but there you have it)
115     if (inputLine.startsWith("\"", start + 5))
116     {
117       start += 6; // Skip over the first quote
118       int end = inputLine.lastIndexOf('"');
119       if (end >= 1)
120       {
121         return inputLine.substring(start, end);
122       }
123       else
124       {
125         return inputLine.substring(start);
126       }
127     }
128     else
129     {
130       return inputLine.substring(start + 5);
131     }
132   }
133
134   public static Color parseModelColor(String inputLine)
135   {
136     try
137     {
138       int colorStart = inputLine.indexOf("color ");
139       String colorString = inputLine.substring(colorStart + 6);
140       String[] rgbStrings = colorString.split(",");
141       float[] rgbValues = new float[4];
142       for (int i = 0; i < rgbStrings.length; i++)
143       {
144         Float f = new Float(rgbStrings[i]);
145         rgbValues[i] = f.floatValue();
146       }
147       if (rgbStrings.length == 4)
148       {
149         return new Color(rgbValues[0], rgbValues[1], rgbValues[2],
150                 rgbValues[3]);
151       }
152       else
153       {
154         return new Color(rgbValues[0], rgbValues[1], rgbValues[2]);
155       }
156     } catch (Exception ex)
157     {
158       logger.warn("Unexpected return from Chimera: " + inputLine, ex);
159     }
160     return Color.white;
161   }
162
163   /**
164    * Create the key to use for forming the model/submodel key into the modelHash
165    * 
166    * @param model
167    *          the model number
168    * @param subModel
169    *          the submodel number
170    * @return the model key as an Integer
171    */
172   public static Integer makeModelKey(int model, int subModel)
173   {
174     return new Integer(model * MAX_SUB_MODELS + subModel);
175   }
176
177   // invoked by the getResdiue (parseConnectivityReplies in
178   // CreateStructureNetworkTask)
179   // atomSpec = #0:1.A or #1:96.B@N
180   public static ChimeraModel getModel(String atomSpec,
181           ChimeraManager chimeraManager)
182   {
183     // System.out.println("getting model for "+atomSpec);
184     String[] split = atomSpec.split(":");
185     // No model specified....
186     if (split[0].length() == 0)
187     {
188       logger.info("Unexpected return from Chimera: " + atomSpec);
189       return null;
190     }
191     // System.out.println("model = "+split[0].substring(1));
192     int model = 0;
193     int submodel = 0;
194     try
195     {
196       String[] subSplit = split[0].substring(1).split("\\.");
197       if (subSplit.length > 0)
198       {
199         model = Integer.parseInt(subSplit[0]);
200       }
201       else
202       {
203         model = Integer.parseInt(split[0].substring(1));
204       }
205
206       if (subSplit.length > 1)
207       {
208         submodel = Integer.parseInt(subSplit[1]);
209       }
210     } catch (Exception e)
211     {
212       // ignore
213       logger.warn("Unexpected return from Chimera: " + atomSpec, e);
214     }
215     return chimeraManager.getChimeraModel(model, submodel);
216   }
217
218   // invoked by the parseConnectivityReplies in CreateStructureNetworkTask
219   // atomSpec = #0:1.A or #1:96.B@N
220   public static ChimeraResidue getResidue(String atomSpec,
221           ChimeraManager chimeraManager)
222   {
223     // System.out.println("Getting residue from: "+atomSpec);
224     ChimeraModel model = getModel(atomSpec, chimeraManager); // Get the model
225     if (model == null)
226     {
227       model = chimeraManager.getChimeraModel();
228     }
229     return getResidue(atomSpec, model);
230   }
231
232   // invoked by the getResdiue (parseConnectivityReplies in
233   // CreateStructureNetworkTask)
234   // atomSpec = #0:1.A or #1:96.B@N
235   public static ChimeraResidue getResidue(String atomSpec,
236           ChimeraModel model)
237   {
238     // System.out.println("Getting residue from: "+atomSpec);
239     String[] split = atomSpec.split(":|@");
240
241     // Split into residue and chain
242     String[] residueChain = split[1].split("\\.");
243
244     if (residueChain[0].length() == 0)
245     {
246       logger.info("Unexpected return from Chimera: " + atomSpec);
247       return null;
248     }
249
250     if (residueChain.length == 2 && residueChain[1].length() > 0)
251     {
252       ChimeraChain chain = model.getChain(residueChain[1]);
253       return chain.getResidue(residueChain[0]);
254     }
255     return model.getResidue("_", residueChain[0]);
256   }
257
258   public static ChimeraChain getChain(String atomSpec, ChimeraModel model)
259   {
260     String[] split = atomSpec.split(":|@");
261
262     // Split into residue and chain
263     String[] residueChain = split[1].split("\\.");
264     if (residueChain.length == 1)
265     {
266       logger.info("Unexpected return from Chimera: " + atomSpec);
267       return null;
268     }
269     return model.getChain(residueChain[1]);
270   }
271
272   public static String getAtomName(String atomSpec)
273   {
274     String[] split = atomSpec.split("@");
275     if (split.length > 1)
276     {
277       return split[1];
278     }
279     return atomSpec;
280   }
281
282   public static boolean isBackbone(String atom)
283   {
284     if (atom.equals("C") || atom.equals("CA") || atom.equals("N")
285             || atom.equals("O") || atom.equals("H"))
286     {
287       return true;
288     }
289     return false;
290   }
291
292   public static String getIntSubtype(String node, String atom)
293   {
294     String[] split = node.split("#| ");
295     String resType = "";
296     if (split.length == 2)
297     {
298       resType = split[0].trim().toUpperCase();
299     }
300     else if (split.length == 3)
301     {
302       resType = split[1].trim().toUpperCase();
303     }
304     if (resType.equalsIgnoreCase("HOH") || resType.equalsIgnoreCase("WAT"))
305     {
306       return "water";
307     }
308     else if (aaNames.containsKey(resType))
309     {
310       if (atom.equals("C") || atom.equals("CA") || atom.equals("N")
311               || atom.equals("O") || atom.equals("H"))
312       {
313         return "mc";
314       }
315       else
316       {
317         return "sc";
318       }
319     }
320     else
321     {
322       return "other";
323     }
324   }
325
326   public static String[] getResKeyParts(String resKey)
327   {
328     // [pdbID[.modelNo]#][residueID][.chainID]
329     // pdbID := 4-character code | "URL" | "path"
330     String[] resKeyParts = new String[4];
331     String[] split = resKey.split("#");
332     String resChain = null;
333     // if no "#" then it is either only a pdb id or a residue or a chain
334     if (split.length == 1)
335     {
336       // pdb id without model
337       if (resKey.length() == 4 && resKey.indexOf("\\.") < 0)
338       {
339         parseModelID(resKey, resKeyParts);
340       }
341       // pdb link or file
342       else if (resKey.startsWith("\""))
343       {
344         parseModelID(resKey, resKeyParts);
345       }
346       // chain and residue or model and number
347       else
348       {
349         String[] splitSplit = resKey.split("\\.");
350         if (splitSplit.length == 1)
351         {
352           // only a chain or a residue
353           resChain = resKey;
354         }
355         else
356         {
357           try
358           {
359             // pdb with a model
360             Integer.parseInt(splitSplit[1]);
361             parseModelID(resKey, resKeyParts);
362           } catch (NumberFormatException ex)
363           {
364             // residue and chain
365             resChain = resKey;
366           }
367         }
368       }
369     }
370     else if (split.length == 2)
371     {
372       // model and residue+chain
373       parseModelID(split[0], resKeyParts);
374       resChain = split[1];
375     }
376     else
377     {
378       // model string with "#"
379       // TODO: [Optional] Are there more possibilities?
380       parseModelID(resKey.substring(0, resKey.lastIndexOf("#")),
381               resKeyParts);
382       resChain = resKey.substring(resKey.lastIndexOf("#") + 1,
383               resKey.length());
384     }
385     if (resChain != null)
386     {
387       // System.out.println(resChain);
388       String[] resChainSplit = resChain.split("\\.");
389       if (resChainSplit.length == 1)
390       {
391         // TODO: [Optional] Find a better way to distinguish between chain and
392         // residue
393         // if only one character and not an int, probably a chain
394         if (resChainSplit[0].length() == 1)
395         {
396           try
397           {
398             Integer.parseInt(resChainSplit[0]);
399             resKeyParts[3] = resChainSplit[0];
400           } catch (NumberFormatException ex)
401           {
402             resKeyParts[2] = resChainSplit[0];
403           }
404         }
405         else
406         {
407           resKeyParts[3] = resChainSplit[0];
408         }
409       }
410       else if (resChainSplit.length == 2)
411       {
412         resKeyParts[2] = resChainSplit[0];
413         resKeyParts[3] = resChainSplit[1];
414       }
415       else
416       {
417         // too many dots?
418         logger.info("Could not parse residue identifier: " + resKey);
419       }
420     }
421     // String print = "";
422     // for (int i = 0; i < resKeyParts.length; i++) {
423     // if (resKeyParts[i] == null) {
424     // print += i + ": null\t";
425     // } else {
426     // print += i + ": " + resKeyParts[i] + ";";
427     // }
428     // }
429     // System.out.println(print);
430     return resKeyParts;
431   }
432
433   public static void parseModelID(String modelID, String[] resKeyParts)
434   {
435     if (modelID.startsWith("\""))
436     {
437       if (modelID.endsWith("\""))
438       {
439         resKeyParts[0] = modelID.substring(1, modelID.length() - 1);
440         return;
441       }
442       else
443       {
444         try
445         {
446           Integer.parseInt(modelID.substring(modelID.lastIndexOf("\"") + 2,
447                   modelID.length()));
448           resKeyParts[0] = modelID.substring(0,
449                   modelID.lastIndexOf("\"") - 1);
450           resKeyParts[1] = modelID.substring(modelID.lastIndexOf("\"") + 2,
451                   modelID.length());
452         } catch (NumberFormatException ex)
453         {
454           resKeyParts[0] = modelID.substring(1);
455         }
456       }
457     }
458     else
459     {
460       String[] modelIDNo = modelID.split("\\.");
461       if (modelIDNo.length == 1)
462       {
463         resKeyParts[0] = modelIDNo[0];
464       }
465       else if (modelIDNo.length == 2)
466       {
467         try
468         {
469           Integer.parseInt(modelIDNo[1]);
470           resKeyParts[0] = modelIDNo[0];
471           resKeyParts[1] = modelIDNo[1];
472         } catch (NumberFormatException ex)
473         {
474           resKeyParts[0] = modelID;
475         }
476       }
477       else
478       {
479         // length > 1, so we probably have a file name with "." in it
480         logger.info("Could not parse model identifier: " + modelID);
481         resKeyParts[0] = modelID;
482       }
483     }
484   }
485
486   /**
487    * This method takes a Cytoscape attribute specification
488    * ([structure#][residue][.chainID]) and returns the lowest-level object
489    * referenced by the spec. For example, if the spec is "1tkk", this method
490    * will return a ChimeraModel. If the spec is ".A", it will return a
491    * ChimeraChain, etc.
492    * 
493    * @param attrSpec
494    *          the specification string
495    * @param chimeraManager
496    *          the Chimera object we're currently using
497    * @return a ChimeraStructuralObject of the lowest type
498    */
499   public static ChimeraStructuralObject fromAttributeOld(String attrSpec,
500           ChimeraManager chimeraManager)
501   {
502     if (attrSpec == null || attrSpec.indexOf(',') > 0
503             || attrSpec.indexOf('-') > 0)
504     {
505       // No support for either lists or ranges
506       logger.warn("No support for identifier: " + attrSpec);
507       return null;
508     }
509
510     String residue = null;
511     String model = null;
512     String chain = null;
513
514     ChimeraModel chimeraModel = null;
515     ChimeraChain chimeraChain = null;
516     ChimeraResidue chimeraResidue = null;
517
518     // System.out.println("Getting object from attribute: "+attrSpec);
519     try
520     {
521       String[] split = attrSpec.split("#");
522       String resChain = null;
523       if (split.length == 1)
524       {
525         // no model
526         resChain = split[0];
527       }
528       else if (split.length == 2)
529       {
530         // model and rest
531         model = split[0];
532         resChain = split[1];
533       }
534       else
535       {
536         // model string with "#"
537         model = attrSpec.substring(0, attrSpec.lastIndexOf("#"));
538         resChain = attrSpec.substring(attrSpec.lastIndexOf("#") + 1,
539                 attrSpec.length());
540       }
541       if (resChain != null)
542       {
543         String[] resChainSplit = resChain.split("\\.");
544         if (resChainSplit.length == 1)
545         {
546           residue = resChainSplit[0];
547         }
548         else if (resChainSplit.length == 2)
549         {
550           residue = resChainSplit[0];
551           chain = resChainSplit[1];
552         }
553         else
554         {
555           // too many dots?
556           logger.warn("No support for identifier: " + attrSpec);
557         }
558       }
559
560       // if (split.length == 1) {
561       // // No model
562       // residue = split[0];
563       // } else if (split.length == 3) {
564       // // We have all three
565       // model = split[0];
566       // residue = split[1];
567       // chain = split[2];
568       // } else if (split.length == 2 && attrSpec.indexOf('#') > 0) {
569       // // Model and Residue
570       // model = split[0];
571       // residue = split[1];
572       // } else {
573       // // Residue and Chain
574       // residue = split[0];
575       // chain = split[1];
576       // }
577
578       // System.out.println("model = " + model + " chain = " + chain +
579       // " residue = " + residue);
580       if (model != null)
581       {
582         List<ChimeraModel> models = chimeraManager.getChimeraModels(model,
583                 ModelType.PDB_MODEL);
584         if (models.size() == 1)
585         {
586           chimeraModel = models.get(0);
587         }
588         else
589         {
590           try
591           {
592             chimeraModel = chimeraManager.getChimeraModel(
593                     Integer.valueOf(model), 0);
594           } catch (NumberFormatException ex)
595           {
596             // ignore
597           }
598         }
599       }
600       if (chimeraModel == null)
601       {
602         chimeraModel = chimeraManager.getChimeraModel();
603       }
604       // System.out.println("ChimeraModel = " + chimeraModel);
605
606       if (chain != null)
607       {
608         chimeraChain = chimeraModel.getChain(chain);
609         // System.out.println("ChimeraChain = " + chimeraChain);
610       }
611       if (residue != null)
612       {
613         if (chimeraChain != null)
614         {
615           chimeraResidue = chimeraChain.getResidue(residue);
616         }
617         else
618         {
619           chimeraResidue = chimeraModel.getResidue("_", residue);
620         }
621         // System.out.println("ChimeraResidue = " + chimeraResidue);
622       }
623
624       if (chimeraResidue != null)
625       {
626         return chimeraResidue;
627       }
628
629       if (chimeraChain != null)
630       {
631         return chimeraChain;
632       }
633
634       if (chimeraModel != null)
635       {
636         return chimeraModel;
637       }
638
639     } catch (Exception ex)
640     {
641       logger.warn("Could not parse residue identifier: " + attrSpec, ex);
642     }
643     return null;
644   }
645
646   public static ChimeraStructuralObject fromAttribute(String attrSpec,
647           ChimeraManager chimeraManager)
648   {
649     // TODO: Make sure it is OK to remove this: || attrSpec.indexOf('-') > 0
650     if (attrSpec == null || attrSpec.indexOf(',') > 0)
651     {
652       // No support for either lists or ranges
653       // System.out.println("No support for identifier: " + attrSpec);
654       logger.warn("No support for identifier: " + attrSpec);
655       return null;
656     }
657     String[] modelIDNoResChain = getResKeyParts(attrSpec);
658
659     ChimeraModel chimeraModel = null;
660     ChimeraChain chimeraChain = null;
661     ChimeraResidue chimeraResidue = null;
662
663     // System.out.println("Getting object from attribute: "+attrSpec);
664     try
665     {
666       if (modelIDNoResChain[0] != null)
667       {
668         String modelID = modelIDNoResChain[0];
669         List<ChimeraModel> models = chimeraManager.getChimeraModels(
670                 modelID, ModelType.PDB_MODEL);
671         if (models.size() == 1)
672         { // usual case with only one model
673           chimeraModel = models.get(0);
674         }
675         else if (models.size() > 1 && modelIDNoResChain[1] != null)
676         {
677           // there are several submodels
678           try
679           {
680             int modelNo = Integer.valueOf(modelIDNoResChain[1]);
681             for (ChimeraModel model : models)
682             {
683               if (model.getSubModelNumber() == modelNo)
684               {
685                 chimeraModel = model;
686                 break;
687               }
688             }
689           } catch (NumberFormatException ex)
690           {
691             // ignore
692           }
693         }
694         else
695         {
696           // TODO: [Optional] What is this doing?
697           try
698           {
699             chimeraModel = chimeraManager.getChimeraModel(
700                     Integer.valueOf(modelID), 0);
701           } catch (NumberFormatException ex)
702           {
703             // ignore
704           }
705         }
706       }
707       if (chimeraModel == null)
708       {
709         // TODO: [Optional] Find a better way to handle this case
710         // If no model can be matched, continue
711         // System.out.println("No matching model could be find for " +
712         // attrSpec);
713         return null;
714         // chimeraModel = chimeraManager.getChimeraModel();
715         // logger.warn("No matching model could be find for " + attrSpec +
716         // ". Trying with "
717         // + chimeraModel.toSpec());
718       }
719       // System.out.println("ChimeraModel = " + chimeraModel);
720
721       if (modelIDNoResChain[3] != null)
722       {
723         chimeraChain = chimeraModel.getChain(modelIDNoResChain[3]);
724         // System.out.println("ChimeraChain = " + chimeraChain);
725       }
726       if (modelIDNoResChain[2] != null)
727       {
728         String residue = modelIDNoResChain[2];
729         if (chimeraChain != null)
730         {
731           chimeraResidue = chimeraChain.getResidue(residue);
732         }
733         else if (chimeraModel.getChain("_") != null)
734         {
735           chimeraResidue = chimeraModel.getResidue("_", residue);
736         }
737         else if (chimeraModel.getChainCount() == 1)
738         {
739           chimeraResidue = chimeraModel.getResidue(chimeraModel
740                   .getChainNames().iterator().next(), residue);
741         }
742         // System.out.println("ChimeraResidue = " + chimeraResidue);
743       }
744
745       if (chimeraResidue != null)
746       {
747         return chimeraResidue;
748       }
749
750       if (chimeraChain != null)
751       {
752         return chimeraChain;
753       }
754
755       if (chimeraModel != null)
756       {
757         return chimeraModel;
758       }
759
760     } catch (Exception ex)
761     {
762       // System.out.println("Could not parse chimera identifier: " +
763       // attrSpec+"("+ex.getMessage()+")");
764       logger.warn("Could not parse chimera identifier: " + attrSpec, ex);
765     }
766     return null;
767   }
768
769   /**
770    * Search for structure references in the residue list
771    * 
772    * @param residueList
773    *          the list of residues
774    * @return a concatenated list of structures encoded in the list
775    */
776   public static String findStructures(String residueList)
777   {
778     if (residueList == null)
779     {
780       return null;
781     }
782     String[] residues = residueList.split(",");
783     Map<String, String> structureNameMap = new HashMap<String, String>();
784     for (int i = 0; i < residues.length; i++)
785     {
786       String[] components = residues[i].split("#");
787       if (components.length > 1)
788       {
789         structureNameMap.put(components[0], components[1]);
790       }
791     }
792     if (structureNameMap.isEmpty())
793     {
794       return null;
795     }
796
797     String structure = null;
798     for (String struct : structureNameMap.keySet())
799     {
800       if (structure == null)
801       {
802         structure = new String();
803       }
804       else
805       {
806         structure = structure.concat(",");
807       }
808       structure = structure.concat(struct);
809     }
810     return structure;
811   }
812
813   // invoked by openStructures in StructureManager
814   public static List<String> parseFuncRes(List<String> residueNames,
815           String modelName)
816   {
817     List<String> resRanges = new ArrayList<String>();
818     for (int i = 0; i < residueNames.size(); i++)
819     {
820       String residue = residueNames.get(i);
821       // Parse out the structure, if there is one
822       String[] components = residue.split("#");
823       if (components.length > 1 && !modelName.equals(components[0]))
824       {
825         continue;
826       }
827       else if (components.length > 1)
828       {
829         residue = components[1];
830       }
831       else if (components.length == 1)
832       {
833         residue = components[0];
834       }
835       // Check to see if we have a range-spec
836       String resRange = "";
837       if (residue == null || residue.equals("") || residue.length() == 0)
838       {
839         continue;
840       }
841       String[] range = residue.split("-", 2);
842       String chain = null;
843       for (int res = 0; res < range.length; res++)
844       {
845         if (res == 1)
846         {
847           resRange = resRange.concat("-");
848           if (chain != null && range[res].indexOf('.') == -1)
849           {
850             range[res] = range[res].concat("." + chain);
851           }
852         }
853
854         if (res == 0 && range.length >= 2 && range[res].indexOf('.') > 0)
855         {
856           // This is a range spec with the leading residue containing a chain
857           // spec
858           String[] resChain = range[res].split("\\.");
859           chain = resChain[1];
860           range[res] = resChain[0];
861         }
862         // Fix weird SFLD syntax...
863         if (range[res].indexOf('|') > 0
864                 && Character.isDigit(range[res].charAt(0)))
865         {
866           int offset = range[res].indexOf('|');
867           String str = range[res].substring(offset + 1)
868                   + range[res].substring(0, offset);
869           range[res] = str;
870         }
871
872         // Convert to legal atom-spec
873         if (Character.isDigit(range[res].charAt(0)))
874         {
875           resRange = resRange.concat(range[res]);
876         }
877         else if (Character.isDigit(range[res].charAt(1)))
878         {
879           resRange = resRange.concat(range[res].substring(1));
880         }
881         else if (range[res].charAt(0) == '.')
882         {
883           // Do we have a chain spec?
884           resRange = resRange.concat(range[res]);
885         }
886         else
887         {
888           resRange = resRange.concat(range[res].substring(3));
889         }
890       }
891       if (!resRanges.contains(resRange))
892       {
893         resRanges.add(resRange);
894       }
895     }
896     return resRanges;
897   }
898
899   static
900   {
901     aaNames = new HashMap<String, String>();
902     aaNames.put("ALA", "A Ala Alanine N[C@@H](C)C(O)=O");
903     aaNames.put("ARG", "R Arg Arginine N[C@@H](CCCNC(N)=N)C(O)=O");
904     aaNames.put("ASN", "N Asn Asparagine N[C@@H](CC(N)=O)C(O)=O");
905     aaNames.put("ASP", "D Asp Aspartic_acid N[C@@H](CC(O)=O)C(O)=O");
906     aaNames.put("CYS", "C Cys Cysteine N[C@@H](CS)C(O)=O");
907     aaNames.put("GLN", "Q Gln Glutamine N[C@H](C(O)=O)CCC(N)=O");
908     aaNames.put("GLU", "E Glu Glumatic_acid N[C@H](C(O)=O)CCC(O)=O");
909     aaNames.put("GLY", "G Gly Glycine NCC(O)=O");
910     aaNames.put("HIS", "H His Histidine N[C@@H](CC1=CN=CN1)C(O)=O");
911     aaNames.put("ILE", "I Ile Isoleucine N[C@]([C@H](C)CC)([H])C(O)=O");
912     aaNames.put("LEU", "L Leu Leucine N[C@](CC(C)C)([H])C(O)=O");
913     aaNames.put("LYS", "K Lys Lysine N[C@](CCCCN)([H])C(O)=O");
914     aaNames.put("DLY", "K Dly D-Lysine NCCCC[C@@H](N)C(O)=O");
915     aaNames.put("MET", "M Met Methionine N[C@](CCSC)([H])C(O)=O");
916     aaNames.put("PHE", "F Phe Phenylalanine N[C@](CC1=CC=CC=C1)([H])C(O)=O");
917     aaNames.put("PRO", "P Pro Proline OC([C@@]1([H])NCCC1)=O");
918     aaNames.put("SER", "S Ser Serine OC[C@](C(O)=O)([H])N");
919     aaNames.put("THR", "T Thr Threonine O[C@H](C)[C@](C(O)=O)([H])N");
920     aaNames.put("TRP",
921             "W Trp Tryptophan N[C@@]([H])(CC1=CN([H])C2=C1C=CC=C2)C(O)=O");
922     aaNames.put("TYR", "Y Tyr Tyrosine N[C@@](C(O)=O)([H])CC1=CC=C(O)C=C1");
923     aaNames.put("VAL", "V Val Valine N[C@@](C(O)=O)([H])C(C)C");
924     aaNames.put("ASX", "B Asx Aspartic_acid_or_Asparagine");
925     aaNames.put("GLX", "Z Glx Glutamine_or_Glutamic_acid");
926     aaNames.put("XAA", "X Xaa Any_or_unknown_amino_acid");
927     aaNames.put("HOH", "HOH HOH Water [H]O[H]");
928   }
929
930   /**
931    * Convert the amino acid type to a full name
932    * 
933    * @param aaType
934    *          the residue type to convert
935    * @return the full name of the residue
936    */
937   public static String toFullName(String aaType)
938   {
939     if (!aaNames.containsKey(aaType))
940     {
941       return aaType;
942     }
943     String[] ids = aaNames.get(aaType).split(" ");
944     return ids[2].replace('_', ' ');
945   }
946
947   /**
948    * Convert the amino acid type to a single letter
949    * 
950    * @param aaType
951    *          the residue type to convert
952    * @return the single letter representation of the residue
953    */
954   public static String toSingleLetter(String aaType)
955   {
956     if (!aaNames.containsKey(aaType))
957     {
958       return aaType;
959     }
960     String[] ids = aaNames.get(aaType).split(" ");
961     return ids[0];
962   }
963
964   /**
965    * Convert the amino acid type to three letters
966    * 
967    * @param aaType
968    *          the residue type to convert
969    * @return the three letter representation of the residue
970    */
971   public static String toThreeLetter(String aaType)
972   {
973     if (!aaNames.containsKey(aaType))
974     {
975       return aaType;
976     }
977     String[] ids = aaNames.get(aaType).split(" ");
978     return ids[1];
979   }
980
981   /**
982    * Convert the amino acid type to its SMILES string
983    * 
984    * @param aaType
985    *          the residue type to convert
986    * @return the SMILES representation of the residue
987    */
988   public static String toSMILES(String aaType)
989   {
990     if (!aaNames.containsKey(aaType))
991     {
992       return null;
993     }
994     String[] ids = aaNames.get(aaType).split(" ");
995     if (ids.length < 4)
996     {
997       return null;
998     }
999     return ids[3];
1000   }
1001
1002   public static String getAlignName(ChimeraStructuralObject chimObj)
1003   {
1004     String name = chimObj.getChimeraModel().toString();
1005     if (chimObj instanceof ChimeraChain)
1006     {
1007       name = ((ChimeraChain) chimObj).toString() + " [" + name + "]";
1008     }
1009     return name;
1010   }
1011 }