removing cyclic call
[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.Mapping;
26 import jalview.datamodel.PDBEntry;
27 import jalview.datamodel.SequenceI;
28
29 import java.util.ArrayList;
30 import java.util.BitSet;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.List;
34 import java.util.Map;
35
36 import com.stevesoft.pat.Regex;
37
38 /**
39  * Utilities for handling DBRef objects and their collections.
40  */
41 public class DBRefUtils
42 {
43         
44         public final static int DB_SOURCE  = 1;
45         public final static int DB_VERSION = 2;
46         public final static int DB_ID      = 4;
47         public final static int DB_MAP     = 8;
48         
49         
50         public final static int SEARCH_MODE_NO_MAP_NO_VERSION = DB_SOURCE | DB_ID;
51         public final static int SEARCH_MODE_FULL = DB_SOURCE | DB_VERSION | DB_ID | DB_MAP;
52
53   /*
54    * lookup from lower-case form of a name to its canonical (standardised) form
55    */
56   private static Map<String, String> canonicalSourceNameLookup = new HashMap<String, String>();
57
58   private static Map<String, String> dasCoordinateSystemsLookup = new HashMap<String, String>();
59
60   static
61   {
62     // TODO load these from a resource file?
63     canonicalSourceNameLookup.put("uniprotkb/swiss-prot",
64             DBRefSource.UNIPROT);
65     canonicalSourceNameLookup.put("uniprotkb/trembl", DBRefSource.UNIPROT);
66
67     // Ensembl values for dbname in xref REST service:
68     canonicalSourceNameLookup.put("uniprot/sptrembl", DBRefSource.UNIPROT);
69     canonicalSourceNameLookup.put("uniprot/swissprot", DBRefSource.UNIPROT);
70
71     canonicalSourceNameLookup.put("pdb", DBRefSource.PDB);
72     canonicalSourceNameLookup.put("ensembl", DBRefSource.ENSEMBL);
73     // Ensembl Gn and Tr are for Ensembl genomic and transcript IDs as served
74     // from ENA.
75     canonicalSourceNameLookup.put("ensembl-tr", DBRefSource.ENSEMBL);
76     canonicalSourceNameLookup.put("ensembl-gn", DBRefSource.ENSEMBL);
77
78     // Make sure we have lowercase entries for all canonical string lookups
79 // BH 2019.01.25 unnecessary -- they are all lower case already
80     //Set<String> keys = canonicalSourceNameLookup.keySet();
81 //    for (String k : keys)
82 //    {
83 //      canonicalSourceNameLookup.put(k.toLowerCase(),
84 //              canonicalSourceNameLookup.get(k));
85 //    }
86
87     dasCoordinateSystemsLookup.put("pdbresnum", DBRefSource.PDB);
88     dasCoordinateSystemsLookup.put("uniprot", DBRefSource.UNIPROT);
89     dasCoordinateSystemsLookup.put("embl", DBRefSource.EMBL);
90     // dasCoordinateSystemsLookup.put("embl", DBRefSource.EMBLCDS);
91   }
92
93   /**
94    * Returns those DBRefEntry objects whose source identifier (once converted to
95    * Jalview's canonical form) is in the list of sources to search for. Returns
96    * null if no matches found.
97    * 
98    * @param dbrefs
99    *          DBRefEntry objects to search
100    * @param sources
101    *          array of sources to select
102    * @return
103    */
104   public static List<DBRefEntry> selectRefs(List<DBRefEntry> dbrefs,
105           String[] sources)
106   {
107     if (dbrefs == null || sources == null)
108     {
109       return dbrefs;
110     }
111
112     // BH TODO
113     HashSet<String> srcs = new HashSet<String>();
114     for (String src : sources)
115     {
116       srcs.add(src.toUpperCase());
117     }
118
119     int nrefs = dbrefs.size();
120     List<DBRefEntry> res = new ArrayList<DBRefEntry>();
121     for (int ib = 0; ib < nrefs; ib++)
122     {
123       DBRefEntry dbr = dbrefs.get(ib);
124       String source = getCanonicalName(dbr.getSource());
125       if (srcs.contains(source.toUpperCase()))
126       {
127         res.add(dbr);
128       }
129     }
130
131     if (res.size() > 0)
132     {
133       //List<DBRefEntry> reply = new DBRefEntry[res.size()];
134       return res;//.toArray(reply);
135     }
136     return null;
137   }
138
139   private static boolean selectRefsBS(List<DBRefEntry> dbrefs, String sourceKeys, BitSet bsSelect) {
140             if (dbrefs == null || sourceKeys == null)
141             {
142               return false;
143             }
144             for (int i = 0, n = dbrefs.size(); i < n; i++)
145             {
146               DBRefEntry dbr = dbrefs.get(i);
147               String sourceKey = dbr.getSourceKey();
148               if (sourceKeys.indexOf(sourceKey) < 0) {
149                   bsSelect.clear(i);
150               }
151             }
152             return !bsSelect.isEmpty();
153   }
154
155
156   /**
157    * isDasCoordinateSystem
158    * 
159    * @param string
160    *          String
161    * @param dBRefEntry
162    *          DBRefEntry
163    * @return boolean true if Source DBRefEntry is compatible with DAS
164    *         CoordinateSystem name
165    */
166
167   public static boolean isDasCoordinateSystem(String string,
168           DBRefEntry dBRefEntry)
169   {
170     if (string == null || dBRefEntry == null)
171     {
172       return false;
173     }
174     String coordsys = dasCoordinateSystemsLookup.get(string.toLowerCase());
175     return coordsys == null ? false
176             : coordsys.equals(dBRefEntry.getSource());
177   }
178
179   /**
180    * look up source in an internal list of database reference sources and return
181    * the canonical jalview name for the source, or the original string if it has
182    * no canonical form.
183    * 
184    * @param source
185    * @return canonical jalview source (one of jalview.datamodel.DBRefSource.*)
186    *         or original source
187    */
188   public static String getCanonicalName(String source)
189   {
190     if (source == null)
191     {
192       return null;
193     }
194     String canonical = canonicalSourceNameLookup.get(source.toLowerCase());
195     return canonical == null ? source : canonical;
196   }
197
198   /**
199    * Returns a (possibly empty) list of those references that match the given
200    * entry. Currently uses a comparator which matches if
201    * <ul>
202    * <li>database sources are the same</li>
203    * <li>accession ids are the same</li>
204    * <li>both have no mapping, or the mappings are the same</li>
205    * </ul>
206    * 
207    * @param ref
208    *          Set of references to search
209    * @param entry
210    *          pattern to match
211    * @param mode   SEARCH_MODE_FULL for all; SEARCH_MODE_NO_MAP_NO_VERSION optional
212    * @return
213    */
214   public static List<DBRefEntry> searchRefs(List<DBRefEntry> ref,
215           DBRefEntry entry, int mode)
216   {
217     return searchRefs(ref, entry,
218             matchDbAndIdAndEitherMapOrEquivalentMapList, mode);
219   }
220
221   /**
222    * Returns a list of those references that match the given accession id
223    * <ul>
224    * <li>database sources are the same</li>
225    * <li>accession ids are the same</li>
226    * <li>both have no mapping, or the mappings are the same</li>
227    * </ul>
228    * 
229    * @param refs
230    *          Set of references to search
231    * @param accId
232    *          accession id to match
233    * @return
234    */
235   public static List<DBRefEntry> searchRefs(List<DBRefEntry> refs, String accId)
236   {
237             List<DBRefEntry> rfs = new ArrayList<DBRefEntry>();
238             if (refs == null || accId == null)
239             {
240               return rfs;
241             }
242             for (int i = 0, n = refs.size(); i < n; i++)
243             {
244                 DBRefEntry e = refs.get(i);
245                 if (accId.equals(e.getAccessionId()))
246               {
247                 rfs.add(e);
248               }
249             }
250             return rfs;
251 //    return searchRefs(refs, new DBRefEntry("", "", accId), matchId, SEARCH_MODE_FULL);
252   }
253
254
255   /**
256    * Returns a (possibly empty) list of those references that match the given
257    * entry, according to the given comparator.
258    * 
259    * @param refs
260    *          an array of database references to search
261    * @param entry
262    *          an entry to compare against
263    * @param comparator
264    * @param mode   SEARCH_MODE_FULL for all; SEARCH_MODE_NO_MAP_NO_VERSION optional
265    * @return
266    */
267   static List<DBRefEntry> searchRefs(List<DBRefEntry> refs, DBRefEntry entry,
268           DbRefComp comparator, int mode)
269   {
270     List<DBRefEntry> rfs = new ArrayList<DBRefEntry>();
271     if (refs == null || entry == null)
272     {
273       return rfs;
274     }
275     for (int i = 0, n = refs.size(); i < n; i++)
276     {
277         DBRefEntry e = refs.get(i);
278       if (comparator.matches(entry, e, SEARCH_MODE_FULL))
279       {
280         rfs.add(e);
281       }
282     }
283     return rfs;
284   }
285
286   interface DbRefComp
287   {
288     default public boolean matches(DBRefEntry refa, DBRefEntry refb) {
289         return matches(refa, refb, SEARCH_MODE_FULL);           
290     };
291
292     public boolean matches(DBRefEntry refa, DBRefEntry refb, int mode);
293 }
294
295   /**
296    * match on all non-null fields in refa
297    */
298   // TODO unused - remove?
299   public static DbRefComp matchNonNullonA = new DbRefComp()
300   {
301                 @Override
302     public boolean matches(DBRefEntry refa, DBRefEntry refb, int mode)
303     {
304       if ((mode & DB_SOURCE) != 0 && refa.getSource() == null
305               || DBRefUtils.getCanonicalName(refb.getSource()).equals(
306                       DBRefUtils.getCanonicalName(refa.getSource())))
307       {
308         if ((mode & DB_VERSION) != 0 && refa.getVersion() == null
309                 || refb.getVersion().equals(refa.getVersion()))
310         {
311           if ((mode & DB_ID) != 0 && refa.getAccessionId() == null
312                   || refb.getAccessionId().equals(refa.getAccessionId()))
313           {
314             if ((mode & DB_MAP) != 0 && refa.getMap() == null || (refb.getMap() != null
315                     && refb.getMap().equals(refa.getMap())))
316             {
317               return true;
318             }
319           }
320         }
321       }
322       return false;
323     }
324   };
325
326   /**
327    * either field is null or field matches for all of source, version, accession
328    * id and map.
329    */
330   // TODO unused - remove?
331   public static DbRefComp matchEitherNonNull = new DbRefComp()
332   {
333     @Override
334     public boolean matches(DBRefEntry refa, DBRefEntry refb, int mode)
335     {
336       if (nullOrEqualSource(refa.getSource(), refb.getSource())
337               && nullOrEqual(refa.getVersion(), refb.getVersion())
338               && nullOrEqual(refa.getAccessionId(), refb.getAccessionId())
339               && nullOrEqual(refa.getMap(), refb.getMap()))
340       {
341         return true;
342       }
343       return false;
344     }
345
346   };
347
348   /**
349    * accession ID and DB must be identical. Version is ignored. Map is either
350    * not defined or is a match (or is compatible?)
351    */
352   // TODO unused - remove?
353   public static DbRefComp matchDbAndIdAndEitherMap = new DbRefComp()
354   {
355     @Override
356     public boolean matches(DBRefEntry refa, DBRefEntry refb, int mode)
357     {
358       if (refa.getSource() != null && refb.getSource() != null
359               && DBRefUtils.getCanonicalName(refb.getSource()).equals(
360                       DBRefUtils.getCanonicalName(refa.getSource())))
361       {
362         // We dont care about version
363         if (refa.getAccessionId() != null && refb.getAccessionId() != null
364                 // FIXME should be && not || here?
365                 || refb.getAccessionId().equals(refa.getAccessionId()))
366         {
367           if ((refa.getMap() == null || refb.getMap() == null)
368                   || (refa.getMap() != null && refb.getMap() != null
369                           && refb.getMap().equals(refa.getMap())))
370           {
371             return true;
372           }
373         }
374       }
375       return false;
376     }
377   };
378
379   /**
380    * accession ID and DB must be identical. Version is ignored. No map on either
381    * or map but no maplist on either or maplist of map on a is the complement of
382    * maplist of map on b.
383    */
384   // TODO unused - remove?
385   public static DbRefComp matchDbAndIdAndComplementaryMapList = new DbRefComp()
386   {
387     @Override
388     public boolean matches(DBRefEntry refa, DBRefEntry refb, int mode)
389     {
390       if (refa.getSource() != null && refb.getSource() != null
391               && DBRefUtils.getCanonicalName(refb.getSource()).equals(
392                       DBRefUtils.getCanonicalName(refa.getSource())))
393       {
394         // We dont care about version
395         if (refa.getAccessionId() != null && refb.getAccessionId() != null
396                 || refb.getAccessionId().equals(refa.getAccessionId()))
397         {
398           if ((refa.getMap() == null && refb.getMap() == null)
399                   || (refa.getMap() != null && refb.getMap() != null))
400           {
401             if ((refb.getMap().getMap() == null
402                     && refa.getMap().getMap() == null)
403                     || (refb.getMap().getMap() != null
404                             && refa.getMap().getMap() != null
405                             && refb.getMap().getMap().getInverse()
406                                     .equals(refa.getMap().getMap())))
407             {
408               return true;
409             }
410           }
411         }
412       }
413       return false;
414     }
415   };
416
417   /**
418    * accession ID and DB must be identical. Version is ignored. No map on both
419    * or or map but no maplist on either or maplist of map on a is equivalent to
420    * the maplist of map on b.
421    */
422   // TODO unused - remove?
423   public static DbRefComp matchDbAndIdAndEquivalentMapList = new DbRefComp()
424   {
425     @Override
426     public boolean matches(DBRefEntry refa, DBRefEntry refb, int mode)
427     {
428       if (refa.getSource() != null && refb.getSource() != null
429               && DBRefUtils.getCanonicalName(refb.getSource()).equals(
430                       DBRefUtils.getCanonicalName(refa.getSource())))
431       {
432         // We dont care about version
433         // if ((refa.getVersion()==null || refb.getVersion()==null)
434         // || refb.getVersion().equals(refa.getVersion()))
435         // {
436         if (refa.getAccessionId() != null && refb.getAccessionId() != null
437                 || refb.getAccessionId().equals(refa.getAccessionId()))
438         {
439           if (refa.getMap() == null && refb.getMap() == null)
440           {
441             return true;
442           }
443           if (refa.getMap() != null && refb.getMap() != null
444                   && ((refb.getMap().getMap() == null
445                           && refa.getMap().getMap() == null)
446                           || (refb.getMap().getMap() != null
447                                   && refa.getMap().getMap() != null
448                                   && refb.getMap().getMap()
449                                           .equals(refa.getMap().getMap()))))
450           {
451             return true;
452           }
453         }
454       }
455       return false;
456     }
457   };
458
459   /**
460    * accession ID and DB must be identical, or null on a. Version is ignored. No
461    * map on either or map but no maplist on either or maplist of map on a is
462    * equivalent to the maplist of map on b.
463    */
464   public static DbRefComp matchDbAndIdAndEitherMapOrEquivalentMapList = new DbRefComp()
465   {
466     @Override
467     public boolean matches(DBRefEntry refa, DBRefEntry refb, int mode)
468     {
469       if (refa.getSource() != null && refb.getSource() != null
470               && DBRefUtils.getCanonicalName(refb.getSource()).equals(
471                       DBRefUtils.getCanonicalName(refa.getSource())))
472       {
473         // We dont care about version
474
475         if (refa.getAccessionId() == null
476                 || refa.getAccessionId().equals(refb.getAccessionId()))
477         {
478           if (refa.getMap() == null || refb.getMap() == null)
479           {
480             return true;
481           }
482           if ((refa.getMap() != null && refb.getMap() != null)
483                   && (refb.getMap().getMap() == null
484                           && refa.getMap().getMap() == null)
485                   || (refb.getMap().getMap() != null
486                           && refa.getMap().getMap() != null
487                           && (refb.getMap().getMap()
488                                   .equals(refa.getMap().getMap()))))
489           {
490             return true;
491           }
492         }
493       }
494       return false;
495     }
496   };
497
498   /**
499    * accession ID only must be identical.
500    */
501   public static DbRefComp matchId = new DbRefComp()
502   {
503     @Override
504     public boolean matches(DBRefEntry refa, DBRefEntry refb, int mode)
505     {
506       if (refa.getAccessionId() != null && refb.getAccessionId() != null
507               && refb.getAccessionId().equals(refa.getAccessionId()))
508       {
509         return true;
510       }
511       return false;
512     }
513   };
514
515   /**
516    * Parses a DBRefEntry and adds it to the sequence, also a PDBEntry if the
517    * database is PDB.
518    * <p>
519    * Used by file parsers to generate DBRefs from annotation within file (eg
520    * Stockholm)
521    * 
522    * @param dbname
523    * @param version
524    * @param acn
525    * @param seq
526    *          where to annotate with reference
527    * @return parsed version of entry that was added to seq (if any)
528    */
529   public static DBRefEntry parseToDbRef(SequenceI seq, String dbname,
530           String version, String acn)
531   {
532     DBRefEntry ref = null;
533     if (dbname != null)
534     {
535       String locsrc = DBRefUtils.getCanonicalName(dbname);
536       if (locsrc.equals(DBRefSource.PDB))
537       {
538         /*
539          * Check for PFAM style stockhom PDB accession id citation e.g.
540          * "1WRI A; 7-80;"
541          */
542         Regex r = new com.stevesoft.pat.Regex(
543                 "([0-9][0-9A-Za-z]{3})\\s*(.?)\\s*;\\s*([0-9]+)-([0-9]+)");
544         if (r.search(acn.trim()))
545         {
546           String pdbid = r.stringMatched(1);
547           String chaincode = r.stringMatched(2);
548           if (chaincode == null)
549           {
550             chaincode = " ";
551           }
552           // String mapstart = r.stringMatched(3);
553           // String mapend = r.stringMatched(4);
554           if (chaincode.equals(" "))
555           {
556             chaincode = "_";
557           }
558           // construct pdb ref.
559           ref = new DBRefEntry(locsrc, version, pdbid + chaincode);
560           PDBEntry pdbr = new PDBEntry();
561           pdbr.setId(pdbid);
562           pdbr.setType(PDBEntry.Type.PDB);
563           pdbr.setChainCode(chaincode);
564           seq.addPDBId(pdbr);
565         }
566         else
567         {
568           System.err.println("Malformed PDB DR line:" + acn);
569         }
570       }
571       else
572       {
573         // default:
574         ref = new DBRefEntry(locsrc, version, acn);
575       }
576     }
577     if (ref != null)
578     {
579       seq.addDBRef(ref);
580     }
581     return ref;
582   }
583
584   /**
585    * Returns true if either object is null, or they are equal
586    * 
587    * @param o1
588    * @param o2
589    * @return
590    */
591   public static boolean nullOrEqual(Object o1, Object o2)
592   {
593     if (o1 == null || o2 == null)
594     {
595       return true;
596     }
597     return o1.equals(o2);
598   }
599
600   /**
601    * canonicalise source string before comparing. null is always wildcard
602    * 
603    * @param o1
604    *          - null or source string to compare
605    * @param o2
606    *          - null or source string to compare
607    * @return true if either o1 or o2 are null, or o1 equals o2 under
608    *         DBRefUtils.getCanonicalName
609    *         (o1).equals(DBRefUtils.getCanonicalName(o2))
610    */
611   public static boolean nullOrEqualSource(String o1, String o2)
612   {
613     if (o1 == null || o2 == null)
614     {
615       return true;
616     }
617     return DBRefUtils.getCanonicalName(o1)
618             .equals(DBRefUtils.getCanonicalName(o2));
619   }
620
621   /**
622    * Selects just the DNA or protein references from a set of references
623    * 
624    * @param selectDna
625    *          if true, select references to 'standard' DNA databases, else to
626    *          'standard' peptide databases
627    * @param refs
628    *          a set of references to select from
629    * @return
630    */
631   public static List<DBRefEntry> selectDbRefs(boolean selectDna,
632           List<DBRefEntry> refs)
633   {
634     return selectRefs(refs,
635             selectDna ? DBRefSource.DNACODINGDBS : DBRefSource.PROTEINDBS);
636     // could attempt to find other cross
637     // refs here - ie PDB xrefs
638     // (not dna, not protein seq)
639   }
640
641   /**
642    * Returns the (possibly empty) list of those supplied dbrefs which have the
643    * specified source database, with a case-insensitive match of source name
644    * 
645    * @param dbRefs
646    * @param source
647    * @return
648    */
649   public static List<DBRefEntry> searchRefsForSource(List<DBRefEntry> dbRefs,
650           String source)
651   {
652     List<DBRefEntry> matches = new ArrayList<DBRefEntry>();
653     if (dbRefs != null && source != null)
654     {
655       for (DBRefEntry dbref : dbRefs)
656       {
657         if (source.equalsIgnoreCase(dbref.getSource()))
658         {
659           matches.add(dbref);
660         }
661       }
662     }
663     return matches;
664   }
665
666   /**
667    * promote direct database references to primary for nucleotide or protein
668    * sequences if they have an appropriate primary ref
669    * <table>
670    * <tr>
671    * <th>Seq Type</th>
672    * <th>Primary DB</th>
673    * <th>Direct which will be promoted</th>
674    * </tr>
675    * <tr align=center>
676    * <td>peptides</td>
677    * <td>Ensembl</td>
678    * <td>Uniprot</td>
679    * </tr>
680    * <tr align=center>
681    * <td>peptides</td>
682    * <td>Ensembl</td>
683    * <td>Uniprot</td>
684    * </tr>
685    * <tr align=center>
686    * <td>dna</td>
687    * <td>Ensembl</td>
688    * <td>ENA</td>
689    * </tr>
690    * </table>
691    * 
692    * @param sequence
693    */
694   public static void ensurePrimaries(SequenceI sequence, List<DBRefEntry> pr)
695   {
696     if (pr.size() == 0)
697     {
698       // nothing to do
699       return;
700     }
701     int sstart = sequence.getStart();
702     int send = sequence.getEnd();
703     boolean isProtein = sequence.isProtein();
704     BitSet bsSelect = new BitSet();
705     
706 //    List<DBRefEntry> selfs = new ArrayList<DBRefEntry>();
707 //    {
708
709 //      List<DBRefEntry> selddfs = selectDbRefs(!isprot, sequence.getDBRefs());
710 //      if (selfs == null || selfs.size() == 0)
711 //      {
712 //        // nothing to do
713 //        return;
714 //      }
715
716     List<DBRefEntry> dbrefs = sequence.getDBRefs();
717     bsSelect.set(0, dbrefs.size());
718     
719     if (!selectRefsBS(dbrefs, isProtein ? DBRefSource.PROTEINDBSKEYS : DBRefSource.DNACODINGDBSKEYS, bsSelect))
720         return;
721     
722 //      selfs.addAll(selfArray);
723 //    }
724
725     // filter non-primary refs
726     for (int ip = pr.size(); --ip >= 0;)
727     {
728       DBRefEntry p = pr.get(ip);
729       for (int i = bsSelect.nextSetBit(0); i >= 0; i = bsSelect.nextSetBit(i + 1)) {
730           if (dbrefs.get(i) == p)
731                   bsSelect.clear(i);
732       }
733 //      while (selfs.contains(p))
734 //      {
735 //        selfs.remove(p);
736 //      }
737     }
738 //    List<DBRefEntry> toPromote = new ArrayList<DBRefEntry>();
739
740     
741     
742     for (int ip = pr.size(), keys = 0; --ip >= 0 && keys != DBRefSource.ALL_MASKS;)
743     {
744       DBRefEntry p = pr.get(ip);
745       if (isProtein)
746       {
747         switch (getCanonicalName(p.getSource()))
748         {
749         case DBRefSource.UNIPROT:
750                 keys |= DBRefSource.UNIPROT_MASK;
751           break;
752         case DBRefSource.ENSEMBL:
753                 keys |= DBRefSource.ENSEMBL_MASK;
754           break;
755         }
756       }
757       else
758       {
759         // TODO: promote transcript refs ??
760       }
761       if (keys == 0 || !selectRefsBS(dbrefs, DBRefSource.PROMTYPES[keys], bsSelect))
762           return;
763 //      if (candidates != null)
764       {
765         for (int ic = bsSelect.nextSetBit(0); ic >= 0; ic = bsSelect.nextSetBit(ic + 1)) 
766 //        for (int ic = 0, n = candidates.size(); ic < n; ic++)
767         {
768                 DBRefEntry cand = dbrefs.get(ic);//candidates.get(ic);  
769           if (cand.hasMap())
770           {
771                   Mapping map = cand.getMap();
772                   SequenceI cto = map.getTo();
773             if (cto != null
774                     && cto != sequence)
775             {
776               // can't promote refs with mappings to other sequences
777               continue;
778             }
779             MapList mlist = map.getMap();
780             if (mlist.getFromLowest() != sstart
781                     && mlist.getFromHighest() != send)
782             {
783               // can't promote refs with mappings from a region of this sequence
784               // - eg CDS
785               continue;
786             }
787           }
788           // and promote
789           cand.setVersion(p.getVersion() + " (promoted)");
790           bsSelect.clear(ic);
791           //selfs.remove(cand);
792 //          toPromote.add(cand);
793           if (!cand.isPrimaryCandidate())
794           {
795             System.out.println(
796                     "Warning: Couldn't promote dbref " + cand.toString()
797                             + " for sequence " + sequence.toString());
798           }
799         }
800       }
801     }
802   }
803
804 }