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