Clover icon

Coverage Report

  1. Project Clover database Mon Sep 2 2024 17:57:51 BST
  2. Package jalview.datamodel.features

File SequenceFeatures.java

 

Coverage histogram

../../../img/srcFileCovDistChart10.png
0% of files have more coverage

Code metrics

34
99
24
1
472
283
47
0.47
4.12
24
1.96

Classes

Class Line # Actions
SequenceFeatures 45 99 47
1.0100%
 

Contributing tests

This file is covered by 600 tests. .

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.datamodel.features;
22   
23    import java.util.ArrayList;
24    import java.util.Collections;
25    import java.util.HashSet;
26    import java.util.List;
27    import java.util.Map;
28    import java.util.Map.Entry;
29    import java.util.Set;
30    import java.util.TreeMap;
31   
32    import intervalstore.api.IntervalI;
33    import jalview.datamodel.SequenceFeature;
34    import jalview.io.gff.SequenceOntologyFactory;
35    import jalview.io.gff.SequenceOntologyI;
36   
37    /**
38    * A class that stores sequence features in a way that supports efficient
39    * querying by type and location (overlap). Intended for (but not limited to)
40    * storage of features for one sequence.
41    *
42    * @author gmcarstairs
43    *
44    */
 
45    public class SequenceFeatures implements SequenceFeaturesI
46    {
47    /*
48    * map from feature type to structured store of features for that type
49    * null types are permitted (but not a good idea!)
50    */
51    private Map<String, FeatureStore> featureStore;
52   
53    /**
54    * Constructor
55    */
 
56  33316 toggle public SequenceFeatures()
57    {
58    /*
59    * use a TreeMap so that features are returned in alphabetical order of type
60    * ? wrap as a synchronized map for add and delete operations
61    */
62    // featureStore = Collections
63    // .synchronizedSortedMap(new TreeMap<String, FeatureStore>());
64  33316 featureStore = new TreeMap<>();
65    }
66   
67    /**
68    * Constructor given a list of features
69    */
 
70  46 toggle public SequenceFeatures(List<SequenceFeature> features)
71    {
72  46 this();
73  46 if (features != null)
74    {
75  43 for (SequenceFeature feature : features)
76    {
77  423 add(feature);
78    }
79    }
80    }
81   
82    /**
83    * {@inheritDoc}
84    */
 
85  212086 toggle @Override
86    public boolean add(SequenceFeature sf)
87    {
88  212086 String type = sf.getType();
89  212086 if (type == null)
90    {
91  2 jalview.bin.Console
92    .errPrintln("Feature type may not be null: " + sf.toString());
93  2 return false;
94    }
95   
96  212084 if (featureStore.get(type) == null)
97    {
98  2565 featureStore.put(type, new FeatureStore());
99    }
100  212084 return featureStore.get(type).addFeature(sf);
101    }
102   
103    /**
104    * {@inheritDoc}
105    */
 
106  287573 toggle @Override
107    public List<SequenceFeature> findFeatures(int from, int to,
108    String... type)
109    {
110  287574 List<SequenceFeature> result = new ArrayList<>();
111   
112  287577 for (FeatureStore featureSet : varargToTypes(type))
113    {
114  119906 result.addAll(featureSet.findOverlappingFeatures(from, to));
115    }
116   
117  287578 return result;
118    }
119   
120    /**
121    * {@inheritDoc}
122    */
 
123  5747 toggle @Override
124    public List<SequenceFeature> getAllFeatures(String... type)
125    {
126  5747 List<SequenceFeature> result = new ArrayList<>();
127   
128  5747 result.addAll(getPositionalFeatures(type));
129   
130  5747 result.addAll(getNonPositionalFeatures());
131   
132  5747 return result;
133    }
134   
135    /**
136    * {@inheritDoc}
137    */
 
138  48 toggle @Override
139    public List<SequenceFeature> getFeaturesByOntology(String... ontologyTerm)
140    {
141  48 if (ontologyTerm == null || ontologyTerm.length == 0)
142    {
143  3 return new ArrayList<>();
144    }
145   
146  45 Set<String> featureTypes = getFeatureTypes(ontologyTerm);
147  45 if (featureTypes.isEmpty())
148    {
149    /*
150    * no features of the specified type or any sub-type
151    */
152  18 return new ArrayList<>();
153    }
154   
155  27 return getAllFeatures(
156    featureTypes.toArray(new String[featureTypes.size()]));
157    }
158   
159    /**
160    * {@inheritDoc}
161    */
 
162  35 toggle @Override
163    public int getFeatureCount(boolean positional, String... type)
164    {
165  35 int result = 0;
166   
167  35 for (FeatureStore featureSet : varargToTypes(type))
168    {
169  55 result += featureSet.getFeatureCount(positional);
170    }
171  35 return result;
172    }
173   
174    /**
175    * {@inheritDoc}
176    */
 
177  22 toggle @Override
178    public int getTotalFeatureLength(String... type)
179    {
180  22 int result = 0;
181   
182  22 for (FeatureStore featureSet : varargToTypes(type))
183    {
184  33 result += featureSet.getTotalFeatureLength();
185    }
186  22 return result;
187    }
188   
189    /**
190    * {@inheritDoc}
191    */
 
192  5897 toggle @Override
193    public List<SequenceFeature> getPositionalFeatures(String... type)
194    {
195  5897 List<SequenceFeature> result = new ArrayList<>();
196   
197  5897 for (FeatureStore featureSet : varargToTypes(type))
198    {
199  3730 result.addAll(featureSet.getPositionalFeatures());
200    }
201  5897 return result;
202    }
203   
204    /**
205    * A convenience method that converts a vararg for feature types to an
206    * Iterable over matched feature sets. If no types are specified, all feature
207    * sets are returned. If one or more types are specified, feature sets for
208    * those types are returned, preserving the order of the types.
209    *
210    * @param type
211    * @return
212    */
 
213  300826 toggle protected Iterable<FeatureStore> varargToTypes(String... type)
214    {
215  300831 if (type == null || type.length == 0)
216    {
217    /*
218    * no vararg parameter supplied - return all
219    */
220  12695 return featureStore.values();
221    }
222   
223  288142 List<FeatureStore> types = new ArrayList<>();
224  288157 for (String theType : type)
225    {
226  288683 if (theType != null && featureStore.containsKey(theType))
227    {
228  120037 types.add(featureStore.get(theType));
229    }
230    }
231  288144 return types;
232    }
233   
234    /**
235    * {@inheritDoc}
236    */
 
237  25 toggle @Override
238    public List<SequenceFeature> getContactFeatures(String... type)
239    {
240  25 List<SequenceFeature> result = new ArrayList<>();
241   
242  25 for (FeatureStore featureSet : varargToTypes(type))
243    {
244  19 result.addAll(featureSet.getContactFeatures());
245    }
246  25 return result;
247    }
248   
249    /**
250    * {@inheritDoc}
251    */
 
252  5813 toggle @Override
253    public List<SequenceFeature> getNonPositionalFeatures(String... type)
254    {
255  5813 List<SequenceFeature> result = new ArrayList<>();
256   
257  5813 for (FeatureStore featureSet : varargToTypes(type))
258    {
259  3700 result.addAll(featureSet.getNonPositionalFeatures());
260    }
261  5813 return result;
262    }
263   
264    /**
265    * {@inheritDoc}
266    */
 
267  227 toggle @Override
268    public boolean delete(SequenceFeature sf)
269    {
270  227 for (FeatureStore featureSet : featureStore.values())
271    {
272  327 if (featureSet.delete(sf))
273    {
274  225 return true;
275    }
276    }
277  2 return false;
278    }
279   
280    /**
281    * {@inheritDoc}
282    */
 
283  935 toggle @Override
284    public boolean hasFeatures()
285    {
286  935 for (FeatureStore featureSet : featureStore.values())
287    {
288  932 if (!featureSet.isEmpty())
289    {
290  924 return true;
291    }
292    }
293  11 return false;
294    }
295   
296    /**
297    * {@inheritDoc}
298    */
 
299  953 toggle @Override
300    public Set<String> getFeatureGroups(boolean positionalFeatures,
301    String... type)
302    {
303  953 Set<String> groups = new HashSet<>();
304   
305  953 for (FeatureStore featureSet : varargToTypes(type))
306    {
307  2001 groups.addAll(featureSet.getFeatureGroups(positionalFeatures));
308    }
309   
310  953 return groups;
311    }
312   
313    /**
314    * {@inheritDoc}
315    */
 
316  804 toggle @Override
317    public Set<String> getFeatureTypesForGroups(boolean positionalFeatures,
318    String... groups)
319    {
320  804 Set<String> result = new HashSet<>();
321   
322  804 for (Entry<String, FeatureStore> featureType : featureStore.entrySet())
323    {
324  3719 Set<String> featureGroups = featureType.getValue()
325    .getFeatureGroups(positionalFeatures);
326  3719 for (String group : groups)
327    {
328  3723 if (featureGroups.contains(group))
329    {
330    /*
331    * yes this feature type includes one of the query groups
332    */
333  1927 result.add(featureType.getKey());
334  1927 break;
335    }
336    }
337    }
338   
339  804 return result;
340    }
341   
342    /**
343    * {@inheritDoc}
344    */
 
345  58 toggle @Override
346    public Set<String> getFeatureTypes(String... soTerm)
347    {
348  58 Set<String> types = new HashSet<>();
349  58 for (Entry<String, FeatureStore> entry : featureStore.entrySet())
350    {
351  93 String type = entry.getKey();
352  93 if (!entry.getValue().isEmpty() && isOntologyTerm(type, soTerm))
353    {
354  65 types.add(type);
355    }
356    }
357  58 return types;
358    }
359   
360    /**
361    * Answers true if the given type matches one of the specified terms (or is a
362    * sub-type of one in the Sequence Ontology), or if no terms are supplied.
363    * Answers false if filter terms are specified and the given term does not
364    * match any of them.
365    *
366    * @param type
367    * @param soTerm
368    * @return
369    */
 
370  99 toggle protected boolean isOntologyTerm(String type, String... soTerm)
371    {
372  99 if (soTerm == null || soTerm.length == 0)
373    {
374  20 return true;
375    }
376  79 SequenceOntologyI so = SequenceOntologyFactory.getInstance();
377  79 for (String term : soTerm)
378    {
379  88 if (type.equals(term) || so.isA(type, term))
380    {
381  51 return true;
382    }
383    }
384  28 return false;
385    }
386   
387    /**
388    * {@inheritDoc}
389    */
 
390  1917 toggle @Override
391    public float getMinimumScore(String type, boolean positional)
392    {
393  1917 return featureStore.containsKey(type)
394    ? featureStore.get(type).getMinimumScore(positional)
395    : Float.NaN;
396    }
397   
398    /**
399    * {@inheritDoc}
400    */
 
401  1891 toggle @Override
402    public float getMaximumScore(String type, boolean positional)
403    {
404  1891 return featureStore.containsKey(type)
405    ? featureStore.get(type).getMaximumScore(positional)
406    : Float.NaN;
407    }
408   
409    /**
410    * A convenience method to sort features by start position ascending (if on
411    * forward strand), or end position descending (if on reverse strand)
412    *
413    * @param features
414    * @param forwardStrand
415    */
 
416  1421 toggle public static void sortFeatures(List<? extends IntervalI> features,
417    final boolean forwardStrand)
418    {
419  1421 Collections.sort(features,
420  1421 forwardStrand ? IntervalI.COMPARE_BEGIN_ASC_END_DESC
421    : IntervalI.COMPARE_END_DESC);
422    }
423   
424    /**
425    * {@inheritDoc} This method is 'semi-optimised': it only inspects features
426    * for types that include the specified group, but has to inspect every
427    * feature of those types for matching feature group. This is efficient unless
428    * a sequence has features that share the same type but are in different
429    * groups - an unlikely case.
430    * <p>
431    * For example, if RESNUM feature is created with group = PDBID, then features
432    * would only be retrieved for those sequences associated with the target
433    * PDBID (group).
434    */
 
435  498 toggle @Override
436    public List<SequenceFeature> getFeaturesForGroup(boolean positional,
437    String group, String... type)
438    {
439  498 List<SequenceFeature> result = new ArrayList<>();
440  498 for (FeatureStore featureSet : varargToTypes(type))
441    {
442  122 if (featureSet.getFeatureGroups(positional).contains(group))
443    {
444  36 result.addAll(featureSet.getFeaturesForGroup(positional, group));
445    }
446    }
447  498 return result;
448    }
449   
450    /**
451    * {@inheritDoc}
452    */
 
453  39 toggle @Override
454    public boolean shiftFeatures(int fromPosition, int shiftBy)
455    {
456  39 boolean modified = false;
457  39 for (FeatureStore fs : featureStore.values())
458    {
459  40 modified |= fs.shiftFeatures(fromPosition, shiftBy);
460    }
461  39 return modified;
462    }
463   
464    /**
465    * {@inheritDoc}
466    */
 
467  2 toggle @Override
468    public void deleteAll()
469    {
470  2 featureStore.clear();
471    }
472    }