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