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