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