Clover icon

Coverage Report

  1. Project Clover database Thu Dec 4 2025 14:43:25 GMT
  2. Package jalview.ext.ensembl

File EnsemblFeatures.java

 

Coverage histogram

../../../img/srcFileCovDistChart0.png
60% of files have more coverage

Code metrics

22
64
12
1
305
167
25
0.39
5.33
12
2.08

Classes

Class Line # Actions
EnsemblFeatures 48 64 25
0.00%
 

Contributing tests

No tests hitting this source file were found.

Source view

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.ext.ensembl;
22   
23    import java.io.IOException;
24    import java.net.MalformedURLException;
25    import java.net.URL;
26    import java.util.ArrayList;
27    import java.util.Iterator;
28    import java.util.List;
29    import java.util.Map;
30   
31    import org.json.simple.parser.ParseException;
32   
33    import jalview.datamodel.Alignment;
34    import jalview.datamodel.AlignmentI;
35    import jalview.datamodel.Sequence;
36    import jalview.datamodel.SequenceFeature;
37    import jalview.datamodel.SequenceI;
38    import jalview.io.gff.SequenceOntologyI;
39    import jalview.util.JSONUtils;
40   
41    /**
42    * A client for fetching and processing Ensembl feature data in GFF format by
43    * calling the overlap REST service
44    *
45    * @author gmcarstairs
46    * @see http://rest.ensembl.org/documentation/info/overlap_id
47    */
 
48    class EnsemblFeatures extends EnsemblRestClient
49    {
50    /*
51    * The default features to retrieve from Ensembl
52    * can override in getSequenceRecords parameter
53    */
54    private EnsemblFeatureType[] featuresWanted = { EnsemblFeatureType.cds,
55    EnsemblFeatureType.exon, EnsemblFeatureType.variation };
56   
57    /**
58    * Default constructor (to use rest.ensembl.org)
59    */
 
60  0 toggle public EnsemblFeatures()
61    {
62  0 super();
63    }
64   
65    /**
66    * Constructor given the target domain to fetch data from
67    *
68    * @param d
69    */
 
70  0 toggle public EnsemblFeatures(String d)
71    {
72  0 super(d);
73    }
74   
 
75  0 toggle @Override
76    public String getDbName()
77    {
78  0 return "ENSEMBL (features)";
79    }
80   
81    /**
82    * Makes a query to the REST overlap endpoint for the given sequence
83    * identifier. This returns an 'alignment' consisting of one 'dummy sequence'
84    * (the genomic sequence for which overlap features are returned by the
85    * service). This sequence will have on it sequence features which are the
86    * real information of interest, such as CDS regions or sequence variations.
87    */
 
88  0 toggle @Override
89    public AlignmentI getSequenceRecords(String query) throws IOException
90    {
91    // TODO: use a vararg String... for getSequenceRecords instead?
92   
93  0 List<String> queries = new ArrayList<>();
94  0 queries.add(query);
95  0 SequenceI seq = parseFeaturesJson(queries);
96  0 if (seq == null)
97  0 return null;
98  0 return new Alignment(new SequenceI[] { seq });
99   
100    }
101   
102    /**
103    * Parses the JSON response into Jalview sequence features and attaches them
104    * to a dummy sequence
105    *
106    * @param br
107    * @return
108    */
 
109  0 toggle @SuppressWarnings("unchecked")
110    private SequenceI parseFeaturesJson(List<String> queries)
111    {
112  0 SequenceI seq = new Sequence("Dummy", "");
113  0 try
114    {
115  0 Iterator<Object> rvals = (Iterator<Object>) getJSON(null, queries, -1,
116    MODE_ITERATOR, null);
117  0 if (rvals == null)
118    {
119  0 return null;
120    }
121  0 while (rvals.hasNext())
122    {
123  0 try
124    {
125  0 Map<String, Object> obj = (Map<String, Object>) rvals.next();
126  0 String type = obj.get("feature_type").toString();
127  0 int start = Integer.parseInt(obj.get("start").toString());
128  0 int end = Integer.parseInt(obj.get("end").toString());
129  0 String source = obj.get("source").toString();
130  0 String strand = obj.get("strand").toString();
131  0 Object phase = obj.get("phase");
132  0 String alleles = JSONUtils
133    .arrayToStringList((List<Object>) obj.get("alleles"));
134  0 String clinSig = JSONUtils.arrayToStringList(
135    (List<Object>) obj.get("clinical_significance"));
136   
137    /*
138    * convert 'variation' to 'sequence_variant', and 'cds' to 'CDS'
139    * so as to have a valid SO term for the feature type
140    * ('gene', 'exon', 'transcript' don't need any conversion)
141    */
142  0 if ("variation".equals(type))
143    {
144  0 type = SequenceOntologyI.SEQUENCE_VARIANT;
145    }
146  0 else if (SequenceOntologyI.CDS.equalsIgnoreCase((type)))
147    {
148  0 type = SequenceOntologyI.CDS;
149    }
150   
151  0 String desc = getFirstNotNull(obj, "alleles", "external_name",
152    JSON_ID);
153  0 SequenceFeature sf = new SequenceFeature(type, desc, start, end,
154    source);
155  0 sf.setStrand("1".equals(strand) ? "+" : "-");
156  0 if (phase != null)
157    {
158  0 sf.setPhase(phase.toString());
159    }
160  0 setFeatureAttribute(sf, obj, "id");
161  0 setFeatureAttribute(sf, obj, "Parent");
162  0 setFeatureAttribute(sf, obj, "consequence_type");
163  0 sf.setValue("alleles", alleles);
164  0 sf.setValue("clinical_significance", clinSig);
165   
166  0 seq.addSequenceFeature(sf);
167   
168    } catch (Throwable t)
169    {
170    // ignore - keep trying other features
171    }
172    }
173    } catch (ParseException | IOException e)
174    {
175  0 e.printStackTrace();
176    // ignore
177    }
178   
179  0 return seq;
180    }
181   
182    /**
183    * Returns the first non-null attribute found (if any) as a string, formatted
184    * suitably for display as feature description or tooltip. Answers null if
185    * none of the attribute keys is present.
186    *
187    * @param obj
188    * @param keys
189    * @return
190    */
 
191  0 toggle @SuppressWarnings("unchecked")
192    protected String getFirstNotNull(Map<String, Object> obj, String... keys)
193    {
194  0 for (String key : keys)
195    {
196  0 Object val = obj.get(key);
197  0 if (val != null)
198    {
199  0 String s = val instanceof List<?>
200    ? JSONUtils.arrayToStringList((List<Object>) val)
201    : val.toString();
202  0 if (!s.isEmpty())
203    {
204  0 return s;
205    }
206    }
207    }
208  0 return null;
209    }
210   
211    /**
212    * A helper method that reads the 'key' entry in the JSON object, and if not
213    * null, sets its string value as an attribute on the sequence feature
214    *
215    * @param sf
216    * @param obj
217    * @param key
218    */
 
219  0 toggle protected void setFeatureAttribute(SequenceFeature sf,
220    Map<String, Object> obj, String key)
221    {
222  0 Object object = obj.get(key);
223  0 if (object != null)
224    {
225  0 sf.setValue(key, object.toString());
226    }
227    }
228   
229    /**
230    * Returns a URL for the REST overlap endpoint
231    *
232    * @param ids
233    * @return
234    */
 
235  0 toggle @Override
236    protected URL getUrl(List<String> ids) throws MalformedURLException
237    {
238  0 StringBuffer urlstring = new StringBuffer(128);
239  0 urlstring.append(getDomain()).append("/overlap/id/").append(ids.get(0));
240   
241    // @see https://github.com/Ensembl/ensembl-rest/wiki/Output-formats
242  0 urlstring.append("?content-type=" + getResponseMimeType());
243   
244    /*
245    * specify object_type=gene in case is shared by transcript and/or protein;
246    * currently only fetching features for gene sequences;
247    * refactor in future if needed to fetch for transcripts
248    */
249  0 urlstring.append("&").append(OBJECT_TYPE).append("=")
250    .append(OBJECT_TYPE_GENE);
251   
252    /*
253    * specify features to retrieve
254    * @see http://rest.ensembl.org/documentation/info/overlap_id
255    * could make the list a configurable entry in .jalview_properties
256    */
257  0 for (EnsemblFeatureType feature : featuresWanted)
258    {
259  0 urlstring.append("&feature=").append(feature.name());
260    }
261   
262  0 return new URL(urlstring.toString());
263    }
264   
 
265  0 toggle @Override
266    protected boolean useGetRequest()
267    {
268  0 return true;
269    }
270   
271    /**
272    * Returns the MIME type for GFF3. For GET requests the Content-type header
273    * describes the required encoding of the response.
274    */
 
275  0 toggle @Override
276    protected String getRequestMimeType()
277    {
278  0 return "application/json";
279    }
280   
281    /**
282    * Returns the MIME type wanted for the response
283    */
 
284  0 toggle @Override
285    protected String getResponseMimeType()
286    {
287  0 return "application/json";
288    }
289   
290    /**
291    * Overloaded method that allows a list of features to retrieve to be
292    * specified
293    *
294    * @param accId
295    * @param features
296    * @return
297    * @throws IOException
298    */
 
299  0 toggle protected AlignmentI getSequenceRecords(String accId,
300    EnsemblFeatureType[] features) throws IOException
301    {
302  0 featuresWanted = features;
303  0 return getSequenceRecords(accId);
304    }
305    }