JAL-2325 applied BSD license for chimera/StrucViz2 code
[jalview.git] / src / ext / edu / ucsf / rbvi / strucviz2 / ChimeraModel.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.awt.Color;
36 import java.util.ArrayList;
37 import java.util.Collection;
38 import java.util.HashSet;
39 import java.util.List;
40 import java.util.Set;
41 import java.util.TreeMap;
42
43 import ext.edu.ucsf.rbvi.strucviz2.StructureManager.ModelType;
44
45 /**
46  * This class provides the implementation for the ChimeraModel, ChimeraChain,
47  * and ChimeraResidue objects
48  * 
49  * @author scooter
50  * 
51  */
52 public class ChimeraModel implements ChimeraStructuralObject
53 {
54
55   private String name; // The name of this model
56
57   private ModelType type; // The type of the model
58
59   private int modelNumber; // The model number
60
61   private int subModelNumber; // The sub-model number
62
63   private Color modelColor = null; // The color of this model (from Chimera)
64
65   private Object userData = null; // User data associated with this model
66
67   private boolean selected = false; // The selected state of this model
68
69   private TreeMap<String, ChimeraChain> chainMap; // The list of chains
70
71   // private TreeMap<String, ChimeraResidue> residueMap; // The list of residues
72   private HashSet<ChimeraResidue> funcResidues; // List of functional residues
73
74   /**
75    * Constructor to create a model
76    * 
77    * @param name
78    *          the name of this model
79    * @param color
80    *          the model Color
81    * @param modelNumber
82    *          the model number
83    * @param subModelNumber
84    *          the sub-model number
85    */
86   public ChimeraModel(String name, ModelType type, int modelNumber,
87           int subModelNumber)
88   {
89     this.name = name;
90     this.type = type;
91     this.modelNumber = modelNumber;
92     this.subModelNumber = subModelNumber;
93
94     this.chainMap = new TreeMap<String, ChimeraChain>();
95     this.funcResidues = new HashSet<ChimeraResidue>();
96   }
97
98   /**
99    * Constructor to create a model from the Chimera input line
100    * 
101    * @param inputLine
102    *          Chimera input line from which to construct this model
103    */
104   // TODO: [Optional] How to distinguish between PDB and MODBASE?
105   // invoked when listing models: listm type molecule; lists level molecule
106   // line = model id #0 type Molecule name 1ert
107   public ChimeraModel(String inputLine)
108   {
109     this.name = ChimUtils.parseModelName(inputLine);
110     // TODO: [Optional] Write a separate method to get model type
111     if (name.startsWith("smiles"))
112     {
113       this.type = ModelType.SMILES;
114     }
115     else
116     {
117       this.type = ModelType.PDB_MODEL;
118     }
119     this.modelNumber = ChimUtils.parseModelNumber(inputLine)[0];
120     this.subModelNumber = ChimUtils.parseModelNumber(inputLine)[1];
121
122     this.chainMap = new TreeMap<String, ChimeraChain>();
123     this.funcResidues = new HashSet<ChimeraResidue>();
124   }
125
126   /**
127    * Add a residue to this model
128    * 
129    * @param residue
130    *          to add to the model
131    */
132   public void addResidue(ChimeraResidue residue)
133   {
134     residue.setChimeraModel(this);
135     // residueMap.put(residue.getIndex(), residue);
136     String chainId = residue.getChainId();
137     if (chainId != null)
138     {
139       addResidue(chainId, residue);
140     }
141     else
142     {
143       addResidue("_", residue);
144     }
145     // Put it in our map so that we can return it in order
146     // residueMap.put(residue.getIndex(), residue);
147   }
148
149   /**
150    * Add a residue to a chain in this model. If the chain associated with
151    * chainId doesn't exist, it will be created.
152    * 
153    * @param chainId
154    *          to add the residue to
155    * @param residue
156    *          to add to the chain
157    */
158   public void addResidue(String chainId, ChimeraResidue residue)
159   {
160     ChimeraChain chain = null;
161     if (!chainMap.containsKey(chainId))
162     {
163       chain = new ChimeraChain(this.modelNumber, this.subModelNumber,
164               chainId);
165       chain.setChimeraModel(this);
166       chainMap.put(chainId, chain);
167     }
168     else
169     {
170       chain = chainMap.get(chainId);
171     }
172     chain.addResidue(residue);
173   }
174
175   /**
176    * Get the ChimeraModel (required for ChimeraStructuralObject interface)
177    * 
178    * @return ChimeraModel
179    */
180   @Override
181   public ChimeraModel getChimeraModel()
182   {
183     return this;
184   }
185
186   /**
187    * Get the model color of this model
188    * 
189    * @return model color of this model
190    */
191   public Color getModelColor()
192   {
193     return this.modelColor;
194   }
195
196   /**
197    * Set the color of this model
198    * 
199    * @param color
200    *          Color of this model
201    */
202   public void setModelColor(Color color)
203   {
204     this.modelColor = color;
205   }
206
207   /**
208    * Return the name of this model
209    * 
210    * @return model name
211    */
212   public String getModelName()
213   {
214     return name;
215   }
216
217   /**
218    * Set the name of this model
219    * 
220    * @param name
221    *          model name
222    */
223   public void setModelName(String name)
224   {
225     this.name = name;
226   }
227
228   /**
229    * Get the model number of this model
230    * 
231    * @return integer model number
232    */
233   public int getModelNumber()
234   {
235     return modelNumber;
236   }
237
238   /**
239    * Set the model number of this model
240    * 
241    * @param modelNumber
242    *          integer model number
243    */
244   public void setModelNumber(int modelNumber)
245   {
246     this.modelNumber = modelNumber;
247   }
248
249   /**
250    * Get the sub-model number of this model
251    * 
252    * @return integer sub-model number
253    */
254   public int getSubModelNumber()
255   {
256     return subModelNumber;
257   }
258
259   /**
260    * Set the sub-model number of this model
261    * 
262    * @param subModelNumber
263    *          integer model number
264    */
265   public void setSubModelNumber(int subModelNumber)
266   {
267     this.subModelNumber = subModelNumber;
268   }
269
270   public ModelType getModelType()
271   {
272     return type;
273   }
274
275   public void setModelType(ModelType type)
276   {
277     this.type = type;
278   }
279
280   public HashSet<ChimeraResidue> getFuncResidues()
281   {
282     return funcResidues;
283   }
284
285   public void setFuncResidues(List<String> residues)
286   {
287     for (String residue : residues)
288     {
289       for (ChimeraChain chain : getChains())
290       {
291         if (residue.indexOf("-") > 0)
292         {
293           funcResidues.addAll(chain.getResidueRange(residue));
294         }
295         else
296         {
297           funcResidues.add(chain.getResidue(residue));
298         }
299       }
300     }
301   }
302
303   /**
304    * Get the user data for this model
305    * 
306    * @return user data
307    */
308   @Override
309   public Object getUserData()
310   {
311     return userData;
312   }
313
314   /**
315    * Set the user data for this model
316    * 
317    * @param data
318    *          user data to associate with this model
319    */
320   @Override
321   public void setUserData(Object data)
322   {
323     this.userData = data;
324   }
325
326   /**
327    * Return the selected state of this model
328    * 
329    * @return the selected state
330    */
331   @Override
332   public boolean isSelected()
333   {
334     return selected;
335   }
336
337   /**
338    * Set the selected state of this model
339    * 
340    * @param selected
341    *          a boolean to set the selected state to
342    */
343   @Override
344   public void setSelected(boolean selected)
345   {
346     this.selected = selected;
347   }
348
349   /**
350    * Return the chains in this model as a List
351    * 
352    * @return the chains in this model as a list
353    */
354   @Override
355   public List<ChimeraStructuralObject> getChildren()
356   {
357     return new ArrayList<ChimeraStructuralObject>(chainMap.values());
358   }
359
360   /**
361    * Return the chains in this model as a colleciton
362    * 
363    * @return the chains in this model
364    */
365   public Collection<ChimeraChain> getChains()
366   {
367     return chainMap.values();
368   }
369
370   /**
371    * Get the number of chains in this model
372    * 
373    * @return integer chain count
374    */
375   public int getChainCount()
376   {
377     return chainMap.size();
378   }
379
380   /**
381    * Get the list of chain names associated with this model
382    * 
383    * @return return the list of chain names for this model
384    */
385   public Collection<String> getChainNames()
386   {
387     return chainMap.keySet();
388   }
389
390   /**
391    * Get the residues associated with this model
392    * 
393    * @return the list of residues in this model
394    */
395   public Collection<ChimeraResidue> getResidues()
396   {
397     Collection<ChimeraResidue> residues = new ArrayList<ChimeraResidue>();
398     for (ChimeraChain chain : getChains())
399     {
400       residues.addAll(chain.getResidues());
401     }
402     return residues;
403   }
404
405   /**
406    * Get the number of residues in this model
407    * 
408    * @return integer residues count
409    */
410   public int getResidueCount()
411   {
412     int count = 0;
413     for (ChimeraChain chain : getChains())
414     {
415       count += chain.getResidueCount();
416     }
417     return count;
418   }
419
420   /**
421    * Get a specific chain from the model
422    * 
423    * @param chain
424    *          the ID of the chain to return
425    * @return ChimeraChain associated with the chain
426    */
427   public ChimeraChain getChain(String chain)
428   {
429     if (chainMap.containsKey(chain))
430     {
431       return chainMap.get(chain);
432     }
433     return null;
434   }
435
436   /**
437    * Return a specific residue based on its index
438    * 
439    * @param index
440    *          of the residue to return
441    * @return the residue associated with that index
442    */
443   public ChimeraResidue getResidue(String chainId, String index)
444   {
445     if (chainMap.containsKey(chainId))
446     {
447       return chainMap.get(chainId).getResidue(index);
448     }
449     return null;
450   }
451
452   /**
453    * Checks if this model has selected children.
454    */
455   @Override
456   public boolean hasSelectedChildren()
457   {
458     if (selected)
459     {
460       return true;
461     }
462     else
463     {
464       for (ChimeraChain chain : getChains())
465       {
466         if (chain.hasSelectedChildren())
467         {
468           return true;
469         }
470       }
471     }
472     return false;
473   }
474
475   /**
476    * Return the list of selected residues
477    * 
478    * @return all selected residues
479    */
480   public List<ChimeraResidue> getSelectedResidues()
481   {
482     List<ChimeraResidue> residueList = new ArrayList<ChimeraResidue>();
483     for (ChimeraChain chain : getChains())
484     {
485       if (selected)
486       {
487         residueList.addAll(chain.getSelectedResidues());
488       }
489       else
490       {
491         residueList.addAll(getResidues());
492       }
493     }
494     return residueList;
495   }
496
497   /**
498    * Return the Chimera specification for this model.
499    */
500   @Override
501   public String toSpec()
502   {
503     if (subModelNumber == 0)
504     {
505       return ("#" + modelNumber);
506     }
507     return ("#" + modelNumber + "." + subModelNumber);
508   }
509
510   /**
511    * Return a string representation for the model. Shorten if longer than 100
512    * characters.
513    */
514   @Override
515   public String toString()
516   {
517     String modelName = "";
518     // TODO: [Optional] Change cutoff for shortening model names in the
519     // structure naviagator dialog
520     if (getChainCount() > 0)
521     {
522       modelName = "Model " + toSpec() + " " + name + " (" + getChainCount()
523               + " chains, " + getResidueCount() + " residues)";
524     }
525     else if (getResidueCount() > 0)
526     {
527       modelName = "Model " + toSpec() + " " + name + " ("
528               + getResidueCount() + " residues)";
529     }
530     else
531     {
532       modelName = "Model " + toSpec() + " " + name + "";
533     }
534
535     Set<String> networkNames = new HashSet<String>();
536     Set<String> nodeNames = new HashSet<String>();
537     Set<String> edgeNames = new HashSet<String>();
538
539     String cytoName = " [";
540     if (networkNames.size() > 0)
541     {
542       if (networkNames.size() == 1)
543       {
544         cytoName += "Network {";
545       }
546       else if (networkNames.size() > 1)
547       {
548         cytoName += "Networks {";
549       }
550       for (String cName : networkNames)
551       {
552         cytoName += cName + ",";
553       }
554       cytoName = cytoName.substring(0, cytoName.length() - 1) + "}, ";
555     }
556     if (nodeNames.size() > 0)
557     {
558       if (nodeNames.size() == 1)
559       {
560         cytoName += "Node {";
561       }
562       else if (nodeNames.size() > 1)
563       {
564         cytoName += "Nodes {";
565       }
566       for (String cName : nodeNames)
567       {
568         cytoName += cName + ",";
569       }
570       cytoName = cytoName.substring(0, cytoName.length() - 1) + "}, ";
571     }
572     if (edgeNames.size() > 0)
573     {
574       if (edgeNames.size() == 1)
575       {
576         cytoName += "Edge {";
577       }
578       else if (edgeNames.size() > 1)
579       {
580         cytoName += "Edges {";
581       }
582       for (String cName : edgeNames)
583       {
584         cytoName += cName + ",";
585       }
586       cytoName = cytoName.substring(0, cytoName.length() - 1) + "}, ";
587     }
588     if (cytoName.endsWith(", "))
589     {
590       cytoName = cytoName.substring(0, cytoName.length() - 2);
591     }
592     cytoName += "]";
593     String nodeName = modelName + cytoName;
594     if (nodeName.length() > 100)
595     {
596       nodeName = nodeName.substring(0, 100) + "...";
597     }
598     return nodeName;
599   }
600
601   @Override
602   public boolean equals(Object otherChimeraModel)
603   {
604     if (!(otherChimeraModel instanceof ChimeraModel))
605     {
606       return false;
607     }
608     ChimeraModel otherCM = ((ChimeraModel) otherChimeraModel);
609     return this.name.equals(otherCM.name)
610             && this.modelNumber == otherCM.modelNumber
611             && this.type == otherCM.type;
612   }
613
614   @Override
615   public int hashCode()
616   {
617     int hashCode = 1;
618     hashCode = hashCode * 37 + this.name.hashCode();
619     hashCode = hashCode * 37 + this.type.hashCode();
620     hashCode = (hashCode * 37) + modelNumber;
621     return hashCode;
622   }
623 }