Project

General

Profile

Download (32 KB) Statistics
| Branch: | Tag: | Revision:
1
package eu.etaxonomy.cdm.strategy.generate;
2

    
3
import static org.junit.Assert.assertNotNull;
4

    
5
import java.math.BigDecimal;
6
import java.util.ArrayList;
7
import java.util.Arrays;
8
import java.util.HashSet;
9
import java.util.List;
10
import java.util.Set;
11
import java.util.UUID;
12

    
13
import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;
14
import org.junit.Assert;
15
import org.junit.Before;
16
import org.junit.Ignore;
17
import org.junit.Test;
18

    
19
import eu.etaxonomy.cdm.model.common.Language;
20
import eu.etaxonomy.cdm.model.description.CategoricalData;
21
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
22
import eu.etaxonomy.cdm.model.description.DescriptiveDataSet;
23
import eu.etaxonomy.cdm.model.description.Distribution;
24
import eu.etaxonomy.cdm.model.description.Feature;
25
import eu.etaxonomy.cdm.model.description.PolytomousKey;
26
import eu.etaxonomy.cdm.model.description.PolytomousKeyNode;
27
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
28
import eu.etaxonomy.cdm.model.description.QuantitativeData;
29
import eu.etaxonomy.cdm.model.description.State;
30
import eu.etaxonomy.cdm.model.description.TaxonDescription;
31
import eu.etaxonomy.cdm.model.description.TextData;
32
import eu.etaxonomy.cdm.model.location.Country;
33
import eu.etaxonomy.cdm.model.name.Rank;
34
import eu.etaxonomy.cdm.model.name.TaxonName;
35
import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
36
import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
37
import eu.etaxonomy.cdm.model.taxon.Classification;
38
import eu.etaxonomy.cdm.model.taxon.Taxon;
39
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
40
import eu.etaxonomy.cdm.model.term.TermNode;
41
import eu.etaxonomy.cdm.model.term.TermTree;
42
import eu.etaxonomy.cdm.model.term.TermType;
43
import eu.etaxonomy.cdm.test.TermTestBase;
44

    
45
/**
46
 * @author m.venin
47
 * @author a.mueller
48
 * @since 16.12.2010
49
 */
50
public class PolytomousKeyGeneratorTest extends TermTestBase {
51

    
52
    @SuppressWarnings("unused")
53
    private static final Logger logger = LogManager.getLogger(PolytomousKeyGeneratorTest.class);
54

    
55
    private static final boolean debug = true;
56

    
57
    private static final String GT_3 = " > 3.0";
58
//    private static final String GT_3_5 = " > 3.5";
59
    private static final String LESS_3 = " < 3.0";
60
//    private static final String LESS_3_5 = " < 3.5";
61

    
62
	private static final boolean QUANTITATIVE = true;
63
	private static final boolean CATEGORICAL = false;
64

    
65
	private Feature featureShape;
66
	private Feature featurePresence;
67
	private Feature featureLength;
68
	private Feature featureColour;
69

    
70
	private State triangular;
71
	private State circular;
72
	private State oval;
73
    private State yellow;
74
	private State blue;
75
	private State yes;
76
	private State no;
77

    
78
	private Classification classification;
79

    
80
	private Taxon taxonGenus;
81
	private Taxon taxon1;
82
	private Taxon taxon2;
83
	private Taxon taxon3;
84
	private Taxon taxon4;
85
	private Taxon taxon5;
86
	private Taxon taxon6;
87
	private Taxon taxon7;
88
	private Taxon taxon8;
89

    
90
	private TaxonDescription tdGenus;
91
    private TaxonDescription td1;
92
	private TaxonDescription td2;
93
	private TaxonDescription td3;
94
	private TaxonDescription td4;
95
	private TaxonDescription td5;
96
	private TaxonDescription td6;
97
	private TaxonDescription td7;
98
	private TaxonDescription td8;
99

    
100
    private CategoricalData catd11;
101
    private CategoricalData catd12;
102
    private CategoricalData catd13;
103
    private CategoricalData catd14;
104
    private CategoricalData catd15;
105
    private CategoricalData catd27;
106
    private CategoricalData catd28;
107
	private QuantitativeData qtd31;
108
    private QuantitativeData qtd32;
109
    private QuantitativeData qtd33;
110
    private QuantitativeData qtd34;
111
    private QuantitativeData qtd35;
112
    private QuantitativeData qtd36;
113
    private QuantitativeData qtd37;
114
    private QuantitativeData qtd38;
115

    
116
	private Set<TaxonDescription> taxa;
117

    
118
	private static UUID uuidFeatureShape = UUID.fromString("a61abb0c-51fb-4af4-aee4-f5894845133f");
119
	private static UUID uuidFeaturePresence = UUID.fromString("03cb2744-52a0-4127-be5d-265fe59b426f");
120
	private static UUID uuidFeatureLength = UUID.fromString("5de4f981-83fb-41d2-9900-b52cf5782a85");
121
	private static UUID uuidFeatureColour = UUID.fromString("7a8deb1a-144f-4be5-ba0d-9e77724697cb");
122

    
123
	private static UUID uuidTdGenus = UUID.fromString("cbdfa57e-1773-4503-9e56-469c9894f6fc");
124
    private static UUID uuidTd1 = UUID.fromString("b392720c-8c64-4cbf-8207-992146f51fd5");
125
    private static UUID uuidTd2 = UUID.fromString("341d8ef1-fd07-4a91-8d53-dd6e729ad20b");
126
    private static UUID uuidTd3 = UUID.fromString("f174180f-86fe-475f-88f4-d0231fa96725");
127
    private static UUID uuidTd4 = UUID.fromString("3c90104f-ff81-43eb-a0f1-17eec1e77f49");
128
    private static UUID uuidTd5 = UUID.fromString("74b12419-4d2f-424d-9ca7-bba4c338df2e");
129
    private static UUID uuidTd6 = UUID.fromString("8df21f07-3bc0-4a88-a270-6c6050509975");
130
    private static UUID uuidTd7 = UUID.fromString("fc064338-adef-4657-bc69-34b0a9cc51a6");
131
    private static UUID uuidTd8 = UUID.fromString("b0458406-8e76-4f1a-9034-79cc661caf2a");
132

    
133
	private PolytomousKeyGenerator generator = new PolytomousKeyGenerator();
134

    
135
	@Before
136
	public void setUp() throws Exception {
137

    
138
	    featureShape = createFeature("Shape of the head", uuidFeatureShape, CATEGORICAL);
139
	    featurePresence = createFeature("Presence of wings", uuidFeaturePresence, CATEGORICAL);
140
	    featureLength = createFeature("Length of wings", uuidFeatureLength, QUANTITATIVE);
141
	    featureColour = createFeature("Colour", uuidFeatureColour, CATEGORICAL);
142

    
143
	    taxonGenus = getTaxon(0);
144
        taxon1 = getTaxon(1);
145
		taxon2 = getTaxon(2);
146
		taxon3 = getTaxon(3);
147
		taxon4 = getTaxon(4);
148
		taxon5 = getTaxon(5);
149
		taxon6 = getTaxon(6);
150
		taxon7 = getTaxon(7);
151
		taxon8 = getTaxon(8);
152

    
153
		tdGenus = createTaxonDescription(taxonGenus, "tdGenus", uuidTdGenus);
154
        td1 = createTaxonDescription(taxon1, "td1", uuidTd1);
155
		td2 = createTaxonDescription(taxon2, "td2", uuidTd2);
156
		td3 = createTaxonDescription(taxon3, "td3", uuidTd3);
157
		td4 = createTaxonDescription(taxon4, "td4", uuidTd4);
158
		td5 = createTaxonDescription(taxon5, "td5", uuidTd5);
159
		td6 = createTaxonDescription(taxon6, "td6", uuidTd6);
160
		td7 = createTaxonDescription(taxon7, "td7", uuidTd7);
161
		td8 = createTaxonDescription(taxon8, "td8", uuidTd8);
162

    
163
		triangular = createState("Triangular");
164
		circular = createState("Circular");
165
		oval = createState("Oval");
166

    
167
		yellow = createState("Yellow");
168
		blue = createState("Blue");
169

    
170
		yes = createState("Yes");
171
		no = createState("No");
172

    
173
		catd11 = CategoricalData.NewInstance(triangular, featureShape);
174
		catd11.addStateData(oval);
175
		catd12 = CategoricalData.NewInstance(triangular, featureShape);
176
		catd13 = CategoricalData.NewInstance(triangular, featureShape);
177
		catd14 = CategoricalData.NewInstance(triangular, featureShape);
178
		catd15 = CategoricalData.NewInstance(circular, featureShape);
179
		CategoricalData catd16 = CategoricalData.NewInstance(circular, featureShape);
180
		CategoricalData catd17 = CategoricalData.NewInstance(circular, featureShape);
181
		CategoricalData catd18 = CategoricalData.NewInstance(circular, featureShape);
182

    
183
		//*************************/
184

    
185
		CategoricalData catd21 = CategoricalData.NewInstance(yes, featurePresence);
186
		CategoricalData catd22 = CategoricalData.NewInstance(yes, featurePresence);
187
		CategoricalData catd23 = CategoricalData.NewInstance(yes, featurePresence);
188
		CategoricalData catd24 = CategoricalData.NewInstance(yes, featurePresence);
189
		CategoricalData catd25 = CategoricalData.NewInstance(yes, featurePresence);
190
		CategoricalData catd26 = CategoricalData.NewInstance(yes, featurePresence);
191
		catd27 = CategoricalData.NewInstance(yes, featurePresence);
192
		catd28 = CategoricalData.NewInstance(no, featurePresence);
193

    
194
		//*************************/
195

    
196
		qtd31 = QuantitativeData.NewExactValueInstance(featureLength, new BigDecimal("0.0"), new BigDecimal("3.0"));
197
//        qtd31 = QuantitativeData.NewMinMaxInstance(featureLength, 0, 3);
198
		//TODO add unit
199
		qtd32 = QuantitativeData.NewMinMaxInstance(featureLength, new BigDecimal("0.0"), new BigDecimal("3.0"));
200
		qtd33 = QuantitativeData.NewMinMaxInstance(featureLength, new BigDecimal("6.0"), new BigDecimal("9.0"));
201
		qtd34 = QuantitativeData.NewMinMaxInstance(featureLength, new BigDecimal("6.0"), new BigDecimal("9.0"));
202
		qtd35 = QuantitativeData.NewMinMaxInstance(featureLength, new BigDecimal("0.0"), new BigDecimal("3.0"));
203
		qtd36 = QuantitativeData.NewMinMaxInstance(featureLength, new BigDecimal("0.0"), new BigDecimal("3.0"));
204
		qtd37 = QuantitativeData.NewMinMaxInstance(featureLength, new BigDecimal("6.0"), new BigDecimal("9.0"));
205
		qtd38 = QuantitativeData.NewMinMaxInstance(featureLength, new BigDecimal("0.0"), new BigDecimal("3.0"));
206

    
207
		//*************************/
208

    
209
		CategoricalData catd41 = CategoricalData.NewInstance(blue, featureColour);
210
		CategoricalData catd42 = CategoricalData.NewInstance(yellow, featureColour);
211
		CategoricalData catd43 = CategoricalData.NewInstance(blue, featureColour);
212
		CategoricalData catd44 = CategoricalData.NewInstance(yellow, featureColour);
213
		CategoricalData catd45 = CategoricalData.NewInstance(blue, featureColour);
214
		CategoricalData catd46 = CategoricalData.NewInstance(blue, featureColour);
215
		CategoricalData catd47 = CategoricalData.NewInstance(blue, featureColour);
216
		CategoricalData catd48 = CategoricalData.NewInstance(blue, featureColour);
217

    
218
		//*************************/
219

    
220
		catd11.clone(tdGenus); //Shape triangular
221

    
222
		td1.addElement(catd11); //Shape triangular
223
		td1.addElement(catd21); //present
224
		td1.addElement(qtd31);  //length 0-3
225
		td1.addElement(catd41); //color blue
226

    
227
		td2.addElement(catd12);  //Shape triangular
228
		td2.addElement(catd22);  //present
229
		td2.addElement(qtd32);   //length 0-3
230
		td2.addElement(catd42);  //color yellow
231

    
232
		td3.addElement(catd13);  //Shape triangular
233
		td3.addElement(catd23);  //present
234
		td3.addElement(qtd33);   //length 6-9
235
		td3.addElement(catd43);  //color blue
236

    
237
		td4.addElement(catd14);  //Shape triangular
238
		td4.addElement(catd24);  //present
239
		td4.addElement(qtd34);   //length 6-9
240
		td4.addElement(catd44);  //color yellow
241

    
242
		td5.addElement(catd15);  //Shape circular
243
		td5.addElement(catd25);  //present
244
		td5.addElement(qtd35);   //length 0-3
245
		td5.addElement(catd45);  //color blue
246

    
247
		td6.addElement(catd16);  //Shape circular
248
		td6.addElement(catd26);  //present
249
		td6.addElement(qtd36);   //length 0-3
250
		td6.addElement(catd46);  //color blue
251

    
252
		td7.addElement(catd17);  //Shape circular
253
		td7.addElement(catd27);  //present
254
		td7.addElement(qtd37);   //length 6-9
255
		td7.addElement(catd47);  //color blue
256

    
257
		td8.addElement(catd18);  //Shape circular
258
		td8.addElement(catd28);  //absent
259
//		taxond8.addElement(qtd38); // This taxon has no wings
260
		td8.addElement(catd48);  //color blue
261

    
262
		/******* add non-character data, this should have no influence **/
263
		TaxonDescription nonCharacterDesc = TaxonDescription.NewInstance(taxon1);
264
		Distribution distribution = Distribution.NewInstance(Country.GERMANY(), PresenceAbsenceTerm.PRESENT());
265
		nonCharacterDesc.addElement(distribution);
266

    
267
		td2.addElement(TextData.NewInstance(Feature.ANATOMY(), "Test", Language.DEFAULT(), null));
268

    
269
		//*************************************************/
270

    
271
		taxa = new HashSet<>();
272
		taxa.add(tdGenus);
273
        taxa.add(td1);
274
		taxa.add(td2);
275
		taxa.add(td3);
276
		taxa.add(td4);
277
		taxa.add(td5);
278
		taxa.add(td6);
279
		taxa.add(td7);
280
		taxa.add(td8);
281

    
282
		classification = Classification.NewInstance("Test Classification");
283
		Taxon rootTaxon = taxonGenus;
284
		TaxonNode genusTaxonNode = classification.addChildTaxon(rootTaxon, null, null);
285
		genusTaxonNode.addChildTaxon(taxon1, null, null);
286
		genusTaxonNode.addChildTaxon(taxon2, null, null);
287
		genusTaxonNode.addChildTaxon(taxon3, null, null);
288
		genusTaxonNode.addChildTaxon(taxon4, null, null);
289
		genusTaxonNode.addChildTaxon(taxon5, null, null);
290
		genusTaxonNode.addChildTaxon(taxon6, null, null);
291
		genusTaxonNode.addChildTaxon(taxon7, null, null);
292
		genusTaxonNode.addChildTaxon(taxon8, null, null);
293
	}
294

    
295
//*************************** TESTS *********************** /
296

    
297
	@Test
298
	public void testInvokeMergeModeOff() {
299
		PolytomousKeyGeneratorConfigurator configurator = createDefaultConfig();
300
		configurator.setMerge(false);
301
		PolytomousKey result = generator.invoke(configurator);
302
		result.setTitleCache("No Merge Key", true);
303
        if (debug) {result.print(System.out);}
304

    
305
	    //Assertions
306
		assertNotNull("Key should exist.", result);
307
        PolytomousKeyNode root = result.getRoot();
308
        Assert.assertEquals(featureShape, root.getFeature());
309
        Assert.assertNull(root.getTaxon());
310

    
311
        //circular
312
        PolytomousKeyNode circularNode = root.getChildAt(0);
313
        assertInnerNode(circularNode, circular, featurePresence);
314

    
315
            //yes
316
            PolytomousKeyNode yesNode = circularNode.getChildAt(0);
317
            assertInnerNode(yesNode, yes, featureLength);
318

    
319
                //<3
320
                PolytomousKeyNode less3Node = yesNode.getChildAt(0);
321
                assertIsTaxonList(less3Node, LESS_3, taxon5, taxon6);
322

    
323
                //>3
324
                assertSingleTaxon(yesNode.getChildAt(1), taxon7, GT_3);
325

    
326
            //no
327
            assertSingleTaxon(circularNode.getChildAt(1), taxon8, no);
328

    
329
        //triangular
330
        PolytomousKeyNode triangularNode = root.getChildAt(1);
331
        assertInnerNode(triangularNode, triangular, featureLength);
332

    
333
            //<3
334
            less3Node = triangularNode.getChildAt(0);
335

    
336

    
337
                //blue
338
                assertSingleTaxon(less3Node.getChildAt(0), taxon1, blue);
339
                //yellow
340
                assertSingleTaxon(less3Node.getChildAt(1), taxon2, yellow);
341

    
342
            //>3
343
            PolytomousKeyNode gt3Node = triangularNode.getChildAt(1);
344
            assertInnerNode(gt3Node, GT_3, featureColour);
345

    
346
                //blue
347
                assertSingleTaxon(gt3Node.getChildAt(0), taxon3, blue);
348
                //yellow
349
                assertSingleTaxon(gt3Node.getChildAt(1), taxon4, yellow);
350

    
351
        //oval
352
        assertSingleTaxon(root.getChildAt(2), taxon1, oval);
353
	}
354

    
355
    @Test
356
    public void testInvokeMergeModeON() {
357
        PolytomousKeyGeneratorConfigurator configurator = createDefaultConfig();
358
        configurator.setMerge(true);
359
        PolytomousKey result = generator.invoke(configurator);
360
        result.setTitleCache("Merge Key", true);
361
        assertNotNull("Key should exist (merge mode ON).", result);
362
        if (debug) {result.print(System.out);}
363

    
364
        //Assertions
365
        assertNotNull("Key should exist.", result);
366
        PolytomousKeyNode root = result.getRoot();
367
        Assert.assertEquals(featureShape, root.getFeature());
368
        Assert.assertNull(root.getTaxon());
369

    
370
        //triangular or oval
371
        PolytomousKeyNode triangularNode = root.getChildAt(0);
372
        assertInnerNode(triangularNode, "Oval or Triangular", featureLength);
373

    
374
            //<3
375
            PolytomousKeyNode lessNode = triangularNode.getChildAt(0);
376
            assertInnerNode(lessNode, LESS_3 , featureColour);
377
                //blue
378
                assertSingleTaxon(lessNode.getChildAt(0), taxon1, blue);
379
                //yellow
380
                assertSingleTaxon(lessNode.getChildAt(1), taxon2, yellow);
381

    
382
            //>3
383
            PolytomousKeyNode gtNode = triangularNode.getChildAt(1);
384
            assertInnerNode(gtNode, GT_3, featureColour);
385
                //blue
386
                assertSingleTaxon(gtNode.getChildAt(0), taxon3, blue);
387
                //yellow
388
                assertSingleTaxon(gtNode.getChildAt(1), taxon4, yellow);
389

    
390
        //circular
391
        PolytomousKeyNode circularNode = root.getChildAt(1);
392
        assertInnerNode(circularNode, circular, featurePresence);
393

    
394
            //yes
395
            PolytomousKeyNode yesNode = circularNode.getChildAt(0);
396
            assertInnerNode(yesNode, yes, featureLength);
397

    
398
                //<3
399
                assertIsTaxonList(yesNode.getChildAt(0), LESS_3 , taxon5, taxon6);
400

    
401
                //>3
402
                assertSingleTaxon(yesNode.getChildAt(1), taxon7, GT_3);
403

    
404
            //no
405
            assertSingleTaxon(circularNode.getChildAt(1), taxon8, no);
406
    }
407

    
408
    @Test
409
    public void testInvokeMergeReuseFeature() {
410
        PolytomousKeyGeneratorConfigurator configurator = createDefaultConfig();
411
        td1.removeElement(qtd31);
412
        td2.removeElement(qtd32);
413
        td3.removeElement(qtd33);
414
        td4.removeElement(qtd34);
415
        catd12.addStateData(oval);
416
        catd12.addStateData(circular);
417
        configurator.setMerge(true);
418
        PolytomousKey result = generator.invoke(configurator);
419
        result.setTitleCache("Merge Key with feature reuse", true);
420
        assertNotNull("Key should exist (merge mode with feature reuse).", result);
421
        if (debug) {result.print(System.out);}
422

    
423
        //Assertions
424
        assertNotNull("Key should exist.", result);
425
        PolytomousKeyNode root = result.getRoot();
426
        Assert.assertEquals(featureShape, root.getFeature());
427

    
428
        //triangular or oval
429
        PolytomousKeyNode ovalOrTriangularNode = root.getChildren().stream()
430
                .filter(pkn->pkn.getStatement().getLabelText(Language.DEFAULT()).equals("Oval or Triangular"))
431
                .findFirst().get();
432
        Assert.assertEquals("Oval or Triangular", label(ovalOrTriangularNode));
433

    
434
            //blue
435
            PolytomousKeyNode blueNode = ovalOrTriangularNode.getChildAt(0);
436
            Assert.assertEquals(blue.getLabel(), label(blueNode));
437

    
438
                //triangular
439
                PolytomousKeyNode triangularNode = blueNode.getChildAt(0);
440
                Assert.assertEquals("Shape of head should be reused in this branch", "Triangular", label(triangularNode));
441

    
442
            //yellow
443
            PolytomousKeyNode yellowNode = ovalOrTriangularNode.getChildAt(1);
444
            Assert.assertEquals(yellow.getLabel(), label(yellowNode));
445

    
446
                //triangular
447
                PolytomousKeyNode ovalNode = yellowNode.getChildAt(1);
448
                Assert.assertEquals("Shape of head should be reused in this branch, "
449
                        + "but only for remaining states triangular and oval. "
450
                        + "'Circular' must not be available anymore", "Oval", label(ovalNode));
451

    
452
        PolytomousKeyNode circularNode = root.getChildren().stream()
453
                .filter(pkn->pkn.getStatement().getLabelText(Language.DEFAULT()).equals("Circular"))
454
                .findFirst().get();
455

    
456
            //presence yes
457
            PolytomousKeyNode presenceNode = circularNode.getChildAt(0);
458
            Assert.assertEquals(yes.getLabel(), label(presenceNode));
459

    
460
                //blue
461
                blueNode = presenceNode.getChildAt(0);
462
                Assert.assertEquals(blue.getLabel(), label(blueNode));
463

    
464
                    //length
465
                    PolytomousKeyNode lowerNode = blueNode.getChildAt(0);
466
                    assertIsTaxonList(lowerNode, LESS_3, taxon5, taxon6);  //test no feature left
467
    }
468

    
469

    
470
    /**
471
     * With dependencies is difficult to check because it only changes the performance
472
     * if data is clean. So in this test we first check some dirty data with
473
     * dependency check and then without.
474
     * In the first run it correctly removes the length of wings check at the end
475
     * as length of wings is not applicable if presence of wings = no.
476
     * In the second run it does the length of wings check as it does not
477
     * use dependency check.
478
     */
479
    @Test
480
    public void testInvokeWithoutDependencies() {
481
        generator = new PolytomousKeyGenerator();
482
        PolytomousKeyGeneratorConfigurator configurator = createDefaultConfig();
483
        configurator.setMerge(true);
484
        configurator.setUseDependencies(true);
485
        catd27.getStateData().get(0).setState(no);
486
        td8.addElement(qtd38);
487

    
488
        PolytomousKey result = generator.invoke(configurator);
489
        result.setTitleCache("Merge Key with dependency", true);
490
        assertNotNull("Key should exist (dependency on)", result);
491
        if (debug) {result.print(System.out);}
492

    
493
        //Assertions
494
        assertNotNull("Key should exist.", result);
495
        PolytomousKeyNode root = result.getRoot();
496
        Assert.assertEquals(featureShape, root.getFeature());
497

    
498
        //circular
499
        PolytomousKeyNode circularNode = root.getChildAt(1);
500
        assertInnerNode(circularNode, circular, featurePresence);
501

    
502
            //no
503
            assertIsTaxonList(circularNode.getChildAt(0), no, taxon8, taxon7);
504

    
505
            //yes
506
            assertIsTaxonList(circularNode.getChildAt(1), yes, taxon5, taxon6);
507

    
508
        //and now without dependency check
509
        configurator.setUseDependencies(false);
510

    
511
        result = generator.invoke(configurator);
512
        result.setTitleCache("Merge Key without dependency", true);
513
        assertNotNull("Key should exist (dependency off)", result);
514
        if (debug) {result.print(System.out);}
515

    
516
        //Assertions
517
        assertNotNull("Key should exist.", result);
518
        root = result.getRoot();
519
        Assert.assertEquals(featureShape, root.getFeature());
520

    
521
        //circular
522
        circularNode = root.getChildAt(1);
523
        assertInnerNode(circularNode, circular, featurePresence);
524

    
525
            //no
526
            PolytomousKeyNode noNode = assertInnerNode(circularNode.getChildAt(0), no, featureLength);
527

    
528
                //as dependency check is switched off we distinguish length, though length should be inapplicable here
529
                assertSingleTaxon(noNode.getChildAt(0), taxon8, LESS_3);
530
                assertSingleTaxon(noNode.getChildAt(1), taxon7, GT_3);
531

    
532
            //yes
533
            assertIsTaxonList(circularNode.getChildAt(1), yes, taxon5, taxon6);
534
    }
535

    
536
    @Test
537
    public void testTaxonomicHierarchy() {
538

    
539
        tdGenus.getElements().clear();
540
        tdGenus.addElements(mergeTaxDescriptions(td1, td2, td3, td4));
541
        TaxonNode genus1Node = classification.getRootNode().getChildNodes().iterator().next();
542
        removeTaxon5_8(genus1Node);
543

    
544
        UUID uuidTdGenus2 = UUID.fromString("3eed217a-fd40-4a38-997f-1f4360133d0d");
545
        Taxon taxonGenus2 = getTaxon(10);
546
        TaxonDescription tdGenus2 = createTaxonDescription(taxonGenus2, "tdGenus2", uuidTdGenus2);
547
        taxa.add(tdGenus2);
548
        TaxonNode genus2Node = classification.getRootNode().addChildTaxon(taxonGenus2, null, null);
549
        genus2Node.addChildTaxon(taxon5, null, null);
550
        genus2Node.addChildTaxon(taxon6, null, null);
551
        genus2Node.addChildTaxon(taxon7, null, null);
552
        genus2Node.addChildTaxon(taxon8, null, null);
553
        tdGenus2.addElements(mergeTaxDescriptions(td5, td6, td7, td8));
554

    
555
        PolytomousKeyGeneratorConfigurator configurator = createDefaultConfig();
556
        PolytomousKey result = generator.invoke(configurator);
557
        result.setTitleCache("Merge Key", true);
558
        assertNotNull("Key should exist (merge mode ON).", result);
559
        if (debug) {result.print(System.out);}
560

    
561
        //Assertions
562
        assertNotNull("Key should exist.", result);
563
        PolytomousKeyNode root = result.getRoot();
564
        Assert.assertEquals(featureShape, root.getFeature());
565
        Assert.assertNull(root.getTaxon());
566

    
567
        //triangular or oval
568
        PolytomousKeyNode triangularNode = root.getChildAt(0);
569
        assertInnerNodeWithTaxon(triangularNode, "Oval or Triangular", featureLength, taxonGenus);
570

    
571
            //<3
572
            PolytomousKeyNode lessNode = triangularNode.getChildAt(0);
573
            assertInnerNode(lessNode, LESS_3 , featureColour);
574
                //blue
575
                assertSingleTaxon(lessNode.getChildAt(0), taxon1, blue);
576
                //yellow
577
                assertSingleTaxon(lessNode.getChildAt(1), taxon2, yellow);
578

    
579
            //>3
580
            PolytomousKeyNode gtNode = triangularNode.getChildAt(1);
581
            assertInnerNode(gtNode, GT_3, featureColour);
582
                //blue
583
                assertSingleTaxon(gtNode.getChildAt(0), taxon3, blue);
584
                //yellow
585
                assertSingleTaxon(gtNode.getChildAt(1), taxon4, yellow);
586

    
587
        //circular
588
        PolytomousKeyNode circularNode = root.getChildAt(1);
589
        assertInnerNodeWithTaxon(circularNode, circular, featurePresence, taxonGenus2);
590

    
591
            //yes
592
            PolytomousKeyNode yesNode = circularNode.getChildAt(0);
593
            assertInnerNode(yesNode, yes, featureLength);
594

    
595
                //<3
596
                assertIsTaxonList(yesNode.getChildAt(0), LESS_3 , taxon5, taxon6);
597

    
598
                //>3
599
                assertSingleTaxon(yesNode.getChildAt(1), taxon7, GT_3);
600

    
601
            //no
602
            assertSingleTaxon(circularNode.getChildAt(1), taxon8, no);
603
    }
604

    
605
    private DescriptionElementBase[] mergeTaxDescriptions(TaxonDescription td5, TaxonDescription td6, TaxonDescription td7,
606
            TaxonDescription td8) {
607
        List<DescriptionElementBase> list = new ArrayList<>();
608
        list.addAll(clonedDescElements(td5.getElements()));
609
        list.addAll(clonedDescElements(td6.getElements()));
610
        list.addAll(clonedDescElements(td7.getElements()));
611
        list.addAll(clonedDescElements(td8.getElements()));
612

    
613
        return list.toArray(new DescriptionElementBase[0]);
614
    }
615

    
616
    private Set<DescriptionElementBase> clonedDescElements(Set<DescriptionElementBase> elements) {
617
        Set<DescriptionElementBase> result = new HashSet<>();
618
        for (DescriptionElementBase deb : elements){
619
            result.add(deb.clone());
620
        }
621
        return result;
622
    }
623

    
624
    private void removeTaxon5_8(TaxonNode genus1Node) {
625
        genus1Node.deleteChildNode(genus1Node.getChildNodes().get(7));
626
        genus1Node.deleteChildNode(genus1Node.getChildNodes().get(6));
627
        genus1Node.deleteChildNode(genus1Node.getChildNodes().get(5));
628
        genus1Node.deleteChildNode(genus1Node.getChildNodes().get(4));
629
    }
630

    
631
    @Test
632
    public void testDependencyScore() {
633
        generator = new PolytomousKeyGenerator();
634
        PolytomousKeyGeneratorConfigurator configurator = createDefaultConfig(); //new PolytomousKeyGeneratorConfigurator();
635
        configurator.getDataSet().getDescriptiveSystem().getRoot().removeChild(0); //remove shape feature
636
        configurator.setMerge(true);
637
        configurator.setUseDependencies(true);
638

    
639
        PolytomousKey result = generator.invoke(configurator);
640
        result.setTitleCache("Test Dependency Score Key", true);
641
        if (debug) {result.print(System.out);}
642

    
643
        //Assertions
644
        assertNotNull("Key should exist.", result);
645
        PolytomousKeyNode root = result.getRoot();
646
        Assert.assertEquals("Root feature should be 'presence' as it inherits score from 'length of wings'", featurePresence, root.getFeature());
647
        //...otherwise it would be color, both have a score of 12.0 but presence comes first in list
648
    }
649

    
650
    @Test
651
    @Ignore
652
    public void testMultipleDependencies() {
653

    
654
        //TODO test only if multiple dependencies are supported for 1 parent feature; not yet implemented; requires restructuring the feature tree, created by createDefaultConfig()
655
//        generator = new PolytomousKeyGenerator();
656
//        PolytomousKeyGeneratorConfigurator configurator = createDefaultConfig(); //new PolytomousKeyGeneratorConfigurator();
657
//        configurator.getDataSet().getDescriptiveSystem().getRoot().removeChild(0); //remove shape feature
658
//        configurator.setMerge(true);
659
//        configurator.setUseDependencies(true);
660
//
661
//        PolytomousKey result = generator.invoke(configurator);
662
//        result.setTitleCache("Test Dependency Score Key", true);
663
//        if (debug) {result.print(System.out);}
664
//
665
//        //Assertions
666
//        assertNotNull("Key should exist.", result);
667
//        PolytomousKeyNode root = result.getRoot();
668
//        Assert.assertEquals("Root feature should be 'presence' as it inherits score from 'length of wings'", featurePresence, root.getFeature());
669
//        //...otherwise it would be colour, both have a score of 12.0 but presence comes first in list
670
    }
671

    
672
    /**
673
     * Asserts that the node is an inner node (has no taxon) and uses the given feature
674
     * and has the given statement label.
675
     */
676
    private void assertInnerNode(PolytomousKeyNode node, String label, Feature feature) {
677
        Assert.assertEquals(label, label(node));
678
        Assert.assertEquals(feature, node.getFeature());
679
        Assert.assertNull(node.getTaxon());
680
    }
681

    
682
    private void assertInnerNodeWithTaxon(PolytomousKeyNode node, String label, Feature feature, Taxon taxon) {
683
        Assert.assertEquals(label, label(node));
684
        Assert.assertEquals(feature, node.getFeature());
685
        Assert.assertEquals(taxon, node.getTaxon());
686
    }
687

    
688
    private PolytomousKeyNode assertInnerNode(PolytomousKeyNode node, State state, Feature feature) {
689
        assertInnerNode(node, state.getLabel(), feature);
690
        return node;
691
    }
692
    private PolytomousKeyNode assertInnerNodeWithTaxon(PolytomousKeyNode node, State state, Feature feature, Taxon taxon) {
693
        assertInnerNodeWithTaxon(node, state.getLabel(), feature, taxon);
694
        return node;
695
    }
696

    
697
    private void assertSingleTaxon(PolytomousKeyNode node, Taxon taxon, String statement) {
698
        Assert.assertNotNull(node.getStatement());
699
        Assert.assertEquals(statement, label(node));
700
        Assert.assertTrue(node.getChildren().isEmpty());
701
        Assert.assertEquals(taxon, node.getTaxon());
702
    }
703

    
704
    private void assertSingleTaxon(PolytomousKeyNode node, Taxon taxon, State state) {
705
        assertSingleTaxon(node, taxon, state.getLabel());
706
    }
707

    
708
    private void assertIsTaxonList(PolytomousKeyNode node, State state, Taxon... taxa) {
709
        assertIsTaxonList(node, state.getLabel(), taxa);
710
    }
711

    
712
    private void assertIsTaxonList(PolytomousKeyNode node, String label, Taxon... taxa) {
713
        Assert.assertNotNull(node.getStatement());
714
        Assert.assertEquals(label, label(node));
715
        Assert.assertNull(node.getFeature());
716
        Assert.assertNull(node.getTaxon());
717
        Assert.assertTrue(node.getChildren().size() > 1);
718
        for (PolytomousKeyNode child : node.getChildren()){
719
            Assert.assertTrue(Arrays.asList(taxa).contains(child.getTaxon()));
720
            Assert.assertNull(child.getStatement());
721
            Assert.assertTrue(child.getChildren().isEmpty());
722
        }
723
    }
724

    
725
    private PolytomousKeyGeneratorConfigurator createDefaultConfig() {
726
        PolytomousKeyGeneratorConfigurator configurator = new PolytomousKeyGeneratorConfigurator();
727
        configurator.setDataSet(createDataSet());
728
        configurator.setDebug(debug);
729
        return configurator;
730
    }
731

    
732
    private DescriptiveDataSet createDataSet() {
733
        DescriptiveDataSet dataset = DescriptiveDataSet.NewInstance();
734
        dataset.setDescriptiveSystem(createFeatureTree());
735
        for (TaxonDescription desc : taxa){
736
            dataset.addDescription(desc);
737
        }
738
        return dataset;
739
    }
740

    
741
    private TermTree<Feature> createFeatureTree() {
742
        TermTree<Feature> result = TermTree.NewInstance(TermType.Feature, Feature.class);
743
        result.getRoot().addChild(featureShape);
744
        TermNode<Feature> nodePresence = result.getRoot().addChild(featurePresence);
745
        TermNode<Feature> nodeLength = nodePresence.addChild(featureLength);
746
        nodeLength.addInapplicableState(featurePresence, no);
747
        nodePresence.addChild(featureColour);
748

    
749
        return result;
750
    }
751

    
752
    private Object label(PolytomousKeyNode node) {
753
        return node.getStatement()== null?"no statement":node.getStatement().getLabelText(Language.DEFAULT());
754
    }
755

    
756
    private TaxonDescription createTaxonDescription(Taxon taxon, String title, UUID uuid) {
757
        TaxonDescription result = TaxonDescription.NewInstance(taxon);
758
        result.setTitleCache(title, true);
759
        result.setUuid(uuid);
760
        return result;
761
    }
762

    
763
    private State createState(String label) {
764
        State state = State.NewInstance("", label, "");
765
        state.getTitleCache();  //for better debugging
766
        return state;
767
    }
768

    
769
    private Feature createFeature(String title, UUID uuid, boolean isQuantitative) {
770
        Feature result = Feature.NewInstance("",title,"");
771
        result.setUuid(uuid);
772
        if (isQuantitative){
773
            result.setSupportsQuantitativeData(true);
774
        }else{
775
            result.setSupportsCategoricalData(true);
776
        }
777
        result.getTitleCache();
778
        return result;
779
    }
780

    
781
    private Taxon getTaxon(int i) {
782
        TaxonName tn = TaxonNameFactory.NewNonViralInstance(Rank.SPECIES());
783
        tn.setGenusOrUninomial("Taxon");
784
        tn.setSpecificEpithet(String.valueOf(i));
785
        Taxon result = Taxon.NewInstance(tn, ReferenceFactory.newBook());
786
        result.getTitleCache();
787
        return result;
788
    }
789

    
790

    
791
}
    (1-1/1)