Merge branch 'JAL-3878_ws-overhaul-3' into with_ws_overhaul-3
[jalview.git] / src / jalview / datamodel / DBRefEntry.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.datamodel;
22
23 import java.util.Locale;
24
25 import jalview.api.DBRefEntryI;
26 import jalview.util.DBRefUtils;
27 import jalview.util.MapList;
28
29 import java.util.List;
30
31 public class DBRefEntry implements DBRefEntryI
32 {
33   String source = "";
34
35   private String version = "";
36
37   private String ucversion;
38
39   private String accessionId = "";
40
41   int sourceKey = Integer.MIN_VALUE;
42
43   String canonicalSourceName;
44
45   boolean isCanonicalAccession = false;
46
47   /*
48    * maps from associated sequence to the database sequence's coordinate system
49    */
50   Mapping map = null;
51
52   public DBRefEntry()
53   {
54
55   }
56
57   /**
58    * 
59    * @param source
60    *          may not be null
61    * @param version
62    *          may be null
63    * @param accessionId
64    *          may be null
65    */
66   public DBRefEntry(String source, String version, String accessionId)
67   {
68     this(source, version, accessionId, null, false);
69   }
70
71   /**
72    * 
73    * @param source
74    *          may not be null
75    * @param version
76    *          may be null
77    * @param accessionId
78    *          may be null
79    */
80   public DBRefEntry(String source, String version, String accessionId,
81           Mapping map)
82   {
83     this(source, version, accessionId, map, false);
84   }
85
86   /**
87    * 
88    * @param source
89    *          canonical source (turned to uppercase; cannot be null)
90    * @param version
91    *          (source dependent version string or null)
92    * @param accessionId
93    *          (source dependent accession number string or null)
94    * @param map
95    *          (mapping from local sequence numbering to source accession
96    *          numbering or null)
97    */
98   public DBRefEntry(String source, String version, String accessionId,
99           Mapping map, boolean isCanonical)
100   {
101
102     this.source = source.toUpperCase(Locale.ROOT);
103     setVersion(version);
104     this.accessionId = accessionId;
105     this.map = map;
106     this.isCanonicalAccession = isCanonical;
107   }
108
109   /**
110    * Clone an entry, this time not allowing any null fields except map.
111    * 
112    */
113   public DBRefEntry(DBRefEntryI entry)
114   {
115     this((entry.getSource() == null ? "" : new String(entry.getSource())),
116             (entry.getVersion() == null ? ""
117                     : new String(entry.getVersion())),
118             (entry.getAccessionId() == null ? ""
119                     : new String(entry.getAccessionId())),
120             (entry.getMap() == null ? null : new Mapping(entry.getMap())),
121             entry.isCanonical());
122   }
123
124   @Override
125   public boolean equals(Object o)
126   {
127     // TODO should also override hashCode to ensure equal objects have equal
128     // hashcodes
129
130     // if (o == null || !(o instanceof DBRefEntry))
131     // {
132     // return false;
133     // }
134     // DBRefEntry entry = (DBRefEntry) o;
135     // if (entry == this)
136     // {
137     // return true;
138     // }
139     Mapping em;
140     return (o != null && o instanceof DBRefEntry && (o == this || equalRef(
141             (DBRefEntry) o)
142             && (map == null) == ((em = ((DBRefEntry) o).map) == null)
143             && (map == null || map.equals(em))));
144     //
145     // {
146     // return true;
147     // }
148     // return false;
149   }
150
151   /**
152    * Answers true if this object is either equivalent to, or can be 'improved'
153    * by, the given entry. Specifically, answers true if
154    * <ul>
155    * <li>source and accession are identical (ignoring case)</li>
156    * <li>version is identical (ignoring case), or this version is of the format
157    * "someSource:0", in which case the version for the other entry replaces
158    * it</li>
159    * <li>mappings are not compared but if this entry has no mapping, replace
160    * with that for the other entry</li>
161    * </ul>
162    * 
163    * @param other
164    * @return
165    */
166   @Override
167   public boolean updateFrom(DBRefEntryI other)
168   {
169     if (other == null)
170     {
171       return false;
172     }
173     if (other == this)
174     {
175       return true;
176     }
177
178     boolean improved = false;
179     /*
180      * source must either match or be both null
181      */
182     String otherSource = other.getSource();
183     if ((source == null && otherSource != null)
184             || (source != null && otherSource == null)
185             || (source != null && !source.equalsIgnoreCase(otherSource)))
186     {
187       return false;
188     }
189
190     /*
191      * accession id must either match or be both null
192      */
193     String otherAccession = other.getAccessionId();
194     if ((accessionId == null && otherAccession != null)
195             || (accessionId != null && otherAccession == null)
196             || (accessionId != null
197                     && !accessionId.equalsIgnoreCase(otherAccession)))
198     {
199       return false;
200     }
201
202     if (!isCanonicalAccession && other.isCanonical())
203     {
204       isCanonicalAccession = true;
205       improved = true;
206     }
207     else
208     {
209       if (isCanonicalAccession && !other.isCanonical())
210       {
211         // other is not an authoritative source of canonical accessions
212         return false;
213       }
214     }
215     /*
216      * if my version is null, "0" or "source:0" then replace with other version,
217      * otherwise the versions have to match
218      */
219     String otherVersion = other.getVersion();
220
221     if ((version == null || version.equals("0") || version.endsWith(":0"))
222             && otherVersion != null)
223     {
224       setVersion(otherVersion);
225     }
226     else
227     {
228       if (version != null && (otherVersion == null
229               || !version.equalsIgnoreCase(otherVersion)))
230       {
231         // FIXME: there may be a problem with old version strings not allowing
232         // updating of dbrefentries
233         return improved;
234       }
235     }
236
237     /*
238      * if I have no mapping, take that of the other dbref 
239      * - providing it had a version and so do I
240      */
241     if (map == null)
242     {
243       setMap(other.getMap());
244     }
245     return true;
246   }
247
248   /**
249    * test for similar DBRef attributes, except for the map object.
250    * 
251    * @param entry
252    * @return true if source, accession and version are equal with those of entry
253    */
254   @Override
255   public boolean equalRef(DBRefEntryI entry)
256   {
257     // TODO is this method and equals() not needed?
258     if (entry == null)
259     {
260       return false;
261     }
262     if (entry == this)
263     {
264       return true;
265     }
266
267     // BH 2019.01.25/2019.02.04 source cannot/should not be null.
268     // for example, StructureChooser has dbRef.getSource().equalsIgnoreCase...
269
270     return (entry != null
271             && (source != null && entry.getSource() != null
272                     && source.equalsIgnoreCase(entry.getSource()))
273             && (accessionId != null && entry.getAccessionId() != null
274                     && accessionId.equalsIgnoreCase(entry.getAccessionId()))
275             && (version != null && entry.getVersion() != null
276                     && version.equalsIgnoreCase(entry.getVersion())));
277   }
278
279   @Override
280   public String getSource()
281   {
282     return source;
283   }
284
285   public int getSourceKey()
286   {
287     return (sourceKey == Integer.MIN_VALUE
288             ? (sourceKey = DBRefSource
289                     .getSourceKey(getCanonicalSourceName()))
290             : sourceKey);
291   }
292
293   /**
294    * can be null
295    */
296   @Override
297   public String getVersion()
298   {
299     return version;
300   }
301
302   /**
303    * can be null
304    */
305   @Override
306   public String getAccessionId()
307   {
308     return accessionId;
309   }
310
311   @Override
312   public void setAccessionId(String accessionId)
313   {
314     this.accessionId = accessionId;
315     // this.accessionId = (accessionId == null ? "" :
316     // accessionId).toUpperCase(Locale.ROOT);
317   }
318
319   /**
320    * CAUTION! allows setting source null or not uppercase!
321    */
322   @Override
323   public void setSource(String source)
324   {
325     this.source = source;
326
327     // this.source = (source == null ? "" : source).toUpperCase(Locale.ROOT);
328     // this.canonicalSourceName = DBRefUtils.getCanonicalName(this.source);
329     // this.sourceKey = DBRefSource.getSourceKey(this.canonicalSourceName);
330   }
331
332   @Override
333   public void setVersion(String version)
334   {
335     this.version = version;
336     this.ucversion = (version == null ? null
337             : version.toUpperCase(Locale.ROOT));
338   }
339
340   @Override
341   public Mapping getMap()
342   {
343     return map;
344   }
345
346   /**
347    * @param map
348    *          the map to set
349    */
350   public void setMap(Mapping map)
351   {
352     this.map = map;
353   }
354
355   public boolean hasMap()
356   {
357     return map != null;
358   }
359
360   /**
361    * 
362    * @return source+":"+accessionId
363    */
364   public String getSrcAccString()
365   {
366     return ((source != null) ? source : "") + ":"
367             + ((accessionId != null) ? accessionId : "");
368   }
369
370   @Override
371   public String toString()
372   {
373     return getSrcAccString();
374   }
375
376   @Override
377   public boolean isPrimaryCandidate()
378   {
379     /*
380      * if a map is present, unless it is 1:1 and has no SequenceI mate, it cannot be a primary reference.  
381      */
382     if (map != null)
383     {
384       SequenceI mto = map.getTo();
385       if (mto != null)
386       {
387         return false;
388       }
389       MapList ml = map.getMap();
390       if (ml.getFromRatio() != ml.getToRatio() || ml.getFromRatio() != 1)
391       {
392         return false;
393       }
394       // check map is between identical single contiguous ranges
395       List<int[]> fromRanges, toRanges;
396       if ((fromRanges = ml.getFromRanges()).size() != 1
397               || (toRanges = ml.getToRanges()).size() != 1)
398       {
399         return false;
400       }
401       if (fromRanges.get(0)[0] != toRanges.get(0)[0]
402               || fromRanges.get(0)[1] != toRanges.get(0)[1])
403       {
404         return false;
405       }
406     }
407     if (version == null)
408     {
409       // no version string implies the reference has not been verified at all.
410       return false;
411     }
412
413     return DBRefSource.isPrimaryCandidate(ucversion);
414   }
415
416   /**
417    * stores the upper-case canonical name of the source for use in
418    * Sequence.getPrimaryDBRefs().
419    * 
420    * @author Bob Hanson
421    * 
422    * @return
423    */
424   public String getCanonicalSourceName()
425   {
426     return (canonicalSourceName == null
427             ? (canonicalSourceName = DBRefUtils
428                     .getCanonicalName(this.source))
429             : canonicalSourceName);
430   }
431
432   /**
433    * 
434    * @param canonical
435    */
436   public void setCanonical(boolean canonical)
437   {
438     isCanonicalAccession = canonical;
439   }
440
441   /**
442    * 
443    * @return true if this is the primary canonical accession for the database
444    *         source
445    */
446   public boolean isCanonical()
447   {
448     // TODO Auto-generated method stub
449     return isCanonicalAccession;
450   }
451 }