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