1 package eu
.etaxonomy
.cdm
.api
.service
;
3 import java
.util
.ArrayList
;
4 import java
.util
.HashMap
;
5 import java
.util
.HashSet
;
6 import java
.util
.Iterator
;
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
;
28 public class IdentificationKeyGenerator
{
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
36 private String before
="<";
37 private String after
=">";
38 private String separator
= ", ";
41 * Sets the features used to generate the key
45 public void setFeatures(List
<Feature
> featuresList
){
46 this.features
= featuresList
;
50 * Sets the base of taxa
54 public void setTaxa(Set
<TaxonDescription
> taxaSet
){
60 * Initializes the function buildBranches() with the starting parameters in order to build the key
62 private void loop_old(){
63 polytomousKey_old
= FeatureTree
.NewInstance();
64 FeatureNode root
= polytomousKey_old
.getRoot();
65 buildBranches_old(root
,features
,taxa
);
69 * Initializes the function buildBranches() with the starting parameters in order to build the key
72 polytomousKey
= polytomousKey
.NewInstance();
73 PolytomousKeyNode root
= polytomousKey
.getRoot();
74 buildBranches(root
,features
,taxa
);
79 * Creates the key and prints it
81 public void makeandprint(){
83 List
<PolytomousKeyNode
> rootlist
= new ArrayList
<PolytomousKeyNode
>();
84 rootlist
.add(polytomousKey
.getRoot());
85 String spaces
= new String();
86 printTree(rootlist
,spaces
);
91 * Recursive function that builds the branches of the identification key (FeatureTree)
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)
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;
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);
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);
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;
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);
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);
172 // if (!childrenExist){
173 // Representation question = father.getQuestion(Language.DEFAULT());
174 // if (question!=null && taxaCovered!= null) question.setLabel(question.getLabel() + " --> " + taxaCovered.toString());
176 // featuresLeft.add(winnerFeature);
180 * Recursive function that builds the branches of the identification key (FeatureTree)
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)
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;
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
);
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
);
211 sign
= before
; // the first element of the list corresponds to taxa before the threshold
213 sign
= after
; // the second to those after
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
);
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
>();
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
;
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
);
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
);
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());
272 featuresLeft
.add(winnerFeature
);
278 * fills a map of the sets of taxa (key) presenting the different states (value) for the given feature.
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
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
>>();
289 List
<StateData
> stateDatas
= categoricalData
.getStates();
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());
296 for (State featureState
: states
){
297 if(!statesDone
.contains(featureState
)){
298 statesDone
.add(featureState
);
300 StateData sd
= new StateData();
301 sd
.setState(featureState
);
302 //((CategoricalData)debsDone.get(0)).addState(sd);// A VOIR
304 Set
<TaxonDescription
> newTaxaCovered
= whichTaxa(feature
,featureState
,taxaCovered
);
305 List
<State
> newStates
= childrenStatesMap
.get(newTaxaCovered
);
306 if (newStates
==null) {
307 newStates
= new ArrayList
<State
>();
309 childrenStatesMap
.put(newTaxaCovered
,newStates
);
311 newStates
.add(featureState
);
314 return childrenStatesMap
;
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
);
334 return newCoveredTaxa
;
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();
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
;
354 if (!(feature
.getLabel()==null)){
355 // System.out.println(feature.getLabel() + bestScore);
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
){
364 for (i
=1;i
<nTaxons
;i
++){
365 score
= score
+ Math
.round((float)(i
+1/2));
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
));
376 if (feature
.isSupportsQuantitativeData()){
377 scoreMap
.put(feature
, quantitativeFeatureScore(feature
,coveredTaxa
, quantitativeFeaturesThresholds
));
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
);
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
);
402 if (type
.equals(StatisticalMeasure
.MIN()) || type
.equals(StatisticalMeasure
.TYPICAL_LOWER_BOUNDARY())) {
403 if (smv
.getValue()<=threshold
) taxaBefore
.add(td
);
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;
435 if (type
.equals(StatisticalMeasure
.MIN())) {
436 lowerboundary
= smv
.getValue();
437 lowerboundarypresent
=true;
439 if (type
.equals(StatisticalMeasure
.TYPICAL_UPPER_BOUNDARY()) && upperboundarypresent
==false) {
440 upperboundary
= smv
.getValue();
441 upperboundarypresent
=true;
443 if (type
.equals(StatisticalMeasure
.TYPICAL_LOWER_BOUNDARY()) && lowerboundarypresent
==false) {
444 lowerboundary
= smv
.getValue();
445 lowerboundarypresent
=true;
448 if (lowerboundarypresent
&& upperboundarypresent
) {
449 allValues
.add(lowerboundary
);
450 allValues
.add(upperboundary
);
458 float bestThreshold
=0;
459 int difference
=allValues
.size();
460 int differenceMin
= difference
;
463 for (i
=0;i
<allValues
.size()/2;i
++) {
464 threshold
= allValues
.get(i
*2+1);
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
++;
471 difference
= Math
.abs(taxaBefore
-taxaAfter
);
472 if (difference
<differenceMin
){
473 differenceMin
=difference
;
474 bestThreshold
= threshold
;
477 quantitativeFeaturesThresholds
.put(feature
, bestThreshold
);
478 int defaultQuantitativeScore
=0;
479 for (i
=0;i
<taxaBefore
;i
++) {
480 defaultQuantitativeScore
+= taxaAfter
- i
;
482 System
.out
.println(taxaBefore
+ ", " + taxaAfter
+ ", " +defaultQuantitativeScore
);
483 return (float)(defaultQuantitativeScore
);
486 private float featureScore(Feature feature
, Set
<TaxonDescription
> coveredTaxa
){
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
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
502 score
= score
+ defaultPower(deb1
,deb2
);
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 ?
512 if ((deb1
.isInstanceOf(CategoricalData
.class))&&(deb2
.isInstanceOf(CategoricalData
.class))) {
513 return defaultCategoricalPower((CategoricalData
)deb1
, (CategoricalData
)deb2
);
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
530 // modifiers not taken into account for this default power
532 // one point each time two taxa have at least a state in common for a given feature
537 private void printTree(List
<PolytomousKeyNode
> polytomousKeyNodes
, String spaces
){
538 if (! polytomousKeyNodes
.isEmpty()){
540 int levelcopy
= level
;
542 String newspaces
= spaces
.concat("\t");
543 for (PolytomousKeyNode polytomousKeyNode
: polytomousKeyNodes
){
544 if (polytomousKeyNode
.getQuestion() != null) {
546 if (polytomousKeyNode
.getStatement().getLabel(Language
.DEFAULT() ) != null){
547 state
= polytomousKeyNode
.getStatement().getLabelText(Language
.DEFAULT());
549 System
.out
.println(newspaces
+ levelcopy
+ " : " + j
+ " " + polytomousKeyNode
.getQuestion().getLabelText(Language
.DEFAULT()) + " = " + state
);
552 else { // TODO never read ?
553 if (polytomousKeyNode
.getStatement().getLabel(Language
.DEFAULT() ) != null){
554 System
.out
.println(newspaces
+ "-> " + polytomousKeyNode
.getStatement().getLabelText(Language
.DEFAULT()));
557 printTree(polytomousKeyNode
.getChildren(),newspaces
);