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