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