JAL-2089 patch broken merge to master for Release 2.10.0b1
[jalview.git] / src / jalview / ws / DasSequenceFeatureFetcher.java
index bef6425..7e069e3 100644 (file)
-/*\r
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)\r
- * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, G Barton, M Clamp, S Searle\r
- * \r
- * This file is part of Jalview.\r
- * \r
- * Jalview is free software: you can redistribute it and/or\r
- * modify it under the terms of the GNU General Public License \r
- * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\r
- * \r
- * Jalview is distributed in the hope that it will be useful, but \r
- * WITHOUT ANY WARRANTY; without even the implied warranty \r
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR \r
- * PURPOSE.  See the GNU General Public License for more details.\r
- * \r
- * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.\r
- */\r
-package jalview.ws;\r
-\r
-import jalview.bin.Cache;\r
-import jalview.datamodel.DBRefEntry;\r
-import jalview.datamodel.SequenceFeature;\r
-import jalview.datamodel.SequenceI;\r
-import jalview.gui.AlignFrame;\r
-import jalview.gui.Desktop;\r
-import jalview.gui.FeatureSettings;\r
-import jalview.util.UrlLink;\r
-import jalview.ws.dbsources.das.api.DasSourceRegistryI;\r
-import jalview.ws.dbsources.das.api.jalviewSourceI;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.Enumeration;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.Iterator;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Set;\r
-import java.util.StringTokenizer;\r
-import java.util.Vector;\r
-\r
-import javax.swing.JOptionPane;\r
-\r
-import org.biodas.jdas.client.FeaturesClient;\r
-import org.biodas.jdas.client.adapters.features.DasGFFAdapter;\r
-import org.biodas.jdas.client.adapters.features.DasGFFAdapter.GFFAdapter;\r
-import org.biodas.jdas.client.threads.FeaturesClientMultipleSources;\r
-import org.biodas.jdas.schema.features.ERRORSEGMENT;\r
-import org.biodas.jdas.schema.features.FEATURE;\r
-import org.biodas.jdas.schema.features.LINK;\r
-import org.biodas.jdas.schema.features.SEGMENT;\r
-import org.biodas.jdas.schema.features.TYPE;\r
-import org.biodas.jdas.schema.features.UNKNOWNFEATURE;\r
-import org.biodas.jdas.schema.features.UNKNOWNSEGMENT;\r
-import org.biodas.jdas.schema.sources.COORDINATES;\r
-\r
-/**\r
- * DOCUMENT ME!\r
- * \r
- * @author $author$\r
- * @version $Revision$\r
- */\r
-public class DasSequenceFeatureFetcher\r
-{\r
-  SequenceI[] sequences;\r
-\r
-  AlignFrame af;\r
-\r
-  FeatureSettings fsettings;\r
-\r
-  StringBuffer sbuffer = new StringBuffer();\r
-\r
-  List<jalviewSourceI> selectedSources;\r
-\r
-  boolean cancelled = false;\r
-\r
-  private void debug(String mesg)\r
-  {\r
-    debug(mesg, null);\r
-  }\r
-\r
-  private void debug(String mesg, Exception e)\r
-  {\r
-    if (Cache.log != null)\r
-    {\r
-      Cache.log.debug(mesg, e);\r
-    }\r
-    else\r
-    {\r
-      System.err.println(mesg);\r
-      if (e != null)\r
-      {\r
-        e.printStackTrace();\r
-      }\r
-    }\r
-  }\r
-\r
-  long startTime;\r
-\r
-  private DasSourceRegistryI sourceRegistry;\r
-  private boolean useJDASMultiThread=true;\r
-  /**\r
-   * Creates a new SequenceFeatureFetcher object. Uses default\r
-   * \r
-   * @param align\r
-   *          DOCUMENT ME!\r
-   * @param ap\r
-   *          DOCUMENT ME!\r
-   */\r
-  public DasSequenceFeatureFetcher(SequenceI[] sequences,\r
-          FeatureSettings fsettings, Vector selectedSources)\r
-  {\r
-    this(sequences, fsettings, selectedSources, true, true, true);\r
-  }\r
-\r
-  public DasSequenceFeatureFetcher(SequenceI[] oursequences,\r
-          FeatureSettings fsettings, List<jalviewSourceI> selectedSources2,\r
-          boolean checkDbrefs, boolean promptFetchDbrefs)\r
-  {\r
-    this(oursequences,fsettings,selectedSources2,checkDbrefs,promptFetchDbrefs,true);\r
-  }\r
-  public DasSequenceFeatureFetcher(SequenceI[] oursequences,\r
-          FeatureSettings fsettings, List<jalviewSourceI> selectedSources2,\r
-          boolean checkDbrefs, boolean promptFetchDbrefs, boolean useJDasMultiThread)\r
-  {\r
-    this.useJDASMultiThread=useJDasMultiThread;\r
-    this.selectedSources = new ArrayList<jalviewSourceI>();\r
-    // filter both sequences and sources to eliminate duplicates\r
-    for (jalviewSourceI src : selectedSources2)\r
-    {\r
-      if (!selectedSources.contains(src))\r
-      {\r
-        selectedSources.add(src);\r
-      }\r
-      ;\r
-    }\r
-    Vector sqs = new Vector();\r
-    for (int i = 0; i < oursequences.length; i++)\r
-    {\r
-      if (!sqs.contains(oursequences[i]))\r
-      {\r
-        sqs.addElement(oursequences[i]);\r
-      }\r
-    }\r
-    sequences = new SequenceI[sqs.size()];\r
-    for (int i = 0; i < sequences.length; i++)\r
-    {\r
-      sequences[i] = (SequenceI) sqs.elementAt(i);\r
-    }\r
-    if (fsettings != null)\r
-    {\r
-      this.fsettings = fsettings;\r
-      this.af = fsettings.af;\r
-      af.setShowSeqFeatures(true);\r
-    }\r
-    int uniprotCount = 0;\r
-    for (jalviewSourceI source : selectedSources)\r
-    {\r
-      for (COORDINATES coords : source.getVersion().getCOORDINATES())\r
-      {\r
-        // TODO: match UniProt coord system canonically (?) - does\r
-        // UniProt==uniprot==UNIPROT ?\r
-        if (coords.getAuthority().toLowerCase().equals("uniprot"))\r
-        {\r
-          uniprotCount++;\r
-          break;\r
-        }\r
-      }\r
-    }\r
-\r
-    int refCount = 0;\r
-    for (int i = 0; i < sequences.length; i++)\r
-    {\r
-      DBRefEntry[] dbref = sequences[i].getDBRef();\r
-      if (dbref != null)\r
-      {\r
-        for (int j = 0; j < dbref.length; j++)\r
-        {\r
-          if (dbref[j].getSource().equals(\r
-                  jalview.datamodel.DBRefSource.UNIPROT))\r
-          {\r
-            refCount++;\r
-            break;\r
-          }\r
-        }\r
-      }\r
-    }\r
-\r
-    if (checkDbrefs && refCount < sequences.length && uniprotCount > 0)\r
-    {\r
-\r
-      int reply = JOptionPane.YES_OPTION;\r
-      if (promptFetchDbrefs)\r
-      {\r
-        reply = JOptionPane\r
-                .showInternalConfirmDialog(\r
-                        Desktop.desktop,\r
-                        "Do you want Jalview to find\n"\r
-                                + "Uniprot Accession ids for given sequence names?",\r
-                        "Find Uniprot Accession Ids",\r
-                        JOptionPane.YES_NO_OPTION,\r
-                        JOptionPane.QUESTION_MESSAGE);\r
-      }\r
-\r
-      if (reply == JOptionPane.YES_OPTION)\r
-      {\r
-        Thread thread = new Thread(new FetchDBRefs());\r
-        thread.start();\r
-      }\r
-      else\r
-      {\r
-        _startFetching();\r
-      }\r
-    }\r
-    else\r
-    {\r
-      _startFetching();\r
-    }\r
-\r
-  }\r
-  private void _startFetching()\r
-  {\r
-    running=true;\r
-    new Thread(new FetchSeqFeatures()).start();\r
-  }\r
-  class FetchSeqFeatures implements Runnable\r
-  {\r
-    public void run()\r
-    {\r
-      startFetching();\r
-      setGuiFetchComplete();\r
-    }\r
-  }\r
-  class FetchDBRefs implements Runnable\r
-  {\r
-    public void run()\r
-    {\r
-      running=true;\r
-      new DBRefFetcher(sequences, af).fetchDBRefs(true);\r
-      startFetching();\r
-      setGuiFetchComplete();\r
-    }\r
-  }\r
-\r
-  /**\r
-   * Spawns Fetcher threads to add features to sequences in the dataset\r
-   */\r
-  void startFetching()\r
-  {\r
-    running=true;\r
-    cancelled = false;\r
-    startTime = System.currentTimeMillis();\r
-    if (af != null)\r
-    {\r
-      af.setProgressBar("Fetching DAS Sequence Features", startTime);\r
-    }\r
-    if (sourceRegistry == null)\r
-    {\r
-      sourceRegistry = Cache.getDasSourceRegistry();\r
-    }\r
-    if (selectedSources == null || selectedSources.size() == 0)\r
-    {\r
-      try\r
-      {\r
-        jalviewSourceI[] sources = sourceRegistry.getSources().toArray(\r
-                new jalviewSourceI[0]);\r
-        String active = jalview.bin.Cache.getDefault("DAS_ACTIVE_SOURCE",\r
-                "uniprot");\r
-        StringTokenizer st = new StringTokenizer(active, "\t");\r
-        selectedSources = new Vector();\r
-        String token;\r
-        while (st.hasMoreTokens())\r
-        {\r
-          token = st.nextToken();\r
-          for (int i = 0; i < sources.length; i++)\r
-          {\r
-            if (sources[i].getTitle().equals(token))\r
-            {\r
-              selectedSources.add(sources[i]);\r
-              break;\r
-            }\r
-          }\r
-        }\r
-      } catch (Exception ex)\r
-      {\r
-        debug("Exception whilst setting default feature sources from registry and local preferences.",\r
-                ex);\r
-      }\r
-    }\r
-\r
-    if (selectedSources == null || selectedSources.size() == 0)\r
-    {\r
-      System.out.println("No DAS Sources active");\r
-      cancelled = true;\r
-      setGuiNoDassourceActive();\r
-      return;\r
-    }\r
-\r
-    sourcesRemaining = selectedSources.size();\r
-    FeaturesClientMultipleSources fc = new FeaturesClientMultipleSources();\r
-    fc.setConnProps(sourceRegistry.getSessionHandler());\r
-    // Now sending requests one at a time to each server\r
-    ArrayList<jalviewSourceI> srcobj = new ArrayList<jalviewSourceI>();\r
-    ArrayList<String> src = new ArrayList<String>();\r
-    List<List<String>> ids = new ArrayList<List<String>>();\r
-    List<List<DBRefEntry>> idobj = new ArrayList<List<DBRefEntry>>();\r
-    List<Map<String, SequenceI>> sqset = new ArrayList<Map<String, SequenceI>>();\r
-    for (jalviewSourceI _sr : selectedSources)\r
-    {\r
-\r
-      Map<String, SequenceI> slist = new HashMap<String, SequenceI>();\r
-      List<DBRefEntry> idob = new ArrayList<DBRefEntry>();\r
-      List<String> qset = new ArrayList<String>();\r
-\r
-      for (SequenceI seq : sequences)\r
-      {\r
-        Object[] idset = nextSequence(_sr, seq);\r
-        if (idset != null)\r
-        {\r
-          List<DBRefEntry> _idob = (List<DBRefEntry>) idset[0];\r
-          List<String> _qset = (List<String>) idset[1];\r
-          if (_idob.size() > 0)\r
-          {\r
-            // add sequence's ref for each id derived from it\r
-            // (space inefficient, but most unambiguous)\r
-            // could replace with hash with _qset values as keys.\r
-            Iterator<DBRefEntry> dbobj = _idob.iterator();\r
-            for (String q : _qset)\r
-            {\r
-              SequenceI osq = slist.get(q);\r
-              DBRefEntry dr = dbobj.next();\r
-              if (osq != null && osq != seq)\r
-              {\r
-                // skip - non-canonical query\r
-              }\r
-              else\r
-              {\r
-                idob.add(dr);\r
-                qset.add(q);\r
-                slist.put(q, seq);\r
-              }\r
-            }\r
-          }\r
-        }\r
-      }\r
-      if (idob.size() > 0)\r
-      {\r
-        srcobj.add(_sr);\r
-        src.add(_sr.getSourceURL());\r
-        ids.add(qset);\r
-        idobj.add(idob);\r
-        sqset.add(slist);\r
-      }\r
-    }\r
-    Map<String, Map<List<String>, Exception>> errors = new HashMap<String, Map<List<String>, Exception>>();\r
-    Map<String, Map<List<String>, DasGFFAdapter>> results = new HashMap<String, Map<List<String>, DasGFFAdapter>>();\r
-    if (!useJDASMultiThread)\r
-    {\r
-      Iterator<String> sources=src.iterator();\r
-      // iterate over each query for each source and do each one individually\r
-      for (List<String> idl:ids)\r
-      {\r
-        String source=sources.next();\r
-        FeaturesClient featuresc=new FeaturesClient(sourceRegistry.getSessionHandler().getConnectionPropertyProviderFor(source));\r
-        for (String id:idl)\r
-        {\r
-          List<String> qid=Arrays.asList(new String[] { id});\r
-          try {\r
-            DasGFFAdapter dga=featuresc.fetchData(source, qid);\r
-            Map<List<String>,DasGFFAdapter> ers=results.get(source);\r
-            if (ers==null)\r
-            {\r
-              results.put(source, ers=new HashMap<List<String>,DasGFFAdapter>());\r
-            }\r
-            ers.put(qid, dga);\r
-          } catch (Exception ex)\r
-          {\r
-            Map<List<String>,Exception> ers=errors.get(source);\r
-            if (ers==null)\r
-            {\r
-              errors.put(source, ers=new HashMap<List<String>,Exception>());\r
-            }\r
-            ers.put(qid, ex);\r
-          }\r
-        }\r
-      }\r
-    } else {\r
-      // pass them all at once\r
-    fc.fetchData(src, ids, false, results, errors);\r
-    fc.shutDown();\r
-    while (!fc.isTerminated())\r
-    {\r
-      try\r
-      {\r
-        Thread.sleep(200);\r
-      } catch (InterruptedException x)\r
-      {\r
-\r
-      }\r
-    }\r
-    }\r
-    Iterator<List<String>> idset = ids.iterator();\r
-    Iterator<List<DBRefEntry>> idobjset = idobj.iterator();\r
-    Iterator<Map<String, SequenceI>> seqset = sqset.iterator();\r
-    for (jalviewSourceI source : srcobj)\r
-    {\r
-      processResponse(seqset.next(), source, idset.next(), idobjset.next(),\r
-              results.get(source.getSourceURL()),\r
-              errors.get(source.getSourceURL()));\r
-    }\r
-  }\r
-\r
-  private void processResponse(Map<String, SequenceI> sequencemap,\r
-          jalviewSourceI jvsource, List<String> ids,\r
-          List<DBRefEntry> idobj, Map<List<String>, DasGFFAdapter> results,\r
-          Map<List<String>, Exception> errors)\r
-  {\r
-    Set<SequenceI> sequences = new HashSet<SequenceI>();\r
-    String source = jvsource.getSourceURL();\r
-    // process features\r
-    DasGFFAdapter result = (results == null) ? null : results.get(ids);\r
-    Exception error = (errors == null) ? null : errors.get(ids);\r
-    if (result == null)\r
-    {\r
-      debug("das source " + source + " could not be contacted. "\r
-              + (error == null ? "" : error.toString()));\r
-    }\r
-    else\r
-    {\r
-\r
-      GFFAdapter gff = result.getGFF();\r
-      List<SEGMENT> segments = gff.getSegments();\r
-      List<ERRORSEGMENT> errorsegs = gff.getErrorSegments();\r
-      List<UNKNOWNFEATURE> unkfeats = gff.getUnknownFeatures();\r
-      List<UNKNOWNSEGMENT> unksegs = gff.getUnknownSegments();\r
-      debug("das source " + source + " returned " + gff.getTotal()\r
-              + " responses. " + (errorsegs != null ? errorsegs.size() : 0)\r
-              + " were incorrect segment queries, "\r
-              + (unkfeats != null ? unkfeats.size() : 0)\r
-              + " were unknown features "\r
-              + (unksegs != null ? unksegs.size() : 0)\r
-              + " were unknown segments and "\r
-              + (segments != null ? segments.size() : 0)\r
-              + " were segment responses.");\r
-      Iterator<DBRefEntry> dbr = idobj.iterator();\r
-      if (segments != null)\r
-      {\r
-        for (SEGMENT seg : segments)\r
-        {\r
-          String id = seg.getId();\r
-          DBRefEntry dbref = idobj.get(ids.indexOf(id));\r
-          SequenceI sequence = sequencemap.get(id);\r
-          boolean added = false;\r
-          sequences.add(sequence);\r
-\r
-          for (FEATURE feat : seg.getFEATURE())\r
-          {\r
-            // standard DAS feature-> jalview sequence feature transformation\r
-            SequenceFeature f = newSequenceFeature(feat, jvsource.getTitle());\r
-            if (!parseSeqFeature(sequence, f, feat, jvsource))\r
-            {\r
-              if (dbref.getMap() != null && f.getBegin() > 0\r
-                      && f.getEnd() > 0)\r
-              {\r
-                debug("mapping from " + f.getBegin() + " - " + f.getEnd());\r
-                SequenceFeature vf[] = null;\r
-\r
-                try\r
-                {\r
-                  vf = dbref.getMap().locateFeature(f);\r
-                } catch (Exception ex)\r
-                {\r
-                  Cache.log\r
-                          .info("Error in 'experimental' mapping of features. Please try to reproduce and then report info to jalview-discuss@jalview.org.");\r
-                  Cache.log.info("Mapping feature from " + f.getBegin()\r
-                          + " to " + f.getEnd() + " in dbref "\r
-                          + dbref.getAccessionId() + " in "\r
-                          + dbref.getSource());\r
-                  Cache.log.info("using das Source " + source);\r
-                  Cache.log.info("Exception", ex);\r
-                }\r
-\r
-                if (vf != null)\r
-                {\r
-                  for (int v = 0; v < vf.length; v++)\r
-                  {\r
-                    debug("mapping to " + v + ": " + vf[v].getBegin()\r
-                            + " - " + vf[v].getEnd());\r
-                    sequence.addSequenceFeature(vf[v]);\r
-                  }\r
-                }\r
-              }\r
-              else\r
-              {\r
-                sequence.addSequenceFeature(f);\r
-              }\r
-            }\r
-          }\r
-        }\r
-        featuresAdded(sequences);\r
-      }\r
-      else\r
-      {\r
-        // System.out.println("No features found for " + seq.getName()\r
-        // + " from: " + e.getDasSource().getNickname());\r
-      }\r
-    }\r
-  }\r
-\r
-  private void setGuiNoDassourceActive()\r
-  {\r
-\r
-    if (af != null)\r
-    {\r
-      af.setProgressBar("No DAS Sources Active", startTime);\r
-    }\r
-    if (getFeatSettings() != null)\r
-    {\r
-      fsettings.noDasSourceActive();\r
-    }\r
-  }\r
-\r
-  /**\r
-   * Update our fsettings dialog reference if we didn't have one when we were\r
-   * first initialised.\r
-   * \r
-   * @return fsettings\r
-   */\r
-  private FeatureSettings getFeatSettings()\r
-  {\r
-    if (fsettings == null)\r
-    {\r
-      if (af != null)\r
-      {\r
-        fsettings = af.featureSettings;\r
-      }\r
-    }\r
-    return fsettings;\r
-  }\r
-\r
-  public void cancel()\r
-  {\r
-    if (af != null)\r
-    {\r
-      af.setProgressBar("DAS Feature Fetching Cancelled", startTime);\r
-    }\r
-    cancelled = true;\r
-  }\r
-\r
-  int sourcesRemaining = 0;\r
-  private boolean running=false;\r
-  private void setGuiFetchComplete()\r
-  {\r
-    running=false;\r
-    if (!cancelled && af != null)\r
-    {\r
-      // only update the progress bar if we've completed the fetch normally\r
-      af.setProgressBar("DAS Feature Fetching Complete", startTime);\r
-    }\r
-\r
-    if (af != null && af.featureSettings != null)\r
-    {\r
-      af.featureSettings.setTableData();\r
-    }\r
-\r
-    if (getFeatSettings() != null)\r
-    {\r
-      fsettings.complete();\r
-    }\r
-  }\r
-\r
-  void featuresAdded(Set<SequenceI> seqs)\r
-  {\r
-    if (af == null)\r
-    {\r
-      // no gui to update with features.\r
-      return;\r
-    }\r
-    af.getFeatureRenderer().featuresAdded();\r
-\r
-    int start = af.getViewport().getStartSeq();\r
-    int end = af.getViewport().getEndSeq();\r
-    int index;\r
-    for (index = start; index < end; index++)\r
-    {\r
-      for (SequenceI seq : seqs)\r
-      {\r
-        if (seq == af.getViewport().getAlignment().getSequenceAt(index)\r
-                .getDatasetSequence())\r
-        {\r
-          af.alignPanel.paintAlignment(true);\r
-          index = end;\r
-          break;\r
-        }\r
-      }\r
-    }\r
-  }\r
-\r
-  Object[] nextSequence(jalviewSourceI dasSource, SequenceI seq)\r
-  {\r
-    if (cancelled)\r
-      return null;\r
-    DBRefEntry[] uprefs = jalview.util.DBRefUtils.selectRefs(\r
-            seq.getDBRef(), new String[]\r
-            {\r
-            // jalview.datamodel.DBRefSource.PDB,\r
-            jalview.datamodel.DBRefSource.UNIPROT,\r
-            // jalview.datamodel.DBRefSource.EMBL - not tested on any EMBL coord\r
-            // sys sources\r
-            });\r
-    // TODO: minimal list of DAS queries to make by querying with untyped ID if\r
-    // distinct from any typed IDs\r
-\r
-    List<DBRefEntry> ids = new ArrayList<DBRefEntry>();\r
-    List<String> qstring = new ArrayList<String>();\r
-    boolean dasCoordSysFound = false;\r
-\r
-    if (uprefs != null)\r
-    {\r
-      // do any of these ids match the source's coordinate system ?\r
-      for (int j = 0; !dasCoordSysFound && j < uprefs.length; j++)\r
-      {\r
-\r
-        for (COORDINATES csys : dasSource.getVersion().getCOORDINATES())\r
-        {\r
-          if (jalview.util.DBRefUtils.isDasCoordinateSystem(\r
-                  csys.getAuthority(), uprefs[j]))\r
-          {\r
-            debug("Launched fetcher for coordinate system "\r
-                    + csys.getAuthority());\r
-            // Will have to pass any mapping information to the fetcher\r
-            // - the start/end for the DBRefEntry may not be the same as the\r
-            // sequence's start/end\r
-\r
-            System.out.println(seq.getName() + " "\r
-                    + (seq.getDatasetSequence() == null) + " "\r
-                    + csys.getUri());\r
-\r
-            dasCoordSysFound = true; // break's out of the loop\r
-            ids.add(uprefs[j]);\r
-            qstring.add(uprefs[j].getAccessionId());\r
-          }\r
-          else\r
-            System.out.println("IGNORE " + csys.getAuthority());\r
-        }\r
-      }\r
-    }\r
-\r
-    if (!dasCoordSysFound)\r
-    {\r
-      String id = null;\r
-      // try and use the name as the sequence id\r
-      if (seq.getName().indexOf("|") > -1)\r
-      {\r
-        id = seq.getName().substring(seq.getName().lastIndexOf("|") + 1);\r
-        if (id.trim().length() < 4)\r
-        {\r
-          // hack - we regard a significant ID as being at least 4\r
-          // non-whitespace characters\r
-          id = seq.getName().substring(0, seq.getName().lastIndexOf("|"));\r
-          if (id.indexOf("|") > -1)\r
-          {\r
-            id = id.substring(id.lastIndexOf("|") + 1);\r
-          }\r
-        }\r
-      }\r
-      else\r
-      {\r
-        id = seq.getName();\r
-      }\r
-      if (id != null)\r
-      {\r
-        DBRefEntry dbre = new DBRefEntry();\r
-        dbre.setAccessionId(id);\r
-        // Should try to call a general feature fetcher that\r
-        // queries many sources with name to discover applicable ID references\r
-        ids.add(dbre);\r
-        qstring.add(dbre.getAccessionId());\r
-      }\r
-    }\r
-\r
-    return new Object[]\r
-    { ids, qstring };\r
-  }\r
-\r
-  /**\r
-   * examine the given sequence feature to determine if it should actually be\r
-   * turned into sequence annotation or database cross references rather than a\r
-   * simple sequence feature.\r
-   * \r
-   * @param seq\r
-   *          the sequence to annotate\r
-   * @param f\r
-   *          the jalview sequence feature generated from the DAS feature\r
-   * @param map\r
-   *          the sequence feature attributes\r
-   * @param source\r
-   *          the source that emitted the feature\r
-   * @return true if feature was consumed as another kind of annotation.\r
-   */\r
-  protected boolean parseSeqFeature(SequenceI seq, SequenceFeature f,\r
-          FEATURE feature, jalviewSourceI source)\r
-  {\r
-    SequenceI mseq = seq;\r
-    while (seq.getDatasetSequence() != null)\r
-    {\r
-      seq = seq.getDatasetSequence();\r
-    }\r
-    if (f.getType() != null)\r
-    {\r
-      String type = f.getType();\r
-      if (type.equalsIgnoreCase("protein_name"))\r
-      {\r
-        // parse name onto the alignment sequence or the dataset sequence.\r
-        if (seq.getDescription() == null\r
-                || seq.getDescription().trim().length() == 0)\r
-        {\r
-          // could look at the note series to pick out the first long name, for\r
-          // the moment just use the whole description string\r
-          seq.setDescription(f.getDescription());\r
-        }\r
-        if (mseq.getDescription() == null\r
-                || mseq.getDescription().trim().length() == 0)\r
-        {\r
-          // could look at the note series to pick out the first long name, for\r
-          // the moment just use the whole description string\r
-          mseq.setDescription(f.getDescription());\r
-        }\r
-        return true;\r
-      }\r
-      // check if source has biosapiens or other sequence ontology label\r
-      if (type.equalsIgnoreCase("DBXREF") || type.equalsIgnoreCase("DBREF"))\r
-      {\r
-        // try to parse the accession out\r
-\r
-        DBRefEntry dbr = new DBRefEntry();\r
-        dbr.setVersion(source.getTitle());\r
-        StringTokenizer st = new StringTokenizer(f.getDescription(), ":");\r
-        if (st.hasMoreTokens())\r
-        {\r
-          dbr.setSource(st.nextToken());\r
-        }\r
-        if (st.hasMoreTokens())\r
-        {\r
-          dbr.setAccessionId(st.nextToken());\r
-        }\r
-        seq.addDBRef(dbr);\r
-\r
-        if (f.links != null && f.links.size() > 0)\r
-        {\r
-          // feature is also appended to enable links to be seen.\r
-          // TODO: consider extending dbrefs to have their own links ?\r
-          // TODO: new feature: extract dbref links from DAS servers and add the\r
-          // URL pattern to the list of DB name associated links in the user's\r
-          // preferences ?\r
-          // for the moment - just fix up the existing feature so it displays\r
-          // correctly.\r
-          // f.setType(dbr.getSource());\r
-          // f.setDescription();\r
-          f.setValue("linkonly", Boolean.TRUE);\r
-          // f.setDescription("");\r
-          Vector newlinks = new Vector();\r
-          Enumeration it = f.links.elements();\r
-          while (it.hasMoreElements())\r
-          {\r
-            String elm;\r
-            UrlLink urllink = new UrlLink(elm = (String) it.nextElement());\r
-            if (urllink.isValid())\r
-            {\r
-              urllink.setLabel(f.getDescription());\r
-              newlinks.addElement(urllink.toString());\r
-            }\r
-            else\r
-            {\r
-              // couldn't parse the link properly. Keep it anyway - just in\r
-              // case.\r
-              debug("couldn't parse link string - " + elm);\r
-              newlinks.addElement(elm);\r
-            }\r
-          }\r
-          f.links = newlinks;\r
-          seq.addSequenceFeature(f);\r
-        }\r
-        return true;\r
-      }\r
-    }\r
-    return false;\r
-  }\r
-\r
-  /**\r
-   * creates a jalview sequence feature from a das feature document\r
-   * \r
-   * @param feat\r
-   * @return sequence feature object created using dasfeature information\r
-   */\r
-  SequenceFeature newSequenceFeature(FEATURE feat, String nickname)\r
-  {\r
-    if (feat == null)\r
-    {\r
-      return null;\r
-    }\r
-    try\r
-    {\r
-      /**\r
-       * Different qNames for a DAS Feature - are string keys to the HashMaps in\r
-       * features "METHOD") || qName.equals("TYPE") || qName.equals("START") ||\r
-       * qName.equals("END") || qName.equals("NOTE") || qName.equals("LINK") ||\r
-       * qName.equals("SCORE")\r
-       */\r
-      String desc = new String();\r
-      if (feat.getNOTE() != null)\r
-      {\r
-        for (String note : feat.getNOTE())\r
-        {\r
-          desc += (String) note;\r
-        }\r
-      }\r
-\r
-      int start = 0, end = 0;\r
-      float score = 0f;\r
-\r
-      try\r
-      {\r
-        start = Integer.parseInt(feat.getSTART().toString());\r
-      } catch (Exception ex)\r
-      {\r
-      }\r
-      try\r
-      {\r
-        end = Integer.parseInt(feat.getEND().toString());\r
-      } catch (Exception ex)\r
-      {\r
-      }\r
-      try\r
-      {\r
-        Object scr = feat.getSCORE();\r
-        if (scr != null)\r
-        {\r
-          score = (float) Double.parseDouble(scr.toString());\r
-\r
-        }\r
-      } catch (Exception ex)\r
-      {\r
-      }\r
-\r
-      SequenceFeature f = new SequenceFeature(\r
-              getTypeString(feat.getTYPE()), desc, start, end, score,\r
-              nickname);\r
-\r
-      if (feat.getLINK() != null)\r
-      {\r
-        for (LINK link : feat.getLINK())\r
-        {\r
-          // Do not put feature extent in link text for non-positional features\r
-          if (f.begin == 0 && f.end == 0)\r
-          {\r
-            f.addLink(f.getType() + " " + link.getContent() + "|"\r
-                    + link.getHref());\r
-          }\r
-          else\r
-          {\r
-            f.addLink(f.getType() + " " + f.begin + "_" + f.end + " "\r
-                    + link.getContent() + "|" + link.getHref());\r
-          }\r
-        }\r
-      }\r
-\r
-      return f;\r
-    } catch (Exception e)\r
-    {\r
-      System.out.println("ERRR " + e);\r
-      e.printStackTrace();\r
-      System.out.println("############");\r
-      debug("Failed to parse " + feat.toString(), e);\r
-      return null;\r
-    }\r
-  }\r
-\r
-  private String getTypeString(TYPE type)\r
-  {\r
-    return type.getContent();\r
-  }\r
-\r
-  public boolean isRunning()\r
-  {\r
-    return running;\r
-  }\r
-\r
-}\r
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ws;
+
+import jalview.bin.Cache;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
+import jalview.gui.FeatureSettings;
+import jalview.util.DBRefUtils;
+import jalview.util.MessageManager;
+import jalview.util.UrlLink;
+import jalview.ws.dbsources.das.api.DasSourceRegistryI;
+import jalview.ws.dbsources.das.api.jalviewSourceI;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.swing.JOptionPane;
+
+import org.biodas.jdas.client.FeaturesClient;
+import org.biodas.jdas.client.adapters.features.DasGFFAdapter;
+import org.biodas.jdas.client.adapters.features.DasGFFAdapter.GFFAdapter;
+import org.biodas.jdas.client.threads.FeaturesClientMultipleSources;
+import org.biodas.jdas.schema.features.ERRORSEGMENT;
+import org.biodas.jdas.schema.features.FEATURE;
+import org.biodas.jdas.schema.features.LINK;
+import org.biodas.jdas.schema.features.SEGMENT;
+import org.biodas.jdas.schema.features.TYPE;
+import org.biodas.jdas.schema.features.UNKNOWNFEATURE;
+import org.biodas.jdas.schema.features.UNKNOWNSEGMENT;
+import org.biodas.jdas.schema.sources.COORDINATES;
+
+/**
+ * DOCUMENT ME!
+ * 
+ * @author $author$
+ * @version $Revision$
+ */
+public class DasSequenceFeatureFetcher
+{
+  SequenceI[] sequences;
+
+  AlignFrame af;
+
+  FeatureSettings fsettings;
+
+  StringBuffer sbuffer = new StringBuffer();
+
+  List<jalviewSourceI> selectedSources;
+
+  boolean cancelled = false;
+
+  private void debug(String mesg)
+  {
+    debug(mesg, null);
+  }
+
+  private void debug(String mesg, Exception e)
+  {
+    if (Cache.log != null)
+    {
+      Cache.log.debug(mesg, e);
+    }
+    else
+    {
+      System.err.println(mesg);
+      if (e != null)
+      {
+        e.printStackTrace();
+      }
+    }
+  }
+
+  long startTime;
+
+  private DasSourceRegistryI sourceRegistry;
+
+  private boolean useJDASMultiThread = true;
+
+  /**
+   * Creates a new SequenceFeatureFetcher object. Uses default
+   * 
+   * @param align
+   *          DOCUMENT ME!
+   * @param ap
+   *          DOCUMENT ME!
+   */
+  public DasSequenceFeatureFetcher(SequenceI[] sequences,
+          FeatureSettings fsettings, Vector selectedSources)
+  {
+    this(sequences, fsettings, selectedSources, true, true, true);
+  }
+
+  public DasSequenceFeatureFetcher(SequenceI[] oursequences,
+          FeatureSettings fsettings, List<jalviewSourceI> selectedSources2,
+          boolean checkDbrefs, boolean promptFetchDbrefs)
+  {
+    this(oursequences, fsettings, selectedSources2, checkDbrefs,
+            promptFetchDbrefs, true);
+  }
+
+  public DasSequenceFeatureFetcher(SequenceI[] oursequences,
+          FeatureSettings fsettings, List<jalviewSourceI> selectedSources2,
+          boolean checkDbrefs, boolean promptFetchDbrefs,
+          boolean useJDasMultiThread)
+  {
+    this.useJDASMultiThread = useJDasMultiThread;
+    this.selectedSources = new ArrayList<jalviewSourceI>();
+    // filter both sequences and sources to eliminate duplicates
+    for (jalviewSourceI src : selectedSources2)
+    {
+      if (!selectedSources.contains(src))
+      {
+        selectedSources.add(src);
+      }
+      ;
+    }
+    Vector sqs = new Vector();
+    for (int i = 0; i < oursequences.length; i++)
+    {
+      if (!sqs.contains(oursequences[i]))
+      {
+        sqs.addElement(oursequences[i]);
+      }
+    }
+    sequences = new SequenceI[sqs.size()];
+    for (int i = 0; i < sequences.length; i++)
+    {
+      sequences[i] = (SequenceI) sqs.elementAt(i);
+    }
+    if (fsettings != null)
+    {
+      this.fsettings = fsettings;
+      this.af = fsettings.af;
+      af.setShowSeqFeatures(true);
+    }
+    int uniprotCount = 0;
+    for (jalviewSourceI source : selectedSources)
+    {
+      for (COORDINATES coords : source.getVersion().getCOORDINATES())
+      {
+        // TODO: match UniProt coord system canonically (?) - does
+        // UniProt==uniprot==UNIPROT ?
+        if (coords.getAuthority().toLowerCase().equals("uniprot"))
+        {
+          uniprotCount++;
+          break;
+        }
+      }
+    }
+
+    int refCount = 0;
+    for (int i = 0; i < sequences.length; i++)
+    {
+      DBRefEntry[] dbref = sequences[i].getDBRefs();
+      if (dbref != null)
+      {
+        for (int j = 0; j < dbref.length; j++)
+        {
+          if (dbref[j].getSource().equals(DBRefSource.UNIPROT))
+          {
+            refCount++;
+            break;
+          }
+        }
+      }
+    }
+
+    if (checkDbrefs && refCount < sequences.length && uniprotCount > 0)
+    {
+
+      int reply = JOptionPane.YES_OPTION;
+      if (promptFetchDbrefs)
+      {
+        reply = JOptionPane
+                .showInternalConfirmDialog(
+                        Desktop.desktop,
+                        MessageManager
+                                .getString("info.you_want_jalview_to_find_uniprot_accessions"),
+                        MessageManager
+                                .getString("label.find_uniprot_accession_ids"),
+                        JOptionPane.YES_NO_OPTION,
+                        JOptionPane.QUESTION_MESSAGE);
+      }
+
+      if (reply == JOptionPane.YES_OPTION)
+      {
+        Thread thread = new Thread(new FetchDBRefs());
+        thread.start();
+      }
+      else
+      {
+        _startFetching();
+      }
+    }
+    else
+    {
+      _startFetching();
+    }
+
+  }
+
+  private void _startFetching()
+  {
+    running = true;
+    new Thread(new FetchSeqFeatures()).start();
+  }
+
+  class FetchSeqFeatures implements Runnable
+  {
+    @Override
+    public void run()
+    {
+      startFetching();
+      setGuiFetchComplete();
+    }
+  }
+
+  class FetchDBRefs implements Runnable
+  {
+    @Override
+    public void run()
+    {
+      running = true;
+      boolean isNucleotide = af.getViewport().getAlignment().isNucleotide();
+      new DBRefFetcher(sequences, af, null, af.featureSettings,
+              isNucleotide).fetchDBRefs(true);
+
+      startFetching();
+      setGuiFetchComplete();
+    }
+  }
+
+  /**
+   * Spawns Fetcher threads to add features to sequences in the dataset
+   */
+  void startFetching()
+  {
+    running = true;
+    cancelled = false;
+    startTime = System.currentTimeMillis();
+    if (af != null)
+    {
+      af.setProgressBar(MessageManager
+              .getString("status.fetching_das_sequence_features"),
+              startTime);
+    }
+    if (sourceRegistry == null)
+    {
+      sourceRegistry = Cache.getDasSourceRegistry();
+    }
+    if (selectedSources == null || selectedSources.size() == 0)
+    {
+      try
+      {
+        jalviewSourceI[] sources = sourceRegistry.getSources().toArray(
+                new jalviewSourceI[0]);
+        String active = Cache.getDefault("DAS_ACTIVE_SOURCE", "uniprot");
+        StringTokenizer st = new StringTokenizer(active, "\t");
+        selectedSources = new Vector();
+        String token;
+        while (st.hasMoreTokens())
+        {
+          token = st.nextToken();
+          for (int i = 0; i < sources.length; i++)
+          {
+            if (sources[i].getTitle().equals(token))
+            {
+              selectedSources.add(sources[i]);
+              break;
+            }
+          }
+        }
+      } catch (Exception ex)
+      {
+        debug("Exception whilst setting default feature sources from registry and local preferences.",
+                ex);
+      }
+    }
+
+    if (selectedSources == null || selectedSources.size() == 0)
+    {
+      System.out.println("No DAS Sources active");
+      cancelled = true;
+      setGuiNoDassourceActive();
+      return;
+    }
+
+    sourcesRemaining = selectedSources.size();
+    FeaturesClientMultipleSources fc = new FeaturesClientMultipleSources();
+    fc.setConnProps(sourceRegistry.getSessionHandler());
+    // Now sending requests one at a time to each server
+    ArrayList<jalviewSourceI> srcobj = new ArrayList<jalviewSourceI>();
+    ArrayList<String> src = new ArrayList<String>();
+    List<List<String>> ids = new ArrayList<List<String>>();
+    List<List<DBRefEntry>> idobj = new ArrayList<List<DBRefEntry>>();
+    List<Map<String, SequenceI>> sqset = new ArrayList<Map<String, SequenceI>>();
+    for (jalviewSourceI _sr : selectedSources)
+    {
+
+      Map<String, SequenceI> slist = new HashMap<String, SequenceI>();
+      List<DBRefEntry> idob = new ArrayList<DBRefEntry>();
+      List<String> qset = new ArrayList<String>();
+
+      for (SequenceI seq : sequences)
+      {
+        Object[] idset = nextSequence(_sr, seq);
+        if (idset != null)
+        {
+          List<DBRefEntry> _idob = (List<DBRefEntry>) idset[0];
+          List<String> _qset = (List<String>) idset[1];
+          if (_idob.size() > 0)
+          {
+            // add sequence's ref for each id derived from it
+            // (space inefficient, but most unambiguous)
+            // could replace with hash with _qset values as keys.
+            Iterator<DBRefEntry> dbobj = _idob.iterator();
+            for (String q : _qset)
+            {
+              SequenceI osq = slist.get(q);
+              DBRefEntry dr = dbobj.next();
+              if (osq != null && osq != seq)
+              {
+                // skip - non-canonical query
+              }
+              else
+              {
+                idob.add(dr);
+                qset.add(q);
+                slist.put(q, seq);
+              }
+            }
+          }
+        }
+      }
+      if (idob.size() > 0)
+      {
+        srcobj.add(_sr);
+        src.add(_sr.getSourceURL());
+        ids.add(qset);
+        idobj.add(idob);
+        sqset.add(slist);
+      }
+    }
+    Map<String, Map<List<String>, Exception>> errors = new HashMap<String, Map<List<String>, Exception>>();
+    Map<String, Map<List<String>, DasGFFAdapter>> results = new HashMap<String, Map<List<String>, DasGFFAdapter>>();
+    if (!useJDASMultiThread)
+    {
+      Iterator<String> sources = src.iterator();
+      // iterate over each query for each source and do each one individually
+      for (List<String> idl : ids)
+      {
+        String source = sources.next();
+        FeaturesClient featuresc = new FeaturesClient(sourceRegistry
+                .getSessionHandler().getConnectionPropertyProviderFor(
+                        source));
+        for (String id : idl)
+        {
+          List<String> qid = Arrays.asList(new String[] { id });
+          try
+          {
+            DasGFFAdapter dga = featuresc.fetchData(source, qid);
+            Map<List<String>, DasGFFAdapter> ers = results.get(source);
+            if (ers == null)
+            {
+              results.put(source,
+                      ers = new HashMap<List<String>, DasGFFAdapter>());
+            }
+            ers.put(qid, dga);
+          } catch (Exception ex)
+          {
+            Map<List<String>, Exception> ers = errors.get(source);
+            if (ers == null)
+            {
+              errors.put(source,
+                      ers = new HashMap<List<String>, Exception>());
+            }
+            ers.put(qid, ex);
+          }
+        }
+      }
+    }
+    else
+    {
+      // pass them all at once
+      fc.fetchData(src, ids, false, results, errors);
+      fc.shutDown();
+      while (!fc.isTerminated())
+      {
+        try
+        {
+          Thread.sleep(200);
+        } catch (InterruptedException x)
+        {
+
+        }
+      }
+    }
+    Iterator<List<String>> idset = ids.iterator();
+    Iterator<List<DBRefEntry>> idobjset = idobj.iterator();
+    Iterator<Map<String, SequenceI>> seqset = sqset.iterator();
+    for (jalviewSourceI source : srcobj)
+    {
+      processResponse(seqset.next(), source, idset.next(), idobjset.next(),
+              results.get(source.getSourceURL()),
+              errors.get(source.getSourceURL()));
+    }
+  }
+
+  private void processResponse(Map<String, SequenceI> sequencemap,
+          jalviewSourceI jvsource, List<String> ids,
+          List<DBRefEntry> idobj, Map<List<String>, DasGFFAdapter> results,
+          Map<List<String>, Exception> errors)
+  {
+    Set<SequenceI> sequences = new HashSet<SequenceI>();
+    String source = jvsource.getSourceURL();
+    // process features
+    DasGFFAdapter result = (results == null) ? null : results.get(ids);
+    Exception error = (errors == null) ? null : errors.get(ids);
+    if (result == null)
+    {
+      debug("das source " + source + " could not be contacted. "
+              + (error == null ? "" : error.toString()));
+    }
+    else
+    {
+
+      GFFAdapter gff = result.getGFF();
+      List<SEGMENT> segments = gff.getSegments();
+      List<ERRORSEGMENT> errorsegs = gff.getErrorSegments();
+      List<UNKNOWNFEATURE> unkfeats = gff.getUnknownFeatures();
+      List<UNKNOWNSEGMENT> unksegs = gff.getUnknownSegments();
+      debug("das source " + source + " returned " + gff.getTotal()
+              + " responses. " + (errorsegs != null ? errorsegs.size() : 0)
+              + " were incorrect segment queries, "
+              + (unkfeats != null ? unkfeats.size() : 0)
+              + " were unknown features "
+              + (unksegs != null ? unksegs.size() : 0)
+              + " were unknown segments and "
+              + (segments != null ? segments.size() : 0)
+              + " were segment responses.");
+      Iterator<DBRefEntry> dbr = idobj.iterator();
+      if (segments != null)
+      {
+        for (SEGMENT seg : segments)
+        {
+          String id = seg.getId();
+          if (ids.indexOf(id) == -1)
+          {
+            id = id.toUpperCase();
+          }
+          DBRefEntry dbref = idobj.get(ids.indexOf(id));
+          SequenceI sequence = sequencemap.get(id);
+          boolean added = false;
+          sequences.add(sequence);
+
+          for (FEATURE feat : seg.getFEATURE())
+          {
+            // standard DAS feature-> jalview sequence feature transformation
+            SequenceFeature f = newSequenceFeature(feat,
+                    jvsource.getTitle());
+            if (!parseSeqFeature(sequence, f, feat, jvsource))
+            {
+              if (dbref.getMap() != null && f.getBegin() > 0
+                      && f.getEnd() > 0)
+              {
+                debug("mapping from " + f.getBegin() + " - " + f.getEnd());
+                SequenceFeature vf[] = null;
+
+                try
+                {
+                  vf = dbref.getMap().locateFeature(f);
+                } catch (Exception ex)
+                {
+                  Cache.log
+                          .warn("Error in 'experimental' mapping of features. Please try to reproduce and then report info to jalview-discuss@jalview.org.");
+                  Cache.log.warn("Mapping feature from " + f.getBegin()
+                          + " to " + f.getEnd() + " in dbref "
+                          + dbref.getAccessionId() + " in "
+                          + dbref.getSource());
+                  Cache.log.warn("using das Source " + source);
+                  Cache.log.warn("Exception", ex);
+                }
+
+                if (vf != null)
+                {
+                  for (int v = 0; v < vf.length; v++)
+                  {
+                    debug("mapping to " + v + ": " + vf[v].getBegin()
+                            + " - " + vf[v].getEnd());
+                    sequence.addSequenceFeature(vf[v]);
+                  }
+                }
+              }
+              else
+              {
+                sequence.addSequenceFeature(f);
+              }
+            }
+          }
+        }
+        featuresAdded(sequences);
+      }
+      else
+      {
+        // System.out.println("No features found for " + seq.getName()
+        // + " from: " + e.getDasSource().getNickname());
+      }
+    }
+  }
+
+  private void setGuiNoDassourceActive()
+  {
+
+    if (af != null)
+    {
+      af.setProgressBar(
+              MessageManager.getString("status.no_das_sources_active"),
+              startTime);
+    }
+    if (getFeatSettings() != null)
+    {
+      fsettings.noDasSourceActive();
+    }
+  }
+
+  /**
+   * Update our fsettings dialog reference if we didn't have one when we were
+   * first initialised.
+   * 
+   * @return fsettings
+   */
+  private FeatureSettings getFeatSettings()
+  {
+    if (fsettings == null)
+    {
+      if (af != null)
+      {
+        fsettings = af.featureSettings;
+      }
+    }
+    return fsettings;
+  }
+
+  public void cancel()
+  {
+    if (af != null)
+    {
+      af.setProgressBar(MessageManager
+              .getString("status.das_feature_fetching_cancelled"),
+              startTime);
+    }
+    cancelled = true;
+  }
+
+  int sourcesRemaining = 0;
+
+  private boolean running = false;
+
+  private void setGuiFetchComplete()
+  {
+    running = false;
+    if (!cancelled && af != null)
+    {
+      // only update the progress bar if we've completed the fetch normally
+      af.setProgressBar(MessageManager
+              .getString("status.das_feature_fetching_complete"), startTime);
+    }
+
+    if (af != null && af.featureSettings != null)
+    {
+      af.featureSettings.discoverAllFeatureData();
+    }
+
+    if (getFeatSettings() != null)
+    {
+      fsettings.complete();
+    }
+  }
+
+  void featuresAdded(Set<SequenceI> seqs)
+  {
+    if (af == null)
+    {
+      // no gui to update with features.
+      return;
+    }
+    af.getFeatureRenderer().featuresAdded();
+
+    int start = af.getViewport().getStartSeq();
+    int end = af.getViewport().getEndSeq();
+    int index;
+    for (index = start; index < end; index++)
+    {
+      for (SequenceI seq : seqs)
+      {
+        if (seq == af.getViewport().getAlignment().getSequenceAt(index)
+                .getDatasetSequence())
+        {
+          af.alignPanel.paintAlignment(true);
+          index = end;
+          break;
+        }
+      }
+    }
+  }
+
+  Object[] nextSequence(jalviewSourceI dasSource, SequenceI seq)
+  {
+    if (cancelled)
+    {
+      return null;
+    }
+    DBRefEntry[] uprefs = DBRefUtils.selectRefs(seq.getDBRefs(),
+            new String[] {
+            // jalview.datamodel.DBRefSource.PDB,
+            DBRefSource.UNIPROT,
+            // jalview.datamodel.DBRefSource.EMBL - not tested on any EMBL coord
+            // sys sources
+            });
+    // TODO: minimal list of DAS queries to make by querying with untyped ID if
+    // distinct from any typed IDs
+
+    List<DBRefEntry> ids = new ArrayList<DBRefEntry>();
+    List<String> qstring = new ArrayList<String>();
+    boolean dasCoordSysFound = false;
+
+    if (uprefs != null)
+    {
+      // do any of these ids match the source's coordinate system ?
+      for (int j = 0; !dasCoordSysFound && j < uprefs.length; j++)
+      {
+
+        for (COORDINATES csys : dasSource.getVersion().getCOORDINATES())
+        {
+          if (DBRefUtils.isDasCoordinateSystem(csys.getAuthority(),
+                  uprefs[j]))
+          {
+            debug("Launched fetcher for coordinate system "
+                    + csys.getAuthority());
+            // Will have to pass any mapping information to the fetcher
+            // - the start/end for the DBRefEntry may not be the same as the
+            // sequence's start/end
+
+            System.out.println(seq.getName() + " "
+                    + (seq.getDatasetSequence() == null) + " "
+                    + csys.getUri());
+
+            dasCoordSysFound = true; // break's out of the loop
+            ids.add(uprefs[j]);
+            qstring.add(uprefs[j].getAccessionId());
+          }
+          else
+          {
+            System.out.println("IGNORE " + csys.getAuthority());
+          }
+        }
+      }
+    }
+
+    if (!dasCoordSysFound)
+    {
+      String id = null;
+      // try and use the name as the sequence id
+      if (seq.getName().indexOf("|") > -1)
+      {
+        id = seq.getName().substring(seq.getName().lastIndexOf("|") + 1);
+        if (id.trim().length() < 4)
+        {
+          // hack - we regard a significant ID as being at least 4
+          // non-whitespace characters
+          id = seq.getName().substring(0, seq.getName().lastIndexOf("|"));
+          if (id.indexOf("|") > -1)
+          {
+            id = id.substring(id.lastIndexOf("|") + 1);
+          }
+        }
+      }
+      else
+      {
+        id = seq.getName();
+      }
+      if (id != null)
+      {
+        DBRefEntry dbre = new DBRefEntry();
+        dbre.setAccessionId(id);
+        // Should try to call a general feature fetcher that
+        // queries many sources with name to discover applicable ID references
+        ids.add(dbre);
+        qstring.add(dbre.getAccessionId());
+      }
+    }
+
+    return new Object[] { ids, qstring };
+  }
+
+  /**
+   * examine the given sequence feature to determine if it should actually be
+   * turned into sequence annotation or database cross references rather than a
+   * simple sequence feature.
+   * 
+   * @param seq
+   *          the sequence to annotate
+   * @param f
+   *          the jalview sequence feature generated from the DAS feature
+   * @param map
+   *          the sequence feature attributes
+   * @param source
+   *          the source that emitted the feature
+   * @return true if feature was consumed as another kind of annotation.
+   */
+  protected boolean parseSeqFeature(SequenceI seq, SequenceFeature f,
+          FEATURE feature, jalviewSourceI source)
+  {
+    SequenceI mseq = seq;
+    while (seq.getDatasetSequence() != null)
+    {
+      seq = seq.getDatasetSequence();
+    }
+    if (f.getType() != null)
+    {
+      String type = f.getType();
+      if (type.equalsIgnoreCase("protein_name"))
+      {
+        // parse name onto the alignment sequence or the dataset sequence.
+        if (seq.getDescription() == null
+                || seq.getDescription().trim().length() == 0)
+        {
+          // could look at the note series to pick out the first long name, for
+          // the moment just use the whole description string
+          seq.setDescription(f.getDescription());
+        }
+        if (mseq.getDescription() == null
+                || mseq.getDescription().trim().length() == 0)
+        {
+          // could look at the note series to pick out the first long name, for
+          // the moment just use the whole description string
+          mseq.setDescription(f.getDescription());
+        }
+        return true;
+      }
+      // check if source has biosapiens or other sequence ontology label
+      if (type.equalsIgnoreCase("DBXREF") || type.equalsIgnoreCase("DBREF"))
+      {
+        // try to parse the accession out
+
+        DBRefEntry dbr = new DBRefEntry();
+        dbr.setVersion(source.getTitle());
+        StringTokenizer st = new StringTokenizer(f.getDescription(), ":");
+        if (st.hasMoreTokens())
+        {
+          dbr.setSource(st.nextToken());
+        }
+        if (st.hasMoreTokens())
+        {
+          dbr.setAccessionId(st.nextToken());
+        }
+        seq.addDBRef(dbr);
+
+        if (f.links != null && f.links.size() > 0)
+        {
+          // feature is also appended to enable links to be seen.
+          // TODO: consider extending dbrefs to have their own links ?
+          // TODO: new feature: extract dbref links from DAS servers and add the
+          // URL pattern to the list of DB name associated links in the user's
+          // preferences ?
+          // for the moment - just fix up the existing feature so it displays
+          // correctly.
+          // f.setType(dbr.getSource());
+          // f.setDescription();
+          f.setValue("linkonly", Boolean.TRUE);
+          // f.setDescription("");
+          Vector newlinks = new Vector();
+          Enumeration it = f.links.elements();
+          while (it.hasMoreElements())
+          {
+            String elm;
+            UrlLink urllink = new UrlLink(elm = (String) it.nextElement());
+            if (urllink.isValid())
+            {
+              urllink.setLabel(f.getDescription());
+              newlinks.addElement(urllink.toString());
+            }
+            else
+            {
+              // couldn't parse the link properly. Keep it anyway - just in
+              // case.
+              debug("couldn't parse link string - " + elm);
+              newlinks.addElement(elm);
+            }
+          }
+          f.links = newlinks;
+          seq.addSequenceFeature(f);
+        }
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * creates a jalview sequence feature from a das feature document
+   * 
+   * @param feat
+   * @return sequence feature object created using dasfeature information
+   */
+  SequenceFeature newSequenceFeature(FEATURE feat, String nickname)
+  {
+    if (feat == null)
+    {
+      return null;
+    }
+    try
+    {
+      /**
+       * Different qNames for a DAS Feature - are string keys to the HashMaps in
+       * features "METHOD") || qName.equals("TYPE") || qName.equals("START") ||
+       * qName.equals("END") || qName.equals("NOTE") || qName.equals("LINK") ||
+       * qName.equals("SCORE")
+       */
+      String desc = new String();
+      if (feat.getNOTE() != null)
+      {
+        for (String note : feat.getNOTE())
+        {
+          desc += note;
+        }
+      }
+
+      int start = 0, end = 0;
+      float score = 0f;
+
+      try
+      {
+        start = Integer.parseInt(feat.getSTART().toString());
+      } catch (Exception ex)
+      {
+      }
+      try
+      {
+        end = Integer.parseInt(feat.getEND().toString());
+      } catch (Exception ex)
+      {
+      }
+      try
+      {
+        Object scr = feat.getSCORE();
+        if (scr != null)
+        {
+          score = (float) Double.parseDouble(scr.toString());
+
+        }
+      } catch (Exception ex)
+      {
+      }
+
+      SequenceFeature f = new SequenceFeature(
+              getTypeString(feat.getTYPE()), desc, start, end, score,
+              nickname);
+
+      if (feat.getLINK() != null)
+      {
+        for (LINK link : feat.getLINK())
+        {
+          // Do not put feature extent in link text for non-positional features
+          if (f.begin == 0 && f.end == 0)
+          {
+            f.addLink(f.getType() + " " + link.getContent() + "|"
+                    + link.getHref());
+          }
+          else
+          {
+            f.addLink(f.getType() + " " + f.begin + "_" + f.end + " "
+                    + link.getContent() + "|" + link.getHref());
+          }
+        }
+      }
+
+      return f;
+    } catch (Exception e)
+    {
+      System.out.println("ERRR " + e);
+      e.printStackTrace();
+      System.out.println("############");
+      debug("Failed to parse " + feat.toString(), e);
+      return null;
+    }
+  }
+
+  private String getTypeString(TYPE type)
+  {
+    return type.getContent();
+  }
+
+  public boolean isRunning()
+  {
+    return running;
+  }
+
+}