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