95ff7d0768ee6563ef632aa07bd9d8cc77102592
[jalview.git] / src / jalview / ws / dbsources / das / datamodel / DasSequenceSource.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3  * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, G Barton, M Clamp, S Searle
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 of the License, or (at your option) any later version.
10  *  
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.ws.dbsources.das.datamodel;
19
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.StringTokenizer;
26 import java.util.Vector;
27
28 import org.biodas.jdas.client.SequenceClient;
29 import org.biodas.jdas.client.adapters.sequence.DasSequenceAdapter;
30 import org.biodas.jdas.client.threads.MultipleConnectionPropertyProviderI;
31 import org.biodas.jdas.client.threads.SequenceClientMultipleSources;
32 import org.biodas.jdas.schema.sequence.SEQUENCE;
33 import org.biodas.jdas.schema.sources.COORDINATES;
34 import org.biodas.jdas.schema.sources.SOURCE;
35 import org.biodas.jdas.schema.sources.VERSION;
36
37 import com.stevesoft.pat.Regex;
38
39 import jalview.ws.dbsources.das.api.jalviewSourceI;
40 import jalview.ws.seqfetcher.*;
41 import jalview.bin.Cache;
42 import jalview.datamodel.Alignment;
43 import jalview.datamodel.AlignmentI;
44 import jalview.datamodel.DBRefEntry;
45 import jalview.datamodel.Sequence;
46 import jalview.datamodel.SequenceI;
47
48 /**
49  * an instance of this class is created for each unique DAS Sequence source (ie
50  * one capable of handling the 'sequence' for a particular MapMaster)
51  * 
52  * @author JimP
53  * 
54  */
55 public class DasSequenceSource extends DbSourceProxyImpl implements
56         DbSourceProxy
57 {
58   private jalviewSourceI jsrc;
59
60   protected SOURCE source = null;
61
62   protected VERSION version = null;
63
64   protected COORDINATES coordsys = null;
65
66   protected String dbname = "DASCS";
67
68   protected String dbrefname = "das:source";
69
70   protected MultipleConnectionPropertyProviderI connprops = null;
71
72   /**
73    * create a new DbSource proxy for a DAS 1 source
74    * 
75    * @param dbnbame
76    *          Human Readable Name to use when fetching from this source
77    * @param dbrefname
78    *          DbRefName for DbRefs attached to sequences retrieved from this
79    *          source
80    * @param source
81    *          Das1Source
82    * @param coordsys
83    *          specific coordinate system to use for this source
84    * @throws Exception
85    *           if source is not capable of the 'sequence' command
86    */
87   public DasSequenceSource(String dbname, String dbrefname, SOURCE source,
88           VERSION version, COORDINATES coordsys,
89           MultipleConnectionPropertyProviderI connprops) throws Exception
90   {
91     if (!(jsrc = new JalviewSource(source, connprops, false))
92             .isSequenceSource())
93     {
94       throw new Exception("Source " + source.getTitle()
95               + " does not support the sequence command.");
96     }
97     this.source = source;
98     this.dbname = dbname;
99     this.dbrefname = dbrefname.toUpperCase();
100     if (coordsys != null)
101     {
102       this.coordsys = coordsys;
103     }
104     this.connprops = connprops;
105   }
106
107   public String getAccessionSeparator()
108   {
109     return "\t";
110   }
111
112   public Regex getAccessionValidator()
113   {
114     /** ? * */
115     return Regex.perlCode("m/([^:]+)(:\\d+,\\d+)?/");
116   }
117
118   public String getDbName()
119   {
120     // TODO: map to
121     return dbname + " (DAS)";
122   }
123
124   public String getDbSource()
125   {
126     return dbrefname;
127   }
128
129   public String getDbVersion()
130   {
131     return coordsys != null ? coordsys.getVersion() : "";
132   }
133
134   public AlignmentI getSequenceRecords(String queries) throws Exception
135   {
136     StringTokenizer st = new StringTokenizer(queries, "\t");
137     List<String> toks = new ArrayList<String>(), src = new ArrayList<String>(), acIds = new ArrayList<String>();
138     while (st.hasMoreTokens())
139     {
140       String t;
141       toks.add(t = st.nextToken());
142       acIds.add(t.replaceAll(":[0-9,]+", ""));
143     }
144     src.add(jsrc.getSourceURL());
145     Map<String, Map<List<String>, DasSequenceAdapter>> resultset = new HashMap<String, Map<List<String>, DasSequenceAdapter>>();
146     Map<String, Map<List<String>, Exception>> errors = new HashMap<String, Map<List<String>, Exception>>();
147
148     // First try multiple sources
149     boolean multiple = true, retry = false;
150     do
151     {
152       if (!multiple)
153       {
154         retry = false;
155         // slow, fetch one at a time.
156         for (String sr : src)
157         {
158           System.err
159                   .println("Retrieving IDs individually from das source: "
160                           + sr);
161           org.biodas.jdas.client.SequenceClient sq = new SequenceClient(
162                   connprops.getConnectionPropertyProviderFor(sr));
163           for (String q : toks)
164           {
165             List<String> qset = Arrays.asList(new String[]
166             { q });
167             try
168             {
169               DasSequenceAdapter s = sq.fetchData(sr, qset);
170               Map<List<String>, DasSequenceAdapter> dss = resultset.get(sr);
171               if (dss == null)
172               {
173                 resultset
174                         .put(sr,
175                                 dss = new HashMap<List<String>, DasSequenceAdapter>());
176               }
177               dss.put(qset, s);
178             } catch (Exception x)
179             {
180               Map<List<String>, Exception> ers = errors.get(sr);
181               if (ers == null)
182               {
183                 errors.put(sr, ers = new HashMap<List<String>, Exception>());
184               }
185               ers.put(qset, x);
186             }
187           }
188         }
189       }
190       else
191       {
192         SequenceClientMultipleSources sclient;
193         sclient = new SequenceClientMultipleSources();
194         sclient.fetchData(src, toks, resultset, errors);
195         sclient.shutDown();
196         while (!sclient.isTerminated())
197         {
198           try
199           {
200             Thread.sleep(200);
201
202           } catch (InterruptedException x)
203           {
204           }
205         }
206         if (resultset.isEmpty() && !errors.isEmpty())
207         {
208           retry = true;
209           multiple = false;
210         }
211       }
212     } while (retry);
213
214     if (resultset.isEmpty())
215     {
216       System.err.println("Sequence Query to " + jsrc.getTitle() + " with '"
217               + queries + "' returned no sequences.");
218       return null;
219     }
220     else
221     {
222       Vector<SequenceI> seqs = null;
223       for (Map.Entry<String, Map<List<String>, DasSequenceAdapter>> resset : resultset
224               .entrySet())
225       {
226         for (Map.Entry<List<String>, DasSequenceAdapter> result : resset
227                 .getValue().entrySet())
228         {
229           DasSequenceAdapter dasseqresp = result.getValue();
230           List<String> accessions = result.getKey();
231           for (SEQUENCE e : dasseqresp.getSequence())
232           {
233             String lbl = e.getId();
234
235             if (acIds.indexOf(lbl) == -1)
236             {
237               System.err
238                       .println("Warning - received sequence event for strange accession code ("
239                               + lbl + ")");
240             }
241             else
242             {
243               if (seqs == null)
244               {
245                 if (e.getContent().length() == 0)
246                 {
247                   System.err
248                           .println("Empty sequence returned for accession code ("
249                                   + lbl
250                                   + ") from "
251                                   + resset.getKey()
252                                   + " (source is " + getDbName());
253                   continue;
254                 }
255               }
256               seqs = new java.util.Vector<SequenceI>();
257               // JDAS returns a sequence complete with any newlines and spaces
258               // in the XML
259               Sequence sq = new Sequence(lbl, e.getContent().replaceAll(
260                       "\\s+", ""));
261               sq.setStart(e.getStart().intValue());
262               sq.addDBRef(new DBRefEntry(getDbSource(), getDbVersion()
263                       + ":" + e.getVersion(), lbl));
264               seqs.addElement(sq);
265             }
266           }
267         }
268       }
269
270       if (seqs == null || seqs.size() == 0)
271         return null;
272       SequenceI[] sqs = new SequenceI[seqs.size()];
273       for (int i = 0, iSize = seqs.size(); i < iSize; i++)
274       {
275         sqs[i] = (SequenceI) seqs.elementAt(i);
276       }
277       Alignment al = new Alignment(sqs);
278       if (jsrc.isFeatureSource())
279       {
280         java.util.Vector<jalviewSourceI> srcs = new java.util.Vector<jalviewSourceI>();
281         srcs.addElement(jsrc);
282         try
283         {
284           jalview.ws.DasSequenceFeatureFetcher dssf = new jalview.ws.DasSequenceFeatureFetcher(
285                   sqs, null, srcs, false, false, multiple);
286           while (dssf.isRunning())
287           {
288             try
289             {
290               Thread.sleep(200);
291             } catch (InterruptedException x)
292             {
293
294             }
295           }
296
297         } catch (Exception x)
298         {
299           Cache.log
300                   .error("Couldn't retrieve features for sequence from its source.",
301                           x);
302         }
303       }
304
305       return al;
306     }
307   }
308
309   public String getTestQuery()
310   {
311     return coordsys == null ? "" : coordsys.getTestRange();
312   }
313
314   public boolean isValidReference(String accession)
315   {
316     // TODO try to validate an accession against source
317     // We don't really know how to do this without querying source
318
319     return true;
320   }
321
322   /**
323    * @return the source
324    */
325   public SOURCE getSource()
326   {
327     return source;
328   }
329
330   /**
331    * @return the coordsys
332    */
333   public COORDINATES getCoordsys()
334   {
335     return coordsys;
336   }
337 }