Merge branch 'features/JAL-1333' into Release_2_8_2_Branch
[jalview.git] / src / ext / edu / ucsf / rbvi / strucviz2 / ChimeraResidue.java
1 /* vim: set ts=2: */
2 /**
3  * Copyright (c) 2006 The Regents of the University of California.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *   1. Redistributions of source code must retain the above copyright
10  *      notice, this list of conditions, and the following disclaimer.
11  *   2. Redistributions in binary form must reproduce the above
12  *      copyright notice, this list of conditions, and the following
13  *      disclaimer in the documentation and/or other materials provided
14  *      with the distribution.
15  *   3. Redistributions must acknowledge that this software was
16  *      originally developed by the UCSF Computer Graphics Laboratory
17  *      under support by the NIH National Center for Research Resources,
18  *      grant P41-RR01081.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
21  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
26  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
27  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
29  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  */
33 package ext.edu.ucsf.rbvi.strucviz2;
34
35 import java.util.ArrayList;
36 import java.util.List;
37 import java.util.regex.Matcher;
38 import java.util.regex.Pattern;
39
40 import org.slf4j.LoggerFactory;
41
42 /**
43  * This class provides the implementation for the ChimeraResidue, object
44  * 
45  * @author scooter
46  * 
47  */
48
49 public class ChimeraResidue implements ChimeraStructuralObject, Comparable<ChimeraResidue> {
50
51         /* Constants */
52         public static final int SINGLE_LETTER = 0; // Display residues as a single
53                                                                                                                                                                                         // letter
54         public static final int THREE_LETTER = 1; // Display residues as three letters
55         public static final int FULL_NAME = 2; // Display full residue names
56
57         private String type; // Residue type
58         private String index; // Residue index
59         private String chainId; // ChainID for this residue
60         private int modelNumber; // model number for this residue
61         private int subModelNumber; // sub-model number for this residue
62         protected int residueNumber;
63         protected String insertionCode;
64         private ChimeraModel chimeraModel; // ChimeraModel the residue is part of
65         private Object userData; // user data to associate with this residue
66         // public static HashMap<String, String> aaNames = null; // a map of amino acid
67         // names
68         private static int displayType = THREE_LETTER; // the current display type
69         private boolean selected = false; // the selection state
70
71         /**
72          * Constructor to create a new ChimeraResidue
73          * 
74          * @param type
75          *          the residue type
76          * @param index
77          *          the index of the residue
78          * @param modelNumber
79          *          the model number this residue is part of
80          */
81         public ChimeraResidue(String type, String index, int modelNumber) {
82                 this(type, index, modelNumber, 0);
83         }
84
85         /**
86          * Constructor to create a new ChimeraResidue
87          * 
88          * @param type
89          *          the residue type
90          * @param index
91          *          the index of the residue
92          * @param modelNumber
93          *          the model number this residue is part of
94          * @param subModelNumber
95          *          the sub-model number this residue is part of
96          */
97         public ChimeraResidue(String type, String index, int modelNumber, int subModelNumber) {
98                 this.type = type;
99                 this.index = index;
100                 this.modelNumber = modelNumber;
101                 this.subModelNumber = subModelNumber;
102                 splitInsertionCode(this.index);
103                 // if (aaNames == null) {
104                 // initNames();
105                 // }
106         }
107
108         /**
109          * Constructor to create a new ChimeraResidue from an input line
110          * 
111          * @param chimeraInputLine
112          *          a Chimera residue description
113          */
114         // invoked when listing (selected) residues: listr spec #0; lists level residue
115         // Line: residue id #0:37.A type MET
116         public ChimeraResidue(String chimeraInputLine) {
117                 // initNames();
118                 String[] split1 = chimeraInputLine.split(":");
119
120                 // First half has model number -- get the number
121                 int numberOffset = split1[0].indexOf('#');
122                 String model = split1[0].substring(numberOffset + 1);
123                 int decimalOffset = model.indexOf('.'); // Do we have a sub-model?
124                 try {
125                         this.subModelNumber = 0;
126                         if (decimalOffset > 0) {
127                                 this.subModelNumber = Integer.parseInt(model.substring(decimalOffset + 1));
128                                 this.modelNumber = Integer.parseInt(model.substring(0, decimalOffset));
129                         } else {
130                                 this.modelNumber = Integer.parseInt(model);
131                         }
132                 } catch (Exception e) {
133                         LoggerFactory.getLogger(ChimeraResidue.class)
134                                         .error("Unexpected return from Chimera: " + model);
135                         this.modelNumber = -1;
136                 }
137
138                 // Second half has residue info: index & type
139                 String[] rTokens = split1[1].split(" ");
140                 this.type = rTokens[2];
141
142                 String[] iTokens = rTokens[0].split("\\.");
143                 if (iTokens.length > 0) {
144                         this.index = iTokens[0];
145
146                         // Careful, might or might not have a chainID
147                         if (iTokens.length > 1)
148                                 this.chainId = iTokens[1];
149                         else
150                                 this.chainId = "_";
151                 } else
152                         this.index = rTokens[0];
153
154                 splitInsertionCode(this.index);
155         }
156
157         /**
158          * Set the selected state for this residue
159          * 
160          * @param selected
161          *          the selection state to set
162          */
163         public void setSelected(boolean selected) {
164                 this.selected = selected;
165         }
166
167         /**
168          * Return the selected state of this residue
169          * 
170          * @return the selected state
171          */
172         public boolean isSelected() {
173                 return selected;
174         }
175
176         /**
177          * Return an array made up of this residue (required for ChimeraStructuralObject interface
178          * 
179          * @return a List with this residue as its sole member
180          */
181         public List<ChimeraStructuralObject> getChildren() {
182                 List<ChimeraStructuralObject> v = new ArrayList<ChimeraStructuralObject>();
183                 v.add(this);
184                 return v;
185         }
186
187         /**
188          * Return the string representation of this residue as follows: "<i>residue_name</i> <i>index</i>"
189          * where <i>residue_name</i> could be either the single letter, three letter, or full name
190          * representation of the amino acid.
191          * 
192          * @return the string representation
193          */
194         public String displayName() {
195                 return toString();
196         }
197
198         /**
199          * Return the string representation of this residue as follows: "<i>residue_name</i> <i>index</i>"
200          * where <i>residue_name</i> could be either the single letter, three letter, or full name
201          * representation of the amino acid.
202          * 
203          * @return the string representation
204          */
205         public String toString() {
206                 if (displayType == FULL_NAME) {
207                         return (ChimUtils.toFullName(type) + " " + index);
208                 } else if (displayType == SINGLE_LETTER) {
209                         return (ChimUtils.toSingleLetter(type) + " " + index);
210                 } else if (displayType == THREE_LETTER) {
211                         return (ChimUtils.toThreeLetter(type) + " " + index);
212                 } else {
213                         return (type + " " + index);
214                 }
215         }
216
217         /**
218          * Return the Chimera specification for this Residue
219          * 
220          * @return Chimera specification
221          */
222         public String toSpec() {
223                 if (!chainId.equals("_"))
224                         return ("#" + modelNumber + ":" + index + "." + chainId);
225                 else
226                         return ("#" + modelNumber + ":" + index + ".");
227         }
228
229         /**
230          * Get the index of this residue
231          * 
232          * @return residue index
233          */
234         public String getIndex() {
235                 return this.index;
236         }
237
238         /**
239          * Get the chainID for this residue
240          * 
241          * @return String value of the chainId
242          */
243         public String getChainId() {
244                 return this.chainId;
245         }
246
247         /**
248          * Get the type for this residue
249          * 
250          * @return residue type
251          */
252         public String getType() {
253                 return this.type;
254         }
255
256         /**
257          * Get the model number for this residue
258          * 
259          * @return the model number
260          */
261         public int getModelNumber() {
262                 return this.modelNumber;
263         }
264
265         /**
266          * Get the sub-model number for this residue
267          * 
268          * @return the sub-model number
269          */
270         public int getSubModelNumber() {
271                 return this.subModelNumber;
272         }
273
274         /**
275          * Get the model this residue is part of
276          * 
277          * @return the ChimeraModel
278          */
279         public ChimeraModel getChimeraModel() {
280                 return this.chimeraModel;
281         }
282
283         /**
284          * Set the model this residue is part of
285          * 
286          * @param chimeraModel
287          *          the ChimeraModel this model is part of
288          */
289         public void setChimeraModel(ChimeraModel chimeraModel) {
290                 this.chimeraModel = chimeraModel;
291         }
292
293         /**
294          * Get the user data for this residue
295          * 
296          * @return user data
297          */
298         public Object getUserData() {
299                 return userData;
300         }
301
302         /**
303          * Set the user data for this Residue
304          * 
305          * @param data
306          *          the user data to associate with this residue
307          */
308         public void setUserData(Object data) {
309                 this.userData = data;
310         }
311
312         public int compareTo(ChimeraResidue c2) {
313                 if (residueNumber < c2.residueNumber)
314                         return -1;
315                 else if (residueNumber == c2.residueNumber) {
316                         if (insertionCode == null && c2.insertionCode == null)
317                                 return 0;
318                         else if (insertionCode == null)
319                                 return -1;
320                         else if (c2.insertionCode == null)
321                                 return 1;
322                         return (insertionCode.compareTo(c2.insertionCode));
323                 }
324                 return 1;
325         }
326
327         public void splitInsertionCode(String residue) {
328                 // OK, split the index into number and insertion code
329                 Pattern p = Pattern.compile("(\\d*)([A-Z]?)");
330                 Matcher m = p.matcher(residue);
331                 if (m.matches()) {
332                         this.residueNumber = Integer.parseInt(m.group(1));
333                         if (m.groupCount() > 1)
334                                 this.insertionCode = m.group(2);
335                         else
336                                 this.insertionCode = null;
337                 }
338         }
339
340         /**********************************************
341          * Static routines
342          *********************************************/
343
344         /**
345          * Initialize the residue names
346          */
347         // private static void initNames() {
348         // // Create our residue name table
349         // aaNames = new HashMap<String, String>();
350         // aaNames.put("ALA", "A Ala Alanine N[C@@H](C)C(O)=O");
351         // aaNames.put("ARG", "R Arg Arginine N[C@@H](CCCNC(N)=N)C(O)=O");
352         // aaNames.put("ASN", "N Asn Asparagine N[C@@H](CC(N)=O)C(O)=O");
353         // aaNames.put("ASP", "D Asp Aspartic_acid N[C@@H](CC(O)=O)C(O)=O");
354         // aaNames.put("CYS", "C Cys Cysteine N[C@@H](CS)C(O)=O");
355         // aaNames.put("GLN", "Q Gln Glutamine N[C@H](C(O)=O)CCC(N)=O");
356         // aaNames.put("GLU", "E Glu Glumatic_acid N[C@H](C(O)=O)CCC(O)=O");
357         // aaNames.put("GLY", "G Gly Glycine NCC(O)=O");
358         // aaNames.put("HIS", "H His Histidine N[C@@H](CC1=CN=CN1)C(O)=O");
359         // aaNames.put("ILE", "I Ile Isoleucine N[C@]([C@H](C)CC)([H])C(O)=O");
360         // aaNames.put("LEU", "L Leu Leucine N[C@](CC(C)C)([H])C(O)=O");
361         // aaNames.put("LYS", "K Lys Lysine N[C@](CCCCN)([H])C(O)=O");
362         // aaNames.put("DLY", "K Dly D-Lysine NCCCC[C@@H](N)C(O)=O");
363         // aaNames.put("MET", "M Met Methionine N[C@](CCSC)([H])C(O)=O");
364         // aaNames.put("PHE", "F Phe Phenylalanine N[C@](CC1=CC=CC=C1)([H])C(O)=O");
365         // aaNames.put("PRO", "P Pro Proline OC([C@@]1([H])NCCC1)=O");
366         // aaNames.put("SER", "S Ser Serine OC[C@](C(O)=O)([H])N");
367         // aaNames.put("THR", "T Thr Threonine O[C@H](C)[C@](C(O)=O)([H])N");
368         // aaNames.put("TRP", "W Trp Tryptophan N[C@@]([H])(CC1=CN([H])C2=C1C=CC=C2)C(O)=O");
369         // aaNames.put("TYR", "Y Tyr Tyrosine N[C@@](C(O)=O)([H])CC1=CC=C(O)C=C1");
370         // aaNames.put("VAL", "V Val Valine N[C@@](C(O)=O)([H])C(C)C");
371         // aaNames.put("ASX", "B Asx Aspartic_acid_or_Asparagine");
372         // aaNames.put("GLX", "Z Glx Glutamine_or_Glutamic_acid");
373         // aaNames.put("XAA", "X Xaa Any_or_unknown_amino_acid");
374         // aaNames.put("HOH", "HOH HOH Water [H]O[H]");
375         // }
376
377         /**
378          * Set the display type.
379          * 
380          * @param type
381          *          the display type
382          */
383         public static void setDisplayType(int type) {
384                 displayType = type;
385         }
386
387         public static int getDisplayType() {
388                 return displayType;
389         }
390
391         public boolean hasSelectedChildren() {
392                 return false;
393         }
394 }