3602056673d41ead4f7a3ed9490a54c7460a826f
[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    * Returns the number of modelled PDB file entries.
108    * 
109    * @return
110    */
111   public int getPdbCount()
112   {
113     return pdbEntry == null ? 0 : pdbEntry.length;
114   }
115
116   public SequenceI[][] getSequence()
117   {
118     return sequence;
119   }
120
121   public String[][] getChains()
122   {
123     return chains;
124   }
125
126   public String getProtocol()
127   {
128     return protocol;
129   }
130
131   // TODO may remove this if calling methods can be pulled up here
132   protected void setPdbentry(PDBEntry[] pdbentry)
133   {
134     this.pdbEntry = pdbentry;
135   }
136
137   protected void setSequence(SequenceI[][] sequence)
138   {
139     this.sequence = sequence;
140   }
141
142   protected void setChains(String[][] chains)
143   {
144     this.chains = chains;
145   }
146
147   /**
148    * Construct a title string for the viewer window based on the data Jalview
149    * knows about
150    * @param viewerName TODO
151    * @param verbose
152    * 
153    * @return
154    */
155   public String getViewerTitle(String viewerName, boolean verbose)
156   {
157     if (getSequence() == null || getSequence().length < 1
158             || getPdbCount() < 1
159             || getSequence()[0].length < 1)
160     {
161       return ("Jalview " + viewerName + " Window");
162     }
163     // TODO: give a more informative title when multiple structures are
164     // displayed.
165     StringBuilder title = new StringBuilder(64);
166     final PDBEntry pdbEntry = getPdbEntry(0);
167     title.append(viewerName + " view for " + getSequence()[0][0].getName()
168             + ":"
169             + pdbEntry.getId());
170   
171     if (verbose)
172     {
173       if (pdbEntry.getProperty() != null)
174       {
175         if (pdbEntry.getProperty().get("method") != null)
176         {
177           title.append(" Method: ");
178           title.append(pdbEntry.getProperty().get("method"));
179         }
180         if (pdbEntry.getProperty().get("chains") != null)
181         {
182           title.append(" Chain:");
183           title.append(pdbEntry.getProperty().get("chains"));
184         }
185       }
186     }
187     return title.toString();
188   }
189
190   /**
191    * Called by after closeViewer is called, to release any resources and
192    * references so they can be garbage collected. Override if needed.
193    */
194   protected void releaseUIResources()
195   {
196
197   }
198
199   public boolean isColourBySequence()
200   {
201     return colourBySequence;
202   }
203
204   public void setColourBySequence(boolean colourBySequence)
205   {
206     this.colourBySequence = colourBySequence;
207   }
208
209   protected void addSequenceAndChain(int pe, SequenceI[] seq,
210           String[] tchain)
211   {
212     if (pe < 0 || pe >= getPdbCount())
213     {
214       throw new Error(MessageManager.formatMessage(
215               "error.implementation_error_no_pdbentry_from_index",
216               new Object[]
217               { Integer.valueOf(pe).toString() }));
218     }
219     final String nullChain = "TheNullChain";
220     List<SequenceI> s = new ArrayList<SequenceI>();
221     List<String> c = new ArrayList<String>();
222     if (getChains() == null)
223     {
224       setChains(new String[getPdbCount()][]);
225     }
226     if (getSequence()[pe] != null)
227     {
228       for (int i = 0; i < getSequence()[pe].length; i++)
229       {
230         s.add(getSequence()[pe][i]);
231         if (getChains()[pe] != null)
232         {
233           if (i < getChains()[pe].length)
234           {
235             c.add(getChains()[pe][i]);
236           }
237           else
238           {
239             c.add(nullChain);
240           }
241         }
242         else
243         {
244           if (tchain != null && tchain.length > 0)
245           {
246             c.add(nullChain);
247           }
248         }
249       }
250     }
251     for (int i = 0; i < seq.length; i++)
252     {
253       if (!s.contains(seq[i]))
254       {
255         s.add(seq[i]);
256         if (tchain != null && i < tchain.length)
257         {
258           c.add(tchain[i] == null ? nullChain : tchain[i]);
259         }
260       }
261     }
262     SequenceI[] tmp = s.toArray(new SequenceI[s.size()]);
263     getSequence()[pe] = tmp;
264     if (c.size() > 0)
265     {
266       String[] tch = c.toArray(new String[c.size()]);
267       for (int i = 0; i < tch.length; i++)
268       {
269         if (tch[i] == nullChain)
270         {
271           tch[i] = null;
272         }
273       }
274       getChains()[pe] = tch;
275     }
276     else
277     {
278       getChains()[pe] = null;
279     }
280   }
281
282   /**
283    * add structures and any known sequence associations
284    * 
285    * @returns the pdb entries added to the current set.
286    */
287   public synchronized PDBEntry[] addSequenceAndChain(PDBEntry[] pdbe, SequenceI[][] seq,
288           String[][] chns)
289   {
290     List<PDBEntry> v = new ArrayList<PDBEntry>();
291     List<int[]> rtn = new ArrayList<int[]>();
292     for (int i = 0; i < getPdbCount(); i++)
293     {
294       v.add(getPdbEntry(i));
295     }
296     for (int i = 0; i < pdbe.length; i++)
297     {
298       int r = v.indexOf(pdbe[i]);
299       if (r == -1 || r >= getPdbCount())
300       {
301         rtn.add(new int[]
302         { v.size(), i });
303         v.add(pdbe[i]);
304       }
305       else
306       {
307         // just make sure the sequence/chain entries are all up to date
308         addSequenceAndChain(r, seq[i], chns[i]);
309       }
310     }
311     pdbe = v.toArray(new PDBEntry[v.size()]);
312     setPdbentry(pdbe);
313     if (rtn.size() > 0)
314     {
315       // expand the tied sequence[] and string[] arrays
316       SequenceI[][] sqs = new SequenceI[getPdbCount()][];
317       String[][] sch = new String[getPdbCount()][];
318       System.arraycopy(getSequence(), 0, sqs, 0, getSequence().length);
319       System.arraycopy(getChains(), 0, sch, 0, this.getChains().length);
320       setSequence(sqs);
321       setChains(sch);
322       pdbe = new PDBEntry[rtn.size()];
323       for (int r = 0; r < pdbe.length; r++)
324       {
325         int[] stri = (rtn.get(r));
326         // record the pdb file as a new addition
327         pdbe[r] = getPdbEntry(stri[0]);
328         // and add the new sequence/chain entries
329         addSequenceAndChain(stri[0], seq[stri[1]], chns[stri[1]]);
330       }
331     }
332     else
333     {
334       pdbe = null;
335     }
336     return pdbe;
337   }
338
339   /**
340    * Add sequences to the pe'th pdbentry's sequence set.
341    * 
342    * @param pe
343    * @param seq
344    */
345   public void addSequence(int pe, SequenceI[] seq)
346   {
347     addSequenceAndChain(pe, seq, null);
348   }
349
350   /**
351    * add the given sequences to the mapping scope for the given pdb file handle
352    * 
353    * @param pdbFile
354    *          - pdbFile identifier
355    * @param seq
356    *          - set of sequences it can be mapped to
357    */
358   public void addSequenceForStructFile(String pdbFile, SequenceI[] seq)
359   {
360     for (int pe = 0; pe < getPdbCount(); pe++)
361     {
362       if (getPdbEntry(pe).getFile().equals(pdbFile))
363       {
364         addSequence(pe, seq);
365       }
366     }
367   }
368
369   @Override
370   public void highlightAtoms(List<AtomSpec> atoms)
371   {
372     if (atoms != null)
373     {
374       for (AtomSpec atom : atoms)
375       {
376         highlightAtom(atom.getAtomIndex(), atom.getPdbResNum(),
377                 atom.getChain(), atom.getPdbFile());
378       }
379     }
380   }
381
382   protected abstract void highlightAtom(int atomIndex, int pdbResNum,
383           String chain, String pdbFile);
384
385   protected boolean isNucleotide()
386   {
387     return this.nucleotide;
388   }
389 }