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