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