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