JAL-1942 small tidy (formatting, explict imports, typed list, spelling)
[jalview.git] / src / jalview / ws / DasSequenceFeatureFetcher.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.bin.Cache;
24 import jalview.datamodel.DBRefEntry;
25 import jalview.datamodel.DBRefSource;
26 import jalview.datamodel.SequenceFeature;
27 import jalview.datamodel.SequenceI;
28 import jalview.gui.AlignFrame;
29 import jalview.gui.Desktop;
30 import jalview.gui.FeatureSettings;
31 import jalview.util.DBRefUtils;
32 import jalview.util.MessageManager;
33 import jalview.util.UrlLink;
34 import jalview.ws.dbsources.das.api.DasSourceRegistryI;
35 import jalview.ws.dbsources.das.api.jalviewSourceI;
36
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.Enumeration;
40 import java.util.HashMap;
41 import java.util.HashSet;
42 import java.util.Iterator;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.Set;
46 import java.util.StringTokenizer;
47 import java.util.Vector;
48
49 import javax.swing.JOptionPane;
50
51 import org.biodas.jdas.client.FeaturesClient;
52 import org.biodas.jdas.client.adapters.features.DasGFFAdapter;
53 import org.biodas.jdas.client.adapters.features.DasGFFAdapter.GFFAdapter;
54 import org.biodas.jdas.client.threads.FeaturesClientMultipleSources;
55 import org.biodas.jdas.schema.features.ERRORSEGMENT;
56 import org.biodas.jdas.schema.features.FEATURE;
57 import org.biodas.jdas.schema.features.LINK;
58 import org.biodas.jdas.schema.features.SEGMENT;
59 import org.biodas.jdas.schema.features.TYPE;
60 import org.biodas.jdas.schema.features.UNKNOWNFEATURE;
61 import org.biodas.jdas.schema.features.UNKNOWNSEGMENT;
62 import org.biodas.jdas.schema.sources.COORDINATES;
63
64 /**
65  * DOCUMENT ME!
66  * 
67  * @author $author$
68  * @version $Revision$
69  */
70 public class DasSequenceFeatureFetcher
71 {
72   SequenceI[] sequences;
73
74   AlignFrame af;
75
76   FeatureSettings fsettings;
77
78   StringBuffer sbuffer = new StringBuffer();
79
80   List<jalviewSourceI> selectedSources;
81
82   boolean cancelled = false;
83
84   private void debug(String mesg)
85   {
86     debug(mesg, null);
87   }
88
89   private void debug(String mesg, Exception e)
90   {
91     if (Cache.log != null)
92     {
93       Cache.log.debug(mesg, e);
94     }
95     else
96     {
97       System.err.println(mesg);
98       if (e != null)
99       {
100         e.printStackTrace();
101       }
102     }
103   }
104
105   long startTime;
106
107   private DasSourceRegistryI sourceRegistry;
108
109   private boolean useJDASMultiThread = true;
110
111   /**
112    * Creates a new SequenceFeatureFetcher object. Uses default
113    * 
114    * @param align
115    *          DOCUMENT ME!
116    * @param ap
117    *          DOCUMENT ME!
118    */
119   public DasSequenceFeatureFetcher(SequenceI[] sequences,
120           FeatureSettings fsettings, Vector selectedSources)
121   {
122     this(sequences, fsettings, selectedSources, true, true, true);
123   }
124
125   public DasSequenceFeatureFetcher(SequenceI[] oursequences,
126           FeatureSettings fsettings, List<jalviewSourceI> selectedSources2,
127           boolean checkDbrefs, boolean promptFetchDbrefs)
128   {
129     this(oursequences, fsettings, selectedSources2, checkDbrefs,
130             promptFetchDbrefs, true);
131   }
132
133   public DasSequenceFeatureFetcher(SequenceI[] oursequences,
134           FeatureSettings fsettings, List<jalviewSourceI> selectedSources2,
135           boolean checkDbrefs, boolean promptFetchDbrefs,
136           boolean useJDasMultiThread)
137   {
138     this.useJDASMultiThread = useJDasMultiThread;
139     this.selectedSources = new ArrayList<jalviewSourceI>();
140     // filter both sequences and sources to eliminate duplicates
141     for (jalviewSourceI src : selectedSources2)
142     {
143       if (!selectedSources.contains(src))
144       {
145         selectedSources.add(src);
146       }
147       ;
148     }
149     Vector sqs = new Vector();
150     for (int i = 0; i < oursequences.length; i++)
151     {
152       if (!sqs.contains(oursequences[i]))
153       {
154         sqs.addElement(oursequences[i]);
155       }
156     }
157     sequences = new SequenceI[sqs.size()];
158     for (int i = 0; i < sequences.length; i++)
159     {
160       sequences[i] = (SequenceI) sqs.elementAt(i);
161     }
162     if (fsettings != null)
163     {
164       this.fsettings = fsettings;
165       this.af = fsettings.af;
166       af.setShowSeqFeatures(true);
167     }
168     int uniprotCount = 0;
169     for (jalviewSourceI source : selectedSources)
170     {
171       for (COORDINATES coords : source.getVersion().getCOORDINATES())
172       {
173         // TODO: match UniProt coord system canonically (?) - does
174         // UniProt==uniprot==UNIPROT ?
175         if (coords.getAuthority().toLowerCase().equals("uniprot"))
176         {
177           uniprotCount++;
178           break;
179         }
180       }
181     }
182
183     int refCount = 0;
184     for (int i = 0; i < sequences.length; i++)
185     {
186       DBRefEntry[] dbref = sequences[i].getDBRefs();
187       if (dbref != null)
188       {
189         for (int j = 0; j < dbref.length; j++)
190         {
191           if (dbref[j].getSource().equals(DBRefSource.UNIPROT))
192           {
193             refCount++;
194             break;
195           }
196         }
197       }
198     }
199
200     if (checkDbrefs && refCount < sequences.length && uniprotCount > 0)
201     {
202
203       int reply = JOptionPane.YES_OPTION;
204       if (promptFetchDbrefs)
205       {
206         reply = JOptionPane
207                 .showInternalConfirmDialog(
208                         Desktop.desktop,
209                         MessageManager
210                                 .getString("info.you_want_jalview_to_find_uniprot_accessions"),
211                         MessageManager
212                                 .getString("label.find_uniprot_accession_ids"),
213                         JOptionPane.YES_NO_OPTION,
214                         JOptionPane.QUESTION_MESSAGE);
215       }
216
217       if (reply == JOptionPane.YES_OPTION)
218       {
219         Thread thread = new Thread(new FetchDBRefs());
220         thread.start();
221       }
222       else
223       {
224         _startFetching();
225       }
226     }
227     else
228     {
229       _startFetching();
230     }
231
232   }
233
234   private void _startFetching()
235   {
236     running = true;
237     new Thread(new FetchSeqFeatures()).start();
238   }
239
240   class FetchSeqFeatures implements Runnable
241   {
242     @Override
243     public void run()
244     {
245       startFetching();
246       setGuiFetchComplete();
247     }
248   }
249
250   class FetchDBRefs implements Runnable
251   {
252     @Override
253     public void run()
254     {
255       running = true;
256       boolean isNucleotide = af.getViewport().getAlignment()
257               .isNucleotide();
258       new DBRefFetcher(sequences, af, null, af.featureSettings,
259               isNucleotide).fetchDBRefs(true);
260
261       startFetching();
262       setGuiFetchComplete();
263     }
264   }
265
266   /**
267    * Spawns Fetcher threads to add features to sequences in the dataset
268    */
269   void startFetching()
270   {
271     running = true;
272     cancelled = false;
273     startTime = System.currentTimeMillis();
274     if (af != null)
275     {
276       af.setProgressBar(MessageManager
277               .getString("status.fetching_das_sequence_features"),
278               startTime);
279     }
280     if (sourceRegistry == null)
281     {
282       sourceRegistry = Cache.getDasSourceRegistry();
283     }
284     if (selectedSources == null || selectedSources.size() == 0)
285     {
286       try
287       {
288         jalviewSourceI[] sources = sourceRegistry.getSources().toArray(
289                 new jalviewSourceI[0]);
290         String active = Cache.getDefault("DAS_ACTIVE_SOURCE",
291                 "uniprot");
292         StringTokenizer st = new StringTokenizer(active, "\t");
293         selectedSources = new Vector();
294         String token;
295         while (st.hasMoreTokens())
296         {
297           token = st.nextToken();
298           for (int i = 0; i < sources.length; i++)
299           {
300             if (sources[i].getTitle().equals(token))
301             {
302               selectedSources.add(sources[i]);
303               break;
304             }
305           }
306         }
307       } catch (Exception ex)
308       {
309         debug("Exception whilst setting default feature sources from registry and local preferences.",
310                 ex);
311       }
312     }
313
314     if (selectedSources == null || selectedSources.size() == 0)
315     {
316       System.out.println("No DAS Sources active");
317       cancelled = true;
318       setGuiNoDassourceActive();
319       return;
320     }
321
322     sourcesRemaining = selectedSources.size();
323     FeaturesClientMultipleSources fc = new FeaturesClientMultipleSources();
324     fc.setConnProps(sourceRegistry.getSessionHandler());
325     // Now sending requests one at a time to each server
326     ArrayList<jalviewSourceI> srcobj = new ArrayList<jalviewSourceI>();
327     ArrayList<String> src = new ArrayList<String>();
328     List<List<String>> ids = new ArrayList<List<String>>();
329     List<List<DBRefEntry>> idobj = new ArrayList<List<DBRefEntry>>();
330     List<Map<String, SequenceI>> sqset = new ArrayList<Map<String, SequenceI>>();
331     for (jalviewSourceI _sr : selectedSources)
332     {
333
334       Map<String, SequenceI> slist = new HashMap<String, SequenceI>();
335       List<DBRefEntry> idob = new ArrayList<DBRefEntry>();
336       List<String> qset = new ArrayList<String>();
337
338       for (SequenceI seq : sequences)
339       {
340         Object[] idset = nextSequence(_sr, seq);
341         if (idset != null)
342         {
343           List<DBRefEntry> _idob = (List<DBRefEntry>) idset[0];
344           List<String> _qset = (List<String>) idset[1];
345           if (_idob.size() > 0)
346           {
347             // add sequence's ref for each id derived from it
348             // (space inefficient, but most unambiguous)
349             // could replace with hash with _qset values as keys.
350             Iterator<DBRefEntry> dbobj = _idob.iterator();
351             for (String q : _qset)
352             {
353               SequenceI osq = slist.get(q);
354               DBRefEntry dr = dbobj.next();
355               if (osq != null && osq != seq)
356               {
357                 // skip - non-canonical query
358               }
359               else
360               {
361                 idob.add(dr);
362                 qset.add(q);
363                 slist.put(q, seq);
364               }
365             }
366           }
367         }
368       }
369       if (idob.size() > 0)
370       {
371         srcobj.add(_sr);
372         src.add(_sr.getSourceURL());
373         ids.add(qset);
374         idobj.add(idob);
375         sqset.add(slist);
376       }
377     }
378     Map<String, Map<List<String>, Exception>> errors = new HashMap<String, Map<List<String>, Exception>>();
379     Map<String, Map<List<String>, DasGFFAdapter>> results = new HashMap<String, Map<List<String>, DasGFFAdapter>>();
380     if (!useJDASMultiThread)
381     {
382       Iterator<String> sources = src.iterator();
383       // iterate over each query for each source and do each one individually
384       for (List<String> idl : ids)
385       {
386         String source = sources.next();
387         FeaturesClient featuresc = new FeaturesClient(sourceRegistry
388                 .getSessionHandler().getConnectionPropertyProviderFor(
389                         source));
390         for (String id : idl)
391         {
392           List<String> qid = Arrays.asList(new String[] { id });
393           try
394           {
395             DasGFFAdapter dga = featuresc.fetchData(source, qid);
396             Map<List<String>, DasGFFAdapter> ers = results.get(source);
397             if (ers == null)
398             {
399               results.put(source,
400                       ers = new HashMap<List<String>, DasGFFAdapter>());
401             }
402             ers.put(qid, dga);
403           } catch (Exception ex)
404           {
405             Map<List<String>, Exception> ers = errors.get(source);
406             if (ers == null)
407             {
408               errors.put(source,
409                       ers = new HashMap<List<String>, Exception>());
410             }
411             ers.put(qid, ex);
412           }
413         }
414       }
415     }
416     else
417     {
418       // pass them all at once
419       fc.fetchData(src, ids, false, results, errors);
420       fc.shutDown();
421       while (!fc.isTerminated())
422       {
423         try
424         {
425           Thread.sleep(200);
426         } catch (InterruptedException x)
427         {
428
429         }
430       }
431     }
432     Iterator<List<String>> idset = ids.iterator();
433     Iterator<List<DBRefEntry>> idobjset = idobj.iterator();
434     Iterator<Map<String, SequenceI>> seqset = sqset.iterator();
435     for (jalviewSourceI source : srcobj)
436     {
437       processResponse(seqset.next(), source, idset.next(), idobjset.next(),
438               results.get(source.getSourceURL()),
439               errors.get(source.getSourceURL()));
440     }
441   }
442
443   private void processResponse(Map<String, SequenceI> sequencemap,
444           jalviewSourceI jvsource, List<String> ids,
445           List<DBRefEntry> idobj, Map<List<String>, DasGFFAdapter> results,
446           Map<List<String>, Exception> errors)
447   {
448     Set<SequenceI> sequences = new HashSet<SequenceI>();
449     String source = jvsource.getSourceURL();
450     // process features
451     DasGFFAdapter result = (results == null) ? null : results.get(ids);
452     Exception error = (errors == null) ? null : errors.get(ids);
453     if (result == null)
454     {
455       debug("das source " + source + " could not be contacted. "
456               + (error == null ? "" : error.toString()));
457     }
458     else
459     {
460
461       GFFAdapter gff = result.getGFF();
462       List<SEGMENT> segments = gff.getSegments();
463       List<ERRORSEGMENT> errorsegs = gff.getErrorSegments();
464       List<UNKNOWNFEATURE> unkfeats = gff.getUnknownFeatures();
465       List<UNKNOWNSEGMENT> unksegs = gff.getUnknownSegments();
466       debug("das source " + source + " returned " + gff.getTotal()
467               + " responses. " + (errorsegs != null ? errorsegs.size() : 0)
468               + " were incorrect segment queries, "
469               + (unkfeats != null ? unkfeats.size() : 0)
470               + " were unknown features "
471               + (unksegs != null ? unksegs.size() : 0)
472               + " were unknown segments and "
473               + (segments != null ? segments.size() : 0)
474               + " were segment responses.");
475       Iterator<DBRefEntry> dbr = idobj.iterator();
476       if (segments != null)
477       {
478         for (SEGMENT seg : segments)
479         {
480           String id = seg.getId();
481           if (ids.indexOf(id) == -1)
482           {
483             id = id.toUpperCase();
484           }
485           DBRefEntry dbref = idobj.get(ids.indexOf(id));
486           SequenceI sequence = sequencemap.get(id);
487           boolean added = false;
488           sequences.add(sequence);
489
490           for (FEATURE feat : seg.getFEATURE())
491           {
492             // standard DAS feature-> jalview sequence feature transformation
493             SequenceFeature f = newSequenceFeature(feat,
494                     jvsource.getTitle());
495             if (!parseSeqFeature(sequence, f, feat, jvsource))
496             {
497               if (dbref.getMap() != null && f.getBegin() > 0
498                       && f.getEnd() > 0)
499               {
500                 debug("mapping from " + f.getBegin() + " - " + f.getEnd());
501                 SequenceFeature vf[] = null;
502
503                 try
504                 {
505                   vf = dbref.getMap().locateFeature(f);
506                 } catch (Exception ex)
507                 {
508                   Cache.log
509                           .warn("Error in 'experimental' mapping of features. Please try to reproduce and then report info to jalview-discuss@jalview.org.");
510                   Cache.log.warn("Mapping feature from " + f.getBegin()
511                           + " to " + f.getEnd() + " in dbref "
512                           + dbref.getAccessionId() + " in "
513                           + dbref.getSource());
514                   Cache.log.warn("using das Source " + source);
515                   Cache.log.warn("Exception", ex);
516                 }
517
518                 if (vf != null)
519                 {
520                   for (int v = 0; v < vf.length; v++)
521                   {
522                     debug("mapping to " + v + ": " + vf[v].getBegin()
523                             + " - " + vf[v].getEnd());
524                     sequence.addSequenceFeature(vf[v]);
525                   }
526                 }
527               }
528               else
529               {
530                 sequence.addSequenceFeature(f);
531               }
532             }
533           }
534         }
535         featuresAdded(sequences);
536       }
537       else
538       {
539         // System.out.println("No features found for " + seq.getName()
540         // + " from: " + e.getDasSource().getNickname());
541       }
542     }
543   }
544
545   private void setGuiNoDassourceActive()
546   {
547
548     if (af != null)
549     {
550       af.setProgressBar(
551               MessageManager.getString("status.no_das_sources_active"),
552               startTime);
553     }
554     if (getFeatSettings() != null)
555     {
556       fsettings.noDasSourceActive();
557     }
558   }
559
560   /**
561    * Update our fsettings dialog reference if we didn't have one when we were
562    * first initialised.
563    * 
564    * @return fsettings
565    */
566   private FeatureSettings getFeatSettings()
567   {
568     if (fsettings == null)
569     {
570       if (af != null)
571       {
572         fsettings = af.featureSettings;
573       }
574     }
575     return fsettings;
576   }
577
578   public void cancel()
579   {
580     if (af != null)
581     {
582       af.setProgressBar(MessageManager
583               .getString("status.das_feature_fetching_cancelled"),
584               startTime);
585     }
586     cancelled = true;
587   }
588
589   int sourcesRemaining = 0;
590
591   private boolean running = false;
592
593   private void setGuiFetchComplete()
594   {
595     running = false;
596     if (!cancelled && af != null)
597     {
598       // only update the progress bar if we've completed the fetch normally
599       af.setProgressBar(MessageManager
600               .getString("status.das_feature_fetching_complete"), startTime);
601     }
602
603     if (af != null && af.featureSettings != null)
604     {
605       af.featureSettings.discoverAllFeatureData();
606     }
607
608     if (getFeatSettings() != null)
609     {
610       fsettings.complete();
611     }
612   }
613
614   void featuresAdded(Set<SequenceI> seqs)
615   {
616     if (af == null)
617     {
618       // no gui to update with features.
619       return;
620     }
621     af.getFeatureRenderer().featuresAdded();
622
623     int start = af.getViewport().getStartSeq();
624     int end = af.getViewport().getEndSeq();
625     int index;
626     for (index = start; index < end; index++)
627     {
628       for (SequenceI seq : seqs)
629       {
630         if (seq == af.getViewport().getAlignment().getSequenceAt(index)
631                 .getDatasetSequence())
632         {
633           af.alignPanel.paintAlignment(true);
634           index = end;
635           break;
636         }
637       }
638     }
639   }
640
641   Object[] nextSequence(jalviewSourceI dasSource, SequenceI seq)
642   {
643     if (cancelled)
644     {
645       return null;
646     }
647     DBRefEntry[] uprefs = DBRefUtils.selectRefs(
648             seq.getDBRefs(), new String[] {
649             // jalview.datamodel.DBRefSource.PDB,
650             DBRefSource.UNIPROT,
651             // jalview.datamodel.DBRefSource.EMBL - not tested on any EMBL coord
652             // sys sources
653             });
654     // TODO: minimal list of DAS queries to make by querying with untyped ID if
655     // distinct from any typed IDs
656
657     List<DBRefEntry> ids = new ArrayList<DBRefEntry>();
658     List<String> qstring = new ArrayList<String>();
659     boolean dasCoordSysFound = false;
660
661     if (uprefs != null)
662     {
663       // do any of these ids match the source's coordinate system ?
664       for (int j = 0; !dasCoordSysFound && j < uprefs.length; j++)
665       {
666
667         for (COORDINATES csys : dasSource.getVersion().getCOORDINATES())
668         {
669           if (DBRefUtils.isDasCoordinateSystem(
670                   csys.getAuthority(), uprefs[j]))
671           {
672             debug("Launched fetcher for coordinate system "
673                     + csys.getAuthority());
674             // Will have to pass any mapping information to the fetcher
675             // - the start/end for the DBRefEntry may not be the same as the
676             // sequence's start/end
677
678             System.out.println(seq.getName() + " "
679                     + (seq.getDatasetSequence() == null) + " "
680                     + csys.getUri());
681
682             dasCoordSysFound = true; // break's out of the loop
683             ids.add(uprefs[j]);
684             qstring.add(uprefs[j].getAccessionId());
685           }
686           else
687           {
688             System.out.println("IGNORE " + csys.getAuthority());
689           }
690         }
691       }
692     }
693
694     if (!dasCoordSysFound)
695     {
696       String id = null;
697       // try and use the name as the sequence id
698       if (seq.getName().indexOf("|") > -1)
699       {
700         id = seq.getName().substring(seq.getName().lastIndexOf("|") + 1);
701         if (id.trim().length() < 4)
702         {
703           // hack - we regard a significant ID as being at least 4
704           // non-whitespace characters
705           id = seq.getName().substring(0, seq.getName().lastIndexOf("|"));
706           if (id.indexOf("|") > -1)
707           {
708             id = id.substring(id.lastIndexOf("|") + 1);
709           }
710         }
711       }
712       else
713       {
714         id = seq.getName();
715       }
716       if (id != null)
717       {
718         DBRefEntry dbre = new DBRefEntry();
719         dbre.setAccessionId(id);
720         // Should try to call a general feature fetcher that
721         // queries many sources with name to discover applicable ID references
722         ids.add(dbre);
723         qstring.add(dbre.getAccessionId());
724       }
725     }
726
727     return new Object[] { ids, qstring };
728   }
729
730   /**
731    * examine the given sequence feature to determine if it should actually be
732    * turned into sequence annotation or database cross references rather than a
733    * simple sequence feature.
734    * 
735    * @param seq
736    *          the sequence to annotate
737    * @param f
738    *          the jalview sequence feature generated from the DAS feature
739    * @param map
740    *          the sequence feature attributes
741    * @param source
742    *          the source that emitted the feature
743    * @return true if feature was consumed as another kind of annotation.
744    */
745   protected boolean parseSeqFeature(SequenceI seq, SequenceFeature f,
746           FEATURE feature, jalviewSourceI source)
747   {
748     SequenceI mseq = seq;
749     while (seq.getDatasetSequence() != null)
750     {
751       seq = seq.getDatasetSequence();
752     }
753     if (f.getType() != null)
754     {
755       String type = f.getType();
756       if (type.equalsIgnoreCase("protein_name"))
757       {
758         // parse name onto the alignment sequence or the dataset sequence.
759         if (seq.getDescription() == null
760                 || seq.getDescription().trim().length() == 0)
761         {
762           // could look at the note series to pick out the first long name, for
763           // the moment just use the whole description string
764           seq.setDescription(f.getDescription());
765         }
766         if (mseq.getDescription() == null
767                 || mseq.getDescription().trim().length() == 0)
768         {
769           // could look at the note series to pick out the first long name, for
770           // the moment just use the whole description string
771           mseq.setDescription(f.getDescription());
772         }
773         return true;
774       }
775       // check if source has biosapiens or other sequence ontology label
776       if (type.equalsIgnoreCase("DBXREF") || type.equalsIgnoreCase("DBREF"))
777       {
778         // try to parse the accession out
779
780         DBRefEntry dbr = new DBRefEntry();
781         dbr.setVersion(source.getTitle());
782         StringTokenizer st = new StringTokenizer(f.getDescription(), ":");
783         if (st.hasMoreTokens())
784         {
785           dbr.setSource(st.nextToken());
786         }
787         if (st.hasMoreTokens())
788         {
789           dbr.setAccessionId(st.nextToken());
790         }
791         seq.addDBRef(dbr);
792
793         if (f.links != null && f.links.size() > 0)
794         {
795           // feature is also appended to enable links to be seen.
796           // TODO: consider extending dbrefs to have their own links ?
797           // TODO: new feature: extract dbref links from DAS servers and add the
798           // URL pattern to the list of DB name associated links in the user's
799           // preferences ?
800           // for the moment - just fix up the existing feature so it displays
801           // correctly.
802           // f.setType(dbr.getSource());
803           // f.setDescription();
804           f.setValue("linkonly", Boolean.TRUE);
805           // f.setDescription("");
806           Vector newlinks = new Vector();
807           Enumeration it = f.links.elements();
808           while (it.hasMoreElements())
809           {
810             String elm;
811             UrlLink urllink = new UrlLink(elm = (String) it.nextElement());
812             if (urllink.isValid())
813             {
814               urllink.setLabel(f.getDescription());
815               newlinks.addElement(urllink.toString());
816             }
817             else
818             {
819               // couldn't parse the link properly. Keep it anyway - just in
820               // case.
821               debug("couldn't parse link string - " + elm);
822               newlinks.addElement(elm);
823             }
824           }
825           f.links = newlinks;
826           seq.addSequenceFeature(f);
827         }
828         return true;
829       }
830     }
831     return false;
832   }
833
834   /**
835    * creates a jalview sequence feature from a das feature document
836    * 
837    * @param feat
838    * @return sequence feature object created using dasfeature information
839    */
840   SequenceFeature newSequenceFeature(FEATURE feat, String nickname)
841   {
842     if (feat == null)
843     {
844       return null;
845     }
846     try
847     {
848       /**
849        * Different qNames for a DAS Feature - are string keys to the HashMaps in
850        * features "METHOD") || qName.equals("TYPE") || qName.equals("START") ||
851        * qName.equals("END") || qName.equals("NOTE") || qName.equals("LINK") ||
852        * qName.equals("SCORE")
853        */
854       String desc = new String();
855       if (feat.getNOTE() != null)
856       {
857         for (String note : feat.getNOTE())
858         {
859           desc += note;
860         }
861       }
862
863       int start = 0, end = 0;
864       float score = 0f;
865
866       try
867       {
868         start = Integer.parseInt(feat.getSTART().toString());
869       } catch (Exception ex)
870       {
871       }
872       try
873       {
874         end = Integer.parseInt(feat.getEND().toString());
875       } catch (Exception ex)
876       {
877       }
878       try
879       {
880         Object scr = feat.getSCORE();
881         if (scr != null)
882         {
883           score = (float) Double.parseDouble(scr.toString());
884
885         }
886       } catch (Exception ex)
887       {
888       }
889
890       SequenceFeature f = new SequenceFeature(
891               getTypeString(feat.getTYPE()), desc, start, end, score,
892               nickname);
893
894       if (feat.getLINK() != null)
895       {
896         for (LINK link : feat.getLINK())
897         {
898           // Do not put feature extent in link text for non-positional features
899           if (f.begin == 0 && f.end == 0)
900           {
901             f.addLink(f.getType() + " " + link.getContent() + "|"
902                     + link.getHref());
903           }
904           else
905           {
906             f.addLink(f.getType() + " " + f.begin + "_" + f.end + " "
907                     + link.getContent() + "|" + link.getHref());
908           }
909         }
910       }
911
912       return f;
913     } catch (Exception e)
914     {
915       System.out.println("ERRR " + e);
916       e.printStackTrace();
917       System.out.println("############");
918       debug("Failed to parse " + feat.toString(), e);
919       return null;
920     }
921   }
922
923   private String getTypeString(TYPE type)
924   {
925     return type.getContent();
926   }
927
928   public boolean isRunning()
929   {
930     return running;
931   }
932
933 }