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