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