e3d587ffd5010b6db9fea57491deea56fa7bc252
[jalview.git] / test / jalview / datamodel / features / SequenceFeaturesTest.java
1 package jalview.datamodel.features;
2
3 import static org.testng.Assert.assertEquals;
4 import static org.testng.Assert.assertFalse;
5 import static org.testng.Assert.assertSame;
6 import static org.testng.Assert.assertTrue;
7
8 import jalview.datamodel.SequenceFeature;
9
10 import java.util.ArrayList;
11 import java.util.Iterator;
12 import java.util.List;
13 import java.util.Set;
14
15 import org.testng.annotations.Test;
16
17 public class SequenceFeaturesTest
18 {
19   @Test(groups = "Functional")
20   public void testGetPositionalFeatures()
21   {
22     SequenceFeaturesI store = new SequenceFeatures();
23     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
24             Float.NaN, null);
25     store.add(sf1);
26     // same range, different description
27     SequenceFeature sf2 = new SequenceFeature("Metal", "desc2", 10, 20,
28             Float.NaN, null);
29     store.add(sf2);
30     // discontiguous range
31     SequenceFeature sf3 = new SequenceFeature("Metal", "desc", 30, 40,
32             Float.NaN, null);
33     store.add(sf3);
34     // overlapping range
35     SequenceFeature sf4 = new SequenceFeature("Metal", "desc", 15, 35,
36             Float.NaN, null);
37     store.add(sf4);
38     // enclosing range
39     SequenceFeature sf5 = new SequenceFeature("Metal", "desc", 5, 50,
40             Float.NaN, null);
41     store.add(sf5);
42     // non-positional feature
43     SequenceFeature sf6 = new SequenceFeature("Metal", "desc", 0, 0,
44             Float.NaN, null);
45     store.add(sf6);
46     // contact feature
47     SequenceFeature sf7 = new SequenceFeature("Disulphide bond", "desc",
48             18, 45, Float.NaN, null);
49     store.add(sf7);
50     // different feature type
51     SequenceFeature sf8 = new SequenceFeature("Pfam", "desc", 30, 40,
52             Float.NaN, null);
53     store.add(sf8);
54     SequenceFeature sf9 = new SequenceFeature("Pfam", "desc", 15, 35,
55             Float.NaN, null);
56     store.add(sf9);
57
58     /*
59      * get all positional features
60      */
61     List<SequenceFeature> features = store.getPositionalFeatures();
62     assertEquals(features.size(), 8);
63     assertTrue(features.contains(sf1));
64     assertTrue(features.contains(sf2));
65     assertTrue(features.contains(sf3));
66     assertTrue(features.contains(sf4));
67     assertTrue(features.contains(sf5));
68     assertFalse(features.contains(sf6)); // non-positional
69     assertTrue(features.contains(sf7));
70     assertTrue(features.contains(sf8));
71     assertTrue(features.contains(sf9));
72
73     /*
74      * get features by type
75      */
76     assertTrue(store.getPositionalFeatures((String) null).isEmpty());
77     assertTrue(store.getPositionalFeatures("Cath").isEmpty());
78     assertTrue(store.getPositionalFeatures("METAL").isEmpty());
79
80     features = store.getPositionalFeatures("Metal");
81     assertEquals(features.size(), 5);
82     assertTrue(features.contains(sf1));
83     assertTrue(features.contains(sf2));
84     assertTrue(features.contains(sf3));
85     assertTrue(features.contains(sf4));
86     assertTrue(features.contains(sf5));
87     assertFalse(features.contains(sf6));
88
89     features = store.getPositionalFeatures("Disulphide bond");
90     assertEquals(features.size(), 1);
91     assertTrue(features.contains(sf7));
92
93     features = store.getPositionalFeatures("Pfam");
94     assertEquals(features.size(), 2);
95     assertTrue(features.contains(sf8));
96     assertTrue(features.contains(sf9));
97   }
98
99   @Test(groups = "Functional")
100   public void testGetContactFeatures()
101   {
102     SequenceFeaturesI store = new SequenceFeatures();
103     // non-contact
104     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
105             Float.NaN, null);
106     store.add(sf1);
107     // non-positional
108     SequenceFeature sf2 = new SequenceFeature("Metal", "desc", 0, 0,
109             Float.NaN, null);
110     store.add(sf2);
111     // contact feature
112     SequenceFeature sf3 = new SequenceFeature("Disulphide bond", "desc",
113             18, 45, Float.NaN, null);
114     store.add(sf3);
115     // repeat for different feature type
116     SequenceFeature sf4 = new SequenceFeature("Pfam", "desc", 10, 20,
117             Float.NaN, null);
118     store.add(sf4);
119     SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 0, 0,
120             Float.NaN, null);
121     store.add(sf5);
122     SequenceFeature sf6 = new SequenceFeature("Disulfide bond", "desc", 18,
123             45, Float.NaN, null);
124     store.add(sf6);
125   
126     /*
127      * get all contact features
128      */
129     List<SequenceFeature> features = store.getContactFeatures();
130     assertEquals(features.size(), 2);
131     assertTrue(features.contains(sf3));
132     assertTrue(features.contains(sf6));
133   
134     /*
135      * get contact features by type
136      */
137     assertTrue(store.getContactFeatures((String) null).isEmpty());
138     assertTrue(store.getContactFeatures("Cath").isEmpty());
139     assertTrue(store.getContactFeatures("Pfam").isEmpty());
140     assertTrue(store.getContactFeatures("DISULPHIDE BOND").isEmpty());
141   
142     features = store.getContactFeatures("Disulphide bond");
143     assertEquals(features.size(), 1);
144     assertTrue(features.contains(sf3));
145   
146     features = store.getContactFeatures("Disulfide bond");
147     assertEquals(features.size(), 1);
148     assertTrue(features.contains(sf6));
149   }
150
151   @Test(groups = "Functional")
152   public void testGetNonPositionalFeatures()
153   {
154     SequenceFeaturesI store = new SequenceFeatures();
155     // positional
156     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
157             Float.NaN, null);
158     store.add(sf1);
159     // non-positional
160     SequenceFeature sf2 = new SequenceFeature("Metal", "desc", 0, 0,
161             Float.NaN, null);
162     store.add(sf2);
163     // contact feature
164     SequenceFeature sf3 = new SequenceFeature("Disulphide bond", "desc",
165             18, 45, Float.NaN, null);
166     store.add(sf3);
167     // repeat for different feature type
168     SequenceFeature sf4 = new SequenceFeature("Pfam", "desc", 10, 20,
169             Float.NaN, null);
170     store.add(sf4);
171     SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 0, 0,
172             Float.NaN, null);
173     store.add(sf5);
174     SequenceFeature sf6 = new SequenceFeature("Disulfide bond", "desc", 18,
175             45, Float.NaN, null);
176     store.add(sf6);
177     // one more non-positional, different description
178     SequenceFeature sf7 = new SequenceFeature("Pfam", "desc2", 0, 0,
179             Float.NaN, null);
180     store.add(sf7);
181   
182     /*
183      * get all non-positional features
184      */
185     List<SequenceFeature> features = store.getNonPositionalFeatures();
186     assertEquals(features.size(), 3);
187     assertTrue(features.contains(sf2));
188     assertTrue(features.contains(sf5));
189     assertTrue(features.contains(sf7));
190   
191     /*
192      * get non-positional features by type
193      */
194     assertTrue(store.getNonPositionalFeatures((String) null).isEmpty());
195     assertTrue(store.getNonPositionalFeatures("Cath").isEmpty());
196     assertTrue(store.getNonPositionalFeatures("PFAM").isEmpty());
197   
198     features = store.getNonPositionalFeatures("Metal");
199     assertEquals(features.size(), 1);
200     assertTrue(features.contains(sf2));
201   
202     features = store.getNonPositionalFeatures("Pfam");
203     assertEquals(features.size(), 2);
204     assertTrue(features.contains(sf5));
205     assertTrue(features.contains(sf7));
206   }
207
208   /**
209    * Helper method to add a feature of no particular type
210    * 
211    * @param sf
212    * @param type
213    * @param from
214    * @param to
215    * @return
216    */
217   SequenceFeature addFeature(SequenceFeaturesI sf, String type, int from,
218           int to)
219   {
220     SequenceFeature sf1 = new SequenceFeature(type, "", from, to,
221             Float.NaN,
222             null);
223     sf.add(sf1);
224     return sf1;
225   }
226
227   @Test(groups = "Functional")
228   public void testFindFeatures()
229   {
230     SequenceFeaturesI sf = new SequenceFeatures();
231     SequenceFeature sf1 = addFeature(sf, "Pfam", 10, 50);
232     SequenceFeature sf2 = addFeature(sf, "Pfam", 1, 15);
233     SequenceFeature sf3 = addFeature(sf, "Pfam", 20, 30);
234     SequenceFeature sf4 = addFeature(sf, "Pfam", 40, 100);
235     SequenceFeature sf5 = addFeature(sf, "Pfam", 60, 100);
236     SequenceFeature sf6 = addFeature(sf, "Pfam", 70, 70);
237     SequenceFeature sf7 = addFeature(sf, "Cath", 10, 50);
238     SequenceFeature sf8 = addFeature(sf, "Cath", 1, 15);
239     SequenceFeature sf9 = addFeature(sf, "Cath", 20, 30);
240     SequenceFeature sf10 = addFeature(sf, "Cath", 40, 100);
241     SequenceFeature sf11 = addFeature(sf, "Cath", 60, 100);
242     SequenceFeature sf12 = addFeature(sf, "Cath", 70, 70);
243   
244     List<SequenceFeature> overlaps = sf.findFeatures(200, 200, "Pfam");
245     assertTrue(overlaps.isEmpty());
246   
247     overlaps = sf.findFeatures( 1, 9, "Pfam");
248     assertEquals(overlaps.size(), 1);
249     assertTrue(overlaps.contains(sf2));
250   
251     overlaps = sf.findFeatures( 5, 18, "Pfam");
252     assertEquals(overlaps.size(), 2);
253     assertTrue(overlaps.contains(sf1));
254     assertTrue(overlaps.contains(sf2));
255   
256     overlaps = sf.findFeatures(30, 40, "Pfam");
257     assertEquals(overlaps.size(), 3);
258     assertTrue(overlaps.contains(sf1));
259     assertTrue(overlaps.contains(sf3));
260     assertTrue(overlaps.contains(sf4));
261   
262     overlaps = sf.findFeatures( 80, 90, "Pfam");
263     assertEquals(overlaps.size(), 2);
264     assertTrue(overlaps.contains(sf4));
265     assertTrue(overlaps.contains(sf5));
266   
267     overlaps = sf.findFeatures( 68, 70, "Pfam");
268     assertEquals(overlaps.size(), 3);
269     assertTrue(overlaps.contains(sf4));
270     assertTrue(overlaps.contains(sf5));
271     assertTrue(overlaps.contains(sf6));
272
273     overlaps = sf.findFeatures(16, 69, "Cath");
274     assertEquals(overlaps.size(), 4);
275     assertTrue(overlaps.contains(sf7));
276     assertFalse(overlaps.contains(sf8));
277     assertTrue(overlaps.contains(sf9));
278     assertTrue(overlaps.contains(sf10));
279     assertTrue(overlaps.contains(sf11));
280     assertFalse(overlaps.contains(sf12));
281
282     assertTrue(sf.findFeatures(0, 1000, "Metal").isEmpty());
283
284     overlaps = sf.findFeatures(7, 7, (String) null);
285     assertTrue(overlaps.isEmpty());
286   }
287
288   @Test(groups = "Functional")
289   public void testDelete()
290   {
291     SequenceFeaturesI sf = new SequenceFeatures();
292     SequenceFeature sf1 = addFeature(sf, "Pfam", 10, 50);
293     assertTrue(sf.getPositionalFeatures().contains(sf1));
294
295     assertFalse(sf.delete(null));
296     SequenceFeature sf2 = new SequenceFeature("Cath", "", 10, 15, 0f, null);
297     assertFalse(sf.delete(sf2)); // not added, can't delete it
298     assertTrue(sf.delete(sf1));
299     assertTrue(sf.getPositionalFeatures().isEmpty());
300   }
301
302   @Test(groups = "Functional")
303   public void testHasFeatures()
304   {
305     SequenceFeaturesI sf = new SequenceFeatures();
306     assertFalse(sf.hasFeatures());
307
308     SequenceFeature sf1 = addFeature(sf, "Pfam", 10, 50);
309     assertTrue(sf.hasFeatures());
310
311     sf.delete(sf1);
312     assertFalse(sf.hasFeatures());
313   }
314
315   /**
316    * Tests for the method that gets feature groups for positional or
317    * non-positional features
318    */
319   @Test(groups = "Functional")
320   public void testGetFeatureGroups()
321   {
322     SequenceFeaturesI sf = new SequenceFeatures();
323     assertTrue(sf.getFeatureGroups(true).isEmpty());
324     assertTrue(sf.getFeatureGroups(false).isEmpty());
325
326     /*
327      * add a non-positional feature (begin/end = 0/0)
328      */
329     SequenceFeature sfx = new SequenceFeature("AType", "Desc", 0, 0, 0f,
330             "AGroup");
331     sf.add(sfx);
332     Set<String> groups = sf.getFeatureGroups(true); // for positional
333     assertTrue(groups.isEmpty());
334     groups = sf.getFeatureGroups(false); // for non-positional
335     assertEquals(groups.size(), 1);
336     assertTrue(groups.contains("AGroup"));
337
338     /*
339      * add, then delete, more non-positional features of different types
340      */
341     SequenceFeature sfy = new SequenceFeature("AnotherType", "Desc", 0, 0,
342             0f,
343             "AnotherGroup");
344     sf.add(sfy);
345     SequenceFeature sfz = new SequenceFeature("AThirdType", "Desc", 0, 0,
346             0f,
347             null);
348     sf.add(sfz);
349     groups = sf.getFeatureGroups(false);
350     assertEquals(groups.size(), 3);
351     assertTrue(groups.contains("AGroup"));
352     assertTrue(groups.contains("AnotherGroup"));
353     assertTrue(groups.contains(null)); // null is a possible group
354     sf.delete(sfz);
355     sf.delete(sfy);
356     groups = sf.getFeatureGroups(false);
357     assertEquals(groups.size(), 1);
358     assertTrue(groups.contains("AGroup"));
359
360     /*
361      * add positional features
362      */
363     SequenceFeature sf1 = new SequenceFeature("Pfam", "Desc", 10, 50, 0f,
364             "PfamGroup");
365     sf.add(sf1);
366     groups = sf.getFeatureGroups(true);
367     assertEquals(groups.size(), 1);
368     assertTrue(groups.contains("PfamGroup"));
369     groups = sf.getFeatureGroups(false); // non-positional unchanged
370     assertEquals(groups.size(), 1);
371     assertTrue(groups.contains("AGroup"));
372
373     SequenceFeature sf2 = new SequenceFeature("Cath", "Desc", 10, 50, 0f,
374             null);
375     sf.add(sf2);
376     groups = sf.getFeatureGroups(true);
377     assertEquals(groups.size(), 2);
378     assertTrue(groups.contains("PfamGroup"));
379     assertTrue(groups.contains(null));
380
381     sf.delete(sf1);
382     sf.delete(sf2);
383     assertTrue(sf.getFeatureGroups(true).isEmpty());
384
385     SequenceFeature sf3 = new SequenceFeature("CDS", "", 10, 50, 0f,
386             "Ensembl");
387     sf.add(sf3);
388     SequenceFeature sf4 = new SequenceFeature("exon", "", 10, 50, 0f,
389             "Ensembl");
390     sf.add(sf4);
391     groups = sf.getFeatureGroups(true);
392     assertEquals(groups.size(), 1);
393     assertTrue(groups.contains("Ensembl"));
394
395     /*
396      * delete last Ensembl group feature from CDS features
397      * but still have one in exon features
398      */
399     sf.delete(sf3);
400     groups = sf.getFeatureGroups(true);
401     assertEquals(groups.size(), 1);
402     assertTrue(groups.contains("Ensembl"));
403
404     /*
405      * delete the last non-positional feature
406      */
407     sf.delete(sfx);
408     groups = sf.getFeatureGroups(false);
409     assertTrue(groups.isEmpty());
410   }
411
412   @Test(groups = "Functional")
413   public void testGetFeatureTypesForGroups()
414   {
415     SequenceFeaturesI sf = new SequenceFeatures();
416     assertTrue(sf.getFeatureTypesForGroups(true, (String) null).isEmpty());
417   
418     /*
419      * add feature with group = "Uniprot", type = "helix"
420      */
421     String groupUniprot = "Uniprot";
422     SequenceFeature sf1 = new SequenceFeature("helix", "Desc", 10, 50, 0f,
423             groupUniprot);
424     sf.add(sf1);
425     Set<String> groups = sf.getFeatureTypesForGroups(true, groupUniprot);
426     assertEquals(groups.size(), 1);
427     assertTrue(groups.contains("helix"));
428     assertTrue(sf.getFeatureTypesForGroups(true, (String) null).isEmpty());
429   
430     /*
431      * add feature with group = "Uniprot", type = "strand"
432      */
433     SequenceFeature sf2 = new SequenceFeature("strand", "Desc", 10, 50, 0f,
434             groupUniprot);
435     sf.add(sf2);
436     groups = sf.getFeatureTypesForGroups(true, groupUniprot);
437     assertEquals(groups.size(), 2);
438     assertTrue(groups.contains("helix"));
439     assertTrue(groups.contains("strand"));
440
441     /*
442      * delete the "strand" Uniprot feature - still have "helix"
443      */
444     sf.delete(sf2);
445     groups = sf.getFeatureTypesForGroups(true, groupUniprot);
446     assertEquals(groups.size(), 1);
447     assertTrue(groups.contains("helix"));
448
449     /*
450      * delete the "helix" Uniprot feature - none left
451      */
452     sf.delete(sf1);
453     assertTrue(sf.getFeatureTypesForGroups(true, groupUniprot).isEmpty());
454
455     /*
456      * add some null group features
457      */
458     SequenceFeature sf3 = new SequenceFeature("strand", "Desc", 10, 50, 0f,
459             null);
460     sf.add(sf3);
461     SequenceFeature sf4 = new SequenceFeature("turn", "Desc", 10, 50, 0f,
462             null);
463     sf.add(sf4);
464     groups = sf.getFeatureTypesForGroups(true, (String) null);
465     assertEquals(groups.size(), 2);
466     assertTrue(groups.contains("strand"));
467     assertTrue(groups.contains("turn"));
468
469     /*
470      * add strand/Cath  and turn/Scop and query for one or both groups
471      * (find feature types for groups selected in Feature Settings)
472      */
473     SequenceFeature sf5 = new SequenceFeature("strand", "Desc", 10, 50, 0f,
474             "Cath");
475     sf.add(sf5);
476     SequenceFeature sf6 = new SequenceFeature("turn", "Desc", 10, 50, 0f,
477             "Scop");
478     sf.add(sf6);
479     groups = sf.getFeatureTypesForGroups(true, "Cath");
480     assertEquals(groups.size(), 1);
481     assertTrue(groups.contains("strand"));
482     groups = sf.getFeatureTypesForGroups(true, "Scop");
483     assertEquals(groups.size(), 1);
484     assertTrue(groups.contains("turn"));
485     groups = sf.getFeatureTypesForGroups(true, "Cath", "Scop");
486     assertEquals(groups.size(), 2);
487     assertTrue(groups.contains("turn"));
488     assertTrue(groups.contains("strand"));
489     // alternative vararg syntax
490     groups = sf.getFeatureTypesForGroups(true, new String[] { "Cath",
491         "Scop" });
492     assertEquals(groups.size(), 2);
493     assertTrue(groups.contains("turn"));
494     assertTrue(groups.contains("strand"));
495   }
496
497   @Test(groups = "Functional")
498   public void testGetFeatureTypes()
499   {
500     SequenceFeaturesI store = new SequenceFeatures();
501     Set<String> types = store.getFeatureTypes();
502     assertTrue(types.isEmpty());
503
504     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
505             Float.NaN, null);
506     store.add(sf1);
507     types = store.getFeatureTypes();
508     assertEquals(types.size(), 1);
509     assertTrue(types.contains("Metal"));
510
511     // null type is rejected...
512     SequenceFeature sf2 = new SequenceFeature(null, "desc", 10, 20,
513             Float.NaN, null);
514     assertFalse(store.add(sf2));
515     types = store.getFeatureTypes();
516     assertEquals(types.size(), 1);
517     assertFalse(types.contains(null));
518     assertTrue(types.contains("Metal"));
519
520     /*
521      * add non-positional feature
522      */
523     SequenceFeature sf3 = new SequenceFeature("Pfam", "desc", 0, 0,
524             Float.NaN, null);
525     store.add(sf3);
526     types = store.getFeatureTypes();
527     assertEquals(types.size(), 2);
528     assertTrue(types.contains("Pfam"));
529
530     /*
531      * add contact feature
532      */
533     SequenceFeature sf4 = new SequenceFeature("Disulphide Bond", "desc",
534             10, 20, Float.NaN, null);
535     store.add(sf4);
536     types = store.getFeatureTypes();
537     assertEquals(types.size(), 3);
538     assertTrue(types.contains("Disulphide Bond"));
539
540     /*
541      * add another Pfam
542      */
543     SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 10, 20,
544             Float.NaN, null);
545     store.add(sf5);
546     types = store.getFeatureTypes();
547     assertEquals(types.size(), 3); // unchanged
548
549     /*
550      * delete first Pfam - still have one
551      */
552     assertTrue(store.delete(sf3));
553     types = store.getFeatureTypes();
554     assertEquals(types.size(), 3);
555     assertTrue(types.contains("Pfam"));
556
557     /*
558      * delete second Pfam - no longer have one
559      */
560     assertTrue(store.delete(sf5));
561     types = store.getFeatureTypes();
562     assertEquals(types.size(), 2);
563     assertFalse(types.contains("Pfam"));
564   }
565
566   @Test(groups = "Functional")
567   public void testGetFeatureCount()
568   {
569     SequenceFeaturesI store = new SequenceFeatures();
570     assertEquals(store.getFeatureCount(true), 0);
571     assertEquals(store.getFeatureCount(false), 0);
572   
573     /*
574      * add positional
575      */
576     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
577             Float.NaN, null);
578     store.add(sf1);
579     assertEquals(store.getFeatureCount(true), 1);
580     assertEquals(store.getFeatureCount(false), 0);
581
582     /*
583      * null feature type is rejected
584      */
585     SequenceFeature sf2 = new SequenceFeature(null, "desc", 10, 20,
586             Float.NaN, null);
587     assertFalse(store.add(sf2));
588     assertEquals(store.getFeatureCount(true), 1);
589     assertEquals(store.getFeatureCount(false), 0);
590   
591     /*
592      * add non-positional feature
593      */
594     SequenceFeature sf3 = new SequenceFeature("Pfam", "desc", 0, 0,
595             Float.NaN, null);
596     store.add(sf3);
597     assertEquals(store.getFeatureCount(true), 1);
598     assertEquals(store.getFeatureCount(false), 1);
599   
600     /*
601      * add contact feature (counts as 1)
602      */
603     SequenceFeature sf4 = new SequenceFeature("Disulphide Bond", "desc",
604             10, 20, Float.NaN, null);
605     store.add(sf4);
606     assertEquals(store.getFeatureCount(true), 2);
607     assertEquals(store.getFeatureCount(false), 1);
608   
609     /*
610      * add another Pfam but this time as a positional feature
611      */
612     SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 10, 20,
613             Float.NaN, null);
614     store.add(sf5);
615     assertEquals(store.getFeatureCount(true), 3); // sf1, sf4, sf5
616     assertEquals(store.getFeatureCount(false), 1); // sf3
617     assertEquals(store.getFeatureCount(true, "Pfam"), 1); // positional
618     assertEquals(store.getFeatureCount(false, "Pfam"), 1); // non-positional
619     // search for type==null
620     assertEquals(store.getFeatureCount(true, (String) null), 0);
621     // search with no type specified
622     assertEquals(store.getFeatureCount(true, (String[]) null), 3);
623     assertEquals(store.getFeatureCount(true, "Metal", "Cath"), 1);
624     assertEquals(store.getFeatureCount(true, "Disulphide Bond"), 1);
625     assertEquals(store.getFeatureCount(true, "Metal", "Pfam", null), 2);
626
627     /*
628      * delete first Pfam (non-positional)
629      */
630     assertTrue(store.delete(sf3));
631     assertEquals(store.getFeatureCount(true), 3);
632     assertEquals(store.getFeatureCount(false), 0);
633   
634     /*
635      * delete second Pfam (positional)
636      */
637     assertTrue(store.delete(sf5));
638     assertEquals(store.getFeatureCount(true), 2);
639     assertEquals(store.getFeatureCount(false), 0);
640   }
641
642   @Test(groups = "Functional")
643   public void testGetAllFeatures()
644   {
645     SequenceFeaturesI store = new SequenceFeatures();
646     List<SequenceFeature> features = store.getAllFeatures();
647     assertTrue(features.isEmpty());
648   
649     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
650             Float.NaN, null);
651     store.add(sf1);
652     features = store.getAllFeatures();
653     assertEquals(features.size(), 1);
654     assertTrue(features.contains(sf1));
655   
656     SequenceFeature sf2 = new SequenceFeature("Metallic", "desc", 10, 20,
657             Float.NaN, null);
658     store.add(sf2);
659     features = store.getAllFeatures();
660     assertEquals(features.size(), 2);
661     assertTrue(features.contains(sf2));
662   
663     /*
664      * add non-positional feature
665      */
666     SequenceFeature sf3 = new SequenceFeature("Pfam", "desc", 0, 0,
667             Float.NaN, null);
668     store.add(sf3);
669     features = store.getAllFeatures();
670     assertEquals(features.size(), 3);
671     assertTrue(features.contains(sf3));
672   
673     /*
674      * add contact feature
675      */
676     SequenceFeature sf4 = new SequenceFeature("Disulphide Bond", "desc",
677             10, 20, Float.NaN, null);
678     store.add(sf4);
679     features = store.getAllFeatures();
680     assertEquals(features.size(), 4);
681     assertTrue(features.contains(sf4));
682   
683     /*
684      * add another Pfam
685      */
686     SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 10, 20,
687             Float.NaN, null);
688     store.add(sf5);
689     features = store.getAllFeatures();
690     assertEquals(features.size(), 5);
691     assertTrue(features.contains(sf5));
692
693     /*
694      * select by type does not apply to non-positional features
695      */
696     features = store.getAllFeatures("Cath");
697     assertEquals(features.size(), 1);
698     assertTrue(features.contains(sf3));
699
700     features = store.getAllFeatures("Pfam", "Cath", "Metal");
701     assertEquals(features.size(), 3);
702     assertTrue(features.contains(sf1));
703     assertTrue(features.contains(sf3));
704     assertTrue(features.contains(sf5));
705   
706     /*
707      * delete first Pfam
708      */
709     assertTrue(store.delete(sf3));
710     features = store.getAllFeatures();
711     assertEquals(features.size(), 4);
712     assertFalse(features.contains(sf3));
713   
714     /*
715      * delete second Pfam
716      */
717     assertTrue(store.delete(sf5));
718     features = store.getAllFeatures();
719     assertEquals(features.size(), 3);
720     assertFalse(features.contains(sf3));
721   }
722
723   @Test(groups = "Functional")
724   public void testGetTotalFeatureLength()
725   {
726     SequenceFeaturesI store = new SequenceFeatures();
727     assertEquals(store.getTotalFeatureLength(), 0);
728
729     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
730             Float.NaN, null);
731     assertTrue(store.add(sf1));
732     assertEquals(store.getTotalFeatureLength(), 11);
733
734     // re-add does nothing!
735     assertFalse(store.add(sf1));
736     assertEquals(store.getTotalFeatureLength(), 11);
737
738     /*
739      * add non-positional feature
740      */
741     SequenceFeature sf3 = new SequenceFeature("Pfam", "desc", 0, 0,
742             Float.NaN, null);
743     store.add(sf3);
744     assertEquals(store.getTotalFeatureLength(), 11);
745
746     /*
747      * add contact feature - counts 1 to feature length
748      */
749     SequenceFeature sf4 = new SequenceFeature("Disulphide Bond", "desc",
750             10, 20, Float.NaN, null);
751     store.add(sf4);
752     assertEquals(store.getTotalFeatureLength(), 12);
753
754     /*
755      * add another Pfam
756      */
757     SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 10, 20,
758             Float.NaN, null);
759     store.add(sf5);
760     assertEquals(store.getTotalFeatureLength(), 23);
761
762     /*
763      * delete features
764      */
765     assertTrue(store.delete(sf3)); // non-positional
766     assertEquals(store.getTotalFeatureLength(), 23); // no change
767
768     assertTrue(store.delete(sf5));
769     assertEquals(store.getTotalFeatureLength(), 12);
770
771     assertTrue(store.delete(sf4)); // contact
772     assertEquals(store.getTotalFeatureLength(), 11);
773
774     assertTrue(store.delete(sf1));
775     assertEquals(store.getTotalFeatureLength(), 0);
776   }
777
778   @Test(groups = "Functional")
779   public void testGetMinimumScore_getMaximumScore()
780   {
781     SequenceFeatures sf = new SequenceFeatures();
782     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 0, 0,
783             Float.NaN, "group"); // non-positional, no score
784     sf.add(sf1);
785     SequenceFeature sf2 = new SequenceFeature("Cath", "desc", 10, 20,
786             Float.NaN, "group"); // positional, no score
787     sf.add(sf2);
788     SequenceFeature sf3 = new SequenceFeature("Metal", "desc", 10, 20, 1f,
789             "group");
790     sf.add(sf3);
791     SequenceFeature sf4 = new SequenceFeature("Metal", "desc", 12, 16, 4f,
792             "group");
793     sf.add(sf4);
794     SequenceFeature sf5 = new SequenceFeature("Cath", "desc", 0, 0, 11f,
795             "group");
796     sf.add(sf5);
797     SequenceFeature sf6 = new SequenceFeature("Cath", "desc", 0, 0, -7f,
798             "group");
799     sf.add(sf6);
800
801     assertEquals(sf.getMinimumScore("nosuchtype", true), Float.NaN);
802     assertEquals(sf.getMinimumScore("nosuchtype", false), Float.NaN);
803     assertEquals(sf.getMaximumScore("nosuchtype", true), Float.NaN);
804     assertEquals(sf.getMaximumScore("nosuchtype", false), Float.NaN);
805
806     // positional features min-max:
807     assertEquals(sf.getMinimumScore("Metal", true), 1f);
808     assertEquals(sf.getMaximumScore("Metal", true), 4f);
809     assertEquals(sf.getMinimumScore("Cath", true), Float.NaN);
810     assertEquals(sf.getMaximumScore("Cath", true), Float.NaN);
811
812     // non-positional features min-max:
813     assertEquals(sf.getMinimumScore("Cath", false), -7f);
814     assertEquals(sf.getMaximumScore("Cath", false), 11f);
815     assertEquals(sf.getMinimumScore("Metal", false), Float.NaN);
816     assertEquals(sf.getMaximumScore("Metal", false), Float.NaN);
817
818     // delete features; min-max should get recomputed
819     sf.delete(sf6);
820     assertEquals(sf.getMinimumScore("Cath", false), 11f);
821     assertEquals(sf.getMaximumScore("Cath", false), 11f);
822     sf.delete(sf4);
823     assertEquals(sf.getMinimumScore("Metal", true), 1f);
824     assertEquals(sf.getMaximumScore("Metal", true), 1f);
825     sf.delete(sf5);
826     assertEquals(sf.getMinimumScore("Cath", false), Float.NaN);
827     assertEquals(sf.getMaximumScore("Cath", false), Float.NaN);
828     sf.delete(sf3);
829     assertEquals(sf.getMinimumScore("Metal", true), Float.NaN);
830     assertEquals(sf.getMaximumScore("Metal", true), Float.NaN);
831     sf.delete(sf1);
832     sf.delete(sf2);
833     assertFalse(sf.hasFeatures());
834     assertEquals(sf.getMinimumScore("Cath", false), Float.NaN);
835     assertEquals(sf.getMaximumScore("Cath", false), Float.NaN);
836     assertEquals(sf.getMinimumScore("Metal", true), Float.NaN);
837     assertEquals(sf.getMaximumScore("Metal", true), Float.NaN);
838   }
839
840   @Test(groups = "Functional")
841   public void testVarargsToTypes()
842   {
843     SequenceFeatures sf = new SequenceFeatures();
844     sf.add(new SequenceFeature("Metal", "desc", 0, 0, Float.NaN, "group"));
845     sf.add(new SequenceFeature("Cath", "desc", 10, 20, Float.NaN, "group"));
846
847     /*
848      * no type specified - get all types stored
849      * they are returned in keyset (alphabetical) order
850      */
851     Iterable<String> types = sf.varargToTypes();
852     Iterator<String> iterator = types.iterator();
853     assertTrue(iterator.hasNext());
854     assertEquals(iterator.next(), "Cath");
855     assertTrue(iterator.hasNext());
856     assertEquals(iterator.next(), "Metal");
857     assertFalse(iterator.hasNext());
858
859     /*
860      * empty array is the same as no vararg parameter supplied
861      * so treated as all stored types
862      */
863     types = sf.varargToTypes(new String[] {});
864     iterator = types.iterator();
865     assertTrue(iterator.hasNext());
866     assertEquals(iterator.next(), "Cath");
867     assertTrue(iterator.hasNext());
868     assertEquals(iterator.next(), "Metal");
869     assertFalse(iterator.hasNext());
870
871     /*
872      * null type specified; this is passed as vararg
873      * String[1] {null}
874      */
875     types = sf.varargToTypes((String) null);
876     assertFalse(types.iterator().hasNext());
877
878     /*
879      * null types array specified; this is passed as vararg null
880      */
881     types = sf.varargToTypes((String[]) null);
882     iterator = types.iterator();
883     assertTrue(iterator.hasNext());
884     assertEquals(iterator.next(), "Cath");
885     assertTrue(iterator.hasNext());
886     assertEquals(iterator.next(), "Metal");
887     assertFalse(iterator.hasNext());
888
889     /*
890      * one type specified
891      */
892     types = sf.varargToTypes("Metal");
893     iterator = types.iterator();
894     assertTrue(iterator.hasNext());
895     assertEquals(iterator.next(), "Metal");
896     assertFalse(iterator.hasNext());
897
898     /*
899      * two types specified - get sorted alphabetically
900      */
901     types = sf.varargToTypes("Metal", "Helix");
902     iterator = types.iterator();
903     assertTrue(iterator.hasNext());
904     assertEquals(iterator.next(), "Helix");
905     assertTrue(iterator.hasNext());
906     assertEquals(iterator.next(), "Metal");
907     assertFalse(iterator.hasNext());
908
909     /*
910      * null type included - should get removed
911      */
912     types = sf.varargToTypes("Metal", null, "Helix");
913     iterator = types.iterator();
914     assertTrue(iterator.hasNext());
915     assertEquals(iterator.next(), "Helix");
916     assertTrue(iterator.hasNext());
917     assertEquals(iterator.next(), "Metal");
918     assertFalse(iterator.hasNext());
919   }
920
921   @Test(groups = "Functional")
922   public void testGetFeatureTypes_byOntology()
923   {
924     SequenceFeaturesI store = new SequenceFeatures();
925   
926     SequenceFeature sf1 = new SequenceFeature("transcript", "desc", 10, 20,
927             Float.NaN, null);
928     store.add(sf1);
929     // mRNA isA mature_transcript isA transcript
930     SequenceFeature sf2 = new SequenceFeature("mRNA", "desc", 10, 20,
931             Float.NaN, null);
932     store.add(sf2);
933     // just to prove non-positional feature types are included
934     SequenceFeature sf3 = new SequenceFeature("mRNA", "desc", 0, 0,
935             Float.NaN, null);
936     store.add(sf3);
937     SequenceFeature sf4 = new SequenceFeature("CDS", "desc", 0, 0,
938             Float.NaN, null);
939     store.add(sf4);
940
941     Set<String> types = store.getFeatureTypes("transcript");
942     assertEquals(types.size(), 2);
943     assertTrue(types.contains("transcript"));
944     assertTrue(types.contains("mRNA"));
945
946     // matches include arguments whether SO terms or not
947     types = store.getFeatureTypes("transcript", "CDS");
948     assertEquals(types.size(), 3);
949     assertTrue(types.contains("transcript"));
950     assertTrue(types.contains("mRNA"));
951     assertTrue(types.contains("CDS"));
952
953     types = store.getFeatureTypes("exon");
954     assertTrue(types.isEmpty());
955   }
956
957   @Test(groups = "Functional")
958   public void testGetFeaturesByOntology()
959   {
960     SequenceFeaturesI store = new SequenceFeatures();
961     List<SequenceFeature> features = store.getFeaturesByOntology();
962     assertTrue(features.isEmpty());
963     assertTrue(store.getFeaturesByOntology(new String[] {}).isEmpty());
964     assertTrue(store.getFeaturesByOntology((String[]) null).isEmpty());
965   
966     SequenceFeature sf1 = new SequenceFeature("transcript", "desc", 10, 20,
967             Float.NaN, null);
968     store.add(sf1);
969
970     // mRNA isA transcript; added here 'as if' non-positional
971     // just to show that non-positional features are included in results
972     SequenceFeature sf2 = new SequenceFeature("mRNA", "desc", 0, 0,
973             Float.NaN, null);
974     store.add(sf2);
975
976     SequenceFeature sf3 = new SequenceFeature("Pfam", "desc", 30, 40,
977             Float.NaN, null);
978     store.add(sf3);
979
980     features = store.getFeaturesByOntology("transcript");
981     assertEquals(features.size(), 2);
982     assertTrue(features.contains(sf1));
983     assertTrue(features.contains(sf2));
984
985     features = store.getFeaturesByOntology("mRNA");
986     assertEquals(features.size(), 1);
987     assertTrue(features.contains(sf2));
988
989     features = store.getFeaturesByOntology("mRNA", "Pfam");
990     assertEquals(features.size(), 2);
991     assertTrue(features.contains(sf2));
992     assertTrue(features.contains(sf3));
993   }
994
995   @Test(groups = "Functional")
996   public void testSortFeatures()
997   {
998     List<SequenceFeature> sfs = new ArrayList<SequenceFeature>();
999     SequenceFeature sf1 = new SequenceFeature("Pfam", "desc", 30, 80,
1000             Float.NaN, null);
1001     sfs.add(sf1);
1002     SequenceFeature sf2 = new SequenceFeature("Rfam", "desc", 40, 50,
1003             Float.NaN, null);
1004     sfs.add(sf2);
1005     SequenceFeature sf3 = new SequenceFeature("Rfam", "desc", 50, 60,
1006             Float.NaN, null);
1007     sfs.add(sf3);
1008
1009     // sort by end position descending
1010     SequenceFeatures.sortFeatures(sfs, false);
1011     assertSame(sfs.get(0), sf1);
1012     assertSame(sfs.get(1), sf3);
1013     assertSame(sfs.get(2), sf2);
1014
1015     // sort by start position ascending
1016     SequenceFeatures.sortFeatures(sfs, true);
1017     assertSame(sfs.get(0), sf1);
1018     assertSame(sfs.get(1), sf2);
1019     assertSame(sfs.get(2), sf3);
1020   }
1021
1022   @Test(groups = "Functional")
1023   public void testGetFeaturesForGroup()
1024   {
1025     SequenceFeaturesI store = new SequenceFeatures();
1026
1027     List<SequenceFeature> features = store.getFeaturesForGroup(true, null);
1028     assertTrue(features.isEmpty());
1029     assertTrue(store.getFeaturesForGroup(false, null).isEmpty());
1030     assertTrue(store.getFeaturesForGroup(true, "Uniprot").isEmpty());
1031     assertTrue(store.getFeaturesForGroup(false, "Uniprot").isEmpty());
1032
1033     SequenceFeature sf1 = new SequenceFeature("Pfam", "desc", 4, 10, 0f,
1034             null);
1035     SequenceFeature sf2 = new SequenceFeature("Pfam", "desc", 0, 0, 0f,
1036             null);
1037     SequenceFeature sf3 = new SequenceFeature("Pfam", "desc", 4, 10, 0f,
1038             "Uniprot");
1039     SequenceFeature sf4 = new SequenceFeature("Metal", "desc", 0, 0, 0f,
1040             "Rfam");
1041     SequenceFeature sf5 = new SequenceFeature("Cath", "desc", 5, 15, 0f,
1042             null);
1043     store.add(sf1);
1044     store.add(sf2);
1045     store.add(sf3);
1046     store.add(sf4);
1047     store.add(sf5);
1048
1049     // positional features for null group, any type
1050     features = store.getFeaturesForGroup(true, null);
1051     assertEquals(features.size(), 2);
1052     assertTrue(features.contains(sf1));
1053     assertTrue(features.contains(sf5));
1054
1055     // positional features for null group, specified type
1056     features = store.getFeaturesForGroup(true, null, new String[] { "Pfam",
1057         "Xfam" });
1058     assertEquals(features.size(), 1);
1059     assertTrue(features.contains(sf1));
1060     features = store.getFeaturesForGroup(true, null, new String[] { "Pfam",
1061         "Xfam", "Cath" });
1062     assertEquals(features.size(), 2);
1063     assertTrue(features.contains(sf1));
1064     assertTrue(features.contains(sf5));
1065
1066     // positional features for non-null group, any type
1067     features = store.getFeaturesForGroup(true, "Uniprot");
1068     assertEquals(features.size(), 1);
1069     assertTrue(features.contains(sf3));
1070     assertTrue(store.getFeaturesForGroup(true, "Rfam").isEmpty());
1071
1072     // positional features for non-null group, specified type
1073     features = store.getFeaturesForGroup(true, "Uniprot", "Pfam", "Xfam",
1074             "Rfam");
1075     assertEquals(features.size(), 1);
1076     assertTrue(features.contains(sf3));
1077     assertTrue(store.getFeaturesForGroup(true, "Uniprot", "Cath").isEmpty());
1078
1079     // non-positional features for null group, any type
1080     features = store.getFeaturesForGroup(false, null);
1081     assertEquals(features.size(), 1);
1082     assertTrue(features.contains(sf2));
1083
1084     // non-positional features for null group, specified type
1085     features = store.getFeaturesForGroup(false, null, "Pfam", "Xfam");
1086     assertEquals(features.size(), 1);
1087     assertTrue(features.contains(sf2));
1088     assertTrue(store.getFeaturesForGroup(false, null, "Cath").isEmpty());
1089
1090     // non-positional features for non-null group, any type
1091     features = store.getFeaturesForGroup(false, "Rfam");
1092     assertEquals(features.size(), 1);
1093     assertTrue(features.contains(sf4));
1094     assertTrue(store.getFeaturesForGroup(false, "Uniprot").isEmpty());
1095
1096     // non-positional features for non-null group, specified type
1097     features = store.getFeaturesForGroup(false, "Rfam", "Pfam", "Metal");
1098     assertEquals(features.size(), 1);
1099     assertTrue(features.contains(sf4));
1100     assertTrue(store.getFeaturesForGroup(false, "Rfam", "Cath", "Pfam")
1101             .isEmpty());
1102   }
1103 }