JAL-1705 refactored code for dbsource reggae validator and added further refactoring...
[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
123             .toLowerCase());
124     return coordsys == null ? false : coordsys.equals(dBRefEntry
125             .getSource());
126   }
127
128   /**
129    * look up source in an internal list of database reference sources and return
130    * the canonical jalview name for the source, or the original string if it has
131    * no canonical form.
132    * 
133    * @param source
134    * @return canonical jalview source (one of jalview.datamodel.DBRefSource.*)
135    *         or original source
136    */
137   public static String getCanonicalName(String source)
138   {
139     if (source == null)
140     {
141       return null;
142     }
143     String canonical = canonicalSourceNameLookup.get(source
144             .toLowerCase());
145     return canonical == null ? source : canonical;
146   }
147
148   /**
149    * Returns an array of those references that match the given entry, or null if
150    * no matches. Currently uses a comparator which matches if
151    * <ul>
152    * <li>database sources are the same</li>
153    * <li>accession ids are the same</li>
154    * <li>both have no mapping, or the mappings are the same</li>
155    * </ul>
156    * 
157    * @param ref
158    *          Set of references to search
159    * @param entry
160    *          pattern to match
161    * @return
162    */
163   public static DBRefEntry[] searchRefs(DBRefEntry[] ref, DBRefEntry entry)
164   {
165     return searchRefs(ref, entry,
166             matchDbAndIdAndEitherMapOrEquivalentMapList);
167   }
168
169   /**
170    * Returns an array of those references that match the given entry, according
171    * to the given comparator. Returns null if no matches.
172    * 
173    * @param refs
174    *          an array of database references to search
175    * @param entry
176    *          an entry to compare against
177    * @param comparator
178    * @return
179    */
180   static DBRefEntry[] searchRefs(DBRefEntry[] refs, DBRefEntry entry,
181           DbRefComp comparator)
182   {
183     if (refs == null || entry == null)
184     {
185       return null;
186     }
187     List<DBRefEntry> rfs = new ArrayList<DBRefEntry>();
188     for (int i = 0; i < refs.length; i++)
189     {
190       if (comparator.matches(entry, refs[i]))
191       {
192         rfs.add(refs[i]);
193       }
194     }
195     return rfs.size() == 0 ? null : rfs.toArray(new DBRefEntry[rfs.size()]);
196   }
197
198   interface DbRefComp
199   {
200     public boolean matches(DBRefEntry refa, DBRefEntry refb);
201   }
202
203   /**
204    * match on all non-null fields in refa
205    */
206   public static DbRefComp matchNonNullonA = new DbRefComp()
207   {
208     public boolean matches(DBRefEntry refa, DBRefEntry refb)
209     {
210       if (refa.getSource() == null
211               || refb.getSource().equals(refa.getSource()))
212       {
213         if (refa.getVersion() == null
214                 || refb.getVersion().equals(refa.getVersion()))
215         {
216           if (refa.getAccessionId() == null
217                   || refb.getAccessionId().equals(refa.getAccessionId()))
218           {
219             if (refa.getMap() == null
220                     || (refb.getMap() != null && refb.getMap().equals(
221                             refa.getMap())))
222             {
223               return true;
224             }
225           }
226         }
227       }
228       return false;
229     }
230   };
231
232   /**
233    * either field is null or field matches for all of source, version, accession
234    * id and map.
235    */
236   public static DbRefComp matchEitherNonNull = new DbRefComp()
237   {
238     public boolean matches(DBRefEntry refa, DBRefEntry refb)
239     {
240       if ((refa.getSource() == null || refb.getSource() == null)
241               || refb.getSource().equals(refa.getSource()))
242       {
243         if ((refa.getVersion() == null || refb.getVersion() == null)
244                 || refb.getVersion().equals(refa.getVersion()))
245         {
246           if ((refa.getAccessionId() == null || refb.getAccessionId() == null)
247                   || refb.getAccessionId().equals(refa.getAccessionId()))
248           {
249             if ((refa.getMap() == null || refb.getMap() == null)
250                     || (refb.getMap() != null && refb.getMap().equals(
251                             refa.getMap())))
252             {
253               return true;
254             }
255           }
256         }
257       }
258       return false;
259     }
260   };
261
262   /**
263    * accession ID and DB must be identical. Version is ignored. Map is either
264    * not defined or is a match (or is compatible?)
265    */
266   public static DbRefComp matchDbAndIdAndEitherMap = new DbRefComp()
267   {
268     public boolean matches(DBRefEntry refa, DBRefEntry refb)
269     {
270       if (refa.getSource() != null && refb.getSource() != null
271               && refb.getSource().equals(refa.getSource()))
272       {
273         // We dont care about version
274         // if ((refa.getVersion()==null || refb.getVersion()==null)
275         // || refb.getVersion().equals(refa.getVersion()))
276         // {
277         if (refa.getAccessionId() != null && refb.getAccessionId() != null
278                 || refb.getAccessionId().equals(refa.getAccessionId()))
279         {
280           if ((refa.getMap() == null || refb.getMap() == null)
281                   || (refa.getMap() != null && refb.getMap() != null && refb
282                           .getMap().equals(refa.getMap())))
283           {
284             return true;
285           }
286         }
287       }
288       return false;
289     }
290   };
291
292   /**
293    * accession ID and DB must be identical. Version is ignored. No map on either
294    * or map but no maplist on either or maplist of map on a is the complement of
295    * maplist of map on b.
296    */
297   public static DbRefComp matchDbAndIdAndComplementaryMapList = new DbRefComp()
298   {
299     public boolean matches(DBRefEntry refa, DBRefEntry refb)
300     {
301       if (refa.getSource() != null && refb.getSource() != null
302               && refb.getSource().equals(refa.getSource()))
303       {
304         // We dont care about version
305         // if ((refa.getVersion()==null || refb.getVersion()==null)
306         // || refb.getVersion().equals(refa.getVersion()))
307         // {
308         if (refa.getAccessionId() != null && refb.getAccessionId() != null
309                 || refb.getAccessionId().equals(refa.getAccessionId()))
310         {
311           if ((refa.getMap() == null && refb.getMap() == null)
312                   || (refa.getMap() != null && refb.getMap() != null))
313           {
314             if ((refb.getMap().getMap() == null && refa.getMap().getMap() == null)
315                     || (refb.getMap().getMap() != null
316                             && refa.getMap().getMap() != null && refb
317                             .getMap().getMap().getInverse()
318                             .equals(refa.getMap().getMap())))
319             {
320               return true;
321             }
322           }
323         }
324       }
325       return false;
326     }
327   };
328
329   /**
330    * accession ID and DB must be identical. Version is ignored. No map on both
331    * or or map but no maplist on either or maplist of map on a is equivalent to
332    * the maplist of map on b.
333    */
334   public static DbRefComp matchDbAndIdAndEquivalentMapList = new DbRefComp()
335   {
336     public boolean matches(DBRefEntry refa, DBRefEntry refb)
337     {
338       if (refa.getSource() != null && refb.getSource() != null
339               && refb.getSource().equals(refa.getSource()))
340       {
341         // We dont care about version
342         // if ((refa.getVersion()==null || refb.getVersion()==null)
343         // || refb.getVersion().equals(refa.getVersion()))
344         // {
345         if (refa.getAccessionId() != null && refb.getAccessionId() != null
346                 || refb.getAccessionId().equals(refa.getAccessionId()))
347         {
348           if (refa.getMap() == null && refb.getMap() == null)
349           {
350             return true;
351           }
352           if (refa.getMap() != null
353                   && refb.getMap() != null
354                   && ((refb.getMap().getMap() == null && refa.getMap()
355                           .getMap() == null) || (refb.getMap().getMap() != null
356                           && refa.getMap().getMap() != null && refb
357                           .getMap().getMap().equals(refa.getMap().getMap()))))
358           {
359             return true;
360           }
361         }
362       }
363       return false;
364     }
365   };
366
367   /**
368    * accession ID and DB must be identical. Version is ignored. No map on either
369    * or map but no maplist on either or maplist of map on a is equivalent to the
370    * maplist of map on b.
371    */
372   public static DbRefComp matchDbAndIdAndEitherMapOrEquivalentMapList = new DbRefComp()
373   {
374     public boolean matches(DBRefEntry refa, DBRefEntry refb)
375     {
376       // System.err.println("Comparing A: "+refa.getSrcAccString()+(refa.hasMap()?" has map.":"."));
377       // System.err.println("Comparing B: "+refb.getSrcAccString()+(refb.hasMap()?" has map.":"."));
378       if (refa.getSource() != null && refb.getSource() != null
379               && refb.getSource().equals(refa.getSource()))
380       {
381         // We dont care about version
382         // if ((refa.getVersion()==null || refb.getVersion()==null)
383         // || refb.getVersion().equals(refa.getVersion()))
384         // {
385         if (refa.getAccessionId() != null && refb.getAccessionId() != null
386                 && refb.getAccessionId().equals(refa.getAccessionId()))
387         {
388           if (refa.getMap() == null || refb.getMap() == null)
389           {
390             return true;
391           }
392           if ((refa.getMap() != null && refb.getMap() != null)
393                   && (refb.getMap().getMap() == null && refa.getMap()
394                           .getMap() == null)
395                   || (refb.getMap().getMap() != null
396                           && refa.getMap().getMap() != null && (refb
397                           .getMap().getMap().equals(refa.getMap().getMap()))))
398           { // getMap().getMap().containsEither(false,refa.getMap().getMap())
399             return true;
400           }
401         }
402       }
403       return false;
404     }
405   };
406
407   /**
408    * Parses a DBRefEntry and adds it to the sequence, also a PDBEntry if the
409    * database is PDB.
410    * <p>
411    * Used by file parsers to generate DBRefs from annotation within file (eg
412    * Stockholm)
413    * 
414    * @param dbname
415    * @param version
416    * @param acn
417    * @param seq
418    *          where to annotate with reference
419    * @return parsed version of entry that was added to seq (if any)
420    */
421   public static DBRefEntry parseToDbRef(SequenceI seq, String dbname,
422           String version, String acn)
423   {
424     DBRefEntry ref = null;
425     if (dbname != null)
426     {
427       String locsrc = DBRefUtils.getCanonicalName(dbname);
428       if (locsrc.equals(DBRefSource.PDB))
429       {
430         /*
431          * Check for PFAM style stockhom PDB accession id citation e.g.
432          * "1WRI A; 7-80;"
433          */
434         Regex r = new com.stevesoft.pat.Regex(
435                 "([0-9][0-9A-Za-z]{3})\\s*(.?)\\s*;\\s*([0-9]+)-([0-9]+)");
436         if (r.search(acn.trim()))
437         {
438           String pdbid = r.stringMatched(1);
439           String chaincode = r.stringMatched(2);
440           if (chaincode==null)
441           {
442             chaincode = " ";
443           }
444           // String mapstart = r.stringMatched(3);
445           // String mapend = r.stringMatched(4);
446           if (chaincode.equals(" "))
447           {
448             chaincode = "_";
449           }
450           // construct pdb ref.
451           ref = new DBRefEntry(locsrc, version, pdbid + chaincode);
452           PDBEntry pdbr = new PDBEntry();
453           pdbr.setId(pdbid);
454           pdbr.setType(PDBEntry.Type.PDB);
455           pdbr.setProperty(new Hashtable());
456           pdbr.setChainCode(chaincode);
457           // pdbr.getProperty().put("CHAIN", chaincode);
458           seq.addPDBId(pdbr);
459         } else {
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   /**
477    * Extract valid accession strings from a query string. Used by the
478    * SequenceFetcher and DBRefFetcher to create valid accession strings from an
479    * ID string for database sources with a Regex validation field.
480    * 
481    * @param proxy
482    * @param q
483    * @return q if proxy.getAccessionValidator()==null, otherwise the matched
484    *         region or the first subgroup match from the matched region
485    */
486   public static String processQueryToAccessionFor(DbSourceProxy proxy,
487           String q)
488   {
489     if (proxy.getAccessionValidator() != null)
490     {
491       Regex vgr = proxy.getAccessionValidator();
492       vgr.search(q);
493       if (vgr.numSubs() > 0)
494       {
495         return (vgr.stringMatched(1));
496       }
497       else
498       {
499         return (vgr.stringMatched());
500       }
501     }
502     else
503     {
504       return (q);
505     }
506   }
507
508 }