JAL-2480 SequenceFeaturesI encapsulates features api
[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.HashMap;
9 import java.util.HashSet;
10 import java.util.List;
11 import java.util.Map;
12 import java.util.Map.Entry;
13 import java.util.Set;
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     featureStore = new HashMap<String, FeatureStore>();
38   }
39
40   /* (non-Javadoc)
41    * @see jalview.datamodel.features.SequenceFeaturesI#add(jalview.datamodel.SequenceFeature)
42    */
43   @Override
44   public boolean add(SequenceFeature sf)
45   {
46     String type = sf.getType();
47
48     if (featureStore.get(type) == null)
49     {
50       featureStore.put(type, new FeatureStore());
51     }
52     return featureStore.get(type).addFeature(sf);
53   }
54
55   /* (non-Javadoc)
56    * @see jalview.datamodel.features.SequenceFeaturesI#findFeatures(int, int, java.lang.String)
57    */
58   @Override
59   public List<SequenceFeature> findFeatures(int from, int to,
60           String... type)
61   {
62     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
63
64     for (String featureType : varargToTypes(type))
65     {
66       FeatureStore features = featureStore.get(featureType);
67       if (features != null)
68       {
69         result.addAll(features.findOverlappingFeatures(from, to));
70       }
71     }
72
73     return result;
74   }
75
76   /* (non-Javadoc)
77    * @see jalview.datamodel.features.SequenceFeaturesI#getAllFeatures(java.lang.String)
78    */
79   @Override
80   public List<SequenceFeature> getAllFeatures(String... type)
81   {
82     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
83
84     result.addAll(getPositionalFeatures(type));
85
86     result.addAll(getNonPositionalFeatures(type));
87
88     return result;
89   }
90
91   /* (non-Javadoc)
92    * @see jalview.datamodel.features.SequenceFeaturesI#getFeatureCount(boolean, java.lang.String)
93    */
94   @Override
95   public int getFeatureCount(boolean positional, String... type)
96   {
97     int result = 0;
98     for (FeatureStore fs : featureStore.values())
99     {
100       result += fs.size(positional);
101     }
102
103     return result;
104   }
105
106   /* (non-Javadoc)
107    * @see jalview.datamodel.features.SequenceFeaturesI#getPositionalFeatures(java.lang.String)
108    */
109   @Override
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   /* (non-Javadoc)
140    * @see jalview.datamodel.features.SequenceFeaturesI#getContactFeatures(java.lang.String)
141    */
142   @Override
143   public List<SequenceFeature> getContactFeatures(String... type)
144   {
145     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
146
147     for (String featureType : varargToTypes(type))
148     {
149       FeatureStore featureSet = featureStore.get(featureType);
150       if (featureSet != null)
151       {
152         result.addAll(featureSet.getContactFeatures());
153       }
154     }
155     return result;
156   }
157
158   /* (non-Javadoc)
159    * @see jalview.datamodel.features.SequenceFeaturesI#getNonPositionalFeatures(java.lang.String)
160    */
161   @Override
162   public List<SequenceFeature> getNonPositionalFeatures(String... type)
163   {
164     List<SequenceFeature> result = new ArrayList<SequenceFeature>();
165
166     for (String featureType : varargToTypes(type))
167     {
168       FeatureStore featureSet = featureStore.get(featureType);
169       if (featureSet != null)
170       {
171         result.addAll(featureSet.getNonPositionalFeatures());
172       }
173     }
174     return result;
175   }
176
177   /* (non-Javadoc)
178    * @see jalview.datamodel.features.SequenceFeaturesI#delete(jalview.datamodel.SequenceFeature)
179    */
180   @Override
181   public boolean delete(SequenceFeature sf)
182   {
183     for (FeatureStore featureSet : featureStore.values())
184     {
185       if (featureSet.delete(sf))
186       {
187         return true;
188       }
189     }
190     return false;
191   }
192
193   /* (non-Javadoc)
194    * @see jalview.datamodel.features.SequenceFeaturesI#hasFeatures()
195    */
196   @Override
197   public boolean hasFeatures()
198   {
199     for (FeatureStore featureSet : featureStore.values())
200     {
201       if (!featureSet.isEmpty())
202       {
203         return true;
204       }
205     }
206     return false;
207   }
208
209   /* (non-Javadoc)
210    * @see jalview.datamodel.features.SequenceFeaturesI#getFeatureGroups(boolean, java.lang.String)
211    */
212   @Override
213   public Set<String> getFeatureGroups(boolean positionalFeatures,
214           String... type)
215   {
216     Set<String> groups = new HashSet<String>();
217
218     Iterable<String> types = varargToTypes(type);
219
220     for (String featureType : types)
221     {
222       FeatureStore featureSet = featureStore.get(featureType);
223       if (featureSet != null)
224       {
225         groups.addAll(featureSet.getFeatureGroups(positionalFeatures));
226       }
227     }
228
229     return groups;
230   }
231
232   /* (non-Javadoc)
233    * @see jalview.datamodel.features.SequenceFeaturesI#getFeatureTypesForGroups(boolean, java.lang.String)
234    */
235   @Override
236   public Set<String> getFeatureTypesForGroups(boolean positionalFeatures,
237           String... groups)
238   {
239     Set<String> result = new HashSet<String>();
240
241     for (Entry<String, FeatureStore> featureType : featureStore.entrySet())
242     {
243       Set<String> featureGroups = featureType.getValue().getFeatureGroups(
244               positionalFeatures);
245       for (String group : groups)
246       {
247         if (featureGroups.contains(group))
248         {
249           /*
250            * yes this feature type includes one of the query groups
251            */
252           result.add(featureType.getKey());
253           break;
254         }
255       }
256     }
257
258     return result;
259   }
260
261   /* (non-Javadoc)
262    * @see jalview.datamodel.features.SequenceFeaturesI#getFeatureTypes()
263    */
264   @Override
265   public Set<String> getFeatureTypes()
266   {
267     return Collections.unmodifiableSet(featureStore.keySet());
268   }
269 }