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