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