JAL-1645 Version-Rel Version 2.9 Year-Rel 2015 Licensing glob
[jalview.git] / src / jalview / ws / DasSequenceFeatureFetcher.java
index 30fe725..345bdd7 100644 (file)
-/*\r
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)\r
- * Copyright (C) 2011 J Procter, AM Waterhouse, 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 java.net.*;\r
-import java.util.*;\r
-\r
-import javax.swing.*;\r
-\r
-import org.biodas.jdas.client.SourcesClient;\r
-import org.biodas.jdas.schema.sources.SOURCE;\r
-import org.biodas.jdas.schema.sources.SOURCES;\r
-import org.biojava.dasobert.das.*;\r
-import org.biojava.dasobert.das2.*;\r
-import org.biojava.dasobert.das2.io.*;\r
-import org.biojava.dasobert.dasregistry.*;\r
-import org.biojava.dasobert.eventmodel.*;\r
-import jalview.bin.Cache;\r
-import jalview.datamodel.*;\r
-import jalview.gui.*;\r
-import jalview.util.UrlLink;\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
-  Vector 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
-  /**\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);\r
-  }\r
-\r
-  public DasSequenceFeatureFetcher(SequenceI[] oursequences,\r
-          FeatureSettings fsettings, Vector ourselectedSources,\r
-          boolean checkDbrefs, boolean promptFetchDbrefs)\r
-  {\r
-    this.selectedSources = new Vector();\r
-    Enumeration sources = ourselectedSources.elements();\r
-    // filter both sequences and sources to eliminate duplicates\r
-    while (sources.hasMoreElements())\r
-    {\r
-      Object src = sources.nextElement();\r
-      if (!selectedSources.contains(src))\r
-      {\r
-        selectedSources.addElement(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 (int i = 0; i < selectedSources.size(); i++)\r
-    {\r
-      DasSource source = (DasSource) selectedSources.elementAt(i);\r
-      DasCoordinateSystem[] coords = source.getCoordinateSystem();\r
-      for (int c = 0; c < coords.length; c++)\r
-      {\r
-        // TODO: match UniProt coord system canonically (?) - does\r
-        // UniProt==uniprot==UNIPROT ?\r
-        if (coords[c].getName().indexOf("UniProt") > -1)\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
-\r
-  class FetchDBRefs implements Runnable\r
-  {\r
-    public void run()\r
-    {\r
-      new DBRefFetcher(sequences, af).fetchDBRefs(true);\r
-      startFetching();\r
-    }\r
-  }\r
-\r
-  /**\r
-   * Spawns a number of dasobert Fetcher threads to add features to sequences in\r
-   * the dataset\r
-   */\r
-  void startFetching()\r
-  {\r
-    cancelled = false;\r
-    startTime = System.currentTimeMillis();\r
-    if (af != null)\r
-    {\r
-      af.setProgressBar("Fetching DAS Sequence Features", startTime);\r
-    }\r
-\r
-    if (selectedSources == null || selectedSources.size() == 0)\r
-    {\r
-      try\r
-      {\r
-        DasSource[] sources = new jalview.gui.DasSourceBrowser()\r
-                .getDASSource();\r
-\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].getNickname().equals(token))\r
-            {\r
-              selectedSources.addElement(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
-    // Now sending requests one at a time to each server\r
-    for (int sourceIndex = 0; sourceIndex < selectedSources.size()\r
-            && !cancelled; sourceIndex++)\r
-    {\r
-      DasSource dasSource = (DasSource) selectedSources\r
-              .elementAt(sourceIndex);\r
-\r
-      nextSequence(dasSource, sequences[0]);\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
-\r
-  void responseComplete(DasSource dasSource, SequenceI seq)\r
-  {\r
-    if (seq != null)\r
-    {\r
-      for (int seqIndex = 0; seqIndex < sequences.length - 1 && !cancelled; seqIndex++)\r
-      {\r
-        if (sequences[seqIndex] == seq)\r
-        {\r
-          nextSequence(dasSource, sequences[++seqIndex]);\r
-          return;\r
-        }\r
-      }\r
-    }\r
-\r
-    sourcesRemaining--;\r
-\r
-    if (sourcesRemaining == 0)\r
-    {\r
-      System.err.println("Fetching Complete.");\r
-      setGuiFetchComplete();\r
-    }\r
-\r
-  }\r
-\r
-  private void setGuiFetchComplete()\r
-  {\r
-\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(SequenceI seq)\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
-      if (seq == af.getViewport().getAlignment().getSequenceAt(index)\r
-              .getDatasetSequence())\r
-      {\r
-        af.alignPanel.paintAlignment(true);\r
-        break;\r
-      }\r
-    }\r
-  }\r
-\r
-  void nextSequence(DasSource dasSource, SequenceI seq)\r
-  {\r
-    if (cancelled)\r
-      return;\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
-    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
-        DasCoordinateSystem cs[] = dasSource.getCoordinateSystem();\r
-\r
-        for (int csIndex = 0; csIndex < cs.length && !dasCoordSysFound; csIndex++)\r
-        {\r
-          if (cs.length > 0\r
-                  && jalview.util.DBRefUtils.isDasCoordinateSystem(\r
-                          cs[csIndex].getName(), uprefs[j]))\r
-          {\r
-            debug("Launched fetcher for coordinate system "\r
-                    + cs[0].getName());\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
-                    + dasSource.getUrl());\r
-\r
-            dasCoordSysFound = true; // break's out of the loop\r
-            createFeatureFetcher(seq, dasSource, uprefs[j]);\r
-          }\r
-          else\r
-            System.out.println("IGNORE " + cs[csIndex].getName());\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
-        // Should try to call a general feature fetcher that\r
-        // queries many sources with name to discover applicable ID references\r
-        createFeatureFetcher(seq, dasSource, id);\r
-      }\r
-    }\r
-\r
-  }\r
-\r
-  /**\r
-   * fetch and add das features to a sequence using the given source URL and\r
-   * compatible DbRef id. new features are mapped using the DbRef mapping to the\r
-   * local coordinate system.\r
-   * \r
-   * @param seq\r
-   * @param SourceUrl\r
-   * @param dbref\r
-   */\r
-  protected void createFeatureFetcher(final SequenceI seq,\r
-          final DasSource dasSource, final DBRefEntry dbref)\r
-  {\r
-\r
-    // ////////////\r
-    // / fetch DAS features\r
-    final Das1Source source = new Das1Source();\r
-    source.setUrl(dasSource.getUrl());\r
-    source.setNickname(dasSource.getNickname());\r
-    if (dbref == null || dbref.getAccessionId() == null\r
-            || dbref.getAccessionId().length() < 1)\r
-    {\r
-      responseComplete(dasSource, seq); // reduce thread count anyhow\r
-      return;\r
-    }\r
-    debug("new Das Feature Fetcher for " + dbref.getSource() + ":"\r
-            + dbref.getAccessionId() + " querying " + dasSource.getUrl());\r
-    FeatureThread fetcher = new FeatureThread(dbref.getAccessionId()\r
-    // + ":" + start + "," + end,\r
-            , source);\r
-\r
-    fetcher.addFeatureListener(new FeatureListener()\r
-    {\r
-      public void comeBackLater(FeatureEvent e)\r
-      {\r
-        responseComplete(dasSource, seq);\r
-        debug("das source " + e.getSource().getNickname()\r
-                + " asked us to come back in " + e.getComeBackLater()\r
-                + " secs.");\r
-      }\r
-\r
-      public void newFeatures(FeatureEvent e)\r
-      {\r
-\r
-        Das1Source ds = e.getSource();\r
-\r
-        Map[] features = e.getFeatures();\r
-        // add features to sequence\r
-        debug("das source " + ds.getUrl() + " returned " + features.length\r
-                + " features");\r
-\r
-        if (features.length > 0)\r
-        {\r
-          for (int i = 0; i < features.length; i++)\r
-          {\r
-            // standard DAS feature-> jalview sequence feature transformation\r
-            SequenceFeature f = newSequenceFeature(features[i],\r
-                    source.getNickname());\r
-            if (!parseSeqFeature(seq, f, features[i], source))\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 " + ds.getUrl());\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
-                    seq.addSequenceFeature(vf[v]);\r
-                  }\r
-                }\r
-              }\r
-              else\r
-              {\r
-                seq.addSequenceFeature(f);\r
-              }\r
-            }\r
-          }\r
-          featuresAdded(seq);\r
-        }\r
-        else\r
-        {\r
-          // System.out.println("No features found for " + seq.getName()\r
-          // + " from: " + e.getDasSource().getNickname());\r
-        }\r
-        responseComplete(dasSource, seq);\r
-\r
-      }\r
-    }\r
-\r
-    );\r
-\r
-    fetcher.start();\r
-  }\r
-\r
-  protected void createFeatureFetcher(final SequenceI seq,\r
-          final DasSource dasSource, String id)\r
-  {\r
-    // ////////////\r
-    // / fetch DAS features\r
-    final Das1Source source = new Das1Source();\r
-    source.setUrl(dasSource.getUrl());\r
-    source.setNickname(dasSource.getNickname());\r
-\r
-    if (id != null)\r
-    {\r
-      id = id.trim();\r
-    }\r
-    if (id != null && id.length() > 0)\r
-    {\r
-      debug("new Das Feature Fetcher for " + id + " querying "\r
-              + dasSource.getUrl());\r
-      FeatureThread fetcher = new FeatureThread(id\r
-      // + ":" + start + "," + end,\r
-              , source);\r
-\r
-      fetcher.addFeatureListener(new FeatureListener()\r
-      {\r
-        public void comeBackLater(FeatureEvent e)\r
-        {\r
-          responseComplete(dasSource, seq);\r
-          debug("das source " + e.getSource().getNickname()\r
-                  + " asked us to come back in " + e.getComeBackLater()\r
-                  + " secs.");\r
-        }\r
-\r
-        public void newFeatures(FeatureEvent e)\r
-        {\r
-\r
-          Das1Source ds = e.getSource();\r
-\r
-          Map[] features = e.getFeatures();\r
-          // add features to sequence\r
-          debug("das source " + ds.getUrl() + " returned "\r
-                  + features.length + " features");\r
-\r
-          if (features.length > 0)\r
-          {\r
-            for (int i = 0; i < features.length; i++)\r
-            {\r
-              // standard DAS feature-> jalview sequence feature transformation\r
-              SequenceFeature f = newSequenceFeature(features[i],\r
-                      source.getNickname());\r
-              if (!parseSeqFeature(seq, f, features[i], source))\r
-              {\r
-                // just add as a simple sequence feature\r
-                seq.addSequenceFeature(f);\r
-              }\r
-            }\r
-\r
-            featuresAdded(seq);\r
-          }\r
-          else\r
-          {\r
-            // System.out.println("No features found for " + seq.getName()\r
-            // + " from: " + e.getDasSource().getNickname());\r
-          }\r
-          responseComplete(dasSource, seq);\r
-\r
-        }\r
-      }\r
-\r
-      );\r
-\r
-      fetcher.start();\r
-    }\r
-    else\r
-    {\r
-      // invalid fetch - indicate it is finished.\r
-      debug("Skipping empty ID for querying " + dasSource.getUrl());\r
-      responseComplete(dasSource, seq);\r
-    }\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
-          Map map, Das1Source 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.getNickname());\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 dasfeature\r
-   * @return sequence feature object created using dasfeature information\r
-   */\r
-  SequenceFeature newSequenceFeature(Map dasfeature, String nickname)\r
-  {\r
-    if (dasfeature == 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 (dasfeature.containsKey("NOTE"))\r
-      {\r
-        desc += (String) dasfeature.get("NOTE");\r
-      }\r
-\r
-      int start = 0, end = 0;\r
-      float score = 0f;\r
-\r
-      try\r
-      {\r
-        start = Integer.parseInt(dasfeature.get("START").toString());\r
-      } catch (Exception ex)\r
-      {\r
-      }\r
-      try\r
-      {\r
-        end = Integer.parseInt(dasfeature.get("END").toString());\r
-      } catch (Exception ex)\r
-      {\r
-      }\r
-      try\r
-      {\r
-        Object scr = dasfeature.get("SCORE");\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
-              (String) dasfeature.get("TYPE"), desc, start, end, score,\r
-              nickname);\r
-\r
-      if (dasfeature.containsKey("LINK"))\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() + "|" + dasfeature.get("LINK"));\r
-        }\r
-        else\r
-        {\r
-          f.addLink(f.getType() + " " + f.begin + "_" + f.end + "|"\r
-                  + dasfeature.get("LINK"));\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 " + dasfeature.toString(), e);\r
-      return null;\r
-    }\r
-  }\r
-\r
-  /**\r
-   * query the default DAS Source Registry for sources. Uses value of jalview\r
-   * property DAS_REGISTRY_URL and the DasSourceBrowser.DEFAULT_REGISTRY if that\r
-   * doesn't exist.\r
-   * \r
-   * @return list of sources\r
-   */\r
-  public static SOURCE[] getDASSources()\r
-  {\r
-\r
-    String registryURL = jalview.bin.Cache.getDefault("DAS_REGISTRY_URL",\r
-            DasSourceBrowser.DEFAULT_REGISTRY);\r
-    return getDASSources(registryURL);\r
-  }\r
-\r
-  /**\r
-   * query the given URL for DasSources.\r
-   * \r
-   * @param registryURL\r
-   *          return sources from registryURL\r
-   */\r
-  public static SOURCE[] getDASSources(String registryURL)\r
-  {\r
-    \r
-    try\r
-    {\r
-      URL url = new URL(registryURL);\r
-      org.biodas.jdas.client.SourcesClientInterface client = new SourcesClient();\r
-      \r
-      SOURCES sources = client.fetchData(registryURL);\r
-\r
-      List<SOURCE> dassources = sources.getSOURCE();\r
-\r
-      return dassources.toArray(new SOURCE[dassources.size()]);\r
-    } catch (Exception ex)\r
-    {\r
-      System.err.println("Failed to contact DAS1 registry at "\r
-              + registryURL);\r
-      ex.printStackTrace();\r
-      return null;\r
-    }\r
-  }\r
-\r
-}\r
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
+ * Copyright (C) 2015 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.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
+import jalview.gui.FeatureSettings;
+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].getDBRef();
+      if (dbref != null)
+      {
+        for (int j = 0; j < dbref.length; j++)
+        {
+          if (dbref[j].getSource().equals(
+                  jalview.datamodel.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
+  {
+    public void run()
+    {
+      startFetching();
+      setGuiFetchComplete();
+    }
+  }
+
+  class FetchDBRefs implements Runnable
+  {
+    public void run()
+    {
+      running = true;
+      new DBRefFetcher(sequences, af).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 = jalview.bin.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 = jalview.util.DBRefUtils.selectRefs(
+            seq.getDBRef(), new String[] {
+            // jalview.datamodel.DBRefSource.PDB,
+            jalview.datamodel.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 (jalview.util.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;
+  }
+
+}