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