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