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