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