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