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