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