JAL-2446 getGroups, getFeatures for positional/non-positional 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.Collections;
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
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    * Adds one sequence feature to the store, and returns true, unless the
41    * feature is already contained in the store, in which case this method
42    * returns false. Containment is determined by SequenceFeature.equals()
43    * comparison.
44    * 
45    * @param sf
46    */
47   public boolean add(SequenceFeature sf)
48   {
49     String type = sf.getType();
50
51     if (featureStore.get(type) == null)
52     {
53       featureStore.put(type, new FeatureStore());
54     }
55     return featureStore.get(type).addFeature(sf);
56   }
57
58   /**
59    * Returns a (possibly empty) list of features of the given type which overlap
60    * the (inclusive) sequence position range
61    * 
62    * @param type
63    * @param from
64    * @param to
65    * @return
66    */
67   public List<SequenceFeature> findFeatures(String type, int from,
68           int to)
69   {
70     FeatureStore features = featureStore.get(type);
71     if (features == null)
72     {
73       return Collections.emptyList();
74     }
75     return features.findOverlappingFeatures(from, to);
76   }
77
78   /**
79    * Answers a list of all positional features stored, in no particular
80    * guaranteed order
81    * 
82    * @return
83    */
84   public List<SequenceFeature> getPositionalFeatures()
85   {
86     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
87     for (FeatureStore featureSet : featureStore.values())
88     {
89       result.addAll(featureSet.getPositionalFeatures());
90     }
91     return result;
92   }
93
94   /**
95    * Answers a list of all features stored, in no particular guaranteed order
96    * 
97    * @return
98    */
99   public List<SequenceFeature> getAllFeatures()
100   {
101     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
102
103     result.addAll(getPositionalFeatures());
104
105     result.addAll(getNonPositionalFeatures());
106
107     return result;
108   }
109
110   /**
111    * Answers a list of all features stored of the specified type, in no
112    * particular guaranteed order
113    * 
114    * @return
115    */
116   public List<SequenceFeature> getAllFeatures(String type)
117   {
118     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
119
120     result.addAll(getPositionalFeatures(type));
121
122     result.addAll(getNonPositionalFeatures(type));
123
124     return result;
125   }
126
127   /**
128    * Answers a list of all non-positional features stored, in no particular
129    * guaranteed order
130    * 
131    * @return
132    */
133   public List<SequenceFeature> getNonPositionalFeatures()
134   {
135     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
136     for (FeatureStore featureSet : featureStore.values())
137     {
138       result.addAll(featureSet.getNonPositionalFeatures());
139     }
140     return result;
141   }
142
143   /**
144    * Answers a list of all contact features stored, in no particular guaranteed
145    * order
146    * 
147    * @return
148    */
149   public List<SequenceFeature> getContactFeatures()
150   {
151     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
152     for (FeatureStore featureSet : featureStore.values())
153     {
154       result.addAll(featureSet.getContactFeatures());
155     }
156     return result;
157   }
158
159   /**
160    * Answers a list of all positional features of the given type, in no
161    * particular guaranteed order
162    * 
163    * @return
164    */
165   public List<SequenceFeature> getPositionalFeatures(String type)
166   {
167     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
168     FeatureStore featureSet = featureStore.get(type);
169     if (featureSet != null)
170     {
171       result.addAll(featureSet.getPositionalFeatures());
172     }
173     return result;
174   }
175
176   /**
177    * Answers a list of all contact features of the given type, in no particular
178    * guaranteed order
179    * 
180    * @return
181    */
182   public List<SequenceFeature> getContactFeatures(String type)
183   {
184     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
185     FeatureStore featureSet = featureStore.get(type);
186     if (featureSet != null)
187     {
188       result.addAll(featureSet.getContactFeatures());
189     }
190     return result;
191   }
192
193   /**
194    * Answers a list of all non-positional features of the given type, in no
195    * particular guaranteed order
196    * 
197    * @return
198    */
199   public List<SequenceFeature> getNonPositionalFeatures(String type)
200   {
201     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
202     FeatureStore featureSet = featureStore.get(type);
203     if (featureSet != null)
204     {
205       result.addAll(featureSet.getNonPositionalFeatures());
206     }
207     return result;
208   }
209
210   /**
211    * Deletes the given feature from the store, returning true if it was found
212    * (and deleted), else false. This method makes no assumption that the feature
213    * is in the 'expected' place in the store, in case it has been modified since
214    * it was added.
215    * 
216    * @param sf
217    */
218   public boolean delete(SequenceFeature sf)
219   {
220     for (FeatureStore featureSet : featureStore.values())
221     {
222       if (featureSet.delete(sf))
223       {
224         return true;
225       }
226     }
227     return false;
228   }
229
230   /**
231    * Answers true if this store contains at least one feature, else false
232    * 
233    * @return
234    */
235   public boolean hasFeatures()
236   {
237     for (FeatureStore featureSet : featureStore.values())
238     {
239       if (!featureSet.isEmpty())
240       {
241         return true;
242       }
243     }
244     return false;
245   }
246
247   /**
248    * Returns a set of the distinct feature groups present in the collection. The
249    * set may include null. The parameter determines whether the groups for
250    * positional or for non-positional features are returned.
251    * 
252    * @param positionalFeatures
253    * @return
254    */
255   public Set<String> getFeatureGroups(boolean positionalFeatures)
256   {
257     Set<String> groups = new HashSet<String>();
258     for (FeatureStore featureSet : featureStore.values())
259     {
260       groups.addAll(featureSet.getFeatureGroups(positionalFeatures));
261     }
262     return groups;
263   }
264
265   /**
266    * Answers the set of distinct feature types for which there is at least one
267    * feature with one of the given feature group(s). The parameter determines
268    * whether the groups for positional or for non-positional features are
269    * returned.
270    * 
271    * @param positionalFeatures
272    * @param groups
273    * @return
274    */
275   public Set<String> getFeatureTypesForGroups(boolean positionalFeatures,
276           String... groups)
277   {
278     Set<String> result = new HashSet<String>();
279     for (Entry<String, FeatureStore> featureType : featureStore.entrySet())
280     {
281       Set<String> featureGroups = featureType.getValue().getFeatureGroups(
282               positionalFeatures);
283       for (String group : groups)
284       {
285         if (featureGroups.contains(group))
286         {
287           /*
288            * yes this feature type includes one of the query groups
289            */
290           result.add(featureType.getKey());
291           break;
292         }
293       }
294     }
295
296     return result;
297   }
298 }