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