28c8a91f605f83818b86202dad621ac3f89bb8f4
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / IdentificationKeyGenerator.java
1 package eu.etaxonomy.cdm.api.service;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.HashSet;
6 import java.util.Iterator;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Set;
10
11 import eu.etaxonomy.cdm.model.common.Language;
12 import eu.etaxonomy.cdm.model.common.Representation;
13 import eu.etaxonomy.cdm.model.description.CategoricalData;
14 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
15 import eu.etaxonomy.cdm.model.description.Feature;
16 import eu.etaxonomy.cdm.model.description.FeatureNode;
17 import eu.etaxonomy.cdm.model.description.FeatureTree;
18 import eu.etaxonomy.cdm.model.description.KeyStatement;
19 import eu.etaxonomy.cdm.model.description.PolytomousKey;
20 import eu.etaxonomy.cdm.model.description.PolytomousKeyNode;
21 import eu.etaxonomy.cdm.model.description.QuantitativeData;
22 import eu.etaxonomy.cdm.model.description.State;
23 import eu.etaxonomy.cdm.model.description.StateData;
24 import eu.etaxonomy.cdm.model.description.StatisticalMeasure;
25 import eu.etaxonomy.cdm.model.description.StatisticalMeasurementValue;
26 import eu.etaxonomy.cdm.model.description.TaxonDescription;
27
28 public class IdentificationKeyGenerator {
29
30 static int level=-1; // global variable needed by the printTree function in order to store the level which is being printed
31 private FeatureTree polytomousKey_old; // the Identification Key
32 private PolytomousKey polytomousKey; // the Identification Key
33 private List<Feature> features; // the features used to generate the key
34 private Set<TaxonDescription> taxa; // the base of taxa
35
36 private String before="<";
37 private String after=">";
38 private String separator = ", ";
39
40 /**
41 * Sets the features used to generate the key
42 *
43 * @param featuresList
44 */
45 public void setFeatures(List<Feature> featuresList){
46 this.features = featuresList;
47 }
48
49 /**
50 * Sets the base of taxa
51 *
52 * @param featuresList
53 */
54 public void setTaxa(Set<TaxonDescription> taxaSet){
55 this.taxa = taxaSet;
56 }
57
58
59 /**
60 * Initializes the function buildBranches() with the starting parameters in order to build the key
61 */
62 private void loop_old(){
63 polytomousKey_old = FeatureTree.NewInstance();
64 FeatureNode root = polytomousKey_old.getRoot();
65 buildBranches_old(root,features,taxa);
66 }
67
68 /**
69 * Initializes the function buildBranches() with the starting parameters in order to build the key
70 */
71 private void loop(){
72 polytomousKey = polytomousKey.NewInstance();
73 PolytomousKeyNode root = polytomousKey.getRoot();
74 buildBranches(root,features,taxa);
75 }
76
77
78 /**
79 * Creates the key and prints it
80 */
81 public void makeandprint(){
82 loop();
83 List<PolytomousKeyNode> rootlist = new ArrayList<PolytomousKeyNode>();
84 rootlist.add(polytomousKey.getRoot());
85 String spaces = new String();
86 printTree(rootlist,spaces);
87 }
88
89
90 /**
91 * Recursive function that builds the branches of the identification key (FeatureTree)
92 *
93 * @param father the node considered
94 * @param featuresLeft List of features that can be used at this point
95 * @param taxaCovered the taxa left at this point (i.e. that verify the description corresponding to the path leading to this node)
96 */
97 private void buildBranches_old(FeatureNode father, List<Feature> featuresLeft, Set<TaxonDescription> taxaCovered){
98 // // this map stores the thresholds giving the best dichotomy of taxa for the corresponding feature supporting quantitative data
99 // Map<Feature,Float> quantitativeFeaturesThresholds = new HashMap<Feature,Float>();
100 // // the scores of the different features are calculated, the thresholds in the same time
101 // Map<Feature,Float> scoreMap = featureScores(featuresLeft, taxaCovered, quantitativeFeaturesThresholds);
102 // // the feature with the best score becomes the one corresponding to the current node
103 // Feature winnerFeature = defaultWinner(taxaCovered.size(), scoreMap);
104 // // the feature is removed from the list of features available to build the next level of the tree
105 // featuresLeft.remove(winnerFeature);
106 // // this boolean indicates if the current father node has children or not (i.e. is a leaf or not) ; (a leaf has a "Question" element)
107 // boolean childrenExist = false;
108 // int i;
109 //
110 // /************** either the feature supports quantitative data... **************/
111 // // NB: in this version, "quantitative features" are dealt with in a dichotomous way
112 // if (winnerFeature.isSupportsQuantitativeData()) {
113 // // first, get the threshold
114 // float threshold = quantitativeFeaturesThresholds.get(winnerFeature);
115 // String sign;
116 //
117 // // then determine which taxa are before and which are after this threshold (dichotomy) in order to create the children of the father node
118 // List<Set<TaxonDescription>> quantitativeStates = determineQuantitativeStates(threshold,winnerFeature,taxaCovered);
119 // for (i=0;i<2;i++) {
120 // Set<TaxonDescription> newTaxaCovered = quantitativeStates.get(i);
121 // if (i==0) sign = before; // the first element of the list corresponds to taxa before the threshold
122 // else sign = after; // the second to those after
123 // if (!(newTaxaCovered.size()==taxaCovered.size())&&newTaxaCovered.size()>0){ // if the taxa are discriminated compared to those of the father node, a child is created
124 // childrenExist = true;
125 // FeatureNode son = FeatureNode.NewInstance();
126 // son.setFeature(winnerFeature);
127 // Representation question = new Representation(null, sign + threshold,null, Language.DEFAULT()); // the question attribute is used to store the state of the feature
128 // son.addQuestion(question);
129 // father.addChild(son);
130 // buildBranches(son,featuresLeft, newTaxaCovered);
131 // }
132 // }
133 // }
134 //
135 // /************** ...or it supports categorical data. **************/
136 // // "categorical features" may present several different states, each one of these might correspond to one child
137 // List<State> statesDone = new ArrayList<State>();
138 // int numberOfStates;
139 // if (winnerFeature.isSupportsCategoricalData()) {
140 // for (TaxonDescription td : taxaCovered){
141 // // go through all the states possible for one feature for the taxa considered
142 // DescriptionElementBase debConcerned = null;
143 // for (DescriptionElementBase deb : td.getElements()) {
144 // if (deb.getFeature().equals(winnerFeature)) debConcerned = deb;
145 // }
146 // // a map is created, the key being the set of taxa that present the state(s) stored in the corresponding value
147 // Map<Set<TaxonDescription>,List<State>> taxonStatesMap = determineCategoricalStates(statesDone,(CategoricalData)debConcerned,winnerFeature,taxaCovered);
148 // if (taxonStatesMap!=null && !taxonStatesMap.isEmpty()) {
149 // for (Map.Entry<Set<TaxonDescription>,List<State>> e : taxonStatesMap.entrySet()){
150 // Set<TaxonDescription> newTaxaCovered = e.getKey();
151 // List<State> listOfStates = e.getValue();
152 // if (!(newTaxaCovered.size()==taxaCovered.size())){ // if the taxa are discriminated compared to those of the father node, a child is created
153 // childrenExist = true;
154 // FeatureNode son = FeatureNode.NewInstance();
155 // StringBuilder questionLabel = new StringBuilder();
156 // numberOfStates = listOfStates.size()-1;
157 // for (State st : listOfStates) {
158 // questionLabel.append(st.getLabel());
159 // if (listOfStates.lastIndexOf(st)!=numberOfStates) questionLabel.append(separator);
160 // }
161 // Representation question = new Representation(null, questionLabel.toString(),null, Language.DEFAULT());
162 // son.addQuestion(question);
163 // son.setFeature(winnerFeature);
164 // father.addChild(son);
165 // featuresLeft.remove(winnerFeature); // TODO was commented before, why ?
166 // buildBranches(son,featuresLeft, newTaxaCovered);
167 // }
168 // }
169 // }
170 // }
171 // }
172 // if (!childrenExist){
173 // Representation question = father.getQuestion(Language.DEFAULT());
174 // if (question!=null && taxaCovered!= null) question.setLabel(question.getLabel() + " --> " + taxaCovered.toString());
175 // }
176 // featuresLeft.add(winnerFeature);
177 }
178
179 /**
180 * Recursive function that builds the branches of the identification key (FeatureTree)
181 *
182 * @param father the node considered
183 * @param featuresLeft List of features that can be used at this point
184 * @param taxaCovered the taxa left at this point (i.e. that verify the description corresponding to the path leading to this node)
185 */
186 private void buildBranches(PolytomousKeyNode father, List<Feature> featuresLeft, Set<TaxonDescription> taxaCovered){
187 // this map stores the thresholds giving the best dichotomy of taxa for the corresponding feature supporting quantitative data
188 Map<Feature,Float> quantitativeFeaturesThresholds = new HashMap<Feature,Float>();
189 // the scores of the different features are calculated, the thresholds in the same time
190 Map<Feature,Float> scoreMap = featureScores(featuresLeft, taxaCovered, quantitativeFeaturesThresholds);
191 // the feature with the best score becomes the one corresponding to the current node
192 Feature winnerFeature = defaultWinner(taxaCovered.size(), scoreMap);
193 // the feature is removed from the list of features available to build the next level of the tree
194 featuresLeft.remove(winnerFeature);
195 // this boolean indicates if the current father node has children or not (i.e. is a leaf or not) ; (a leaf has a "Question" element)
196 boolean childrenExist = false;
197 int i;
198
199 /************** either the feature supports quantitative data... **************/
200 // NB: in this version, "quantitative features" are dealt with in a dichotomous way
201 if (winnerFeature.isSupportsQuantitativeData()) {
202 // first, get the threshold
203 float threshold = quantitativeFeaturesThresholds.get(winnerFeature);
204 String sign;
205
206 // then determine which taxa are before and which are after this threshold (dichotomy) in order to create the children of the father node
207 List<Set<TaxonDescription>> quantitativeStates = determineQuantitativeStates(threshold,winnerFeature,taxaCovered);
208 for (i=0 ; i<2 ; i++) {
209 Set<TaxonDescription> newTaxaCovered = quantitativeStates.get(i);
210 if (i==0){
211 sign = before; // the first element of the list corresponds to taxa before the threshold
212 } else{
213 sign = after; // the second to those after
214 }
215 if ( (newTaxaCovered.size()!=taxaCovered.size()) && newTaxaCovered.size() > 0){ // if the taxa are discriminated compared to those of the father node, a child is created
216 childrenExist = true;
217 PolytomousKeyNode son = PolytomousKeyNode.NewInstance();
218 son.setFeature(winnerFeature);
219 KeyStatement statement = KeyStatement.NewInstance(sign + threshold); // the question attribute is used to store the state of the feature
220 son.setStatement(statement);
221 father.addChild(son);
222 buildBranches(son,featuresLeft, newTaxaCovered);
223 }
224 }
225 }
226
227 /************** ...or it supports categorical data. **************/
228 // "categorical features" may present several different states, each one of these might correspond to one child
229 List<State> statesDone = new ArrayList<State>();
230 int numberOfStates;
231 if (winnerFeature.isSupportsCategoricalData()) {
232 for (TaxonDescription td : taxaCovered){
233 // go through all the states possible for one feature for the taxa considered
234 DescriptionElementBase debConcerned = null;
235 for (DescriptionElementBase deb : td.getElements()) {
236 if (deb.getFeature().equals(winnerFeature)) debConcerned = deb;
237 }
238 // a map is created, the key being the set of taxa that present the state(s) stored in the corresponding value
239 Map<Set<TaxonDescription>,List<State>> taxonStatesMap = determineCategoricalStates(statesDone,(CategoricalData)debConcerned,winnerFeature,taxaCovered);
240 if (taxonStatesMap!=null && !taxonStatesMap.isEmpty()) {
241 for (Map.Entry<Set<TaxonDescription>,List<State>> e : taxonStatesMap.entrySet()){
242 Set<TaxonDescription> newTaxaCovered = e.getKey();
243 List<State> listOfStates = e.getValue();
244 if (!(newTaxaCovered.size()==taxaCovered.size())){ // if the taxa are discriminated compared to those of the father node, a child is created
245 childrenExist = true;
246 PolytomousKeyNode son = PolytomousKeyNode.NewInstance();
247 StringBuilder questionLabel = new StringBuilder();
248 numberOfStates = listOfStates.size()-1;
249 for (State st : listOfStates) {
250 questionLabel.append(st.getLabel());
251 if (listOfStates.lastIndexOf(st)!=numberOfStates) questionLabel.append(separator);
252 }
253 KeyStatement statement = KeyStatement.NewInstance(questionLabel.toString());
254 son.setStatement(statement);
255 son.setFeature(winnerFeature);
256 father.addChild(son);
257 featuresLeft.remove(winnerFeature); // TODO was commented before, why ?
258 buildBranches(son,featuresLeft, newTaxaCovered);
259 }
260 }
261 }
262 }
263 }
264 if (! childrenExist){
265 KeyStatement fatherStatement = father.getStatement();
266 String statementString = fatherStatement.getLabelText(Language.DEFAULT());
267 if (statementString !=null && taxaCovered != null){
268 String label = statementString + " --> " + taxaCovered.toString();
269 fatherStatement.putLabel(label, Language.DEFAULT());
270 }
271 }
272 featuresLeft.add(winnerFeature);
273 }
274
275
276
277 /**
278 * fills a map of the sets of taxa (key) presenting the different states (value) for the given feature.
279 *
280 * @param statesDone the list of states already done for this feature
281 * @param categoricalData the element from which the states are extracted
282 * @param feature the feature corresponding to the CategoricalData
283 * @param taxaCovered the base of taxa considered
284 * @return
285 */
286 private Map<Set<TaxonDescription>,List<State>> determineCategoricalStates(List<State> statesDone, CategoricalData categoricalData, Feature feature, Set<TaxonDescription> taxaCovered){
287 Map<Set<TaxonDescription>,List<State>> childrenStatesMap = new HashMap<Set<TaxonDescription>,List<State>>();
288
289 List<StateData> stateDatas = categoricalData.getStates();
290
291 List<State> states = new ArrayList<State>(); // In this function states only are considered, modifiers are not
292 for (StateData sd : stateDatas){
293 states.add(sd.getState());
294 }
295
296 for (State featureState : states){
297 if(!statesDone.contains(featureState)){
298 statesDone.add(featureState);
299
300 StateData sd = new StateData();
301 sd.setState(featureState);
302 //((CategoricalData)debsDone.get(0)).addState(sd);// A VOIR
303
304 Set<TaxonDescription> newTaxaCovered = whichTaxa(feature,featureState,taxaCovered);
305 List<State> newStates = childrenStatesMap.get(newTaxaCovered);
306 if (newStates==null) {
307 newStates = new ArrayList<State>();
308
309 childrenStatesMap.put(newTaxaCovered,newStates);
310 }
311 newStates.add(featureState);
312 }
313 }
314 return childrenStatesMap;
315 }
316
317 // returns the list of taxa from previously covered taxa, which have the state featureState for the feature feature
318 private Set<TaxonDescription> whichTaxa(Feature feature, State featureState, Set<TaxonDescription> taxaCovered){
319 Set<TaxonDescription> newCoveredTaxa = new HashSet<TaxonDescription>();
320 for (TaxonDescription td : taxaCovered){
321 Set<DescriptionElementBase> elements = td.getElements();
322 for (DescriptionElementBase deb : elements){
323 if (deb.isInstanceOf(CategoricalData.class)) {
324 if (deb.getFeature().equals(feature)) {
325 List<StateData> stateDatas = ((CategoricalData)deb).getStates();
326 for (StateData sd : stateDatas) {
327 if (sd.getState().equals(featureState))
328 newCoveredTaxa.add(td);
329 }
330 }
331 }
332 }
333 }
334 return newCoveredTaxa;
335 }
336
337 //change names
338 private Feature defaultWinner(int nTaxons, Map<Feature,Float> scores){
339 float meanScore = defaultMeanScore(nTaxons);
340 float bestScore = nTaxons*nTaxons;
341 Feature feature = null;
342 Iterator it = scores.entrySet().iterator();
343 float newScore;
344 while (it.hasNext()){
345 Map.Entry<Feature,Float> pair = (Map.Entry)it.next();
346 if (pair.getValue()!=null){
347 newScore = Math.abs((Float)pair.getValue()-meanScore);
348 if (newScore < bestScore){
349 feature = (Feature)pair.getKey();
350 bestScore = newScore;
351 }
352 }
353 }
354 if (!(feature.getLabel()==null)){
355 // System.out.println(feature.getLabel() + bestScore);
356 }
357 return feature;
358 }
359
360 // rutiliser et vrif si rien de trop <- FIXME please do not comment in french or at least use proper file encoding
361 private float defaultMeanScore(int nTaxons){
362 int i;
363 float score=0;
364 for (i=1;i<nTaxons;i++){
365 score = score + Math.round((float)(i+1/2));
366 }
367 return score;
368 }
369
370 private Map<Feature,Float> featureScores(List<Feature> featuresLeft, Set<TaxonDescription> coveredTaxa, Map<Feature,Float> quantitativeFeaturesThresholds){
371 Map<Feature,Float> scoreMap = new HashMap<Feature,Float>();
372 for (Feature feature : featuresLeft){
373 if (feature.isSupportsCategoricalData()) {
374 scoreMap.put(feature, featureScore(feature,coveredTaxa));
375 }
376 if (feature.isSupportsQuantitativeData()){
377 scoreMap.put(feature, quantitativeFeatureScore(feature,coveredTaxa, quantitativeFeaturesThresholds));
378 }
379 }
380 return scoreMap;
381 }
382
383 private List<Set<TaxonDescription>> determineQuantitativeStates (Float threshold, Feature feature, Set<TaxonDescription> taxa){
384 List<Set<TaxonDescription>> list = new ArrayList<Set<TaxonDescription>>();
385 Set<TaxonDescription> taxaBefore = new HashSet<TaxonDescription>();
386 Set<TaxonDescription> taxaAfter = new HashSet<TaxonDescription>();
387 list.add(taxaBefore);
388 list.add(taxaAfter);
389 for (TaxonDescription td : taxa){
390 Set<DescriptionElementBase> elements = td.getElements();
391 for (DescriptionElementBase deb : elements){
392 if (deb.getFeature().equals(feature)) {
393 if (deb.isInstanceOf(QuantitativeData.class)) {
394 QuantitativeData qd = (QuantitativeData)deb;
395 Set<StatisticalMeasurementValue> values = qd.getStatisticalValues();
396 for (StatisticalMeasurementValue smv : values){
397 StatisticalMeasure type = smv.getType();
398 // DONT FORGET sample size, MEAN etc
399 if (type.equals(StatisticalMeasure.MAX()) || type.equals(StatisticalMeasure.TYPICAL_UPPER_BOUNDARY())) {
400 if (smv.getValue()>=threshold) taxaAfter.add(td);
401 }
402 if (type.equals(StatisticalMeasure.MIN()) || type.equals(StatisticalMeasure.TYPICAL_LOWER_BOUNDARY())) {
403 if (smv.getValue()<=threshold) taxaBefore.add(td);
404 }
405 }
406 }
407 }
408 }
409 }
410 return list;
411 }
412
413 private float quantitativeFeatureScore(Feature feature, Set<TaxonDescription> coveredTaxa, Map<Feature,Float> quantitativeFeaturesThresholds){
414 List<Float> allValues = new ArrayList<Float>();
415 boolean lowerboundarypresent;
416 boolean upperboundarypresent;
417 float lowerboundary=0;
418 float upperboundary=0;
419 for (TaxonDescription td : coveredTaxa){
420 Set<DescriptionElementBase> elements = td.getElements();
421 for (DescriptionElementBase deb : elements){
422 if (deb.getFeature().equals(feature)) {
423 if (deb.isInstanceOf(QuantitativeData.class)) {
424 QuantitativeData qd = (QuantitativeData)deb;
425 Set<StatisticalMeasurementValue> values = qd.getStatisticalValues();
426 lowerboundarypresent = false;
427 upperboundarypresent = false;
428 for (StatisticalMeasurementValue smv : values){
429 StatisticalMeasure type = smv.getType();
430 // DONT FORGET sample size, MEAN etc
431 if (type.equals(StatisticalMeasure.MAX())) {
432 upperboundary = smv.getValue();
433 upperboundarypresent=true;
434 }
435 if (type.equals(StatisticalMeasure.MIN())) {
436 lowerboundary = smv.getValue();
437 lowerboundarypresent=true;
438 }
439 if (type.equals(StatisticalMeasure.TYPICAL_UPPER_BOUNDARY()) && upperboundarypresent==false) {
440 upperboundary = smv.getValue();
441 upperboundarypresent=true;
442 }
443 if (type.equals(StatisticalMeasure.TYPICAL_LOWER_BOUNDARY()) && lowerboundarypresent==false) {
444 lowerboundary = smv.getValue();
445 lowerboundarypresent=true;
446 }
447 }
448 if (lowerboundarypresent && upperboundarypresent) {
449 allValues.add(lowerboundary);
450 allValues.add(upperboundary);
451 }
452 }
453 }
454 }
455 }
456 int i,j;
457 float threshold=0;
458 float bestThreshold=0;
459 int difference=allValues.size();
460 int differenceMin = difference;
461 int taxaBefore=0;
462 int taxaAfter=0;
463 for (i=0;i<allValues.size()/2;i++) {
464 threshold = allValues.get(i*2+1);
465 taxaBefore=0;
466 taxaAfter=0;
467 for (j=0;j<allValues.size()/2;j++) {
468 if (allValues.get(j*2+1)<=threshold) taxaBefore++;
469 if (allValues.get(j*2)>=threshold) taxaAfter++;
470 }
471 difference = Math.abs(taxaBefore-taxaAfter);
472 if (difference<differenceMin){
473 differenceMin=difference;
474 bestThreshold = threshold;
475 }
476 }
477 quantitativeFeaturesThresholds.put(feature, bestThreshold);
478 int defaultQuantitativeScore=0;
479 for (i=0;i<taxaBefore;i++) {
480 defaultQuantitativeScore += taxaAfter - i;
481 }
482 System.out.println(taxaBefore + ", " + taxaAfter + ", " +defaultQuantitativeScore);
483 return (float)(defaultQuantitativeScore);
484 }
485
486 private float featureScore(Feature feature, Set<TaxonDescription> coveredTaxa){
487 int i,j;
488 float score =0;
489 TaxonDescription[] coveredTaxaArray = coveredTaxa.toArray(new TaxonDescription[coveredTaxa.size()]); // I did not figure a better way to do this
490 for (i=0 ; i<coveredTaxaArray.length; i++){
491 Set<DescriptionElementBase> elements1 = coveredTaxaArray[i].getElements();
492 DescriptionElementBase deb1 = null;
493 for (DescriptionElementBase deb : elements1){
494 if (deb.getFeature().equals(feature)) deb1 = deb; // finds the DescriptionElementBase corresponding to the concerned Feature
495 }
496 for (j=i+1 ; j< coveredTaxaArray.length ; j++){
497 Set<DescriptionElementBase> elements2 = coveredTaxaArray[j].getElements();
498 DescriptionElementBase deb2 = null;
499 for (DescriptionElementBase deb : elements2){
500 if (deb.getFeature().equals(feature)) deb2 = deb; // finds the DescriptionElementBase corresponding to the concerned Feature
501 }
502 score = score + defaultPower(deb1,deb2);
503 }
504 }
505 return score;
506 }
507
508 private float defaultPower(DescriptionElementBase deb1, DescriptionElementBase deb2){
509 if (deb1==null || deb2==null) {
510 return -1; //what if the two taxa don't have this feature in common ?
511 }
512 if ((deb1.isInstanceOf(CategoricalData.class))&&(deb2.isInstanceOf(CategoricalData.class))) {
513 return defaultCategoricalPower((CategoricalData)deb1, (CategoricalData)deb2);
514 }
515 else return 0;
516 }
517
518 private float defaultCategoricalPower(CategoricalData deb1, CategoricalData deb2){
519 List<StateData> states1 = deb1.getStates();
520 List<StateData> states2 = deb2.getStates();
521 boolean bool = false;
522 Iterator<StateData> stateData1Iterator = states1.iterator() ;
523 while (!bool && stateData1Iterator.hasNext()) {
524 Iterator<StateData> stateData2Iterator = states2.iterator() ;
525 StateData stateData1 = stateData1Iterator.next();
526 //bool = states2.contains(strIterator.next());
527 while (!bool && stateData2Iterator.hasNext()) {
528 bool = stateData1.getState().equals(stateData2Iterator.next().getState()); // checks if the states are the same
529 }
530 // modifiers not taken into account for this default power
531 }
532 // one point each time two taxa have at least a state in common for a given feature
533 if (bool) return 0;
534 else return 1;
535 }
536
537 private void printTree(List<PolytomousKeyNode> polytomousKeyNodes, String spaces){
538 if (! polytomousKeyNodes.isEmpty()){
539 level++;
540 int levelcopy = level;
541 int j=1;
542 String newspaces = spaces.concat("\t");
543 for (PolytomousKeyNode polytomousKeyNode : polytomousKeyNodes){
544 if (polytomousKeyNode.getQuestion() != null) {
545 String state = null;
546 if (polytomousKeyNode.getStatement().getLabel(Language.DEFAULT() ) != null){
547 state = polytomousKeyNode.getStatement().getLabelText(Language.DEFAULT());
548 }
549 System.out.println(newspaces + levelcopy + " : " + j + " " + polytomousKeyNode.getQuestion().getLabelText(Language.DEFAULT()) + " = " + state);
550 j++;
551 }
552 else { // TODO never read ?
553 if (polytomousKeyNode.getStatement().getLabel(Language.DEFAULT() ) != null){
554 System.out.println(newspaces + "-> " + polytomousKeyNode.getStatement().getLabelText(Language.DEFAULT()));
555 }
556 }
557 printTree(polytomousKeyNode.getChildren(),newspaces);
558 }
559 }
560 }
561
562 }
563