f8d86d55fef62123725f3d29cb75c9f36439dbaa
[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.warn("Not binding " + dsa.getVorbaId()
101               + " to Sequence Feature - has multiple dataset sequence references.");
102       return;
103     }
104     jalview.datamodel.SequenceFeature sf = (jalview.datamodel.SequenceFeature) jvobj;
105     dsSeq.addSequenceFeature(sf = getJalviewSeqFeature(dsa));
106     jvobj = sf;
107     bindjvvobj(sf, dsa);
108   }
109
110   @Override
111   public void conflict()
112   {
113     log.warn("Untested sequencefeature conflict code");
114     DataSetAnnotations dsa = (DataSetAnnotations) vobj;
115     jalview.datamodel.SequenceFeature feature = (jalview.datamodel.SequenceFeature) jvobj;
116     jalview.datamodel.SequenceFeature sf = getJalviewSeqFeature(dsa);
117     replaceJvObjMapping(feature, sf); // switch binding of dsa from old feature
118                                       // to newly created feature
119     dsSeq.addSequenceFeature(sf); // add new imported feature
120     addToDocument(); // and create a new feature in the document
121   }
122
123   @Override
124   public void updateToDoc()
125   {
126     DataSetAnnotations dsa = (DataSetAnnotations) vobj;
127     jalview.datamodel.SequenceFeature feature = (jalview.datamodel.SequenceFeature) jvobj;
128     if (dsa.getSeqRefCount() != 1)
129     {
130       replaceJvObjMapping(feature, null);
131       Cache.log.warn(
132               "Binding of annotation to jalview feature has changed. Removing binding and recreating.");
133       doSync(); // re-verify bindings.
134     }
135     else
136     {
137       // Sync the features from Jalview
138       long oldref = dsa.get__last_hash();
139       getDSAnnotationFromJalview(dsa, feature);
140       if (oldref != dsa.hashCode())
141       {
142         Cache.log
143                 .debug("Updated dataset sequence annotation from feature.");
144         addProvenance(dsa.getProvenance(), "modified");
145       }
146     }
147
148   }
149
150   @Override
151   public void updateFromDoc()
152   {
153     DataSetAnnotations dsa = (DataSetAnnotations) vobj;
154     jalview.datamodel.SequenceFeature feature = (jalview.datamodel.SequenceFeature) jvobj;
155     if (dsa.getSeqRefCount() != 1)
156     {
157       // conflicting update from document - we cannot map this feature anymore.
158       replaceJvObjMapping(feature, null);
159       Cache.log.warn("annotation (" + dsa.getVorbaId()
160               + " bound to jalview feature cannot be mapped. Removing binding, deleting feature, and deleting feature.");
161       // - consider deleting the feature ?
162       dsSeq.deleteFeature(feature);
163       // doSync();
164     }
165     else
166     {
167       // Sync the features to Jalview - easiest to delete and add the feature
168       // again
169       jalview.datamodel.SequenceFeature newsf = getJalviewSeqFeature(dsa);
170       dsSeq.deleteFeature(feature);
171       replaceJvObjMapping(feature, newsf);
172       dsSeq.addSequenceFeature(newsf);
173       if (feature.otherDetails != null)
174       {
175         // TODO later: leave this to finalise method ?
176         feature.otherDetails.clear();
177       }
178     }
179   }
180
181   /**
182    * correctly create/update a RangeAnnotation from a jalview sequence feature
183    * TODO: refactor to a method in jalview.io.vamsas.RangeAnnotation class
184    * 
185    * @param dsa
186    *          (typically DataSetAnnotations or AlignmentSequenceAnnotation)
187    * @param feature
188    *          (the feature to be mapped from)
189    * @return
190    */
191   private RangeAnnotation getDSAnnotationFromJalview(RangeAnnotation dsa,
192           jalview.datamodel.SequenceFeature feature)
193   {
194     dsa.setType(feature.getType());
195     Seg vSeg = new Seg();
196     vSeg.setStart(feature.getBegin());
197     vSeg.setEnd(feature.getEnd());
198     vSeg.setInclusive(true);
199     if (dsa.getSegCount() > 1)
200     {
201       Cache.log.debug(
202               "About to destroy complex annotation in vamsas document mapped to sequence feature ("
203                       + dsa.getVorbaId() + ")");
204     }
205     dsa.setSeg(new Seg[] { vSeg });
206     dsa.setDescription(feature.getDescription());
207     dsa.setStatus(feature.getStatus());
208     if (feature.links != null && feature.links.size() > 0)
209     {
210       for (int i = 0, iSize = feature.links.size(); i < iSize; i++)
211       {
212         String link = feature.links.elementAt(i);
213         UrlLink ulink = new UrlLink(link);
214         if (ulink.isValid())
215         {
216           // We only add static links to the document.
217           Link vLink = new Link();
218           vLink.setContent(ulink.getLabel());
219           vLink.setHref(ulink.getTarget());
220           dsa.addLink(vLink);
221         }
222       }
223     }
224     dsa.setGroup(feature.getFeatureGroup());
225     if (!Float.isNaN(feature.getScore()))
226     {
227       Score fscore = new Score();
228       dsa.setScore(new Score[] { fscore });
229       fscore.setContent(feature.getScore());
230       fscore.setName(feature.getType());
231     }
232     if (feature.otherDetails != null)
233     {
234       Iterator<String> iter = feature.otherDetails.keySet().iterator();
235       Vector props = dsa.getPropertyAsReference();
236       while (iter.hasNext())
237       {
238         String key = iter.next();
239         if (!key.equalsIgnoreCase("score")
240                 && !key.equalsIgnoreCase("status"))
241         {
242           Property nprop = new Property();
243           nprop.setName(key);
244           Object vlu = feature.getValue(key);
245           nprop.setContent(feature.getValue(key).toString());
246           boolean valid = false;
247           if (vlu instanceof String)
248           {
249             nprop.setType(uk.ac.vamsas.objects.utils.Properties.STRINGTYPE);
250             valid = true;
251           }
252           else if (vlu instanceof Integer)
253           {
254             valid = true;
255             nprop.setType(
256                     uk.ac.vamsas.objects.utils.Properties.INTEGERTYPE);
257           }
258           else if (vlu instanceof Float)
259           {
260             nprop.setType(uk.ac.vamsas.objects.utils.Properties.FLOATTYPE);
261             valid = true;
262           }
263           if (valid)
264           {
265             if (props != null)
266             {
267               uk.ac.vamsas.objects.utils.Properties.addOrReplace(props,
268                       nprop);
269             }
270             else
271             {
272               dsa.addProperty(nprop);
273             }
274           }
275         }
276       }
277     }
278     return dsa;
279   }
280
281   private SequenceFeature getJalviewSeqFeature(RangeAnnotation dseta)
282   {
283     int[] se = getBounds(dseta);
284
285     /*
286      * try to identify feature score
287      */
288     boolean scoreFound = false;
289     float theScore = 0f;
290     String featureType = dseta.getType();
291     if (dseta.getScoreCount() > 0)
292     {
293       Enumeration scr = dseta.enumerateScore();
294       while (scr.hasMoreElements())
295       {
296         Score score = (Score) scr.nextElement();
297         if (score.getName().equals(featureType))
298         {
299           theScore = score.getContent();
300           scoreFound = true;
301         }
302       }
303     }
304
305     SequenceFeature sf = null;
306     if (scoreFound)
307     {
308       sf = new SequenceFeature(featureType, dseta.getDescription(), se[0],
309               se[1], theScore, dseta.getGroup());
310     }
311     else
312     {
313       sf = new SequenceFeature(featureType, dseta.getDescription(), se[0],
314               se[1], dseta.getGroup());
315     }
316     sf.setStatus(dseta.getStatus());
317     if (dseta.getLinkCount() > 0)
318     {
319       Link[] links = dseta.getLink();
320       for (int i = 0; i < links.length; i++)
321       {
322         // TODO: use URLLink parsing/validation here.
323         sf.addLink(links[i].getContent() + "|" + links[i].getHref());
324       }
325     }
326     if (dseta.getScoreCount() > 0)
327     {
328       Enumeration scr = dseta.enumerateScore();
329       while (scr.hasMoreElements())
330       {
331         Score score = (Score) scr.nextElement();
332         if (!score.getName().equals(sf.getType()))
333         {
334           sf.setValue(score.getName(), "" + score.getContent());
335         }
336       }
337     }
338     // other details
339     Enumeration props = dseta.enumerateProperty();
340     while (props.hasMoreElements())
341     {
342       Property p = (Property) props.nextElement();
343       Object val = null;
344       if (Properties.isValid(p))
345       {
346         if (Properties.isString(p))
347         {
348           val = p.getContent();
349         }
350         if (Properties.isBoolean(p))
351         {
352           try
353           {
354             val = Boolean.valueOf(p.getContent());
355           } catch (Exception e)
356           {
357           }
358         }
359         if (Properties.isFloat(p))
360         {
361           try
362           {
363             val = Float.valueOf(p.getContent());
364
365           } catch (Exception e)
366           {
367           }
368         }
369         if (Properties.isInteger(p))
370         {
371           try
372           {
373             val = Integer.valueOf(p.getContent());
374           } catch (Exception e)
375           {
376           }
377         }
378         if (val != null)
379         {
380           sf.setValue(p.getName(), val);
381         }
382       }
383     }
384
385     return sf;
386   }
387
388 }