9bdd6b3aa3389136913c75d577a21e27dbb92cc5
[jalview.git] / src / jalview / io / vamsas / Sequencemapping.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.io.vamsas;
22
23 import jalview.bin.Cache;
24 import jalview.datamodel.AlignedCodonFrame;
25 import jalview.datamodel.AlignmentI;
26 import jalview.datamodel.DBRefEntry;
27 import jalview.datamodel.Mapping;
28 import jalview.datamodel.SequenceI;
29 import jalview.gui.Desktop;
30 import jalview.io.VamsasAppDatastore;
31 import jalview.structure.StructureSelectionManager;
32 import jalview.util.MapList;
33
34 import java.util.Vector;
35
36 import uk.ac.vamsas.objects.core.AlignmentSequence;
37 import uk.ac.vamsas.objects.core.DataSet;
38 import uk.ac.vamsas.objects.core.Sequence;
39 import uk.ac.vamsas.objects.core.SequenceMapping;
40 import uk.ac.vamsas.objects.core.SequenceType;
41 import uk.ac.vamsas.objects.utils.SymbolDictionary;
42
43 /**
44  * binds a vamsas sequence mapping object from the vamsas document to a maplist
45  * object associated with a mapping in the Jalview model. We use the maplist
46  * object because these are referred to both in the Mapping object associated
47  * with a DBRefEntry and in the array of jalview.datamodel.AlCodonFrame objects
48  * that Jalview uses to propagate sequence mapping position highlighting across
49  * the views.
50  * 
51  * @author JimP
52  * 
53  */
54 public class Sequencemapping extends Rangetype
55 {
56   public Sequencemapping(VamsasAppDatastore datastore,
57           SequenceMapping sequenceMapping)
58   {
59     super(datastore, sequenceMapping, MapList.class);
60     doJvUpdate();
61   }
62
63   private SequenceType from;
64
65   private DataSet ds;
66
67   private Mapping mjvmapping;
68
69   /**
70    * create or update a vamsas sequence mapping corresponding to a jalview
71    * Mapping between two dataset sequences
72    * 
73    * @param datastore
74    * @param mjvmapping
75    * @param from
76    * @param ds
77    */
78   public Sequencemapping(VamsasAppDatastore datastore,
79  Mapping mjvmapping,
80           uk.ac.vamsas.objects.core.SequenceType from,
81           uk.ac.vamsas.objects.core.DataSet ds)
82   {
83     super(datastore, mjvmapping.getMap(), SequenceMapping.class);
84     this.from = from;
85     this.ds = ds;
86     this.mjvmapping = mjvmapping;
87     validate();
88     doSync();
89   }
90
91   /**
92    * local check that extant mapping context is valid
93    */
94   public void validate()
95   {
96
97     SequenceMapping sequenceMapping = (SequenceMapping) vobj;
98     if (sequenceMapping == null)
99     {
100       return;
101     }
102     if (from != null && sequenceMapping.getLoc() != from)
103     {
104       Cache.log.warn("Probable IMPLEMENTATION ERROR: " + from
105               + " doesn't match the local mapping sequence.");
106     }
107     if (ds != null && sequenceMapping.is__stored_in_document()
108             && sequenceMapping.getV_parent() != ds)
109     {
110       Cache.log
111               .warn("Probable IMPLEMENTATION ERROR: "
112                       + ds
113                       + " doesn't match the parent of the bound sequence mapping object.");
114     }
115   }
116
117   public void addToDocument()
118   {
119     add(mjvmapping, from, ds);
120   }
121
122   public void addFromDocument()
123   {
124     add((SequenceMapping) vobj);
125   }
126
127   public void conflict()
128   {
129     conflict(mjvmapping, (SequenceMapping) vobj);
130
131   }
132
133   public void updateToDoc()
134   {
135     update(mjvmapping, (SequenceMapping) vobj);
136   }
137
138   public void updateFromDoc()
139   {
140     update((SequenceMapping) vobj, (Mapping) jvobj);
141   }
142
143   private void conflict(Mapping mjvmapping, SequenceMapping sequenceMapping)
144   {
145     System.err.println("Conflict in update of sequenceMapping "
146             + sequenceMapping.getVorbaId());
147   }
148
149   private void add(Mapping mjvmapping,
150           uk.ac.vamsas.objects.core.SequenceType from, DataSet ds)
151   {
152     SequenceI jvto = mjvmapping.getTo();
153     while (jvto.getDatasetSequence() != null)
154     {
155       jvto = jvto.getDatasetSequence();
156     }
157     SequenceType to = (SequenceType) getjv2vObj(jvto);
158     if (to == null)
159     {
160       Cache.log
161               .warn("FIXME NONFATAL - do a second update: Ignoring Forward Reference to seuqence not yet bound to vamsas seuqence object");
162       return;
163     }
164     SequenceMapping sequenceMapping = new SequenceMapping();
165     sequenceMapping.setLoc(from);
166     sequenceMapping.setMap(to);
167     boolean dnaToProt = false, sense = false;
168     // ensure that we create a mapping with the correct sense
169     if (((Sequence) sequenceMapping.getLoc()).getDictionary().equals(
170             SymbolDictionary.STANDARD_NA))
171     {
172       if (((Sequence) sequenceMapping.getMap()).getDictionary().equals(
173               SymbolDictionary.STANDARD_AA))
174       {
175         dnaToProt = true;
176         sense = true;
177       }
178     }
179     else
180     {
181       if (((Sequence) sequenceMapping.getMap()).getDictionary().equals(
182               SymbolDictionary.STANDARD_NA))
183       {
184         dnaToProt = true;
185         sense = false;
186       }
187     }
188
189     if (!dnaToProt)
190     {
191       Cache.log
192               .warn("Ignoring Mapping - don't support protein to protein mapping in vamsas document yet.");
193       return;
194     }
195     if (ds == null)
196     {
197       // locate dataset for storage of SequenceMapping
198       if (sense)
199       {
200         ds = (DataSet) ((uk.ac.vamsas.client.Vobject) sequenceMapping
201                 .getLoc()).getV_parent();
202       }
203       else
204       {
205         ds = (DataSet) ((uk.ac.vamsas.client.Vobject) sequenceMapping
206                 .getMap()).getV_parent();
207       }
208     }
209     if (sense)
210     {
211       this.initMapType(sequenceMapping, mjvmapping.getMap(), true);
212     }
213     else
214     {
215       this.initMapType(sequenceMapping, mjvmapping.getMap().getInverse(),
216               true);
217     }
218     ds.addSequenceMapping(sequenceMapping);
219     sequenceMapping.setProvenance(this
220             .dummyProvenance("user defined coding region translation")); // TODO:
221     // correctly
222     // construct
223     // provenance
224     // based
225     // on
226     // source
227     // of
228     // mapping
229     bindjvvobj(mjvmapping.getMap(), sequenceMapping);
230
231     Cache.log.debug("Successfully created mapping "
232             + sequenceMapping.getVorbaId());
233   }
234
235   // private void update(MapList mjvmapping,
236   // SequenceMapping sequenceMapping)
237   {
238     Cache.log
239             .error("Not implemented: Jalview Update Alcodon Mapping:TODO!");
240   }
241
242   private void update(SequenceMapping sequenceMapping,
243  Mapping mjvmapping)
244   {
245     Cache.log
246             .error("Not implemented: Update DBRef Mapping from Jalview");
247   }
248
249   private void update(Mapping mjvmapping,
250           SequenceMapping sequenceMapping)
251   {
252     Cache.log
253             .error("Not implemented: Jalview Update Sequence DBRef Mapping");
254   }
255
256   /**
257    * bind a SequenceMapping to a live AlCodonFrame element limitations:
258    * Currently, jalview only deals with mappings between dataset sequences, and
259    * even then, only between those that map from DNA to Protein.
260    * 
261    * @param sequenceMapping
262    */
263   private void add(SequenceMapping sequenceMapping)
264   {
265     Object mobj;
266     SequenceI from = null, to = null;
267     boolean dnaToProt = false, sense = false;
268     Sequence sdloc = null, sdmap = null;
269     if (sequenceMapping.getLoc() instanceof AlignmentSequence)
270     {
271       sdloc = (Sequence) ((AlignmentSequence) sequenceMapping.getLoc())
272               .getRefid();
273     }
274     else
275     {
276       sdloc = ((Sequence) sequenceMapping.getLoc());
277     }
278     if (sequenceMapping.getMap() instanceof AlignmentSequence)
279     {
280       sdmap = (Sequence) ((AlignmentSequence) sequenceMapping.getMap())
281               .getRefid();
282     }
283     else
284     {
285       sdmap = ((Sequence) sequenceMapping.getMap());
286     }
287     if (sdloc == null || sdmap == null)
288     {
289       Cache.log.info("Ignoring non sequence-sequence mapping");
290       return;
291     }
292     mobj = this.getvObj2jv(sdloc);
293     if (mobj instanceof SequenceI)
294     {
295       from = (SequenceI) mobj;
296     }
297     mobj = this.getvObj2jv(sdmap);
298     if (mobj instanceof SequenceI)
299     {
300       to = (SequenceI) mobj;
301     }
302     if (from == null || to == null)
303     {
304
305       Cache.log
306               .error("Probable Vamsas implementation error : unbound dataset sequences involved in a mapping are being parsed!");
307       return;
308     }
309
310     if (sdloc.getDictionary().equals(
311 SymbolDictionary.STANDARD_NA))
312     {
313       if (sdmap.getDictionary().equals(
314 SymbolDictionary.STANDARD_AA))
315       {
316         dnaToProt = true;
317         sense = true;
318       }
319       // else {
320
321       // }
322     }
323     else
324     {
325       if (sdmap.getDictionary().equals(
326 SymbolDictionary.STANDARD_NA))
327       {
328         dnaToProt = true;
329         sense = false;
330       }
331     }
332     // create mapping storage object and make each dataset alignment reference
333     // it.
334     AlignmentI dsLoc = (AlignmentI) getvObj2jv(sdloc.getV_parent());
335     AlignmentI dsMap = (AlignmentI) getvObj2jv(sdmap.getV_parent());
336     AlignedCodonFrame acf = new AlignedCodonFrame();
337
338     if (dsLoc != null && dsLoc != dsMap)
339     {
340       dsLoc.addCodonFrame(acf);
341     }
342     if (dsMap != null)
343     {
344       dsMap.addCodonFrame(acf);
345     }
346     // create and add the new mapping to (each) dataset's codonFrame
347
348     MapList mapping = null;
349     if (dnaToProt)
350     {
351       if (!sense)
352       {
353         mapping = this.parsemapType(sequenceMapping, 1, 3); // invert sense
354         mapping = new MapList(mapping.getToRanges(),
355                 mapping.getFromRanges(), mapping.getToRatio(),
356                 mapping.getFromRatio());
357         acf.addMap(to, from, mapping);
358       }
359       else
360       {
361         mapping = this.parsemapType(sequenceMapping, 3, 1); // correct sense
362         acf.addMap(from, to, mapping);
363       }
364     }
365     else
366     {
367       mapping = this.parsemapType(sequenceMapping, 1, 1); // correct sense
368       acf.addMap(from, to, mapping);
369     }
370     bindjvvobj(mapping, sequenceMapping);
371     StructureSelectionManager
372             .getStructureSelectionManager(Desktop.instance).addMapping(acf);
373     // Try to link up any conjugate database references in the two sequences
374     // matchConjugateDBRefs(from, to, mapping);
375     // Try to propagate any dbrefs across this mapping.
376
377   }
378
379   /**
380    * Complete any 'to' references in Mapping objects associated with conjugate
381    * DBRefEntry under given mapping
382    * 
383    * @param from
384    *          sequence corresponding to from reference for sequence mapping
385    * @param to
386    *          sequence correspondeing to to reference for sequence mapping
387    * @param smap
388    *          maplist parsed in same sense as from and to
389    */
390   private void matchConjugateDBRefs(SequenceI from, SequenceI to,
391           MapList smap)
392   {
393     if (from.getDBRef() == null && to.getDBRef() == null)
394     {
395       if (Cache.log.isDebugEnabled())
396       {
397         Cache.log.debug("Not matching conjugate refs for "
398                 + from.getName() + " and " + to.getName());
399       }
400       return;
401     }
402     if (Cache.log.isDebugEnabled())
403     {
404       Cache.log.debug("Matching conjugate refs for "
405               + from.getName() + " and " + to.getName());
406     }
407     DBRefEntry[] fdb = from.getDBRef();
408     DBRefEntry[] tdb = new DBRefEntry[to
409             .getDBRef().length];
410     int tdblen = to.getDBRef().length;
411     System.arraycopy(to.getDBRef(), 0, tdb, 0, tdblen);
412     Vector matched = new Vector();
413     MapList smapI = smap.getInverse();
414     for (int f = 0; f < fdb.length; f++)
415     {
416       DBRefEntry fe = fdb[f];
417       Mapping fmp = fe.getMap();
418       boolean fmpnnl = fmp != null;
419       // if (fmpnnl && fmp.getTo()!=null)
420       // {
421       // Cache.log.debug("Not overwriting existing To reference in
422       // "+fe);
423       // continue;
424       // }
425       // smap from maps from fe.local to fe.map
426       boolean smapfromlocal2fe = (fmpnnl) ? smap.equals(fmp.getMap())
427               : false;
428       // smap from maps from fe.map to fe.local.
429       boolean smapfromfemap2local = (fmpnnl) ? smapI.equals(fmp.getMap())
430               : false;
431       for (int t = 0; t < tdblen; t++)
432       {
433         DBRefEntry te = tdb[t];
434         if (te != null)
435         {
436           if (fe.getSource().equals(te.getSource())
437                   && fe.getAccessionId().equals(te.getAccessionId()))
438           {
439             Mapping tmp = te.getMap();
440             boolean tmpnnl = tmp != null;
441             if (tmpnnl && tmp.getTo() != null)
442             {
443
444             }
445             // smap to maps from te.local to te.map
446             boolean smaptolocal2tm = (tmpnnl) ? smap.equals(tmp.getMap())
447                     : false;
448             // smap to maps from te.map to te.local
449             boolean smaptotemap2local = (tmpnnl) ? smapI.equals(fmp
450                     .getMap()) : false;
451             if (smapfromlocal2fe && smaptotemap2local)
452             {
453               // smap implies mapping from to to from
454               fmp.setTo(to);
455               tmp.setTo(from);
456             }
457             else if (smapfromfemap2local && smaptolocal2tm)
458             {
459               fmp.setTo(to);
460             }
461           }
462
463         }
464       }
465     }
466   }
467 }