JAL-3949 - refactor logging from jalview.bin.Cache to jalview.bin.Console
[jalview.git] / src / jalview / ws / seqfetcher / ASequenceFetcher.java
index 05d541f..5b80541 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.seqfetcher;\r
-\r
-import jalview.datamodel.AlignmentI;\r
-import jalview.datamodel.DBRefEntry;\r
-import jalview.datamodel.SequenceI;\r
-import jalview.util.DBRefUtils;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Enumeration;\r
-import java.util.HashSet;\r
-import java.util.Hashtable;\r
-import java.util.Iterator;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Stack;\r
-import java.util.Vector;\r
-\r
-public class ASequenceFetcher\r
-{\r
-\r
-  /**\r
-   * set of databases we can retrieve entries from\r
-   */\r
-  protected Hashtable<String, Map<String, DbSourceProxy>> FETCHABLEDBS;\r
-\r
-  public ASequenceFetcher()\r
-  {\r
-    super();\r
-  }\r
-\r
-  /**\r
-   * get list of supported Databases\r
-   * \r
-   * @return database source string for each database - only the latest version\r
-   *         of a source db is bound to each source.\r
-   */\r
-  public String[] getSupportedDb()\r
-  {\r
-    if (FETCHABLEDBS == null)\r
-      return null;\r
-    String[] sf = new String[FETCHABLEDBS.size()];\r
-    Enumeration e = FETCHABLEDBS.keys();\r
-    int i = 0;\r
-    while (e.hasMoreElements())\r
-    {\r
-      sf[i++] = (String) e.nextElement();\r
-    }\r
-    ;\r
-    return sf;\r
-  }\r
-\r
-  public boolean isFetchable(String source)\r
-  {\r
-    Enumeration e = FETCHABLEDBS.keys();\r
-    while (e.hasMoreElements())\r
-    {\r
-      String db = (String) e.nextElement();\r
-      if (source.compareToIgnoreCase(db) == 0)\r
-        return true;\r
-    }\r
-    jalview.bin.Cache.log.warn("isFetchable doesn't know about '" + source\r
-            + "'");\r
-    return false;\r
-  }\r
-\r
-  public SequenceI[] getSequences(jalview.datamodel.DBRefEntry[] refs)\r
-  {\r
-    SequenceI[] ret = null;\r
-    Vector<SequenceI> rseqs = new Vector();\r
-    Hashtable<String, List<String>> queries = new Hashtable();\r
-    for (int r = 0; r < refs.length; r++)\r
-    {\r
-      if (!queries.containsKey(refs[r].getSource()))\r
-      {\r
-        queries.put(refs[r].getSource(), new ArrayList<String>());\r
-      }\r
-      List<String> qset = queries.get(refs[r].getSource());\r
-      if (!qset.contains(refs[r].getAccessionId()))\r
-      {\r
-        qset.add(refs[r].getAccessionId());\r
-      }\r
-    }\r
-    Enumeration<String> e = queries.keys();\r
-    while (e.hasMoreElements())\r
-    {\r
-      List<String> query = null;\r
-      String db = null;\r
-      db = e.nextElement();\r
-      query = queries.get(db);\r
-      if (!isFetchable(db))\r
-      {\r
-        reportStdError(db, query, new Exception(\r
-                "Don't know how to fetch from this database :" + db));\r
-        continue;\r
-      }\r
-      Iterator<DbSourceProxy> fetchers = getSourceProxy(db).iterator();\r
-      Stack<String> queriesLeft = new Stack<String>();\r
-      // List<String> queriesFailed = new ArrayList<String>();\r
-      queriesLeft.addAll(query);\r
-      while (fetchers.hasNext())\r
-      {\r
-        List<String> queriesMade = new ArrayList<String>();\r
-        HashSet queriesFound = new HashSet<String>();\r
-        try\r
-        {\r
-          DbSourceProxy fetcher = fetchers.next();\r
-          boolean doMultiple = fetcher.getAccessionSeparator() != null; // No\r
-          // separator\r
-          // - no\r
-          // Multiple\r
-          // Queries\r
-          while (!queriesLeft.isEmpty())\r
-          {\r
-            StringBuffer qsb = new StringBuffer();\r
-            do\r
-            {\r
-              if (qsb.length() > 0)\r
-              {\r
-                qsb.append(fetcher.getAccessionSeparator());\r
-              }\r
-              String q = queriesLeft.pop();\r
-              queriesMade.add(q);\r
-              qsb.append(q);\r
-            } while (doMultiple && !queriesLeft.isEmpty());\r
-\r
-            AlignmentI seqset = null;\r
-            try\r
-            {\r
-              // create a fetcher and go to it\r
-              seqset = fetcher.getSequenceRecords(qsb.toString()); // ,\r
-              // queriesFailed);\r
-            } catch (Exception ex)\r
-            {\r
-              System.err.println("Failed to retrieve the following from "\r
-                      + db);\r
-              System.err.println(qsb);\r
-              ex.printStackTrace(System.err);\r
-            }\r
-            // TODO: Merge alignment together - perhaps\r
-            if (seqset != null)\r
-            {\r
-              SequenceI seqs[] = seqset.getSequencesArray();\r
-              if (seqs != null)\r
-              {\r
-                for (int is = 0; is < seqs.length; is++)\r
-                {\r
-                  rseqs.addElement(seqs[is]);\r
-                  DBRefEntry[] frefs = DBRefUtils.searchRefs(seqs[is]\r
-                          .getDBRef(), new DBRefEntry(db, null, null));\r
-                  if (frefs != null)\r
-                  {\r
-                    for (DBRefEntry dbr : frefs)\r
-                    {\r
-                      queriesFound.add(dbr.getAccessionId());\r
-                      queriesMade.remove(dbr.getAccessionId());\r
-                    }\r
-                  }\r
-                  seqs[is] = null;\r
-                }\r
-              }\r
-              else\r
-              {\r
-                if (fetcher.getRawRecords() != null)\r
-                {\r
-                  System.out.println("# Retrieved from " + db + ":"\r
-                          + qsb.toString());\r
-                  StringBuffer rrb = fetcher.getRawRecords();\r
-                  /*\r
-                   * for (int rr = 0; rr<rrb.length; rr++) {\r
-                   */\r
-                  String hdr;\r
-                  // if (rr<qs.length)\r
-                  // {\r
-                  hdr = "# " + db + ":" + qsb.toString();\r
-                  /*\r
-                   * } else { hdr = "# part "+rr; }\r
-                   */\r
-                  System.out.println(hdr);\r
-                  if (rrb != null)\r
-                    System.out.println(rrb);\r
-                  System.out.println("# end of " + hdr);\r
-                }\r
-\r
-              }\r
-            }\r
-\r
-          }\r
-        } catch (Exception ex)\r
-        {\r
-          reportStdError(db, queriesMade, ex);\r
-        }\r
-        if (queriesMade.size() > 0)\r
-        {\r
-          System.out.println("# Adding " + queriesMade.size()\r
-                  + " ids back to queries list for searching again (" + db\r
-                  + ".");\r
-          queriesLeft.addAll(queriesMade);\r
-        }\r
-      }\r
-    }\r
-    if (rseqs.size() > 0)\r
-    {\r
-      ret = new SequenceI[rseqs.size()];\r
-      Enumeration sqs = rseqs.elements();\r
-      int si = 0;\r
-      while (sqs.hasMoreElements())\r
-      {\r
-        SequenceI s = (SequenceI) sqs.nextElement();\r
-        ret[si++] = s;\r
-        s.updatePDBIds();\r
-      }\r
-    }\r
-    return ret;\r
-  }\r
-\r
-  public void reportStdError(String db, List<String> queriesMade,\r
-          Exception ex)\r
-  {\r
-\r
-    System.err.println("Failed to retrieve the following references from "\r
-            + db);\r
-    int n = 0;\r
-    for (String qv : queriesMade)\r
-    {\r
-      System.err.print(" " + qv + ";");\r
-      if (n++ > 10)\r
-      {\r
-        System.err.println();\r
-        n = 0;\r
-      }\r
-    }\r
-    System.err.println();\r
-    ex.printStackTrace();\r
-  }\r
-\r
-  /**\r
-   * Retrieve an instance of the proxy for the given source\r
-   * \r
-   * @param db\r
-   *          database source string TODO: add version string/wildcard for\r
-   *          retrieval of specific DB source/version combinations.\r
-   * @return an instance of DbSourceProxy for that db.\r
-   */\r
-  public List<DbSourceProxy> getSourceProxy(String db)\r
-  {\r
-    List<DbSourceProxy> dbs;\r
-    Map<String, DbSourceProxy> dblist = FETCHABLEDBS.get(db);\r
-    if (dblist == null)\r
-    {\r
-      return new ArrayList<DbSourceProxy>();\r
-    }\r
-    ;\r
-    if (dblist.size() > 1)\r
-    {\r
-      DbSourceProxy[] l = dblist.values().toArray(new DbSourceProxy[0]);\r
-      int i = 0;\r
-      String[] nm = new String[l.length];\r
-      for (DbSourceProxy s : l)\r
-      {\r
-        nm[i++] = s.getDbName().toLowerCase();\r
-      }\r
-      jalview.util.QuickSort.sort(nm, l);\r
-      dbs = new ArrayList<DbSourceProxy>();\r
-      for (i = l.length - 1; i >= 0; i--)\r
-      {\r
-        dbs.add(l[i]);\r
-      }\r
-    }\r
-    else\r
-    {\r
-      dbs = new ArrayList<DbSourceProxy>(dblist.values());\r
-    }\r
-    return dbs;\r
-  }\r
-\r
-  /**\r
-   * constructs and instance of the proxy and registers it as a valid\r
-   * dbrefsource\r
-   * \r
-   * @param dbSourceProxy\r
-   *          reference for class implementing\r
-   *          jalview.ws.seqfetcher.DbSourceProxy\r
-   * @throws java.lang.IllegalArgumentException\r
-   *           if class does not implement jalview.ws.seqfetcher.DbSourceProxy\r
-   */\r
-  protected void addDBRefSourceImpl(Class dbSourceProxy)\r
-          throws java.lang.IllegalArgumentException\r
-  {\r
-    DbSourceProxy proxy = null;\r
-    try\r
-    {\r
-      Object proxyObj = dbSourceProxy.getConstructor(null)\r
-              .newInstance(null);\r
-      if (!DbSourceProxy.class.isInstance(proxyObj))\r
-      {\r
-        throw new IllegalArgumentException(\r
-                dbSourceProxy.toString()\r
-                        + " does not implement the jalview.ws.seqfetcher.DbSourceProxy");\r
-      }\r
-      proxy = (DbSourceProxy) proxyObj;\r
-    } catch (IllegalArgumentException e)\r
-    {\r
-      throw e;\r
-    } catch (Exception e)\r
-    {\r
-      // Serious problems if this happens.\r
-      throw new Error("DBRefSource Implementation Exception", e);\r
-    }\r
-    addDbRefSourceImpl(proxy);\r
-  }\r
-\r
-  /**\r
-   * add the properly initialised DbSourceProxy object 'proxy' to the list of\r
-   * sequence fetchers\r
-   * \r
-   * @param proxy\r
-   */\r
-  protected void addDbRefSourceImpl(DbSourceProxy proxy)\r
-  {\r
-    if (proxy != null)\r
-    {\r
-      if (FETCHABLEDBS == null)\r
-      {\r
-        FETCHABLEDBS = new Hashtable<String, Map<String, DbSourceProxy>>();\r
-      }\r
-      Map<String, DbSourceProxy> slist = FETCHABLEDBS.get(proxy\r
-              .getDbSource());\r
-      if (slist == null)\r
-      {\r
-        FETCHABLEDBS.put(proxy.getDbSource(),\r
-                slist = new Hashtable<String, DbSourceProxy>());\r
-      }\r
-      slist.put(proxy.getDbName(), proxy);\r
-    }\r
-  }\r
-\r
-  /**\r
-   * test if the database handler for dbName contains the given dbProperty when\r
-   * a dbName resolves to a set of proxies - this method will return the result\r
-   * of the test for the first instance. TODO implement additional method to\r
-   * query all sources for a db to find one with a particular property\r
-   * \r
-   * @param dbName\r
-   * @param dbProperty\r
-   * @return true if proxy has the given property\r
-   */\r
-  public boolean hasDbSourceProperty(String dbName, String dbProperty)\r
-  {\r
-    // TODO: decide if invalidDbName exception is thrown here.\r
-\r
-    List<DbSourceProxy> proxies = getSourceProxy(dbName);\r
-    if (proxies != null)\r
-    {\r
-      for (DbSourceProxy proxy : proxies)\r
-      {\r
-        if (proxy.getDbSourceProperties() != null)\r
-        {\r
-          return proxy.getDbSourceProperties().containsKey(dbProperty);\r
-        }\r
-      }\r
-    }\r
-    return false;\r
-  }\r
-\r
-  /**\r
-   * select sources which are implemented by instances of the given class\r
-   * \r
-   * @param class that implements DbSourceProxy\r
-   * @return null or vector of source names for fetchers\r
-   */\r
-  public String[] getDbInstances(Class class1)\r
-  {\r
-    if (!jalview.ws.seqfetcher.DbSourceProxy.class.isAssignableFrom(class1))\r
-    {\r
-      throw new Error(\r
-              "Implmentation Error - getDbInstances must be given a class that implements jalview.ws.seqfetcher.DbSourceProxy (was given '"\r
-                      + class1 + "')");\r
-    }\r
-    if (FETCHABLEDBS == null)\r
-    {\r
-      return null;\r
-    }\r
-    String[] sources = null;\r
-    Vector src = new Vector();\r
-    Enumeration dbs = FETCHABLEDBS.keys();\r
-    while (dbs.hasMoreElements())\r
-    {\r
-      String dbn = (String) dbs.nextElement();\r
-      for (DbSourceProxy dbp : FETCHABLEDBS.get(dbn).values())\r
-      {\r
-        if (class1.isAssignableFrom(dbp.getClass()))\r
-        {\r
-          src.addElement(dbn);\r
-        }\r
-      }\r
-    }\r
-    if (src.size() > 0)\r
-    {\r
-      src.copyInto(sources = new String[src.size()]);\r
-    }\r
-    return sources;\r
-  }\r
-\r
-  public DbSourceProxy[] getDbSourceProxyInstances(Class class1)\r
-  {\r
-    ArrayList<DbSourceProxy> prlist = new ArrayList<DbSourceProxy>();\r
-    for (String fetchable : getSupportedDb())\r
-      for (DbSourceProxy pr : getSourceProxy(fetchable))\r
-      {\r
-        if (class1.isInstance(pr))\r
-        {\r
-          prlist.add(pr);\r
-        }\r
-      }\r
-    if (prlist.size() == 0)\r
-    {\r
-      return null;\r
-    }\r
-    return prlist.toArray(new DbSourceProxy[0]);\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.seqfetcher;
+
+import jalview.api.FeatureSettingsModelI;
+import jalview.bin.Console;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.SequenceI;
+import jalview.util.DBRefUtils;
+import jalview.util.MessageManager;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+import java.util.Vector;
+
+public class ASequenceFetcher
+{
+
+  /*
+   * set of databases we can retrieve entries from
+   */
+  protected Hashtable<String, Map<String, DbSourceProxy>> fetchableDbs;
+
+  /*
+   * comparator to sort by tier (0/1/2) and name
+   */
+  private Comparator<DbSourceProxy> proxyComparator;
+
+  /**
+   * Constructor
+   */
+  protected ASequenceFetcher()
+  {
+    super();
+
+    /*
+     * comparator to sort proxies by tier and name
+     */
+    proxyComparator = new Comparator<DbSourceProxy>()
+    {
+      @Override
+      public int compare(DbSourceProxy o1, DbSourceProxy o2)
+      {
+        /*
+         * Tier 0 precedes 1 precedes 2
+         */
+        int compared = Integer.compare(o1.getTier(), o2.getTier());
+        if (compared == 0)
+        {
+          // defend against NullPointer - should never happen
+          String o1Name = o1.getDbName();
+          String o2Name = o2.getDbName();
+          if (o1Name != null && o2Name != null)
+          {
+            compared = o1Name.compareToIgnoreCase(o2Name);
+          }
+        }
+        return compared;
+      }
+    };
+  }
+
+  /**
+   * get array of supported Databases
+   * 
+   * @return database source string for each database - only the latest version
+   *         of a source db is bound to each source.
+   */
+  public String[] getSupportedDb()
+  {
+    if (fetchableDbs == null)
+    {
+      return null;
+    }
+    String[] sf = fetchableDbs.keySet()
+            .toArray(new String[fetchableDbs.size()]);
+    return sf;
+  }
+
+  public boolean isFetchable(String source)
+  {
+    for (String db : fetchableDbs.keySet())
+    {
+      if (source.equalsIgnoreCase(db))
+      {
+        return true;
+      }
+    }
+    Console.warn("isFetchable doesn't know about '" + source + "'");
+    return false;
+  }
+
+  /**
+   * Fetch sequences for the given cross-references
+   * 
+   * @param refs
+   * @param dna
+   *          if true, only fetch from nucleotide data sources, else peptide
+   * @return
+   */
+  public SequenceI[] getSequences(List<DBRefEntry> refs, boolean dna)
+  {
+    Vector<SequenceI> rseqs = new Vector<>();
+    Hashtable<String, List<String>> queries = new Hashtable<>();
+    for (DBRefEntry ref : refs)
+    {
+      String canonical = DBRefUtils.getCanonicalName(ref.getSource());
+      if (!queries.containsKey(canonical))
+      {
+        queries.put(canonical, new ArrayList<String>());
+      }
+      List<String> qset = queries.get(canonical);
+      if (!qset.contains(ref.getAccessionId()))
+      {
+        qset.add(ref.getAccessionId());
+      }
+    }
+    Enumeration<String> e = queries.keys();
+    while (e.hasMoreElements())
+    {
+      List<String> query = null;
+      String db = null;
+      db = e.nextElement();
+      query = queries.get(db);
+      if (!isFetchable(db))
+      {
+        reportStdError(db, query, new Exception(
+                "Don't know how to fetch from this database :" + db));
+        continue;
+      }
+
+      Stack<String> queriesLeft = new Stack<>();
+      queriesLeft.addAll(query);
+
+      List<DbSourceProxy> proxies = getSourceProxy(db);
+      for (DbSourceProxy fetcher : proxies)
+      {
+        List<String> queriesMade = new ArrayList<>();
+        HashSet<String> queriesFound = new HashSet<>();
+        try
+        {
+          if (fetcher.isDnaCoding() != dna)
+          {
+            continue; // wrong sort of data
+          }
+          boolean doMultiple = fetcher.getMaximumQueryCount() > 1;
+          while (!queriesLeft.isEmpty())
+          {
+            StringBuffer qsb = new StringBuffer();
+            do
+            {
+              if (qsb.length() > 0)
+              {
+                qsb.append(fetcher.getAccessionSeparator());
+              }
+              String q = queriesLeft.pop();
+              queriesMade.add(q);
+              qsb.append(q);
+            } while (doMultiple && !queriesLeft.isEmpty());
+
+            AlignmentI seqset = null;
+            try
+            {
+              // create a fetcher and go to it
+              seqset = fetcher.getSequenceRecords(qsb.toString());
+            } catch (Exception ex)
+            {
+              System.err.println(
+                      "Failed to retrieve the following from " + db);
+              System.err.println(qsb);
+              ex.printStackTrace(System.err);
+            }
+            // TODO: Merge alignment together - perhaps
+            if (seqset != null)
+            {
+              SequenceI seqs[] = seqset.getSequencesArray();
+              if (seqs != null)
+              {
+                for (int is = 0; is < seqs.length; is++)
+                {
+                  rseqs.addElement(seqs[is]);
+                  // BH 2015.01.25 check about version/accessid being null here
+                  List<DBRefEntry> frefs = DBRefUtils.searchRefs(
+                          seqs[is].getDBRefs(),
+                          new DBRefEntry(db, null, null), DBRefUtils.SEARCH_MODE_FULL);
+                  for (DBRefEntry dbr : frefs)
+                  {
+                    queriesFound.add(dbr.getAccessionId());
+                    queriesMade.remove(dbr.getAccessionId());
+                  }
+                  seqs[is] = null;
+                }
+              }
+              else
+              {
+                if (fetcher.getRawRecords() != null)
+                {
+                  System.out.println(
+                          "# Retrieved from " + db + ":" + qsb.toString());
+                  StringBuffer rrb = fetcher.getRawRecords();
+                  /*
+                   * for (int rr = 0; rr<rrb.length; rr++) {
+                   */
+                  String hdr;
+                  // if (rr<qs.length)
+                  // {
+                  hdr = "# " + db + ":" + qsb.toString();
+                  /*
+                   * } else { hdr = "# part "+rr; }
+                   */
+                  System.out.println(hdr);
+                  if (rrb != null)
+                  {
+                    System.out.println(rrb);
+                  }
+                  System.out.println("# end of " + hdr);
+                }
+
+              }
+            }
+
+          }
+        } catch (Exception ex)
+        {
+          reportStdError(db, queriesMade, ex);
+        }
+        if (queriesMade.size() > 0)
+        {
+          System.out.println("# Adding " + queriesMade.size()
+                  + " ids back to queries list for searching again (" + db
+                  + ")");
+          queriesLeft.addAll(queriesMade);
+        }
+      }
+    }
+
+    SequenceI[] result = null;
+    if (rseqs.size() > 0)
+    {
+      result = new SequenceI[rseqs.size()];
+      int si = 0;
+      for (SequenceI s : rseqs)
+      {
+        result[si++] = s;
+        s.updatePDBIds();
+      }
+    }
+    return result;
+  }
+
+  public void reportStdError(String db, List<String> queriesMade,
+          Exception ex)
+  {
+
+    System.err.println(
+            "Failed to retrieve the following references from " + db);
+    int n = 0;
+    for (String qv : queriesMade)
+    {
+      System.err.print(" " + qv + ";");
+      if (n++ > 10)
+      {
+        System.err.println();
+        n = 0;
+      }
+    }
+    System.err.println();
+    ex.printStackTrace();
+  }
+
+  /**
+   * Returns a list of proxies for the given source
+   * 
+   * @param db
+   *          database source string TODO: add version string/wildcard for
+   *          retrieval of specific DB source/version combinations.
+   * @return a list of DbSourceProxy for the db
+   */
+  public List<DbSourceProxy> getSourceProxy(String db)
+  {
+    db = DBRefUtils.getCanonicalName(db);
+    Map<String, DbSourceProxy> dblist = fetchableDbs.get(db);
+    if (dblist == null)
+    {
+      return new ArrayList<>();
+    }
+
+    /*
+     * sort so that primary sources precede secondary
+     */
+    List<DbSourceProxy> dbs = new ArrayList<>(dblist.values());
+    Collections.sort(dbs, proxyComparator);
+    return dbs;
+  }
+
+  /**
+   * constructs an instance of the proxy and registers it as a valid dbrefsource
+   * 
+   * @param dbSourceProxy
+   *          reference for class implementing
+   *          jalview.ws.seqfetcher.DbSourceProxy
+   */
+  protected void addDBRefSourceImpl(
+          Class<? extends DbSourceProxy> dbSourceProxy)
+          throws IllegalArgumentException
+  {
+    DbSourceProxy proxy = null;
+    try
+    {
+      DbSourceProxy proxyObj = dbSourceProxy.getConstructor().newInstance();
+      proxy = proxyObj;
+    } catch (IllegalArgumentException e)
+    {
+      throw e;
+    } catch (Exception e)
+    {
+      // Serious problems if this happens.
+      throw new Error(MessageManager
+              .getString("error.dbrefsource_implementation_exception"), e);
+    }
+    addDbRefSourceImpl(proxy);
+  }
+
+  /**
+   * add the properly initialised DbSourceProxy object 'proxy' to the list of
+   * sequence fetchers
+   * 
+   * @param proxy
+   */
+  protected void addDbRefSourceImpl(DbSourceProxy proxy)
+  {
+    if (proxy != null)
+    {
+      if (fetchableDbs == null)
+      {
+        fetchableDbs = new Hashtable<>();
+      }
+      Map<String, DbSourceProxy> slist = fetchableDbs
+              .get(proxy.getDbSource());
+      if (slist == null)
+      {
+        fetchableDbs.put(proxy.getDbSource(),
+                slist = new Hashtable<>());
+      }
+      slist.put(proxy.getDbName(), proxy);
+    }
+  }
+
+  /**
+   * select sources which are implemented by instances of the given class
+   * 
+   * @param class
+   *          that implements DbSourceProxy
+   * @return null or vector of source names for fetchers
+   */
+  public String[] getDbInstances(Class class1)
+  {
+    if (!DbSourceProxy.class.isAssignableFrom(class1))
+    {
+      throw new Error(MessageManager.formatMessage(
+              "error.implementation_error_dbinstance_must_implement_interface",
+              new String[]
+              { class1.toString() }));
+    }
+    if (fetchableDbs == null)
+    {
+      return null;
+    }
+    String[] sources = null;
+    Vector<String> src = new Vector<>();
+    Enumeration<String> dbs = fetchableDbs.keys();
+    while (dbs.hasMoreElements())
+    {
+      String dbn = dbs.nextElement();
+      for (DbSourceProxy dbp : fetchableDbs.get(dbn).values())
+      {
+        if (class1.isAssignableFrom(dbp.getClass()))
+        {
+          src.addElement(dbn);
+        }
+      }
+    }
+    if (src.size() > 0)
+    {
+      src.copyInto(sources = new String[src.size()]);
+    }
+    return sources;
+  }
+
+  public DbSourceProxy[] getDbSourceProxyInstances(Class class1)
+  {
+    List<DbSourceProxy> prlist = new ArrayList<>();
+    for (String fetchable : getSupportedDb())
+    {
+      for (DbSourceProxy pr : getSourceProxy(fetchable))
+      {
+        if (class1.isInstance(pr))
+        {
+          prlist.add(pr);
+        }
+      }
+    }
+    if (prlist.size() == 0)
+    {
+      return null;
+    }
+    return prlist.toArray(new DbSourceProxy[0]);
+  }
+
+  /**
+   * Returns a preferred feature colouring scheme for the given source, or null
+   * if none is defined.
+   * 
+   * @param source
+   * @return
+   */
+  public FeatureSettingsModelI getFeatureColourScheme(String source)
+  {
+    /*
+     * return the first non-null colour scheme for any proxy for
+     * this database source
+     */
+    for (DbSourceProxy proxy : getSourceProxy(source))
+    {
+      FeatureSettingsModelI preferredColours = proxy
+              .getFeatureColourScheme();
+      if (preferredColours != null)
+      {
+        return preferredColours;
+      }
+    }
+    return null;
+  }
+}