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;
14
import org.apache.logging.log4j.Logger;
15
import org.junit.Assert;
16
import org.junit.Before;
17
import org.junit.Ignore;
18
import org.junit.Test;
19

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

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

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

    
56
    private static final boolean debug = false;
57

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

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

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

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

    
79
	private Classification classification;
80

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

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

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

    
117
	private Set<TaxonDescription> taxa;
118

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

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

    
134
	private PolytomousKeyGenerator generator = new PolytomousKeyGenerator();
135

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

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

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

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

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

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

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

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

    
184
		//*************************/
185

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

    
195
		//*************************/
196

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

    
208
		//*************************/
209

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

    
219
		//*************************/
220

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

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

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

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

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

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

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

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

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

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

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

    
270
		//*************************************************/
271

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

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

    
296
//*************************** TESTS *********************** /
297

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

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

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

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

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

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

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

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

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

    
337

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
470

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

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

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

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

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

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

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

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

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

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

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

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

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

    
537
    @Test
538
    public void testTaxonomicHierarchy() {
539

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
750
        return result;
751
    }
752

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

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

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

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

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

    
791

    
792
}
    (1-1/1)