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