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