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