JAL-1742 don't offer to align another chain to the same structure
[jalview.git] / src / jalview / structures / models / AAStructureBindingModel.java
1 package jalview.structures.models;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 import jalview.api.StructureSelectionManagerProvider;
7 import jalview.datamodel.PDBEntry;
8 import jalview.datamodel.SequenceI;
9 import jalview.structure.AtomSpec;
10 import jalview.structure.StructureListener;
11 import jalview.structure.StructureSelectionManager;
12 import jalview.util.Comparison;
13 import jalview.util.MessageManager;
14
15 /**
16  * 
17  * A base class to hold common function for protein structure model binding.
18  * Initial version created by refactoring JMol and Chimera binding models, but
19  * other structure viewers could in principle be accommodated in future.
20  * 
21  * @author gmcarstairs
22  *
23  */
24 public abstract class AAStructureBindingModel extends
25         SequenceStructureBindingModel implements StructureListener,
26         StructureSelectionManagerProvider
27 {
28
29   private StructureSelectionManager ssm;
30
31   private PDBEntry[] pdbEntry;
32
33   /*
34    * sequences mapped to each pdbentry
35    */
36   private SequenceI[][] sequence;
37
38   /*
39    * array of target chains for sequences - tied to pdbentry and sequence[]
40    */
41   private String[][] chains;
42
43   /*
44    * datasource protocol for access to PDBEntrylatest
45    */
46   String protocol = null;
47
48   protected boolean colourBySequence = true;
49
50   private boolean nucleotide;
51
52   /**
53    * Constructor
54    * 
55    * @param ssm
56    * @param seqs
57    */
58   public AAStructureBindingModel(StructureSelectionManager ssm,
59           SequenceI[][] seqs)
60   {
61     this.ssm = ssm;
62     this.sequence = seqs;
63   }
64
65   /**
66    * Constructor
67    * 
68    * @param ssm
69    * @param pdbentry
70    * @param sequenceIs
71    * @param chains
72    * @param protocol
73    */
74   public AAStructureBindingModel(StructureSelectionManager ssm,
75           PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String[][] chains,
76           String protocol)
77   {
78     this.ssm = ssm;
79     this.sequence = sequenceIs;
80     this.nucleotide = Comparison.isNucleotide(sequenceIs);
81     this.chains = chains;
82     this.pdbEntry = pdbentry;
83     this.protocol = protocol;
84     if (chains == null)
85     {
86       this.chains = new String[pdbentry.length][];
87     }
88   }
89
90   public StructureSelectionManager getSsm()
91   {
92     return ssm;
93   }
94
95   /**
96    * Returns the i'th PDBEntry (or null)
97    * 
98    * @param i
99    * @return
100    */
101   public PDBEntry getPdbEntry(int i)
102   {
103     return (pdbEntry != null && pdbEntry.length > i) ? pdbEntry[i] : null;
104   }
105
106   /**
107    * Answers true if this binding includes the given PDB id, else false
108    * 
109    * @param pdbId
110    * @return
111    */
112   public boolean hasPdbId(String pdbId)
113   {
114     if (pdbEntry != null)
115     {
116       for (PDBEntry pdb : pdbEntry)
117       {
118         if (pdb.getId().equals(pdbId))
119         {
120           return true;
121         }
122       }
123     }
124     return false;
125   }
126
127   /**
128    * Returns the number of modelled PDB file entries.
129    * 
130    * @return
131    */
132   public int getPdbCount()
133   {
134     return pdbEntry == null ? 0 : pdbEntry.length;
135   }
136
137   public SequenceI[][] getSequence()
138   {
139     return sequence;
140   }
141
142   public String[][] getChains()
143   {
144     return chains;
145   }
146
147   public String getProtocol()
148   {
149     return protocol;
150   }
151
152   // TODO may remove this if calling methods can be pulled up here
153   protected void setPdbentry(PDBEntry[] pdbentry)
154   {
155     this.pdbEntry = pdbentry;
156   }
157
158   protected void setSequence(SequenceI[][] sequence)
159   {
160     this.sequence = sequence;
161   }
162
163   protected void setChains(String[][] chains)
164   {
165     this.chains = chains;
166   }
167
168   /**
169    * Construct a title string for the viewer window based on the data Jalview
170    * knows about
171    * @param viewerName TODO
172    * @param verbose
173    * 
174    * @return
175    */
176   public String getViewerTitle(String viewerName, boolean verbose)
177   {
178     if (getSequence() == null || getSequence().length < 1
179             || getPdbCount() < 1
180             || getSequence()[0].length < 1)
181     {
182       return ("Jalview " + viewerName + " Window");
183     }
184     // TODO: give a more informative title when multiple structures are
185     // displayed.
186     StringBuilder title = new StringBuilder(64);
187     final PDBEntry pdbEntry = getPdbEntry(0);
188     title.append(viewerName + " view for " + getSequence()[0][0].getName()
189             + ":"
190             + pdbEntry.getId());
191   
192     if (verbose)
193     {
194       if (pdbEntry.getProperty() != null)
195       {
196         if (pdbEntry.getProperty().get("method") != null)
197         {
198           title.append(" Method: ");
199           title.append(pdbEntry.getProperty().get("method"));
200         }
201         if (pdbEntry.getProperty().get("chains") != null)
202         {
203           title.append(" Chain:");
204           title.append(pdbEntry.getProperty().get("chains"));
205         }
206       }
207     }
208     return title.toString();
209   }
210
211   /**
212    * Called by after closeViewer is called, to release any resources and
213    * references so they can be garbage collected. Override if needed.
214    */
215   protected void releaseUIResources()
216   {
217
218   }
219
220   public boolean isColourBySequence()
221   {
222     return colourBySequence;
223   }
224
225   public void setColourBySequence(boolean colourBySequence)
226   {
227     this.colourBySequence = colourBySequence;
228   }
229
230   protected void addSequenceAndChain(int pe, SequenceI[] seq,
231           String[] tchain)
232   {
233     if (pe < 0 || pe >= getPdbCount())
234     {
235       throw new Error(MessageManager.formatMessage(
236               "error.implementation_error_no_pdbentry_from_index",
237               new Object[]
238               { Integer.valueOf(pe).toString() }));
239     }
240     final String nullChain = "TheNullChain";
241     List<SequenceI> s = new ArrayList<SequenceI>();
242     List<String> c = new ArrayList<String>();
243     if (getChains() == null)
244     {
245       setChains(new String[getPdbCount()][]);
246     }
247     if (getSequence()[pe] != null)
248     {
249       for (int i = 0; i < getSequence()[pe].length; i++)
250       {
251         s.add(getSequence()[pe][i]);
252         if (getChains()[pe] != null)
253         {
254           if (i < getChains()[pe].length)
255           {
256             c.add(getChains()[pe][i]);
257           }
258           else
259           {
260             c.add(nullChain);
261           }
262         }
263         else
264         {
265           if (tchain != null && tchain.length > 0)
266           {
267             c.add(nullChain);
268           }
269         }
270       }
271     }
272     for (int i = 0; i < seq.length; i++)
273     {
274       if (!s.contains(seq[i]))
275       {
276         s.add(seq[i]);
277         if (tchain != null && i < tchain.length)
278         {
279           c.add(tchain[i] == null ? nullChain : tchain[i]);
280         }
281       }
282     }
283     SequenceI[] tmp = s.toArray(new SequenceI[s.size()]);
284     getSequence()[pe] = tmp;
285     if (c.size() > 0)
286     {
287       String[] tch = c.toArray(new String[c.size()]);
288       for (int i = 0; i < tch.length; i++)
289       {
290         if (tch[i] == nullChain)
291         {
292           tch[i] = null;
293         }
294       }
295       getChains()[pe] = tch;
296     }
297     else
298     {
299       getChains()[pe] = null;
300     }
301   }
302
303   /**
304    * add structures and any known sequence associations
305    * 
306    * @returns the pdb entries added to the current set.
307    */
308   public synchronized PDBEntry[] addSequenceAndChain(PDBEntry[] pdbe, SequenceI[][] seq,
309           String[][] chns)
310   {
311     List<PDBEntry> v = new ArrayList<PDBEntry>();
312     List<int[]> rtn = new ArrayList<int[]>();
313     for (int i = 0; i < getPdbCount(); i++)
314     {
315       v.add(getPdbEntry(i));
316     }
317     for (int i = 0; i < pdbe.length; i++)
318     {
319       int r = v.indexOf(pdbe[i]);
320       if (r == -1 || r >= getPdbCount())
321       {
322         rtn.add(new int[]
323         { v.size(), i });
324         v.add(pdbe[i]);
325       }
326       else
327       {
328         // just make sure the sequence/chain entries are all up to date
329         addSequenceAndChain(r, seq[i], chns[i]);
330       }
331     }
332     pdbe = v.toArray(new PDBEntry[v.size()]);
333     setPdbentry(pdbe);
334     if (rtn.size() > 0)
335     {
336       // expand the tied sequence[] and string[] arrays
337       SequenceI[][] sqs = new SequenceI[getPdbCount()][];
338       String[][] sch = new String[getPdbCount()][];
339       System.arraycopy(getSequence(), 0, sqs, 0, getSequence().length);
340       System.arraycopy(getChains(), 0, sch, 0, this.getChains().length);
341       setSequence(sqs);
342       setChains(sch);
343       pdbe = new PDBEntry[rtn.size()];
344       for (int r = 0; r < pdbe.length; r++)
345       {
346         int[] stri = (rtn.get(r));
347         // record the pdb file as a new addition
348         pdbe[r] = getPdbEntry(stri[0]);
349         // and add the new sequence/chain entries
350         addSequenceAndChain(stri[0], seq[stri[1]], chns[stri[1]]);
351       }
352     }
353     else
354     {
355       pdbe = null;
356     }
357     return pdbe;
358   }
359
360   /**
361    * Add sequences to the pe'th pdbentry's sequence set.
362    * 
363    * @param pe
364    * @param seq
365    */
366   public void addSequence(int pe, SequenceI[] seq)
367   {
368     addSequenceAndChain(pe, seq, null);
369   }
370
371   /**
372    * add the given sequences to the mapping scope for the given pdb file handle
373    * 
374    * @param pdbFile
375    *          - pdbFile identifier
376    * @param seq
377    *          - set of sequences it can be mapped to
378    */
379   public void addSequenceForStructFile(String pdbFile, SequenceI[] seq)
380   {
381     for (int pe = 0; pe < getPdbCount(); pe++)
382     {
383       if (getPdbEntry(pe).getFile().equals(pdbFile))
384       {
385         addSequence(pe, seq);
386       }
387     }
388   }
389
390   @Override
391   public void highlightAtoms(List<AtomSpec> atoms)
392   {
393     if (atoms != null)
394     {
395       for (AtomSpec atom : atoms)
396       {
397         highlightAtom(atom.getAtomIndex(), atom.getPdbResNum(),
398                 atom.getChain(), atom.getPdbFile());
399       }
400     }
401   }
402
403   protected abstract void highlightAtom(int atomIndex, int pdbResNum,
404           String chain, String pdbFile);
405
406   protected boolean isNucleotide()
407   {
408     return this.nucleotide;
409   }
410 }