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