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