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