JAL-2446 getFeatureTypesForGroup() added
[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 features stored (including non-positional), in no
80    * particular guaranteed order
81    * 
82    * @return
83    */
84   public List<SequenceFeature> getFeatures()
85   {
86     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
87     for (FeatureStore featureSet : featureStore.values())
88     {
89       result.addAll(featureSet.getFeatures());
90     }
91     return result;
92   }
93
94   /**
95    * Answers a list of all non-positional features stored, in no particular
96    * guaranteed order
97    * 
98    * @return
99    */
100   public List<SequenceFeature> getNonPositionalFeatures()
101   {
102     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
103     for (FeatureStore featureSet : featureStore.values())
104     {
105       result.addAll(featureSet.getNonPositionalFeatures());
106     }
107     return result;
108   }
109
110   /**
111    * Answers a list of all contact features stored, in no particular guaranteed
112    * order
113    * 
114    * @return
115    */
116   public List<SequenceFeature> getContactFeatures()
117   {
118     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
119     for (FeatureStore featureSet : featureStore.values())
120     {
121       result.addAll(featureSet.getContactFeatures());
122     }
123     return result;
124   }
125
126   /**
127    * Answers a list of all features of the given type (including
128    * non-positional), in no particular guaranteed order
129    * 
130    * @return
131    */
132   public List<SequenceFeature> getFeatures(String type)
133   {
134     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
135     FeatureStore featureSet = featureStore.get(type);
136     if (featureSet != null)
137     {
138       result.addAll(featureSet.getFeatures());
139     }
140     return result;
141   }
142
143   /**
144    * Answers a list of all contact features of the given type, in no particular
145    * guaranteed order
146    * 
147    * @return
148    */
149   public List<SequenceFeature> getContactFeatures(String type)
150   {
151     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
152     FeatureStore featureSet = featureStore.get(type);
153     if (featureSet != null)
154     {
155       result.addAll(featureSet.getContactFeatures());
156     }
157     return result;
158   }
159
160   /**
161    * Answers a list of all non-positional features of the given type, in no
162    * particular guaranteed order
163    * 
164    * @return
165    */
166   public List<SequenceFeature> getNonPositionalFeatures(String type)
167   {
168     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
169     FeatureStore featureSet = featureStore.get(type);
170     if (featureSet != null)
171     {
172       result.addAll(featureSet.getNonPositionalFeatures());
173     }
174     return result;
175   }
176
177   /**
178    * Deletes the given feature from the store, returning true if it was found
179    * (and deleted), else false. This method makes no assumption that the feature
180    * is in the 'expected' place in the store, in case it has been modified since
181    * it was added.
182    * 
183    * @param sf
184    */
185   public boolean delete(SequenceFeature sf)
186   {
187     for (FeatureStore featureSet : featureStore.values())
188     {
189       if (featureSet.delete(sf))
190       {
191         return true;
192       }
193     }
194     return false;
195   }
196
197   /**
198    * Answers true if this store contains at least one feature, else false
199    * 
200    * @return
201    */
202   public boolean hasFeatures()
203   {
204     for (FeatureStore featureSet : featureStore.values())
205     {
206       if (!featureSet.isEmpty())
207       {
208         return true;
209       }
210     }
211     return false;
212   }
213
214   /**
215    * Returns a set of the distinct feature groups present in the collection. The
216    * set may include null.
217    * 
218    * @return
219    */
220   public Set<String> getFeatureGroups()
221   {
222     Set<String> groups = new HashSet<String>();
223     for (FeatureStore featureSet : featureStore.values())
224     {
225       groups.addAll(featureSet.getFeatureGroups());
226     }
227     return groups;
228   }
229
230   /**
231    * Answers the set of distinct feature types for which there is at least one
232    * feature with the given feature group
233    * 
234    * @param group
235    * @return
236    */
237   public Set<String> getFeatureTypesForGroup(String group)
238   {
239     Set<String> result = new HashSet<String>();
240     for (Entry<String, FeatureStore> featureType : featureStore.entrySet())
241     {
242       if (featureType.getValue().getFeatureGroups().contains(group))
243       {
244         /*
245          * yes this feature type includes the query group
246          */
247         result.add(featureType.getKey());
248       }
249     }
250
251     return result;
252   }
253 }