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