ed6d860b3738b751f244706e0085ede5e2c9606d
[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
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.Hashtable;
32 import java.util.List;
33 import java.util.Map;
34
35 import com.stevesoft.pat.Regex;
36
37 /**
38  * Utilities for handling DBRef objects and their collections.
39  */
40 public class DBRefUtils
41 {
42   /*
43    * lookup from lower-case form of a name to its canonical (standardised) form
44    */
45   private static Map<String, String> canonicalSourceNameLookup = new HashMap<String, String>();
46
47   private static Map<String, String> dasCoordinateSystemsLookup = new HashMap<String, String>();
48
49   static
50   {
51     // TODO load these from a resource file?
52     canonicalSourceNameLookup.put("uniprotkb/swiss-prot",
53             DBRefSource.UNIPROT);
54     canonicalSourceNameLookup.put("uniprotkb/trembl", DBRefSource.UNIPROT);
55
56     // Ensembl values for dbname in xref REST service:
57     canonicalSourceNameLookup.put("uniprot/sptrembl", DBRefSource.UNIPROT);
58     canonicalSourceNameLookup.put("uniprot/swissprot", DBRefSource.UNIPROT);
59
60     canonicalSourceNameLookup.put("pdb", DBRefSource.PDB);
61     canonicalSourceNameLookup.put("ensembl", DBRefSource.ENSEMBL);
62
63     dasCoordinateSystemsLookup.put("pdbresnum", DBRefSource.PDB);
64     dasCoordinateSystemsLookup.put("uniprot", DBRefSource.UNIPROT);
65     dasCoordinateSystemsLookup.put("embl", DBRefSource.EMBL);
66     // dasCoordinateSystemsLookup.put("embl", DBRefSource.EMBLCDS);
67   }
68
69   /**
70    * Returns those DBRefEntry objects whose source identifier (once converted to
71    * Jalview's canonical form) is in the list of sources to search for. Returns
72    * null if no matches found.
73    * 
74    * @param dbrefs
75    *          DBRefEntry objects to search
76    * @param sources
77    *          array of sources to select
78    * @return
79    */
80   public static DBRefEntry[] selectRefs(DBRefEntry[] dbrefs,
81           String[] sources)
82   {
83     if (dbrefs == null || sources == null)
84     {
85       return dbrefs;
86     }
87     HashSet<String> srcs = new HashSet<String>();
88     for (String src : sources)
89     {
90       srcs.add(src);
91     }
92
93     List<DBRefEntry> res = new ArrayList<DBRefEntry>();
94     for (DBRefEntry dbr : dbrefs)
95     {
96       String source = getCanonicalName(dbr.getSource());
97       if (srcs.contains(source))
98       {
99         res.add(dbr);
100       }
101     }
102
103     if (res.size() > 0)
104     {
105       DBRefEntry[] reply = new DBRefEntry[res.size()];
106       return res.toArray(reply);
107     }
108     return null;
109   }
110
111   /**
112    * isDasCoordinateSystem
113    * 
114    * @param string
115    *          String
116    * @param dBRefEntry
117    *          DBRefEntry
118    * @return boolean true if Source DBRefEntry is compatible with DAS
119    *         CoordinateSystem name
120    */
121
122   public static boolean isDasCoordinateSystem(String string,
123           DBRefEntry dBRefEntry)
124   {
125     if (string == null || dBRefEntry == null)
126     {
127       return false;
128     }
129     String coordsys = dasCoordinateSystemsLookup.get(string.toLowerCase());
130     return coordsys == null ? false : coordsys.equals(dBRefEntry
131             .getSource());
132   }
133
134   /**
135    * look up source in an internal list of database reference sources and return
136    * the canonical jalview name for the source, or the original string if it has
137    * no canonical form.
138    * 
139    * @param source
140    * @return canonical jalview source (one of jalview.datamodel.DBRefSource.*)
141    *         or original source
142    */
143   public static String getCanonicalName(String source)
144   {
145     if (source == null)
146     {
147       return null;
148     }
149     String canonical = canonicalSourceNameLookup.get(source.toLowerCase());
150     return canonical == null ? source : canonical;
151   }
152
153   /**
154    * Returns a (possibly empty) list of those references that match the given
155    * entry. Currently uses a comparator which matches if
156    * <ul>
157    * <li>database sources are the same</li>
158    * <li>accession ids are the same</li>
159    * <li>both have no mapping, or the mappings are the same</li>
160    * </ul>
161    * 
162    * @param ref
163    *          Set of references to search
164    * @param entry
165    *          pattern to match
166    * @return
167    */
168   public static List<DBRefEntry> searchRefs(DBRefEntry[] ref,
169           DBRefEntry entry)
170   {
171     return searchRefs(ref, entry,
172             matchDbAndIdAndEitherMapOrEquivalentMapList);
173   }
174
175   /**
176    * Returns a list of those references that match the given accession id
177    * <ul>
178    * <li>database sources are the same</li>
179    * <li>accession ids are the same</li>
180    * <li>both have no mapping, or the mappings are the same</li>
181    * </ul>
182    * 
183    * @param refs
184    *          Set of references to search
185    * @param accId
186    *          accession id to match
187    * @return
188    */
189   public static List<DBRefEntry> searchRefs(DBRefEntry[] refs, String accId)
190   {
191     return searchRefs(refs, new DBRefEntry("", "", accId), matchId);
192   }
193
194   /**
195    * Returns a (possibly empty) list of those references that match the given
196    * entry, according to the given comparator.
197    * 
198    * @param refs
199    *          an array of database references to search
200    * @param entry
201    *          an entry to compare against
202    * @param comparator
203    * @return
204    */
205   static List<DBRefEntry> searchRefs(DBRefEntry[] refs, DBRefEntry entry,
206           DbRefComp comparator)
207   {
208     List<DBRefEntry> rfs = new ArrayList<DBRefEntry>();
209     if (refs == null || entry == null)
210     {
211       return rfs;
212     }
213     for (int i = 0; i < refs.length; i++)
214     {
215       if (comparator.matches(entry, refs[i]))
216       {
217         rfs.add(refs[i]);
218       }
219     }
220     return rfs;
221   }
222
223   interface DbRefComp
224   {
225     public boolean matches(DBRefEntry refa, DBRefEntry refb);
226   }
227
228   /**
229    * match on all non-null fields in refa
230    */
231   // TODO unused - remove?
232   public static DbRefComp matchNonNullonA = new DbRefComp()
233   {
234     @Override
235     public boolean matches(DBRefEntry refa, DBRefEntry refb)
236     {
237       if (refa.getSource() == null
238               || refb.getSource().equals(refa.getSource()))
239       {
240         if (refa.getVersion() == null
241                 || refb.getVersion().equals(refa.getVersion()))
242         {
243           if (refa.getAccessionId() == null
244                   || refb.getAccessionId().equals(refa.getAccessionId()))
245           {
246             if (refa.getMap() == null
247                     || (refb.getMap() != null && refb.getMap().equals(
248                             refa.getMap())))
249             {
250               return true;
251             }
252           }
253         }
254       }
255       return false;
256     }
257   };
258
259   /**
260    * either field is null or field matches for all of source, version, accession
261    * id and map.
262    */
263   // TODO unused - remove?
264   public static DbRefComp matchEitherNonNull = new DbRefComp()
265   {
266     @Override
267     public boolean matches(DBRefEntry refa, DBRefEntry refb)
268     {
269       if (nullOrEqual(refa.getSource(), refb.getSource())
270               && nullOrEqual(refa.getVersion(), refb.getVersion())
271               && nullOrEqual(refa.getAccessionId(), refb.getAccessionId())
272               && nullOrEqual(refa.getMap(), refb.getMap()))
273       {
274         return true;
275       }
276       return false;
277     }
278   };
279
280   /**
281    * accession ID and DB must be identical. Version is ignored. Map is either
282    * not defined or is a match (or is compatible?)
283    */
284   // TODO unused - remove?
285   public static DbRefComp matchDbAndIdAndEitherMap = new DbRefComp()
286   {
287     @Override
288     public boolean matches(DBRefEntry refa, DBRefEntry refb)
289     {
290       if (refa.getSource() != null && refb.getSource() != null
291               && refb.getSource().equals(refa.getSource()))
292       {
293         // We dont care about version
294         if (refa.getAccessionId() != null && refb.getAccessionId() != null
295         // FIXME should be && not || here?
296                 || refb.getAccessionId().equals(refa.getAccessionId()))
297         {
298           if ((refa.getMap() == null || refb.getMap() == null)
299                   || (refa.getMap() != null && refb.getMap() != null && refb
300                           .getMap().equals(refa.getMap())))
301           {
302             return true;
303           }
304         }
305       }
306       return false;
307     }
308   };
309
310   /**
311    * accession ID and DB must be identical. Version is ignored. No map on either
312    * or map but no maplist on either or maplist of map on a is the complement of
313    * maplist of map on b.
314    */
315   // TODO unused - remove?
316   public static DbRefComp matchDbAndIdAndComplementaryMapList = new DbRefComp()
317   {
318     @Override
319     public boolean matches(DBRefEntry refa, DBRefEntry refb)
320     {
321       if (refa.getSource() != null && refb.getSource() != null
322               && refb.getSource().equals(refa.getSource()))
323       {
324         // We dont care about version
325         if (refa.getAccessionId() != null && refb.getAccessionId() != null
326                 || refb.getAccessionId().equals(refa.getAccessionId()))
327         {
328           if ((refa.getMap() == null && refb.getMap() == null)
329                   || (refa.getMap() != null && refb.getMap() != null))
330           {
331             if ((refb.getMap().getMap() == null && refa.getMap().getMap() == null)
332                     || (refb.getMap().getMap() != null
333                             && refa.getMap().getMap() != null && refb
334                             .getMap().getMap().getInverse()
335                             .equals(refa.getMap().getMap())))
336             {
337               return true;
338             }
339           }
340         }
341       }
342       return false;
343     }
344   };
345
346   /**
347    * accession ID and DB must be identical. Version is ignored. No map on both
348    * or or map but no maplist on either or maplist of map on a is equivalent to
349    * the maplist of map on b.
350    */
351   // TODO unused - remove?
352   public static DbRefComp matchDbAndIdAndEquivalentMapList = new DbRefComp()
353   {
354     @Override
355     public boolean matches(DBRefEntry refa, DBRefEntry refb)
356     {
357       if (refa.getSource() != null && refb.getSource() != null
358               && refb.getSource().equals(refa.getSource()))
359       {
360         // We dont care about version
361         // if ((refa.getVersion()==null || refb.getVersion()==null)
362         // || refb.getVersion().equals(refa.getVersion()))
363         // {
364         if (refa.getAccessionId() != null && refb.getAccessionId() != null
365                 || refb.getAccessionId().equals(refa.getAccessionId()))
366         {
367           if (refa.getMap() == null && refb.getMap() == null)
368           {
369             return true;
370           }
371           if (refa.getMap() != null
372                   && refb.getMap() != null
373                   && ((refb.getMap().getMap() == null && refa.getMap()
374                           .getMap() == null) || (refb.getMap().getMap() != null
375                           && refa.getMap().getMap() != null && refb
376                           .getMap().getMap().equals(refa.getMap().getMap()))))
377           {
378             return true;
379           }
380         }
381       }
382       return false;
383     }
384   };
385
386   /**
387    * accession ID and DB must be identical, or null on a. Version is ignored. No
388    * map on either or map but no maplist on either or maplist of map on a is
389    * equivalent to the maplist of map on b.
390    */
391   public static DbRefComp matchDbAndIdAndEitherMapOrEquivalentMapList = new DbRefComp()
392   {
393     @Override
394     public boolean matches(DBRefEntry refa, DBRefEntry refb)
395     {
396       if (refa.getSource() != null && refb.getSource() != null
397               && refb.getSource().equals(refa.getSource()))
398       {
399         // We dont care about version
400
401         if (refa.getAccessionId() == null
402                 || refa.getAccessionId().equals(refb.getAccessionId()))
403         {
404           if (refa.getMap() == null || refb.getMap() == null)
405           {
406             return true;
407           }
408           if ((refa.getMap() != null && refb.getMap() != null)
409                   && (refb.getMap().getMap() == null && refa.getMap()
410                           .getMap() == null)
411                   || (refb.getMap().getMap() != null
412                           && refa.getMap().getMap() != null && (refb
413                           .getMap().getMap().equals(refa.getMap().getMap()))))
414           {
415             return true;
416           }
417         }
418       }
419       return false;
420     }
421   };
422
423   /**
424    * accession ID only must be identical.
425    */
426   public static DbRefComp matchId = new DbRefComp()
427   {
428     @Override
429     public boolean matches(DBRefEntry refa, DBRefEntry refb)
430     {
431       if (refa.getAccessionId() != null && refb.getAccessionId() != null
432               && refb.getAccessionId().equals(refa.getAccessionId()))
433       {
434         return true;
435       }
436       return false;
437     }
438   };
439
440   /**
441    * Parses a DBRefEntry and adds it to the sequence, also a PDBEntry if the
442    * database is PDB.
443    * <p>
444    * Used by file parsers to generate DBRefs from annotation within file (eg
445    * Stockholm)
446    * 
447    * @param dbname
448    * @param version
449    * @param acn
450    * @param seq
451    *          where to annotate with reference
452    * @return parsed version of entry that was added to seq (if any)
453    */
454   public static DBRefEntry parseToDbRef(SequenceI seq, String dbname,
455           String version, String acn)
456   {
457     DBRefEntry ref = null;
458     if (dbname != null)
459     {
460       String locsrc = DBRefUtils.getCanonicalName(dbname);
461       if (locsrc.equals(DBRefSource.PDB))
462       {
463         /*
464          * Check for PFAM style stockhom PDB accession id citation e.g.
465          * "1WRI A; 7-80;"
466          */
467         Regex r = new com.stevesoft.pat.Regex(
468                 "([0-9][0-9A-Za-z]{3})\\s*(.?)\\s*;\\s*([0-9]+)-([0-9]+)");
469         if (r.search(acn.trim()))
470         {
471           String pdbid = r.stringMatched(1);
472           String chaincode = r.stringMatched(2);
473           if (chaincode == null)
474           {
475             chaincode = " ";
476           }
477           // String mapstart = r.stringMatched(3);
478           // String mapend = r.stringMatched(4);
479           if (chaincode.equals(" "))
480           {
481             chaincode = "_";
482           }
483           // construct pdb ref.
484           ref = new DBRefEntry(locsrc, version, pdbid + chaincode);
485           PDBEntry pdbr = new PDBEntry();
486           pdbr.setId(pdbid);
487           pdbr.setType(PDBEntry.Type.PDB);
488           pdbr.setProperty(new Hashtable());
489           pdbr.setChainCode(chaincode);
490           // pdbr.getProperty().put("CHAIN", chaincode);
491           seq.addPDBId(pdbr);
492         }
493         else
494         {
495           System.err.println("Malformed PDB DR line:" + acn);
496         }
497       }
498       else
499       {
500         // default:
501         ref = new DBRefEntry(locsrc, version, acn);
502       }
503     }
504     if (ref != null)
505     {
506       seq.addDBRef(ref);
507     }
508     return ref;
509   }
510
511   /**
512    * Returns true if either object is null, or they are equal
513    * 
514    * @param o1
515    * @param o2
516    * @return
517    */
518   public static boolean nullOrEqual(Object o1, Object o2)
519   {
520     if (o1 == null || o2 == null)
521     {
522       return true;
523     }
524     return (o1 == null ? o2.equals(o1) : o1.equals(o2));
525   }
526
527   /**
528    * Selects just the DNA or protein references from a set of references
529    * 
530    * @param selectDna
531    *          if true, select references to 'standard' DNA databases, else to
532    *          'standard' peptide databases
533    * @param refs
534    *          a set of references to select from
535    * @return
536    */
537   public static DBRefEntry[] selectDbRefs(boolean selectDna,
538           DBRefEntry[] refs)
539   {
540     return selectRefs(refs, selectDna ? DBRefSource.DNACODINGDBS
541             : DBRefSource.PROTEINDBS);
542     // could attempt to find other cross
543     // refs here - ie PDB xrefs
544     // (not dna, not protein seq)
545   }
546
547   /**
548    * Returns the (possibly empty) list of those supplied dbrefs which have the
549    * specified source databse
550    * 
551    * @param dbRefs
552    * @param source
553    * @return
554    */
555   public static List<DBRefEntry> searchRefsForSource(DBRefEntry[] dbRefs,
556           String source)
557   {
558     List<DBRefEntry> matches = new ArrayList<DBRefEntry>();
559     if (dbRefs != null && source != null)
560     {
561       for (DBRefEntry dbref : dbRefs)
562       {
563         if (source.equals(dbref.getSource()))
564         {
565           matches.add(dbref);
566         }
567       }
568     }
569     return matches;
570   }
571
572 }