JAL-2483 JAL-2481 findAllFeatures without scanning all features
[jalview.git] / src / jalview / datamodel / features / SequenceFeatures.java
1 package jalview.datamodel.features;
2
3 import jalview.datamodel.SequenceFeature;
4
5 import java.util.ArrayList;
6 import java.util.Arrays;
7 import java.util.HashMap;
8 import java.util.HashSet;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.Map.Entry;
12 import java.util.Set;
13
14 /**
15  * A class that stores sequence features in a way that supports efficient
16  * querying by type and location (overlap). Intended for (but not limited to)
17  * storage of features for one sequence.
18  * 
19  * @author gmcarstairs
20  *
21  */
22 public class SequenceFeatures implements SequenceFeaturesI
23 {
24
25   /*
26    * map from feature type to structured store of features for that type
27    * null types are permitted (but not a good idea!)
28    */
29   private Map<String, FeatureStore> featureStore;
30
31   /**
32    * Constructor
33    */
34   public SequenceFeatures()
35   {
36     featureStore = new HashMap<String, FeatureStore>();
37   }
38
39   /**
40    * {@inheritDoc}
41    */
42   @Override
43   public boolean add(SequenceFeature sf)
44   {
45     String type = sf.getType();
46
47     if (featureStore.get(type) == null)
48     {
49       featureStore.put(type, new FeatureStore());
50     }
51     return featureStore.get(type).addFeature(sf);
52   }
53
54   /**
55    * {@inheritDoc}
56    */
57   @Override
58   public List<SequenceFeature> findFeatures(int from, int to,
59           String... type)
60   {
61     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
62
63     for (String featureType : varargToTypes(type))
64     {
65       FeatureStore features = featureStore.get(featureType);
66       if (features != null)
67       {
68         result.addAll(features.findOverlappingFeatures(from, to));
69       }
70     }
71
72     return result;
73   }
74
75   /**
76    * {@inheritDoc}
77    */
78   @Override
79   public List<SequenceFeature> getAllFeatures(String... type)
80   {
81     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
82
83     result.addAll(getPositionalFeatures(type));
84
85     result.addAll(getNonPositionalFeatures(type));
86
87     return result;
88   }
89
90   /**
91    * {@inheritDoc}
92    */
93   @Override
94   public int getFeatureCount(boolean positional, String... type)
95   {
96     int result = 0;
97
98     for (String featureType : varargToTypes(type))
99     {
100       FeatureStore featureSet = featureStore.get(featureType);
101       if (featureSet != null)
102       {
103         result += featureSet.getFeatureCount(positional);
104       }
105     }
106     return result;
107   }
108
109   /**
110    * {@inheritDoc}
111    */
112   @Override
113   public int getTotalFeatureLength(String... type)
114   {
115     int result = 0;
116
117     for (String featureType : varargToTypes(type))
118     {
119       FeatureStore featureSet = featureStore.get(featureType);
120       if (featureSet != null)
121       {
122         result += featureSet.getTotalFeatureLength();
123       }
124     }
125     return result;
126
127   }
128
129   /**
130    * {@inheritDoc}
131    */
132   @Override
133   public List<SequenceFeature> getPositionalFeatures(String... type)
134   {
135     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
136
137     for (String featureType : varargToTypes(type))
138     {
139       FeatureStore featureSet = featureStore.get(featureType);
140       if (featureSet != null)
141       {
142         result.addAll(featureSet.getPositionalFeatures());
143       }
144     }
145     return result;
146   }
147
148   /**
149    * A convenience method that converts a vararg for feature types to an
150    * Iterable, replacing the value with the stored feature types if it is null
151    * or empty
152    * 
153    * @param type
154    * @return
155    */
156   protected Iterable<String> varargToTypes(String... type)
157   {
158     return type == null || type.length == 0 ? featureStore
159             .keySet() : Arrays.asList(type);
160   }
161
162   /**
163    * {@inheritDoc}
164    */
165   @Override
166   public List<SequenceFeature> getContactFeatures(String... type)
167   {
168     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
169
170     for (String featureType : varargToTypes(type))
171     {
172       FeatureStore featureSet = featureStore.get(featureType);
173       if (featureSet != null)
174       {
175         result.addAll(featureSet.getContactFeatures());
176       }
177     }
178     return result;
179   }
180
181   /**
182    * {@inheritDoc}
183    */
184   @Override
185   public List<SequenceFeature> getNonPositionalFeatures(String... type)
186   {
187     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
188
189     for (String featureType : varargToTypes(type))
190     {
191       FeatureStore featureSet = featureStore.get(featureType);
192       if (featureSet != null)
193       {
194         result.addAll(featureSet.getNonPositionalFeatures());
195       }
196     }
197     return result;
198   }
199
200   /**
201    * {@inheritDoc}
202    */
203   @Override
204   public boolean delete(SequenceFeature sf)
205   {
206     for (FeatureStore featureSet : featureStore.values())
207     {
208       if (featureSet.delete(sf))
209       {
210         return true;
211       }
212     }
213     return false;
214   }
215
216   /**
217    * {@inheritDoc}
218    */
219   @Override
220   public boolean hasFeatures()
221   {
222     for (FeatureStore featureSet : featureStore.values())
223     {
224       if (!featureSet.isEmpty())
225       {
226         return true;
227       }
228     }
229     return false;
230   }
231
232   /**
233    * {@inheritDoc}
234    */
235   @Override
236   public Set<String> getFeatureGroups(boolean positionalFeatures,
237           String... type)
238   {
239     Set<String> groups = new HashSet<String>();
240
241     Iterable<String> types = varargToTypes(type);
242
243     for (String featureType : types)
244     {
245       FeatureStore featureSet = featureStore.get(featureType);
246       if (featureSet != null)
247       {
248         groups.addAll(featureSet.getFeatureGroups(positionalFeatures));
249       }
250     }
251
252     return groups;
253   }
254
255   /**
256    * {@inheritDoc}
257    */
258   @Override
259   public Set<String> getFeatureTypesForGroups(boolean positionalFeatures,
260           String... groups)
261   {
262     Set<String> result = new HashSet<String>();
263
264     for (Entry<String, FeatureStore> featureType : featureStore.entrySet())
265     {
266       Set<String> featureGroups = featureType.getValue().getFeatureGroups(
267               positionalFeatures);
268       for (String group : groups)
269       {
270         if (featureGroups.contains(group))
271         {
272           /*
273            * yes this feature type includes one of the query groups
274            */
275           result.add(featureType.getKey());
276           break;
277         }
278       }
279     }
280
281     return result;
282   }
283
284   /**
285    * {@inheritDoc}
286    */
287   @Override
288   public Set<String> getFeatureTypes()
289   {
290     Set<String> types = new HashSet<String>();
291     for (Entry<String, FeatureStore> entry : featureStore.entrySet())
292     {
293       if (!entry.getValue().isEmpty())
294       {
295         types.add(entry.getKey());
296       }
297     }
298     return types;
299   }
300
301   /**
302    * {@inheritDoc}
303    */
304   @Override
305   public float getMinimumScore(String type, boolean positional)
306   {
307     return featureStore.containsKey(type) ? featureStore.get(type)
308             .getMinimumScore(positional) : Float.NaN;
309   }
310
311   /**
312    * {@inheritDoc}
313    */
314   @Override
315   public float getMaximumScore(String type, boolean positional)
316   {
317     return featureStore.containsKey(type) ? featureStore.get(type)
318             .getMaximumScore(positional) : Float.NaN;
319   }
320 }