JAL-1705 DbSourceProxy properties converted to methods, tidy/format code
[jalview.git] / src / jalview / ws / seqfetcher / ASequenceFetcher.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.ws.seqfetcher;
22
23 import jalview.bin.Cache;
24 import jalview.datamodel.AlignmentI;
25 import jalview.datamodel.DBRefEntry;
26 import jalview.datamodel.SequenceI;
27 import jalview.util.DBRefUtils;
28 import jalview.util.MessageManager;
29 import jalview.util.QuickSort;
30
31 import java.util.ArrayList;
32 import java.util.Enumeration;
33 import java.util.HashSet;
34 import java.util.Hashtable;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Stack;
39 import java.util.Vector;
40
41 public class ASequenceFetcher
42 {
43
44   /**
45    * set of databases we can retrieve entries from
46    */
47   protected Hashtable<String, Map<String, DbSourceProxy>> FETCHABLEDBS;
48
49   public ASequenceFetcher()
50   {
51     super();
52   }
53
54   /**
55    * get list of supported Databases
56    * 
57    * @return database source string for each database - only the latest version
58    *         of a source db is bound to each source.
59    */
60   public String[] getSupportedDb()
61   {
62     if (FETCHABLEDBS == null)
63     {
64       return null;
65     }
66     String[] sf = new String[FETCHABLEDBS.size()];
67     Enumeration<String> e = FETCHABLEDBS.keys();
68     int i = 0;
69     while (e.hasMoreElements())
70     {
71       sf[i++] = e.nextElement();
72     }
73     ;
74     return sf;
75   }
76
77   public boolean isFetchable(String source)
78   {
79     Enumeration<String> e = FETCHABLEDBS.keys();
80     while (e.hasMoreElements())
81     {
82       String db = e.nextElement();
83       if (source.compareToIgnoreCase(db) == 0)
84       {
85         return true;
86       }
87     }
88     Cache.log.warn("isFetchable doesn't know about '" + source
89             + "'");
90     return false;
91   }
92
93   public SequenceI[] getSequences(DBRefEntry[] refs)
94   {
95     SequenceI[] ret = null;
96     Vector<SequenceI> rseqs = new Vector<SequenceI>();
97     Hashtable<String, List<String>> queries = new Hashtable<String, List<String>>();
98     for (int r = 0; r < refs.length; r++)
99     {
100       if (!queries.containsKey(refs[r].getSource()))
101       {
102         queries.put(refs[r].getSource(), new ArrayList<String>());
103       }
104       List<String> qset = queries.get(refs[r].getSource());
105       if (!qset.contains(refs[r].getAccessionId()))
106       {
107         qset.add(refs[r].getAccessionId());
108       }
109     }
110     Enumeration<String> e = queries.keys();
111     while (e.hasMoreElements())
112     {
113       List<String> query = null;
114       String db = null;
115       db = e.nextElement();
116       query = queries.get(db);
117       if (!isFetchable(db))
118       {
119         reportStdError(db, query, new Exception(
120                 "Don't know how to fetch from this database :" + db));
121         continue;
122       }
123       Iterator<DbSourceProxy> fetchers = getSourceProxy(db).iterator();
124       Stack<String> queriesLeft = new Stack<String>();
125       // List<String> queriesFailed = new ArrayList<String>();
126       queriesLeft.addAll(query);
127       while (fetchers.hasNext())
128       {
129         List<String> queriesMade = new ArrayList<String>();
130         HashSet<String> queriesFound = new HashSet<String>();
131         try
132         {
133           DbSourceProxy fetcher = fetchers.next();
134           boolean doMultiple = fetcher.getAccessionSeparator() != null;
135           // No separator - no Multiple Queries
136           while (!queriesLeft.isEmpty())
137           {
138             StringBuffer qsb = new StringBuffer();
139             do
140             {
141               if (qsb.length() > 0)
142               {
143                 qsb.append(fetcher.getAccessionSeparator());
144               }
145               String q = queriesLeft.pop();
146               queriesMade.add(q);
147               qsb.append(q);
148             } while (doMultiple && !queriesLeft.isEmpty());
149
150             AlignmentI seqset = null;
151             try
152             {
153               // create a fetcher and go to it
154               seqset = fetcher.getSequenceRecords(qsb.toString()); // ,
155               // queriesFailed);
156             } catch (Exception ex)
157             {
158               System.err.println("Failed to retrieve the following from "
159                       + db);
160               System.err.println(qsb);
161               ex.printStackTrace(System.err);
162             }
163             // TODO: Merge alignment together - perhaps
164             if (seqset != null)
165             {
166               SequenceI seqs[] = seqset.getSequencesArray();
167               if (seqs != null)
168               {
169                 for (int is = 0; is < seqs.length; is++)
170                 {
171                   rseqs.addElement(seqs[is]);
172                   DBRefEntry[] frefs = DBRefUtils.searchRefs(seqs[is]
173                           .getDBRef(), new DBRefEntry(db, null, null));
174                   if (frefs != null)
175                   {
176                     for (DBRefEntry dbr : frefs)
177                     {
178                       queriesFound.add(dbr.getAccessionId());
179                       queriesMade.remove(dbr.getAccessionId());
180                     }
181                   }
182                   seqs[is] = null;
183                 }
184               }
185               else
186               {
187                 if (fetcher.getRawRecords() != null)
188                 {
189                   System.out.println("# Retrieved from " + db + ":"
190                           + qsb.toString());
191                   StringBuffer rrb = fetcher.getRawRecords();
192                   /*
193                    * for (int rr = 0; rr<rrb.length; rr++) {
194                    */
195                   String hdr;
196                   // if (rr<qs.length)
197                   // {
198                   hdr = "# " + db + ":" + qsb.toString();
199                   /*
200                    * } else { hdr = "# part "+rr; }
201                    */
202                   System.out.println(hdr);
203                   if (rrb != null)
204                   {
205                     System.out.println(rrb);
206                   }
207                   System.out.println("# end of " + hdr);
208                 }
209
210               }
211             }
212
213           }
214         } catch (Exception ex)
215         {
216           reportStdError(db, queriesMade, ex);
217         }
218         if (queriesMade.size() > 0)
219         {
220           System.out.println("# Adding " + queriesMade.size()
221                   + " ids back to queries list for searching again (" + db
222                   + ".");
223           queriesLeft.addAll(queriesMade);
224         }
225       }
226     }
227     if (rseqs.size() > 0)
228     {
229       ret = new SequenceI[rseqs.size()];
230       Enumeration<SequenceI> sqs = rseqs.elements();
231       int si = 0;
232       while (sqs.hasMoreElements())
233       {
234         SequenceI s = sqs.nextElement();
235         ret[si++] = s;
236         s.updatePDBIds();
237       }
238     }
239     return ret;
240   }
241
242   public void reportStdError(String db, List<String> queriesMade,
243           Exception ex)
244   {
245
246     System.err.println("Failed to retrieve the following references from "
247             + db);
248     int n = 0;
249     for (String qv : queriesMade)
250     {
251       System.err.print(" " + qv + ";");
252       if (n++ > 10)
253       {
254         System.err.println();
255         n = 0;
256       }
257     }
258     System.err.println();
259     ex.printStackTrace();
260   }
261
262   /**
263    * Retrieve an instance of the proxy for the given source
264    * 
265    * @param db
266    *          database source string TODO: add version string/wildcard for
267    *          retrieval of specific DB source/version combinations.
268    * @return an instance of DbSourceProxy for that db.
269    */
270   public List<DbSourceProxy> getSourceProxy(String db)
271   {
272     List<DbSourceProxy> dbs;
273     Map<String, DbSourceProxy> dblist = FETCHABLEDBS.get(db);
274     if (dblist == null)
275     {
276       return new ArrayList<DbSourceProxy>();
277     }
278     ;
279     if (dblist.size() > 1)
280     {
281       DbSourceProxy[] l = dblist.values().toArray(new DbSourceProxy[0]);
282       int i = 0;
283       String[] nm = new String[l.length];
284       // make sure standard dbs appear first, followed by reference das sources,
285       // followed by anything else.
286       for (DbSourceProxy s : l)
287       {
288         nm[i++] = "" + s.getTier() + s.getDbName().toLowerCase();
289       }
290       QuickSort.sort(nm, l);
291       dbs = new ArrayList<DbSourceProxy>();
292       for (i = l.length - 1; i >= 0; i--)
293       {
294         dbs.add(l[i]);
295       }
296     }
297     else
298     {
299       dbs = new ArrayList<DbSourceProxy>(dblist.values());
300     }
301     return dbs;
302   }
303
304   /**
305    * constructs an instance of the proxy and registers it as a valid dbrefsource
306    * 
307    * @param dbSourceProxy
308    *          reference for class implementing
309    *          jalview.ws.seqfetcher.DbSourceProxy
310    */
311   protected void addDBRefSourceImpl(
312           Class<? extends DbSourceProxy> dbSourceProxy)
313           throws IllegalArgumentException
314   {
315     DbSourceProxy proxy = null;
316     try
317     {
318       DbSourceProxy proxyObj = dbSourceProxy.getConstructor().newInstance();
319       proxy = proxyObj;
320     } catch (IllegalArgumentException e)
321     {
322       throw e;
323     } catch (Exception e)
324     {
325       // Serious problems if this happens.
326       throw new Error(
327               MessageManager
328                       .getString("error.dbrefsource_implementation_exception"),
329               e);
330     }
331     addDbRefSourceImpl(proxy);
332   }
333
334   /**
335    * add the properly initialised DbSourceProxy object 'proxy' to the list of
336    * sequence fetchers
337    * 
338    * @param proxy
339    */
340   protected void addDbRefSourceImpl(DbSourceProxy proxy)
341   {
342     if (proxy != null)
343     {
344       if (FETCHABLEDBS == null)
345       {
346         FETCHABLEDBS = new Hashtable<String, Map<String, DbSourceProxy>>();
347       }
348       Map<String, DbSourceProxy> slist = FETCHABLEDBS.get(proxy
349               .getDbSource());
350       if (slist == null)
351       {
352         FETCHABLEDBS.put(proxy.getDbSource(),
353                 slist = new Hashtable<String, DbSourceProxy>());
354       }
355       slist.put(proxy.getDbName(), proxy);
356     }
357   }
358
359   /**
360    * select sources which are implemented by instances of the given class
361    * 
362    * @param class that implements DbSourceProxy
363    * @return null or vector of source names for fetchers
364    */
365   public String[] getDbInstances(Class class1)
366   {
367     if (!DbSourceProxy.class.isAssignableFrom(class1))
368     {
369       throw new Error(
370               MessageManager
371                       .formatMessage(
372                               "error.implementation_error_dbinstance_must_implement_interface",
373                               new String[] { class1.toString() }));
374     }
375     if (FETCHABLEDBS == null)
376     {
377       return null;
378     }
379     String[] sources = null;
380     Vector<String> src = new Vector<String>();
381     Enumeration<String> dbs = FETCHABLEDBS.keys();
382     while (dbs.hasMoreElements())
383     {
384       String dbn = dbs.nextElement();
385       for (DbSourceProxy dbp : FETCHABLEDBS.get(dbn).values())
386       {
387         if (class1.isAssignableFrom(dbp.getClass()))
388         {
389           src.addElement(dbn);
390         }
391       }
392     }
393     if (src.size() > 0)
394     {
395       src.copyInto(sources = new String[src.size()]);
396     }
397     return sources;
398   }
399
400   public DbSourceProxy[] getDbSourceProxyInstances(Class class1)
401   {
402     List<DbSourceProxy> prlist = new ArrayList<DbSourceProxy>();
403     for (String fetchable : getSupportedDb())
404     {
405       for (DbSourceProxy pr : getSourceProxy(fetchable))
406       {
407         if (class1.isInstance(pr))
408         {
409           prlist.add(pr);
410         }
411       }
412     }
413     if (prlist.size() == 0)
414     {
415       return null;
416     }
417     return prlist.toArray(new DbSourceProxy[0]);
418   }
419
420 }