Merge commit 'alpha/update_2_12_for_2_11_2_series_merge^2' into HEAD
[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   private static Regex PARSE_REGEX;
313
314   private static Regex getParseRegex()
315   {
316     return (PARSE_REGEX == null ? PARSE_REGEX = Platform.newRegex(
317             "([0-9][0-9A-Za-z]{3})\\s*(.?)\\s*;\\s*([0-9]+)-([0-9]+)")
318             : PARSE_REGEX);
319   }
320
321   /**
322    * Parses a DBRefEntry and adds it to the sequence, also a PDBEntry if the
323    * database is PDB.
324    * <p>
325    * Used by file parsers to generate DBRefs from annotation within file (eg
326    * Stockholm)
327    * 
328    * @param dbname
329    * @param version
330    * @param acn
331    * @param seq
332    *          where to annotate with reference
333    * @return parsed version of entry that was added to seq (if any)
334    */
335   public static DBRefEntry parseToDbRef(SequenceI seq, String dbname,
336           String version, String acn)
337   {
338     DBRefEntry ref = null;
339     if (dbname != null)
340     {
341       String locsrc = DBRefUtils.getCanonicalName(dbname);
342       if (locsrc.equals(DBRefSource.PDB))
343       {
344         /*
345          * Check for PFAM style stockhom PDB accession id citation e.g.
346          * "1WRI A; 7-80;"
347          */
348         Regex r = getParseRegex();
349         if (r.search(acn.trim()))
350         {
351           String pdbid = r.stringMatched(1);
352           String chaincode = r.stringMatched(2);
353           if (chaincode == null)
354           {
355             chaincode = " ";
356           }
357           // String mapstart = r.stringMatched(3);
358           // String mapend = r.stringMatched(4);
359           if (chaincode.equals(" "))
360           {
361             chaincode = "_";
362           }
363           // construct pdb ref.
364           ref = new DBRefEntry(locsrc, version, pdbid + chaincode);
365           PDBEntry pdbr = new PDBEntry();
366           pdbr.setId(pdbid);
367           pdbr.setType(PDBEntry.Type.PDB);
368           pdbr.setChainCode(chaincode);
369           seq.addPDBId(pdbr);
370         }
371         else
372         {
373           System.err.println("Malformed PDB DR line:" + acn);
374         }
375       }
376       else
377       {
378         // default:
379         ref = new DBRefEntry(locsrc, version, acn.trim());
380       }
381     }
382     if (ref != null)
383     {
384       seq.addDBRef(ref);
385     }
386     return ref;
387   }
388
389         /**
390          * accession ID and DB must be identical. Version is ignored. Map is either not
391          * defined or is a match (or is compatible?)
392          */
393         // TODO unused - remove?
394         public static DbRefComp matchDbAndIdAndEitherMap = new DbRefComp() {
395                 @Override
396                 public boolean matches(DBRefEntry refa, DBRefEntry refb, int mode) {
397                         if (refa.getSource() != null && refb.getSource() != null && DBRefUtils.getCanonicalName(refb.getSource())
398                                         .equals(DBRefUtils.getCanonicalName(refa.getSource()))) {
399                                 // We dont care about version
400                                 if (refa.getAccessionId() != null && refb.getAccessionId() != null
401                                                 // FIXME should be && not || here?
402                                                 || refb.getAccessionId().equals(refa.getAccessionId())) {
403                                         if ((refa.getMap() == null || refb.getMap() == null) || (refa.getMap() != null
404                                                         && refb.getMap() != null && refb.getMap().equals(refa.getMap()))) {
405                                                 return true;
406                                         }
407                                 }
408                         }
409                         return false;
410                 }
411         };
412
413         /**
414          * accession ID and DB must be identical. Version is ignored. No map on either
415          * or map but no maplist on either or maplist of map on a is the complement of
416          * maplist of map on b.
417          */
418         // TODO unused - remove?
419         public static DbRefComp matchDbAndIdAndComplementaryMapList = new DbRefComp() {
420                 @Override
421                 public boolean matches(DBRefEntry refa, DBRefEntry refb, int mode) {
422                         if (refa.getSource() != null && refb.getSource() != null && DBRefUtils.getCanonicalName(refb.getSource())
423                                         .equals(DBRefUtils.getCanonicalName(refa.getSource()))) {
424                                 // We dont care about version
425                                 if (refa.getAccessionId() != null && refb.getAccessionId() != null
426                                                 || refb.getAccessionId().equals(refa.getAccessionId())) {
427                                         if ((refa.getMap() == null && refb.getMap() == null)
428                                                         || (refa.getMap() != null && refb.getMap() != null)) {
429                                                 if ((refb.getMap().getMap() == null && refa.getMap().getMap() == null)
430                                                                 || (refb.getMap().getMap() != null && refa.getMap().getMap() != null
431                                                                                 && refb.getMap().getMap().getInverse().equals(refa.getMap().getMap()))) {
432                                                         return true;
433                                                 }
434                                         }
435                                 }
436                         }
437                         return false;
438                 }
439         };
440
441         /**
442          * accession ID and DB must be identical. Version is ignored. No map on both or
443          * or map but no maplist on either or maplist of map on a is equivalent to the
444          * maplist of map on b.
445          */
446         // TODO unused - remove?
447         public static DbRefComp matchDbAndIdAndEquivalentMapList = new DbRefComp() {
448                 @Override
449                 public boolean matches(DBRefEntry refa, DBRefEntry refb, int mode) {
450                         if (refa.getSource() != null && refb.getSource() != null && DBRefUtils.getCanonicalName(refb.getSource())
451                                         .equals(DBRefUtils.getCanonicalName(refa.getSource()))) {
452                                 // We dont care about version
453                                 // if ((refa.getVersion()==null || refb.getVersion()==null)
454                                 // || refb.getVersion().equals(refa.getVersion()))
455                                 // {
456                                 if (refa.getAccessionId() != null && refb.getAccessionId() != null
457                                                 || refb.getAccessionId().equals(refa.getAccessionId())) {
458                                         if (refa.getMap() == null && refb.getMap() == null) {
459                                                 return true;
460                                         }
461                                         if (refa.getMap() != null && refb.getMap() != null
462                                                         && ((refb.getMap().getMap() == null && refa.getMap().getMap() == null)
463                                                                         || (refb.getMap().getMap() != null && refa.getMap().getMap() != null
464                                                                                         && refb.getMap().getMap().equals(refa.getMap().getMap())))) {
465                                                 return true;
466                                         }
467                                 }
468                         }
469                         return false;
470                 }
471         };
472
473         /**
474          * accession ID and DB must be identical, or null on a. Version is ignored. No
475          * map on either or map but no maplist on either or maplist of map on a is
476          * equivalent to the maplist of map on b.
477          */
478         public static DbRefComp matchDbAndIdAndEitherMapOrEquivalentMapList = new DbRefComp() 
479         {
480                 @Override
481                 public boolean matches(DBRefEntry refa, DBRefEntry refb, int mode) 
482                 {
483                         if (refa.getSource() != null && refb.getSource() != null && DBRefUtils.getCanonicalName(refb.getSource())
484                                         .equals(DBRefUtils.getCanonicalName(refa.getSource()))) 
485                         {
486                                 // We dont care about version
487                                 if (refa.getAccessionId() == null || refa.getAccessionId().equals(refb.getAccessionId())) 
488                                 {
489                                         if (refa.getMap() == null || refb.getMap() == null) 
490                                         {
491                                                 return true;
492                                         }
493                                         if ((refa.getMap() != null && refb.getMap() != null)
494                                                         && (refb.getMap().getMap() == null && refa.getMap().getMap() == null)
495                                                         || (refb.getMap().getMap() != null && refa.getMap().getMap() != null
496                                                                         && (refb.getMap().getMap().equals(refa.getMap().getMap())))) 
497                                         {
498                                                 return true;
499                                         }
500                                 }
501                         }
502                         return false;
503                 }
504         };
505
506         /**
507    * Returns the (possibly empty) list of those supplied dbrefs which have the
508    * specified source database, with a case-insensitive match of source name
509    * 
510    * @param dbRefs
511    * @param source
512    * @return
513    */
514   public static List<DBRefEntry> searchRefsForSource(DBRefEntry[] dbRefs,
515           String source)
516   {
517     List<DBRefEntry> matches = new ArrayList<>();
518     if (dbRefs != null && source != null)
519     {
520       for (DBRefEntry dbref : dbRefs)
521       {
522         if (source.equalsIgnoreCase(dbref.getSource()))
523         {
524           matches.add(dbref);
525         }
526       }
527     }
528     return matches;
529   }
530
531         /**
532          * Returns true if either object is null, or they are equal
533          * 
534          * @param o1
535          * @param o2
536          * @return
537          */
538         public static boolean nullOrEqual(Object o1, Object o2) {
539                 if (o1 == null || o2 == null) {
540                         return true;
541                 }
542                 return o1.equals(o2);
543         }
544
545         /**
546          * canonicalise source string before comparing. null is always wildcard
547          * 
548          * @param o1 - null or source string to compare
549          * @param o2 - null or source string to compare
550          * @return true if either o1 or o2 are null, or o1 equals o2 under
551          *         DBRefUtils.getCanonicalName
552          *         (o1).equals(DBRefUtils.getCanonicalName(o2))
553          */
554         public static boolean nullOrEqualSource(String o1, String o2) {
555                 if (o1 == null || o2 == null) {
556                         return true;
557                 }
558                 return DBRefUtils.getCanonicalName(o1).equals(DBRefUtils.getCanonicalName(o2));
559         }
560
561         /**
562          * Selects just the DNA or protein references from a set of references
563          * 
564          * @param selectDna if true, select references to 'standard' DNA databases, else
565          *                  to 'standard' peptide databases
566          * @param refs      a set of references to select from
567          * @return
568          */
569         public static List<DBRefEntry> selectDbRefs(boolean selectDna, List<DBRefEntry> refs) {
570                 return selectRefs(refs, selectDna ? DBRefSource.DNACODINGDBS : DBRefSource.PROTEINDBS);
571                 // could attempt to find other cross
572                 // refs here - ie PDB xrefs
573                 // (not dna, not protein seq)
574         }
575
576         /**
577          * Returns the (possibly empty) list of those supplied dbrefs which have the
578          * specified source database, with a case-insensitive match of source name
579          * 
580          * @param dbRefs
581          * @param source
582          * @return
583          */
584         public static List<DBRefEntry> searchRefsForSource(List<DBRefEntry> dbRefs, String source) {
585                 List<DBRefEntry> matches = new ArrayList<DBRefEntry>();
586                 if (dbRefs != null && source != null) {
587                         for (DBRefEntry dbref : dbRefs) {
588                                 if (source.equalsIgnoreCase(dbref.getSource())) {
589                                         matches.add(dbref);
590                                 }
591                         }
592                 }
593                 return matches;
594         }
595
596         /**
597          * promote direct database references to primary for nucleotide or protein
598          * sequences if they have an appropriate primary ref
599          * <table>
600          * <tr>
601          * <th>Seq Type</th>
602          * <th>Primary DB</th>
603          * <th>Direct which will be promoted</th>
604          * </tr>
605          * <tr align=center>
606          * <td>peptides</td>
607          * <td>Ensembl</td>
608          * <td>Uniprot</td>
609          * </tr>
610          * <tr align=center>
611          * <td>peptides</td>
612          * <td>Ensembl</td>
613          * <td>Uniprot</td>
614          * </tr>
615          * <tr align=center>
616          * <td>dna</td>
617          * <td>Ensembl</td>
618          * <td>ENA</td>
619          * </tr>
620          * </table>
621          * 
622          * @param sequence
623          */
624         public static void ensurePrimaries(SequenceI sequence, List<DBRefEntry> pr) {
625                 if (pr.size() == 0) {
626                         // nothing to do
627                         return;
628                 }
629                 int sstart = sequence.getStart();
630                 int send = sequence.getEnd();
631                 boolean isProtein = sequence.isProtein();
632                 BitSet bsSelect = new BitSet();
633
634 //    List<DBRefEntry> selfs = new ArrayList<DBRefEntry>();
635 //    {
636
637 //      List<DBRefEntry> selddfs = selectDbRefs(!isprot, sequence.getDBRefs());
638 //      if (selfs == null || selfs.size() == 0)
639 //      {
640 //        // nothing to do
641 //        return;
642 //      }
643
644                 List<DBRefEntry> dbrefs = sequence.getDBRefs();
645                 bsSelect.set(0, dbrefs.size());
646
647                 if (!selectRefsBS(dbrefs, isProtein ? DBRefSource.PROTEIN_MASK : DBRefSource.DNA_CODING_MASK, bsSelect))
648                         return;
649
650 //      selfs.addAll(selfArray);
651 //    }
652
653                 // filter non-primary refs
654                 for (int ip = pr.size(); --ip >= 0;) {
655                         DBRefEntry p = pr.get(ip);
656                         for (int i = bsSelect.nextSetBit(0); i >= 0; i = bsSelect.nextSetBit(i + 1)) {
657                                 if (dbrefs.get(i) == p)
658                                         bsSelect.clear(i);
659                         }
660 //      while (selfs.contains(p))
661 //      {
662 //        selfs.remove(p);
663 //      }
664                 }
665 //    List<DBRefEntry> toPromote = new ArrayList<DBRefEntry>();
666
667                 for (int ip = pr.size(), keys = 0; --ip >= 0 && keys != DBRefSource.PRIMARY_MASK;) {
668                         DBRefEntry p = pr.get(ip);
669                         if (isProtein) {
670                                 switch (getCanonicalName(p.getSource())) {
671                                 case DBRefSource.UNIPROT:
672                                         keys |= DBRefSource.UNIPROT_MASK;
673                                         break;
674                                 case DBRefSource.ENSEMBL:
675                                         keys |= DBRefSource.ENSEMBL_MASK;
676                                         break;
677                                 }
678                         } else {
679                                 // TODO: promote transcript refs ??
680                         }
681                         if (keys == 0 || !selectRefsBS(dbrefs, keys, bsSelect))
682                                 return;
683 //      if (candidates != null)
684                         {
685                                 for (int ic = bsSelect.nextSetBit(0); ic >= 0; ic = bsSelect.nextSetBit(ic + 1))
686 //        for (int ic = 0, n = candidates.size(); ic < n; ic++)
687                                 {
688                                         DBRefEntry cand = dbrefs.get(ic);// candidates.get(ic);
689                                         if (cand.hasMap()) {
690                                                 Mapping map = cand.getMap();
691                                                 SequenceI cto = map.getTo();
692                                                 if (cto != null && cto != sequence) {
693                                                         // can't promote refs with mappings to other sequences
694                                                         continue;
695                                                 }
696                                                 MapList mlist = map.getMap();
697                                                 if (mlist.getFromLowest() != sstart && mlist.getFromHighest() != send) {
698                                                         // can't promote refs with mappings from a region of this sequence
699                                                         // - eg CDS
700                                                         continue;
701                                                 }
702                                         }
703                                         // and promote - not that version must be non-null here, 
704                                         // as p must have passed isPrimaryCandidate()
705                                         cand.setVersion(p.getVersion() + " (promoted)");
706                                         bsSelect.clear(ic);
707                                         // selfs.remove(cand);
708 //          toPromote.add(cand);
709                                         if (!cand.isPrimaryCandidate()) {
710                                                 System.out.println("Warning: Couldn't promote dbref " + cand.toString() + " for sequence "
711                                                                 + sequence.toString());
712                                         }
713                                 }
714                         }
715                 }
716         }
717
718 }