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