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