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