JAL-2089 patch broken merge to master for Release 2.10.0b1
[jalview.git] / src / jalview / io / vamsas / Sequencefeature.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.SequenceFeature;
25 import jalview.datamodel.SequenceI;
26 import jalview.io.VamsasAppDatastore;
27 import jalview.util.UrlLink;
28
29 import java.util.Enumeration;
30 import java.util.Iterator;
31 import java.util.Vector;
32
33 import uk.ac.vamsas.objects.core.DataSetAnnotations;
34 import uk.ac.vamsas.objects.core.Link;
35 import uk.ac.vamsas.objects.core.Property;
36 import uk.ac.vamsas.objects.core.Provenance;
37 import uk.ac.vamsas.objects.core.RangeAnnotation;
38 import uk.ac.vamsas.objects.core.Score;
39 import uk.ac.vamsas.objects.core.Seg;
40 import uk.ac.vamsas.objects.utils.Properties;
41
42 /**
43  * @author JimP
44  * 
45  */
46 public class Sequencefeature extends Rangetype
47 {
48
49   uk.ac.vamsas.objects.core.DataSet dataset;
50
51   uk.ac.vamsas.objects.core.Sequence sequence;
52
53   private SequenceI dsSeq;
54
55   public Sequencefeature(VamsasAppDatastore vamsasAppDatastore,
56           SequenceFeature sequenceFeature,
57           uk.ac.vamsas.objects.core.DataSet dataset,
58           uk.ac.vamsas.objects.core.Sequence sequence)
59   {
60     super(vamsasAppDatastore, sequenceFeature, DataSetAnnotations.class);
61     this.dataset = dataset;
62     this.sequence = sequence;
63     doSync();
64   }
65
66   public Sequencefeature(VamsasAppDatastore vamsasAppDatastore,
67           DataSetAnnotations dseta, SequenceI dsSeq)
68   {
69     super(vamsasAppDatastore, dseta,
70             jalview.datamodel.SequenceFeature.class);
71     this.dsSeq = dsSeq;
72     doJvUpdate();
73   }
74
75   @Override
76   public void addToDocument()
77   {
78     DataSetAnnotations dsa = (DataSetAnnotations) vobj;
79     jalview.datamodel.SequenceFeature feature = (jalview.datamodel.SequenceFeature) jvobj;
80     dsa = (DataSetAnnotations) getDSAnnotationFromJalview(
81             new DataSetAnnotations(), feature);
82     if (dsa.getProvenance() == null)
83     {
84       dsa.setProvenance(new Provenance());
85     }
86     addProvenance(dsa.getProvenance(), "created"); // JBPNote - need
87     // to update
88     dsa.addSeqRef(sequence); // we have just created this annotation
89     // - so safe to use this
90     bindjvvobj(feature, dsa);
91     dataset.addDataSetAnnotations(dsa);
92   }
93
94   @Override
95   public void addFromDocument()
96   {
97     DataSetAnnotations dsa = (DataSetAnnotations) vobj;
98     if (dsa.getSeqRefCount() != 1)
99     {
100       Cache.log
101               .warn("Not binding "
102                       + dsa.getVorbaId()
103                       + " to Sequence Feature - has multiple dataset sequence references.");
104       return;
105     }
106     jalview.datamodel.SequenceFeature sf = (jalview.datamodel.SequenceFeature) jvobj;
107     dsSeq.addSequenceFeature(sf = getJalviewSeqFeature(dsa));
108     jvobj = sf;
109     bindjvvobj(sf, dsa);
110   }
111
112   @Override
113   public void conflict()
114   {
115     log.warn("Untested sequencefeature conflict code");
116     DataSetAnnotations dsa = (DataSetAnnotations) vobj;
117     jalview.datamodel.SequenceFeature feature = (jalview.datamodel.SequenceFeature) jvobj;
118     jalview.datamodel.SequenceFeature sf = getJalviewSeqFeature(dsa);
119     replaceJvObjMapping(feature, sf); // switch binding of dsa from old feature
120                                       // to newly created feature
121     dsSeq.addSequenceFeature(sf); // add new imported feature
122     addToDocument(); // and create a new feature in the document
123   }
124
125   @Override
126   public void updateToDoc()
127   {
128     DataSetAnnotations dsa = (DataSetAnnotations) vobj;
129     jalview.datamodel.SequenceFeature feature = (jalview.datamodel.SequenceFeature) jvobj;
130     if (dsa.getSeqRefCount() != 1)
131     {
132       replaceJvObjMapping(feature, null);
133       Cache.log
134               .warn("Binding of annotation to jalview feature has changed. Removing binding and recreating.");
135       doSync(); // re-verify bindings.
136     }
137     else
138     {
139       // Sync the features from Jalview
140       long oldref = dsa.get__last_hash();
141       getDSAnnotationFromJalview(dsa, feature);
142       if (oldref != dsa.hashCode())
143       {
144         Cache.log
145                 .debug("Updated dataset sequence annotation from feature.");
146         addProvenance(dsa.getProvenance(), "modified");
147       }
148     }
149
150   }
151
152   @Override
153   public void updateFromDoc()
154   {
155     DataSetAnnotations dsa = (DataSetAnnotations) vobj;
156     jalview.datamodel.SequenceFeature feature = (jalview.datamodel.SequenceFeature) jvobj;
157     if (dsa.getSeqRefCount() != 1)
158     {
159       // conflicting update from document - we cannot map this feature anymore.
160       replaceJvObjMapping(feature, null);
161       Cache.log
162               .warn("annotation ("
163                       + dsa.getVorbaId()
164                       + " bound to jalview feature cannot be mapped. Removing binding, deleting feature, and deleting feature.");
165       // - consider deleting the feature ?
166       dsSeq.deleteFeature(feature);
167       // doSync();
168     }
169     else
170     {
171       // Sync the features to Jalview - easiest to delete and add the feature
172       // again
173       jalview.datamodel.SequenceFeature newsf = getJalviewSeqFeature(dsa);
174       dsSeq.deleteFeature(feature);
175       replaceJvObjMapping(feature, newsf);
176       dsSeq.addSequenceFeature(newsf);
177       if (feature.otherDetails != null)
178       {
179         // TODO later: leave this to finalise method ?
180         feature.otherDetails.clear();
181       }
182     }
183   }
184
185   /**
186    * correctly create/update a RangeAnnotation from a jalview sequence feature
187    * TODO: refactor to a method in jalview.io.vamsas.RangeAnnotation class
188    * 
189    * @param dsa
190    *          (typically DataSetAnnotations or AlignmentSequenceAnnotation)
191    * @param feature
192    *          (the feature to be mapped from)
193    * @return
194    */
195   private RangeAnnotation getDSAnnotationFromJalview(RangeAnnotation dsa,
196           jalview.datamodel.SequenceFeature feature)
197   {
198     dsa.setType(feature.getType());
199     Seg vSeg = new Seg();
200     vSeg.setStart(feature.getBegin());
201     vSeg.setEnd(feature.getEnd());
202     vSeg.setInclusive(true);
203     if (dsa.getSegCount() > 1)
204     {
205       Cache.log
206               .debug("About to destroy complex annotation in vamsas document mapped to sequence feature ("
207                       + dsa.getVorbaId() + ")");
208     }
209     dsa.setSeg(new Seg[] { vSeg });
210     dsa.setDescription(feature.getDescription());
211     dsa.setStatus(feature.getStatus());
212     if (feature.links != null && feature.links.size() > 0)
213     {
214       for (int i = 0, iSize = feature.links.size(); i < iSize; i++)
215       {
216         String link = feature.links.elementAt(i);
217         UrlLink ulink = new UrlLink(link);
218         if (ulink.isValid())
219         {
220           // We only add static links to the document.
221           Link vLink = new Link();
222           vLink.setContent(ulink.getLabel());
223           vLink.setHref(ulink.getTarget());
224           dsa.addLink(vLink);
225         }
226       }
227     }
228     dsa.setGroup(feature.getFeatureGroup());
229     if (!Float.isNaN(feature.getScore()))
230     {
231       Score fscore = new Score();
232       dsa.setScore(new Score[] { fscore });
233       fscore.setContent(feature.getScore());
234       fscore.setName(feature.getType());
235     }
236     if (feature.otherDetails != null)
237     {
238       Iterator<String> iter = feature.otherDetails.keySet().iterator();
239       Vector props = dsa.getPropertyAsReference();
240       while (iter.hasNext())
241       {
242         String key = iter.next();
243         if (!key.equalsIgnoreCase("score")
244                 && !key.equalsIgnoreCase("status"))
245         {
246           Property nprop = new Property();
247           nprop.setName(key);
248           Object vlu = feature.getValue(key);
249           nprop.setContent(feature.getValue(key).toString());
250           boolean valid = false;
251           if (vlu instanceof String)
252           {
253             nprop.setType(uk.ac.vamsas.objects.utils.Properties.STRINGTYPE);
254             valid = true;
255           }
256           else if (vlu instanceof Integer)
257           {
258             valid = true;
259             nprop.setType(uk.ac.vamsas.objects.utils.Properties.INTEGERTYPE);
260           }
261           else if (vlu instanceof Float)
262           {
263             nprop.setType(uk.ac.vamsas.objects.utils.Properties.FLOATTYPE);
264             valid = true;
265           }
266           if (valid)
267           {
268             if (props != null)
269             {
270               uk.ac.vamsas.objects.utils.Properties.addOrReplace(props,
271                       nprop);
272             }
273             else
274             {
275               dsa.addProperty(nprop);
276             }
277           }
278         }
279       }
280     }
281     return dsa;
282   }
283
284   private SequenceFeature getJalviewSeqFeature(RangeAnnotation dseta)
285   {
286     int[] se = getBounds(dseta);
287     SequenceFeature sf = new jalview.datamodel.SequenceFeature(
288             dseta.getType(), dseta.getDescription(), dseta.getStatus(),
289             se[0], se[1], dseta.getGroup());
290     if (dseta.getLinkCount() > 0)
291     {
292       Link[] links = dseta.getLink();
293       for (int i = 0; i < links.length; i++)
294       {
295         // TODO: use URLLink parsing/validation here.
296         sf.addLink(links[i].getContent() + "|" + links[i].getHref());
297       }
298     }
299     if (dseta.getScoreCount() > 0)
300     {
301       Enumeration scr = dseta.enumerateScore();
302       while (scr.hasMoreElements())
303       {
304         Score score = (Score) scr.nextElement();
305         if (score.getName().equals(sf.getType()))
306         {
307           sf.setScore(score.getContent());
308         }
309         else
310         {
311           sf.setValue(score.getName(), "" + score.getContent());
312         }
313       }
314     }
315     // other details
316     Enumeration props = dseta.enumerateProperty();
317     while (props.hasMoreElements())
318     {
319       Property p = (Property) props.nextElement();
320       Object val = null;
321       if (Properties.isValid(p))
322       {
323         if (Properties.isString(p))
324         {
325           val = p.getContent();
326         }
327         if (Properties.isBoolean(p))
328         {
329           try
330           {
331             val = new Boolean(p.getContent());
332           } catch (Exception e)
333           {
334           }
335         }
336         if (Properties.isFloat(p))
337         {
338           try
339           {
340             val = new Float(p.getContent());
341
342           } catch (Exception e)
343           {
344           }
345         }
346         if (Properties.isInteger(p))
347         {
348           try
349           {
350             val = new Integer(p.getContent());
351           } catch (Exception e)
352           {
353           }
354         }
355         if (val != null)
356         {
357           sf.setValue(p.getName(), val);
358         }
359       }
360     }
361
362     return sf;
363   }
364
365 }