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