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