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