e8494e4271da581915e4f062cfe7be8602f17fc4
[jalview.git] / src / jalview / ws / DBRefFetcher.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3  * Copyright (C) 2014 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.ws;
22
23 import jalview.analysis.AlignSeq;
24 import jalview.bin.Cache;
25 import jalview.datamodel.AlignmentI;
26 import jalview.datamodel.DBRefEntry;
27 import jalview.datamodel.DBRefSource;
28 import jalview.datamodel.Mapping;
29 import jalview.datamodel.SequenceFeature;
30 import jalview.datamodel.SequenceI;
31 import jalview.gui.AlignFrame;
32 import jalview.gui.CutAndPasteTransfer;
33 import jalview.gui.Desktop;
34 import jalview.gui.IProgressIndicator;
35 import jalview.gui.OOMWarning;
36 import jalview.util.MessageManager;
37 import jalview.ws.dbsources.das.api.jalviewSourceI;
38 import jalview.ws.seqfetcher.DbSourceProxy;
39
40 import java.util.ArrayList;
41 import java.util.Enumeration;
42 import java.util.Hashtable;
43 import java.util.List;
44 import java.util.StringTokenizer;
45 import java.util.Vector;
46
47 import uk.ac.ebi.picr.model.UPEntry;
48
49 /**
50  * Implements a runnable for validating a sequence against external databases
51  * and then propagating references and features onto the sequence(s)
52  * 
53  * @author $author$
54  * @version $Revision$
55  */
56 public class DBRefFetcher implements Runnable
57 {
58   SequenceI[] dataset;
59
60   IProgressIndicator af;
61
62   CutAndPasteTransfer output = new CutAndPasteTransfer();
63
64   StringBuffer sbuffer = new StringBuffer();
65
66   boolean running = false;
67
68   /**
69    * picr client instance
70    */
71   uk.ac.ebi.www.picr.AccessionMappingService.AccessionMapperInterface picrClient = null;
72
73   // /This will be a collection of Vectors of sequenceI refs.
74   // The key will be the seq name or accession id of the seq
75   Hashtable seqRefs;
76
77   DbSourceProxy[] dbSources;
78
79   SequenceFetcher sfetcher;
80
81   private SequenceI[] alseqs;
82
83   /**
84    * when true - retrieved sequences will be trimmed to cover longest derived alignment sequence 
85    */
86   private boolean trimDsSeqs=true;
87
88   public DBRefFetcher()
89   {
90   }
91
92   /**
93    * Creates a new SequenceFeatureFetcher object and fetches from the currently
94    * selected set of databases.
95    * 
96    * @param seqs
97    *          fetch references for these sequences
98    * @param af
99    *          the parent alignframe for progress bar monitoring.
100    */
101   public DBRefFetcher(SequenceI[] seqs, AlignFrame af)
102   {
103     this(seqs, af, null);
104   }
105
106   /**
107    * Creates a new SequenceFeatureFetcher object and fetches from the currently
108    * selected set of databases.
109    * 
110    * @param seqs
111    *          fetch references for these sequences
112    * @param af
113    *          the parent alignframe for progress bar monitoring.
114    * @param sources
115    *          array of database source strings to query references from
116    */
117   public DBRefFetcher(SequenceI[] seqs, AlignFrame af,
118           DbSourceProxy[] sources)
119   {
120     this.af = af;
121     alseqs = new SequenceI[seqs.length];
122     SequenceI[] ds = new SequenceI[seqs.length];
123     for (int i = 0; i < seqs.length; i++)
124     {
125       alseqs[i] = seqs[i];
126       if (seqs[i].getDatasetSequence() != null)
127         ds[i] = seqs[i].getDatasetSequence();
128       else
129         ds[i] = seqs[i];
130     }
131     this.dataset = ds;
132     // TODO Jalview 2.5 lots of this code should be in the gui package!
133     sfetcher = jalview.gui.SequenceFetcher.getSequenceFetcherSingleton(af);
134     // set default behaviour for transferring excess sequence data to the dataset 
135     trimDsSeqs = Cache.getDefault("TRIM_FETCHED_DATASET_SEQS", true);
136     if (sources == null)
137     {
138       // af.featureSettings_actionPerformed(null);
139       String[] defdb = null, otherdb = sfetcher
140               .getDbInstances(jalview.ws.dbsources.das.datamodel.DasSequenceSource.class);
141       List<DbSourceProxy> selsources = new ArrayList<DbSourceProxy>();
142       Vector dasselsrc = (af.featureSettings != null) ? af.featureSettings
143               .getSelectedSources() : new jalview.gui.DasSourceBrowser()
144               .getSelectedSources();
145       Enumeration<jalviewSourceI> en = dasselsrc.elements();
146       while (en.hasMoreElements())
147       {
148         jalviewSourceI src = en.nextElement();
149         List<DbSourceProxy> sp = src.getSequenceSourceProxies();
150         if (sp != null)
151         {
152           selsources.addAll(sp);
153           if (sp.size() > 1)
154           {
155             Cache.log.debug("Added many Db Sources for :" + src.getTitle());
156           }
157         }
158       }
159       // select appropriate databases based on alignFrame context.
160       if (af.getViewport().getAlignment().isNucleotide())
161       {
162         defdb = DBRefSource.DNACODINGDBS;
163       }
164       else
165       {
166         defdb = DBRefSource.PROTEINDBS;
167       }
168       List<DbSourceProxy> srces = new ArrayList<DbSourceProxy>();
169       for (String ddb : defdb)
170       {
171         List<DbSourceProxy> srcesfordb = sfetcher.getSourceProxy(ddb);
172         if (srcesfordb != null)
173         {
174           srces.addAll(srcesfordb);
175         }
176       }
177
178       // append the selected sequence sources to the default dbs
179       srces.addAll(selsources);
180       dbSources = srces.toArray(new DbSourceProxy[0]);
181     }
182     else
183     {
184       // we assume the caller knows what they're doing and ensured that all the
185       // db source names are valid
186       dbSources = sources;
187     }
188   }
189
190   /**
191    * retrieve all the das sequence sources and add them to the list of db
192    * sources to retrieve from
193    */
194   public void appendAllDasSources()
195   {
196     if (dbSources == null)
197     {
198       dbSources = new DbSourceProxy[0];
199     }
200     // append additional sources
201     DbSourceProxy[] otherdb = sfetcher
202             .getDbSourceProxyInstances(jalview.ws.dbsources.das.datamodel.DasSequenceSource.class);
203     if (otherdb != null && otherdb.length > 0)
204     {
205       DbSourceProxy[] newsrc = new DbSourceProxy[dbSources.length
206               + otherdb.length];
207       System.arraycopy(dbSources, 0, newsrc, 0, dbSources.length);
208       System.arraycopy(otherdb, 0, newsrc, dbSources.length, otherdb.length);
209       dbSources = newsrc;
210     }
211   }
212
213   /**
214    * start the fetcher thread
215    * 
216    * @param waitTillFinished
217    *          true to block until the fetcher has finished
218    */
219   public void fetchDBRefs(boolean waitTillFinished)
220   {
221     Thread thread = new Thread(this);
222     thread.start();
223     running = true;
224
225     if (waitTillFinished)
226     {
227       while (running)
228       {
229         try
230         {
231           Thread.sleep(500);
232         } catch (Exception ex)
233         {
234         }
235       }
236     }
237   }
238
239   /**
240    * The sequence will be added to a vector of sequences belonging to key which
241    * could be either seq name or dbref id
242    * 
243    * @param seq
244    *          SequenceI
245    * @param key
246    *          String
247    */
248   void addSeqId(SequenceI seq, String key)
249   {
250     key = key.toUpperCase();
251
252     Vector seqs;
253     if (seqRefs.containsKey(key))
254     {
255       seqs = (Vector) seqRefs.get(key);
256
257       if (seqs != null && !seqs.contains(seq))
258       {
259         seqs.addElement(seq);
260       }
261       else if (seqs == null)
262       {
263         seqs = new Vector();
264         seqs.addElement(seq);
265       }
266
267     }
268     else
269     {
270       seqs = new Vector();
271       seqs.addElement(seq);
272     }
273
274     seqRefs.put(key, seqs);
275   }
276
277   /**
278    * DOCUMENT ME!
279    */
280   public void run()
281   {
282     if (dbSources == null)
283     {
284       throw new Error("Implementation error. Must initialise dbSources");
285     }
286     running = true;
287     long startTime = System.currentTimeMillis();
288     af.setProgressBar("Fetching db refs", startTime);
289     try
290     {
291       if (Cache.getDefault("DBREFFETCH_USEPICR", false))
292       {
293         picrClient = new uk.ac.ebi.www.picr.AccessionMappingService.AccessionMapperServiceLocator()
294                 .getAccessionMapperPort();
295       }
296     } catch (Exception e)
297     {
298       System.err.println("Couldn't locate PICR service instance.\n");
299       e.printStackTrace();
300     }
301     int db = 0;
302     Vector sdataset = new Vector();
303     for (int s = 0; s < dataset.length; s++)
304     {
305       sdataset.addElement(dataset[s]);
306     }
307     while (sdataset.size() > 0 && db < dbSources.length)
308     {
309       int maxqlen = 1; // default number of queries made to at one time
310       System.err.println("Verifying against " + dbSources[db].getDbName());
311       boolean dn = false;
312
313       // iterate through db for each remaining un-verified sequence
314       SequenceI[] currSeqs = new SequenceI[sdataset.size()];
315       sdataset.copyInto(currSeqs);// seqs that are to be validated against
316       // dbSources[db]
317       Vector queries = new Vector(); // generated queries curSeq
318       seqRefs = new Hashtable();
319
320       int seqIndex = 0;
321
322       jalview.ws.seqfetcher.DbSourceProxy dbsource = dbSources[db];
323       {
324         // for moment, we dumbly iterate over all retrieval sources for a
325         // particular database
326         // TODO: introduce multithread multisource queries and logic to remove a
327         // query from other sources if any source for a database returns a
328         // record
329         if (dbsource.getDbSourceProperties().containsKey(
330                 DBRefSource.MULTIACC))
331         {
332           maxqlen = ((Integer) dbsource.getDbSourceProperties().get(
333                   DBRefSource.MULTIACC)).intValue();
334         }
335         else
336         {
337           maxqlen = 1;
338         }
339         while (queries.size() > 0 || seqIndex < currSeqs.length)
340         {
341           if (queries.size() > 0)
342           {
343             // Still queries to make for current seqIndex
344             StringBuffer queryString = new StringBuffer("");
345             int numq = 0, nqSize = (maxqlen > queries.size()) ? queries
346                     .size() : maxqlen;
347
348             while (queries.size() > 0 && numq < nqSize)
349             {
350               String query = (String) queries.elementAt(0);
351               if (dbsource.isValidReference(query))
352               {
353                 queryString.append((numq == 0) ? "" : dbsource
354                         .getAccessionSeparator());
355                 queryString.append(query);
356                 numq++;
357               }
358               // remove the extracted query string
359               queries.removeElementAt(0);
360             }
361             // make the queries and process the response
362             AlignmentI retrieved = null;
363             try
364             {
365               if (jalview.bin.Cache.log.isDebugEnabled())
366               {
367                 jalview.bin.Cache.log.debug("Querying "
368                         + dbsource.getDbName() + " with : '"
369                         + queryString.toString() + "'");
370               }
371               retrieved = dbsource.getSequenceRecords(queryString
372                       .toString());
373             } catch (Exception ex)
374             {
375               ex.printStackTrace();
376             } catch (OutOfMemoryError err)
377             {
378               new OOMWarning("retrieving database references ("
379                       + queryString.toString() + ")", err);
380             }
381             if (retrieved != null)
382             {
383               transferReferences(sdataset, dbsource.getDbSource(),
384                       retrieved,trimDsSeqs);
385             }
386           }
387           else
388           {
389             // make some more strings for use as queries
390             for (int i = 0; (seqIndex < dataset.length) && (i < 50); seqIndex++, i++)
391             {
392               SequenceI sequence = dataset[seqIndex];
393               DBRefEntry[] uprefs = jalview.util.DBRefUtils.selectRefs(
394                       sequence.getDBRef(), new String[]
395                       { dbsource.getDbSource() }); // jalview.datamodel.DBRefSource.UNIPROT
396               // });
397               // check for existing dbrefs to use
398               if (uprefs != null && uprefs.length > 0)
399               {
400                 for (int j = 0; j < uprefs.length; j++)
401                 {
402                   addSeqId(sequence, uprefs[j].getAccessionId());
403                   queries.addElement(uprefs[j].getAccessionId()
404                           .toUpperCase());
405                 }
406               }
407               else
408               {
409                 // generate queries from sequence ID string
410                 StringTokenizer st = new StringTokenizer(
411                         sequence.getName(), "|");
412                 while (st.hasMoreTokens())
413                 {
414                   String token = st.nextToken();
415                   UPEntry[] presp = null;
416                   if (picrClient != null)
417                   {
418                     // resolve the string against PICR to recover valid IDs
419                     try
420                     {
421                       presp = picrClient.getUPIForAccession(token, null,
422                               picrClient.getMappedDatabaseNames(), null,
423                               true);
424                     } catch (Exception e)
425                     {
426                       System.err.println("Exception with Picr for '"
427                               + token + "'\n");
428                       e.printStackTrace();
429                     }
430                   }
431                   if (presp != null && presp.length > 0)
432                   {
433                     for (int id = 0; id < presp.length; id++)
434                     {
435                       // construct sequences from response if sequences are
436                       // present, and do a transferReferences
437                       // otherwise transfer non sequence x-references directly.
438                     }
439                     System.out
440                             .println("Validated ID against PICR... (for what its worth):"
441                                     + token);
442                     addSeqId(sequence, token);
443                     queries.addElement(token.toUpperCase());
444                   }
445                   else
446                   {
447                     // if ()
448                     // System.out.println("Not querying source with token="+token+"\n");
449                     addSeqId(sequence, token);
450                     queries.addElement(token.toUpperCase());
451                   }
452                 }
453               }
454             }
455           }
456         }
457       }
458       // advance to next database
459       db++;
460     } // all databases have been queries.
461     if (sbuffer.length() > 0)
462     {
463       output.setText(MessageManager.getString("label.your_sequences_have_been_verified")
464               + sbuffer.toString());
465       Desktop.addInternalFrame(output, MessageManager.getString("label.sequence_names_updated"), 600, 300);
466       // The above is the dataset, we must now find out the index
467       // of the viewed sequence
468
469     }
470
471     af.setProgressBar(MessageManager.getString("label.dbref_search_completed"), startTime);
472     // promptBeforeBlast();
473
474     running = false;
475
476   }
477
478   /**
479    * Verify local sequences in seqRefs against the retrieved sequence database
480    * records.
481    * @param trimDatasetSeqs 
482    * 
483    */
484   void transferReferences(Vector sdataset, String dbSource,
485           AlignmentI retrievedAl, boolean trimDatasetSeqs) // File
486   // file)
487   {
488     System.out.println("trimming ? "+trimDatasetSeqs);
489     if (retrievedAl == null || retrievedAl.getHeight() == 0)
490     {
491       return;
492     }
493     SequenceI[] retrieved = recoverDbSequences(retrievedAl
494             .getSequencesArray());
495     SequenceI sequence = null;
496     boolean transferred = false;
497     StringBuffer messages = new StringBuffer();
498
499     // Vector entries = new Uniprot().getUniprotEntries(file);
500
501     int i, iSize = retrieved.length; // entries == null ? 0 : entries.size();
502     // UniprotEntry entry;
503     for (i = 0; i < iSize; i++)
504     {
505       SequenceI entry = retrieved[i]; // (UniprotEntry) entries.elementAt(i);
506
507       // Work out which sequences this sequence matches,
508       // taking into account all accessionIds and names in the file
509       Vector sequenceMatches = new Vector();
510       // look for corresponding accession ids
511       DBRefEntry[] entryRefs = jalview.util.DBRefUtils.selectRefs(
512               entry.getDBRef(), new String[]
513               { dbSource });
514       if (entryRefs == null)
515       {
516         System.err
517                 .println("Dud dbSource string ? no entryrefs selected for "
518                         + dbSource + " on " + entry.getName());
519         continue;
520       }
521       for (int j = 0; j < entryRefs.length; j++)
522       {
523         String accessionId = entryRefs[j].getAccessionId(); // .getAccession().elementAt(j).toString();
524         // match up on accessionId
525         if (seqRefs.containsKey(accessionId.toUpperCase()))
526         {
527           Vector seqs = (Vector) seqRefs.get(accessionId);
528           for (int jj = 0; jj < seqs.size(); jj++)
529           {
530             sequence = (SequenceI) seqs.elementAt(jj);
531             if (!sequenceMatches.contains(sequence))
532             {
533               sequenceMatches.addElement(sequence);
534             }
535           }
536         }
537       }
538       if (sequenceMatches.size() == 0)
539       {
540         // failed to match directly on accessionId==query so just compare all
541         // sequences to entry
542         Enumeration e = seqRefs.keys();
543         while (e.hasMoreElements())
544         {
545           Vector sqs = (Vector) seqRefs.get(e.nextElement());
546           if (sqs != null && sqs.size() > 0)
547           {
548             Enumeration sqe = sqs.elements();
549             while (sqe.hasMoreElements())
550             {
551               sequenceMatches.addElement(sqe.nextElement());
552             }
553           }
554         }
555       }
556       // look for corresponding names
557       // this is uniprot specific ?
558       // could be useful to extend this so we try to find any 'significant'
559       // information in common between two sequence objects.
560       /*
561        * DBRefEntry[] entryRefs =
562        * jalview.util.DBRefUtils.selectRefs(entry.getDBRef(), new String[] {
563        * dbSource }); for (int j = 0; j < entry.getName().size(); j++) { String
564        * name = entry.getName().elementAt(j).toString(); if
565        * (seqRefs.containsKey(name)) { Vector seqs = (Vector) seqRefs.get(name);
566        * for (int jj = 0; jj < seqs.size(); jj++) { sequence = (SequenceI)
567        * seqs.elementAt(jj); if (!sequenceMatches.contains(sequence)) {
568        * sequenceMatches.addElement(sequence); } } } }
569        */
570       // sequenceMatches now contains the set of all sequences associated with
571       // the returned db record
572       String entrySeq = entry.getSequenceAsString().toUpperCase();
573       for (int m = 0; m < sequenceMatches.size(); m++)
574       {
575         sequence = (SequenceI) sequenceMatches.elementAt(m);
576         // only update start and end positions and shift features if there are
577         // no existing references
578         // TODO: test for legacy where uniprot or EMBL refs exist but no
579         // mappings are made (but content matches retrieved set)
580         boolean updateRefFrame = sequence.getDBRef() == null
581                 || sequence.getDBRef().length == 0;
582         // TODO:
583         // verify sequence against the entry sequence
584
585         String nonGapped = AlignSeq.extractGaps("-. ",
586                 sequence.getSequenceAsString()).toUpperCase();
587
588         int absStart = entrySeq.indexOf(nonGapped);
589         int mapStart = entry.getStart();
590         jalview.datamodel.Mapping mp;
591
592         if (absStart == -1)
593         {
594           // Is local sequence contained in dataset sequence?
595           absStart = nonGapped.indexOf(entrySeq);
596           if (absStart == -1)
597           { // verification failed.
598             messages.append(sequence.getName()
599                     + " SEQUENCE NOT %100 MATCH \n");
600             continue;
601           }
602           transferred = true;
603           sbuffer.append(sequence.getName() + " HAS " + absStart
604                   + " PREFIXED RESIDUES COMPARED TO " + dbSource + "\n");
605           //
606           // + " - ANY SEQUENCE FEATURES"
607           // + " HAVE BEEN ADJUSTED ACCORDINGLY \n");
608           // absStart = 0;
609           // create valid mapping between matching region of local sequence and
610           // the mapped sequence
611           mp = new Mapping(null, new int[]
612           { sequence.getStart() + absStart,
613               sequence.getStart() + absStart + entrySeq.length() - 1 },
614                   new int[]
615                   { entry.getStart(),
616                       entry.getStart() + entrySeq.length() - 1 }, 1, 1);
617           updateRefFrame = false; // mapping is based on current start/end so
618           // don't modify start and end
619         }
620         else
621         {
622           transferred = true;
623           // update start and end of local sequence to place it in entry's
624           // reference frame.
625           // apply identity map map from whole of local sequence to matching
626           // region of database
627           // sequence
628           mp = null; // Mapping.getIdentityMap();
629           // new Mapping(null,
630           // new int[] { absStart+sequence.getStart(),
631           // absStart+sequence.getStart()+entrySeq.length()-1},
632           // new int[] { entry.getStart(), entry.getEnd() }, 1, 1);
633           // relocate local features for updated start
634           if (updateRefFrame)
635           {
636             if (sequence.getSequenceFeatures() != null)
637             {
638               SequenceFeature[] sf = sequence.getSequenceFeatures();
639               int start = sequence.getStart();
640               int end = sequence.getEnd();
641               int startShift = 1 - absStart - start; // how much the features
642                                                      // are
643               // to be shifted by
644               for (int sfi = 0; sfi < sf.length; sfi++)
645               {
646                 if (sf[sfi].getBegin() >= start && sf[sfi].getEnd() <= end)
647                 {
648                   // shift feature along by absstart
649                   sf[sfi].setBegin(sf[sfi].getBegin() + startShift);
650                   sf[sfi].setEnd(sf[sfi].getEnd() + startShift);
651                 }
652               }
653             }
654           }
655         }
656
657         System.out.println("Adding dbrefs to " + sequence.getName()
658                 + " from " + dbSource + " sequence : " + entry.getName());
659         sequence.transferAnnotation(entry, mp);
660         // unknownSequences.remove(sequence);
661         int absEnd = absStart + nonGapped.length();
662         absStart += 1;
663         if (!trimDatasetSeqs) {
664           // insert full length sequence from record
665           sequence.setSequence(entry.getSequenceAsString());
666           sequence.setStart(entry.getStart());
667         }
668         if (updateRefFrame)
669         {
670           // finally, update local sequence reference frame if we're allowed
671           if (trimDatasetSeqs) {
672             // just fix start/end
673             sequence.setStart(absStart);
674             sequence.setEnd(absEnd);
675           }
676           // search for alignment sequences to update coordinate frame for
677           for (int alsq = 0; alsq < alseqs.length; alsq++)
678           {
679             if (alseqs[alsq].getDatasetSequence() == sequence)
680             {
681               String ngAlsq = AlignSeq.extractGaps("-. ",
682                       alseqs[alsq].getSequenceAsString()).toUpperCase();
683               int oldstrt = alseqs[alsq].getStart();
684               alseqs[alsq].setStart(sequence.getSequenceAsString()
685                       .toUpperCase().indexOf(ngAlsq)
686                       + sequence.getStart());
687               if (oldstrt != alseqs[alsq].getStart())
688               {
689                 alseqs[alsq].setEnd(ngAlsq.length()
690                         + alseqs[alsq].getStart() - 1);
691               }
692             }
693           }
694           // TODO: search for all other references to this dataset sequence, and
695           // update start/end
696           // TODO: update all AlCodonMappings which involve this alignment
697           // sequence (e.g. Q30167 cdna translation from exon2 product (vamsas
698           // demo)
699         }
700         // and remove it from the rest
701         // TODO: decide if we should remove annotated sequence from set
702         sdataset.remove(sequence);
703         // TODO: should we make a note of sequences that have received new DB
704         // ids, so we can query all enabled DAS servers for them ?
705       }
706     }
707     if (!transferred)
708     {
709       // report the ID/sequence mismatches
710       sbuffer.append(messages);
711     }
712   }
713
714   /**
715    * loop thru and collect additional sequences in Map.
716    * 
717    * @param sequencesArray
718    * @return
719    */
720   private SequenceI[] recoverDbSequences(SequenceI[] sequencesArray)
721   {
722     Vector nseq = new Vector();
723     for (int i = 0; sequencesArray != null && i < sequencesArray.length; i++)
724     {
725       nseq.addElement(sequencesArray[i]);
726       DBRefEntry dbr[] = sequencesArray[i].getDBRef();
727       jalview.datamodel.Mapping map = null;
728       for (int r = 0; (dbr != null) && r < dbr.length; r++)
729       {
730         if ((map = dbr[r].getMap()) != null)
731         {
732           if (map.getTo() != null && !nseq.contains(map.getTo()))
733           {
734             nseq.addElement(map.getTo());
735           }
736         }
737       }
738     }
739     if (nseq.size() > 0)
740     {
741       sequencesArray = new SequenceI[nseq.size()];
742       nseq.toArray(sequencesArray);
743     }
744     return sequencesArray;
745   }
746 }