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
|
}
|