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