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