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