9650b8c46d47f38793f1b7a436b8a6add8e19ac1
[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    * DAS sources are tier 1 - if we have a direct DB connection then we should prefer it
74    */
75   private int tier=1;
76
77   /**
78    * create a new DbSource proxy for a DAS 1 source
79    * 
80    * @param dbnbame
81    *          Human Readable Name to use when fetching from this source
82    * @param dbrefname
83    *          DbRefName for DbRefs attached to sequences retrieved from this
84    *          source
85    * @param source
86    *          Das1Source
87    * @param coordsys
88    *          specific coordinate system to use for this source
89    * @throws Exception
90    *           if source is not capable of the 'sequence' command
91    */
92   public DasSequenceSource(String dbname, String dbrefname, SOURCE source,
93           VERSION version, COORDINATES coordsys,
94           MultipleConnectionPropertyProviderI connprops) throws Exception
95   {
96     if (!(jsrc = new JalviewSource(source, connprops, false))
97             .isSequenceSource())
98     {
99       throw new Exception("Source " + source.getTitle()
100               + " does not support the sequence command.");
101     }
102     this.tier = 1+((jsrc.isLocal() || jsrc.isReferenceSource()) ? 0 : 1);
103     this.source = source;
104     this.dbname = dbname;
105     this.dbrefname = dbrefname.toUpperCase();
106     if (coordsys != null)
107     {
108       this.coordsys = coordsys;
109     }
110     this.connprops = connprops;
111   }
112
113   public String getAccessionSeparator()
114   {
115     return "\t";
116   }
117
118   public Regex getAccessionValidator()
119   {
120     /** ? * */
121     return Regex.perlCode("m/([^:]+)(:\\d+,\\d+)?/");
122   }
123
124   public String getDbName()
125   {
126     // TODO: map to
127     return dbname + " (DAS)";
128   }
129
130   public String getDbSource()
131   {
132     return dbrefname;
133   }
134
135   public String getDbVersion()
136   {
137     return coordsys != null ? coordsys.getVersion() : "";
138   }
139
140   public AlignmentI getSequenceRecords(String queries) throws Exception
141   {
142     StringTokenizer st = new StringTokenizer(queries, "\t");
143     List<String> toks = new ArrayList<String>(), src = new ArrayList<String>(), acIds = new ArrayList<String>();
144     while (st.hasMoreTokens())
145     {
146       String t;
147       toks.add(t = st.nextToken());
148       acIds.add(t.replaceAll(":[0-9,]+", ""));
149     }
150     src.add(jsrc.getSourceURL());
151     Map<String, Map<List<String>, DasSequenceAdapter>> resultset = new HashMap<String, Map<List<String>, DasSequenceAdapter>>();
152     Map<String, Map<List<String>, Exception>> errors = new HashMap<String, Map<List<String>, Exception>>();
153
154     // First try multiple sources
155     boolean multiple = true, retry = false;
156     do
157     {
158       if (!multiple)
159       {
160         retry = false;
161         // slow, fetch one at a time.
162         for (String sr : src)
163         {
164           System.err
165                   .println("Retrieving IDs individually from das source: "
166                           + sr);
167           org.biodas.jdas.client.SequenceClient sq = new SequenceClient(
168                   connprops.getConnectionPropertyProviderFor(sr));
169           for (String q : toks)
170           {
171             List<String> qset = Arrays.asList(new String[]
172             { q });
173             try
174             {
175               DasSequenceAdapter s = sq.fetchData(sr, qset);
176               Map<List<String>, DasSequenceAdapter> dss = resultset.get(sr);
177               if (dss == null)
178               {
179                 resultset
180                         .put(sr,
181                                 dss = new HashMap<List<String>, DasSequenceAdapter>());
182               }
183               dss.put(qset, s);
184             } catch (Exception x)
185             {
186               Map<List<String>, Exception> ers = errors.get(sr);
187               if (ers == null)
188               {
189                 errors.put(sr, ers = new HashMap<List<String>, Exception>());
190               }
191               ers.put(qset, x);
192             }
193           }
194         }
195       }
196       else
197       {
198         SequenceClientMultipleSources sclient;
199         sclient = new SequenceClientMultipleSources();
200         sclient.fetchData(src, toks, resultset, errors);
201         sclient.shutDown();
202         while (!sclient.isTerminated())
203         {
204           try
205           {
206             Thread.sleep(200);
207
208           } catch (InterruptedException x)
209           {
210           }
211         }
212         if (resultset.isEmpty() && !errors.isEmpty())
213         {
214           retry = true;
215           multiple = false;
216         }
217       }
218     } while (retry);
219
220     if (resultset.isEmpty())
221     {
222       System.err.println("Sequence Query to " + jsrc.getTitle() + " with '"
223               + queries + "' returned no sequences.");
224       return null;
225     }
226     else
227     {
228       Vector<SequenceI> seqs = null;
229       for (Map.Entry<String, Map<List<String>, DasSequenceAdapter>> resset : resultset
230               .entrySet())
231       {
232         for (Map.Entry<List<String>, DasSequenceAdapter> result : resset
233                 .getValue().entrySet())
234         {
235           DasSequenceAdapter dasseqresp = result.getValue();
236           List<String> accessions = result.getKey();
237           for (SEQUENCE e : dasseqresp.getSequence())
238           {
239             String lbl = e.getId();
240
241             if (acIds.indexOf(lbl) == -1)
242             {
243               System.err
244                       .println("Warning - received sequence event for strange accession code ("
245                               + lbl + ")");
246             }
247             else
248             {
249               if (seqs == null)
250               {
251                 if (e.getContent().length() == 0)
252                 {
253                   System.err
254                           .println("Empty sequence returned for accession code ("
255                                   + lbl
256                                   + ") from "
257                                   + resset.getKey()
258                                   + " (source is " + getDbName());
259                   continue;
260                 }
261               }
262               seqs = new java.util.Vector<SequenceI>();
263               // JDAS returns a sequence complete with any newlines and spaces
264               // in the XML
265               Sequence sq = new Sequence(lbl, e.getContent().replaceAll(
266                       "\\s+", ""));
267               sq.setStart(e.getStart().intValue());
268               sq.addDBRef(new DBRefEntry(getDbSource(), getDbVersion()
269                       + ":" + e.getVersion(), lbl));
270               seqs.addElement(sq);
271             }
272           }
273         }
274       }
275
276       if (seqs == null || seqs.size() == 0)
277         return null;
278       SequenceI[] sqs = new SequenceI[seqs.size()];
279       for (int i = 0, iSize = seqs.size(); i < iSize; i++)
280       {
281         sqs[i] = (SequenceI) seqs.elementAt(i);
282       }
283       Alignment al = new Alignment(sqs);
284       if (jsrc.isFeatureSource())
285       {
286         java.util.Vector<jalviewSourceI> srcs = new java.util.Vector<jalviewSourceI>();
287         srcs.addElement(jsrc);
288         try
289         {
290           jalview.ws.DasSequenceFeatureFetcher dssf = new jalview.ws.DasSequenceFeatureFetcher(
291                   sqs, null, srcs, false, false, multiple);
292           while (dssf.isRunning())
293           {
294             try
295             {
296               Thread.sleep(200);
297             } catch (InterruptedException x)
298             {
299
300             }
301           }
302
303         } catch (Exception x)
304         {
305           Cache.log
306                   .error("Couldn't retrieve features for sequence from its source.",
307                           x);
308         }
309       }
310
311       return al;
312     }
313   }
314
315   public String getTestQuery()
316   {
317     return coordsys == null ? "" : coordsys.getTestRange();
318   }
319
320   public boolean isValidReference(String accession)
321   {
322     // TODO try to validate an accession against source
323     // We don't really know how to do this without querying source
324
325     return true;
326   }
327
328   /**
329    * @return the source
330    */
331   public SOURCE getSource()
332   {
333     return source;
334   }
335
336   /**
337    * @return the coordsys
338    */
339   public COORDINATES getCoordsys()
340   {
341     return coordsys;
342   }
343
344   @Override
345   public int getTier()
346   {
347     return tier;
348   }
349 }