JAL-1705 DbSourceProxy properties converted to methods, tidy/format code
[jalview.git] / src / jalview / util / DBRefUtils.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.util;
22
23 import jalview.datamodel.DBRefEntry;
24 import jalview.datamodel.DBRefSource;
25 import jalview.datamodel.PDBEntry;
26 import jalview.datamodel.SequenceI;
27 import jalview.ws.seqfetcher.DbSourceProxy;
28
29 import java.util.ArrayList;
30 import java.util.HashMap;
31 import java.util.Hashtable;
32 import java.util.List;
33 import java.util.Map;
34
35 import com.stevesoft.pat.Regex;
36
37 public class DBRefUtils
38 {
39   private static Map<String, String> canonicalSourceNameLookup = new HashMap<String, String>();
40
41   private static Map<String, String> dasCoordinateSystemsLookup = new HashMap<String, String>();
42
43   static
44   {
45     // TODO load these from a resource file?
46     canonicalSourceNameLookup.put("uniprotkb/swiss-prot",
47             DBRefSource.UNIPROT);
48     canonicalSourceNameLookup.put("uniprotkb/trembl", DBRefSource.UNIPROT);
49     canonicalSourceNameLookup.put("pdb", DBRefSource.PDB);
50
51     dasCoordinateSystemsLookup.put("pdbresnum", DBRefSource.PDB);
52     dasCoordinateSystemsLookup.put("uniprot", DBRefSource.UNIPROT);
53     dasCoordinateSystemsLookup.put("embl", DBRefSource.EMBL);
54     // dasCoordinateSystemsLookup.put("embl", DBRefSource.EMBLCDS);
55   }
56
57   /**
58    * Utilities for handling DBRef objects and their collections.
59    */
60   /**
61    * 
62    * @param dbrefs
63    *          Vector of DBRef objects to search
64    * @param sources
65    *          String[] array of source DBRef IDs to retrieve
66    * @return Vector
67    */
68   public static DBRefEntry[] selectRefs(DBRefEntry[] dbrefs,
69           String[] sources)
70   {
71     if (dbrefs == null)
72     {
73       return null;
74     }
75     if (sources == null)
76     {
77       return dbrefs;
78     }
79     Map<String, Integer> srcs = new HashMap<String, Integer>();
80     ArrayList<DBRefEntry> res = new ArrayList<DBRefEntry>();
81
82     for (int i = 0; i < sources.length; i++)
83     {
84       srcs.put(new String(sources[i]), new Integer(i));
85     }
86     for (int i = 0, j = dbrefs.length; i < j; i++)
87     {
88       if (srcs.containsKey(dbrefs[i].getSource()))
89       {
90         res.add(dbrefs[i]);
91       }
92     }
93
94     if (res.size() > 0)
95     {
96       DBRefEntry[] reply = new DBRefEntry[res.size()];
97       return res.toArray(reply);
98     }
99     res = null;
100     // there are probable memory leaks in the hashtable!
101     return null;
102   }
103
104   /**
105    * isDasCoordinateSystem
106    * 
107    * @param string
108    *          String
109    * @param dBRefEntry
110    *          DBRefEntry
111    * @return boolean true if Source DBRefEntry is compatible with DAS
112    *         CoordinateSystem name
113    */
114
115   public static boolean isDasCoordinateSystem(String string,
116           DBRefEntry dBRefEntry)
117   {
118     if (string == null || dBRefEntry == null)
119     {
120       return false;
121     }
122     String coordsys = dasCoordinateSystemsLookup.get(string.toLowerCase());
123     return coordsys == null ? false : coordsys.equals(dBRefEntry
124             .getSource());
125   }
126
127   /**
128    * look up source in an internal list of database reference sources and return
129    * the canonical jalview name for the source, or the original string if it has
130    * no canonical form.
131    * 
132    * @param source
133    * @return canonical jalview source (one of jalview.datamodel.DBRefSource.*)
134    *         or original source
135    */
136   public static String getCanonicalName(String source)
137   {
138     if (source == null)
139     {
140       return null;
141     }
142     String canonical = canonicalSourceNameLookup.get(source.toLowerCase());
143     return canonical == null ? source : canonical;
144   }
145
146   /**
147    * Returns an array of those references that match the given entry, or null if
148    * no matches. Currently uses a comparator which matches if
149    * <ul>
150    * <li>database sources are the same</li>
151    * <li>accession ids are the same</li>
152    * <li>both have no mapping, or the mappings are the same</li>
153    * </ul>
154    * 
155    * @param ref
156    *          Set of references to search
157    * @param entry
158    *          pattern to match
159    * @return
160    */
161   public static DBRefEntry[] searchRefs(DBRefEntry[] ref, DBRefEntry entry)
162   {
163     return searchRefs(ref, entry,
164             matchDbAndIdAndEitherMapOrEquivalentMapList);
165   }
166
167   /**
168    * Returns an array of those references that match the given entry, according
169    * to the given comparator. Returns null if no matches.
170    * 
171    * @param refs
172    *          an array of database references to search
173    * @param entry
174    *          an entry to compare against
175    * @param comparator
176    * @return
177    */
178   static DBRefEntry[] searchRefs(DBRefEntry[] refs, DBRefEntry entry,
179           DbRefComp comparator)
180   {
181     if (refs == null || entry == null)
182     {
183       return null;
184     }
185     List<DBRefEntry> rfs = new ArrayList<DBRefEntry>();
186     for (int i = 0; i < refs.length; i++)
187     {
188       if (comparator.matches(entry, refs[i]))
189       {
190         rfs.add(refs[i]);
191       }
192     }
193     return rfs.size() == 0 ? null : rfs.toArray(new DBRefEntry[rfs.size()]);
194   }
195
196   interface DbRefComp
197   {
198     public boolean matches(DBRefEntry refa, DBRefEntry refb);
199   }
200
201   /**
202    * match on all non-null fields in refa
203    */
204   public static DbRefComp matchNonNullonA = new DbRefComp()
205   {
206     public boolean matches(DBRefEntry refa, DBRefEntry refb)
207     {
208       if (refa.getSource() == null
209               || refb.getSource().equals(refa.getSource()))
210       {
211         if (refa.getVersion() == null
212                 || refb.getVersion().equals(refa.getVersion()))
213         {
214           if (refa.getAccessionId() == null
215                   || refb.getAccessionId().equals(refa.getAccessionId()))
216           {
217             if (refa.getMap() == null
218                     || (refb.getMap() != null && refb.getMap().equals(
219                             refa.getMap())))
220             {
221               return true;
222             }
223           }
224         }
225       }
226       return false;
227     }
228   };
229
230   /**
231    * either field is null or field matches for all of source, version, accession
232    * id and map.
233    */
234   public static DbRefComp matchEitherNonNull = new DbRefComp()
235   {
236     public boolean matches(DBRefEntry refa, DBRefEntry refb)
237     {
238       if ((refa.getSource() == null || refb.getSource() == null)
239               || refb.getSource().equals(refa.getSource()))
240       {
241         if ((refa.getVersion() == null || refb.getVersion() == null)
242                 || refb.getVersion().equals(refa.getVersion()))
243         {
244           if ((refa.getAccessionId() == null || refb.getAccessionId() == null)
245                   || refb.getAccessionId().equals(refa.getAccessionId()))
246           {
247             if ((refa.getMap() == null || refb.getMap() == null)
248                     || (refb.getMap() != null && refb.getMap().equals(
249                             refa.getMap())))
250             {
251               return true;
252             }
253           }
254         }
255       }
256       return false;
257     }
258   };
259
260   /**
261    * accession ID and DB must be identical. Version is ignored. Map is either
262    * not defined or is a match (or is compatible?)
263    */
264   public static DbRefComp matchDbAndIdAndEitherMap = new DbRefComp()
265   {
266     public boolean matches(DBRefEntry refa, DBRefEntry refb)
267     {
268       if (refa.getSource() != null && refb.getSource() != null
269               && refb.getSource().equals(refa.getSource()))
270       {
271         // We dont care about version
272         // if ((refa.getVersion()==null || refb.getVersion()==null)
273         // || refb.getVersion().equals(refa.getVersion()))
274         // {
275         if (refa.getAccessionId() != null && refb.getAccessionId() != null
276                 || refb.getAccessionId().equals(refa.getAccessionId()))
277         {
278           if ((refa.getMap() == null || refb.getMap() == null)
279                   || (refa.getMap() != null && refb.getMap() != null && refb
280                           .getMap().equals(refa.getMap())))
281           {
282             return true;
283           }
284         }
285       }
286       return false;
287     }
288   };
289
290   /**
291    * accession ID and DB must be identical. Version is ignored. No map on either
292    * or map but no maplist on either or maplist of map on a is the complement of
293    * maplist of map on b.
294    */
295   public static DbRefComp matchDbAndIdAndComplementaryMapList = new DbRefComp()
296   {
297     public boolean matches(DBRefEntry refa, DBRefEntry refb)
298     {
299       if (refa.getSource() != null && refb.getSource() != null
300               && refb.getSource().equals(refa.getSource()))
301       {
302         // We dont care about version
303         // if ((refa.getVersion()==null || refb.getVersion()==null)
304         // || refb.getVersion().equals(refa.getVersion()))
305         // {
306         if (refa.getAccessionId() != null && refb.getAccessionId() != null
307                 || refb.getAccessionId().equals(refa.getAccessionId()))
308         {
309           if ((refa.getMap() == null && refb.getMap() == null)
310                   || (refa.getMap() != null && refb.getMap() != null))
311           {
312             if ((refb.getMap().getMap() == null && refa.getMap().getMap() == null)
313                     || (refb.getMap().getMap() != null
314                             && refa.getMap().getMap() != null && refb
315                             .getMap().getMap().getInverse()
316                             .equals(refa.getMap().getMap())))
317             {
318               return true;
319             }
320           }
321         }
322       }
323       return false;
324     }
325   };
326
327   /**
328    * accession ID and DB must be identical. Version is ignored. No map on both
329    * or or map but no maplist on either or maplist of map on a is equivalent to
330    * the maplist of map on b.
331    */
332   public static DbRefComp matchDbAndIdAndEquivalentMapList = new DbRefComp()
333   {
334     public boolean matches(DBRefEntry refa, DBRefEntry refb)
335     {
336       if (refa.getSource() != null && refb.getSource() != null
337               && refb.getSource().equals(refa.getSource()))
338       {
339         // We dont care about version
340         // if ((refa.getVersion()==null || refb.getVersion()==null)
341         // || refb.getVersion().equals(refa.getVersion()))
342         // {
343         if (refa.getAccessionId() != null && refb.getAccessionId() != null
344                 || refb.getAccessionId().equals(refa.getAccessionId()))
345         {
346           if (refa.getMap() == null && refb.getMap() == null)
347           {
348             return true;
349           }
350           if (refa.getMap() != null
351                   && refb.getMap() != null
352                   && ((refb.getMap().getMap() == null && refa.getMap()
353                           .getMap() == null) || (refb.getMap().getMap() != null
354                           && refa.getMap().getMap() != null && refb
355                           .getMap().getMap().equals(refa.getMap().getMap()))))
356           {
357             return true;
358           }
359         }
360       }
361       return false;
362     }
363   };
364
365   /**
366    * accession ID and DB must be identical. Version is ignored. No map on either
367    * or map but no maplist on either or maplist of map on a is equivalent to the
368    * maplist of map on b.
369    */
370   public static DbRefComp matchDbAndIdAndEitherMapOrEquivalentMapList = new DbRefComp()
371   {
372     public boolean matches(DBRefEntry refa, DBRefEntry refb)
373     {
374       // System.err.println("Comparing A: "+refa.getSrcAccString()+(refa.hasMap()?" has map.":"."));
375       // System.err.println("Comparing B: "+refb.getSrcAccString()+(refb.hasMap()?" has map.":"."));
376       if (refa.getSource() != null && refb.getSource() != null
377               && refb.getSource().equals(refa.getSource()))
378       {
379         // We dont care about version
380         // if ((refa.getVersion()==null || refb.getVersion()==null)
381         // || refb.getVersion().equals(refa.getVersion()))
382         // {
383         if (refa.getAccessionId() != null && refb.getAccessionId() != null
384                 && refb.getAccessionId().equals(refa.getAccessionId()))
385         {
386           if (refa.getMap() == null || refb.getMap() == null)
387           {
388             return true;
389           }
390           if ((refa.getMap() != null && refb.getMap() != null)
391                   && (refb.getMap().getMap() == null && refa.getMap()
392                           .getMap() == null)
393                   || (refb.getMap().getMap() != null
394                           && refa.getMap().getMap() != null && (refb
395                           .getMap().getMap().equals(refa.getMap().getMap()))))
396           { // getMap().getMap().containsEither(false,refa.getMap().getMap())
397             return true;
398           }
399         }
400       }
401       return false;
402     }
403   };
404
405   /**
406    * Parses a DBRefEntry and adds it to the sequence, also a PDBEntry if the
407    * database is PDB.
408    * <p>
409    * Used by file parsers to generate DBRefs from annotation within file (eg
410    * Stockholm)
411    * 
412    * @param dbname
413    * @param version
414    * @param acn
415    * @param seq
416    *          where to annotate with reference
417    * @return parsed version of entry that was added to seq (if any)
418    */
419   public static DBRefEntry parseToDbRef(SequenceI seq, String dbname,
420           String version, String acn)
421   {
422     DBRefEntry ref = null;
423     if (dbname != null)
424     {
425       String locsrc = DBRefUtils.getCanonicalName(dbname);
426       if (locsrc.equals(DBRefSource.PDB))
427       {
428         /*
429          * Check for PFAM style stockhom PDB accession id citation e.g.
430          * "1WRI A; 7-80;"
431          */
432         Regex r = new com.stevesoft.pat.Regex(
433                 "([0-9][0-9A-Za-z]{3})\\s*(.?)\\s*;\\s*([0-9]+)-([0-9]+)");
434         if (r.search(acn.trim()))
435         {
436           String pdbid = r.stringMatched(1);
437           String chaincode = r.stringMatched(2);
438           if (chaincode == null)
439           {
440             chaincode = " ";
441           }
442           // String mapstart = r.stringMatched(3);
443           // String mapend = r.stringMatched(4);
444           if (chaincode.equals(" "))
445           {
446             chaincode = "_";
447           }
448           // construct pdb ref.
449           ref = new DBRefEntry(locsrc, version, pdbid + chaincode);
450           PDBEntry pdbr = new PDBEntry();
451           pdbr.setId(pdbid);
452           pdbr.setType(PDBEntry.Type.PDB);
453           pdbr.setProperty(new Hashtable());
454           pdbr.setChainCode(chaincode);
455           // pdbr.getProperty().put("CHAIN", chaincode);
456           seq.addPDBId(pdbr);
457         }
458         else
459         {
460           System.err.println("Malformed PDB DR line:" + acn);
461         }
462       }
463       else
464       {
465         // default:
466         ref = new DBRefEntry(locsrc, version, acn);
467       }
468     }
469     if (ref != null)
470     {
471       seq.addDBRef(ref);
472     }
473     return ref;
474   }
475
476 }