Project

General

Profile

Download (20.7 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
 * Copyright (C) 2007 EDIT
3
 * European Distributed Institute of Taxonomy
4
 * http://www.e-taxonomy.eu
5
 *
6
 * The contents of this file are subject to the Mozilla Public License Version 1.1
7
 * See LICENSE.TXT at the top of this package for the full license terms.
8
 */
9

    
10
package eu.etaxonomy.cdm.model.description;
11

    
12
import java.util.ArrayList;
13
import java.util.HashSet;
14
import java.util.List;
15
import java.util.Set;
16

    
17
import javax.persistence.Column;
18
import javax.persistence.Entity;
19
import javax.persistence.FetchType;
20
import javax.persistence.Index;
21
import javax.persistence.JoinColumn;
22
import javax.persistence.JoinTable;
23
import javax.persistence.ManyToMany;
24
import javax.persistence.ManyToOne;
25
import javax.persistence.OneToMany;
26
import javax.persistence.OrderBy;
27
import javax.persistence.OrderColumn;
28
import javax.persistence.Table;
29
import javax.persistence.Transient;
30
import javax.validation.constraints.NotNull;
31
import javax.xml.bind.annotation.XmlAccessType;
32
import javax.xml.bind.annotation.XmlAccessorType;
33
import javax.xml.bind.annotation.XmlAttribute;
34
import javax.xml.bind.annotation.XmlElement;
35
import javax.xml.bind.annotation.XmlElementWrapper;
36
import javax.xml.bind.annotation.XmlIDREF;
37
import javax.xml.bind.annotation.XmlRootElement;
38
import javax.xml.bind.annotation.XmlSchemaType;
39
import javax.xml.bind.annotation.XmlType;
40

    
41
import org.apache.log4j.Logger;
42
import org.hibernate.annotations.Cascade;
43
import org.hibernate.annotations.CascadeType;
44
import org.hibernate.annotations.Type;
45
import org.hibernate.envers.Audited;
46

    
47
import eu.etaxonomy.cdm.hibernate.HHH_9751_Util;
48
import eu.etaxonomy.cdm.model.common.DefinedTermBase;
49
import eu.etaxonomy.cdm.model.common.IHasTermType;
50
import eu.etaxonomy.cdm.model.common.ITreeNode;
51
import eu.etaxonomy.cdm.model.common.TermType;
52
import eu.etaxonomy.cdm.model.common.VersionableEntity;
53

    
54
/**
55
 * The class for tree nodes within a {@link FeatureTree feature tree} structure.
56
 * Feature nodes are the elementary components of such a tree since they might
57
 * be related to other nodes as a parent or as a child. A feature node belongs
58
 * at most to one feature tree. It cannot have more than one parent node but
59
 * may have several child nodes. Parent/child relations are bidirectional:
60
 * a node N1 is the parent of a node N2 if and only if the node N2 is a child of
61
 * the node N1.
62
 *
63
 * @author  m.doering
64
 * @since 08-Nov-2007 13:06:16
65
 */
66
@SuppressWarnings("serial")
67
@XmlAccessorType(XmlAccessType.FIELD)
68
@XmlType(name = "FeatureNode", propOrder = {
69
		"featureTree",
70
		"termType",
71
		"feature",
72
		"parent",
73
		"treeIndex",
74
		"sortIndex",
75
		"children",
76
		"onlyApplicableIf",
77
		"inapplicableIf"
78
})
79
@XmlRootElement(name = "FeatureNode")
80
@Entity
81
@Audited
82
@Table(name="FeatureNode", indexes = { @Index(name = "featureNodeTreeIndex", columnList = "treeIndex") })
83
public class FeatureNode <T extends DefinedTermBase> extends VersionableEntity
84
            implements ITreeNode<FeatureNode<T>>, IHasTermType, Cloneable {
85
	private static final Logger logger = Logger.getLogger(FeatureNode.class);
86

    
87
    //This is the main key a node belongs to. Although other keys may also reference
88
	//<code>this</code> node, a node usually belongs to a given key.
89
	@XmlElement(name = "FeatureTree")
90
    @XmlIDREF
91
    @XmlSchemaType(name = "IDREF")
92
    @ManyToOne(fetch = FetchType.LAZY, targetEntity=FeatureTree.class)
93
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE}) //TODO this usage is incorrect, needed only for OneToMany, check why it is here, can it be removed??
94
	 //TODO Val #3379
95
//    @NotNull
96
	private FeatureTree<T> featureTree;
97

    
98
    /**
99
     * The {@link TermType type} of this term node.
100
     * Must be the same type as for the {@link FeatureTree term collection}
101
     * this node belongs to and as the term type of the term this node links to.
102
     */
103
    @XmlAttribute(name ="TermType")
104
    @Column(name="termType")
105
    @NotNull
106
    @Type(type = "eu.etaxonomy.cdm.hibernate.EnumUserType",
107
        parameters = {@org.hibernate.annotations.Parameter(name  = "enumClass", value = "eu.etaxonomy.cdm.model.common.TermType")}
108
    )
109
    @Audited
110
    private TermType termType;
111

    
112
    @XmlElement(name = "Feature")
113
    @XmlIDREF
114
    @XmlSchemaType(name = "IDREF")
115
    @ManyToOne(fetch = FetchType.LAZY, targetEntity=DefinedTermBase.class)
116
	private T feature;
117

    
118
    @XmlElement(name = "Parent")
119
    @XmlIDREF
120
    @XmlSchemaType(name = "IDREF")
121
    @ManyToOne(fetch = FetchType.LAZY, targetEntity=FeatureNode.class)
122
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
123
	@JoinColumn(name="parent_id")
124
	private FeatureNode<T> parent;
125

    
126

    
127
    @XmlElement(name = "treeIndex")
128
    @Column(length=255)
129
    private String treeIndex;
130

    
131
    @XmlElementWrapper(name = "Children")
132
    @XmlElement(name = "Child")
133
    //see https://dev.e-taxonomy.eu/trac/ticket/3722
134
    @OrderColumn(name="sortIndex")
135
    @OrderBy("sortIndex")
136
	@OneToMany(fetch = FetchType.LAZY, mappedBy="parent", targetEntity=FeatureNode.class)
137
	@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
138
	private List<FeatureNode<T>> children = new ArrayList<>();
139

    
140
    //see https://dev.e-taxonomy.eu/trac/ticket/3722
141
    private Integer sortIndex;
142

    
143
	@XmlElementWrapper(name = "OnlyApplicableIf")
144
	@XmlElement(name = "OnlyApplicableIf")
145
	@XmlIDREF
146
	@XmlSchemaType(name="IDREF")
147
	@ManyToMany(fetch = FetchType.LAZY)
148
//	@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})  remove cascade #5755
149
	@JoinTable(name="FeatureNode_DefinedTermBase_OnlyApplicable")
150
	private final Set<State> onlyApplicableIf = new HashSet<>();
151

    
152
	@XmlElementWrapper(name = "InapplicableIf")
153
	@XmlElement(name = "InapplicableIf")
154
	@XmlIDREF
155
	@XmlSchemaType(name="IDREF")
156
	@ManyToMany(fetch = FetchType.LAZY)
157
//	@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})  remove cascade #5755
158
	@JoinTable(name="FeatureNode_DefinedTermBase_InapplicableIf")
159
	private final Set<State> inapplicableIf = new HashSet<>();
160

    
161
// ***************************** FACTORY *********************************/
162

    
163
	/**
164
     * Creates a new empty term node instance.
165
     *
166
     * @see #NewInstance(Feature)
167
     */
168
    public static <T extends DefinedTermBase<T>> FeatureNode<T> NewInstance(TermType termType){
169
        return new FeatureNode<>(termType);
170
    }
171

    
172
	/**
173
	 * Creates a new empty feature node instance.
174
	 *
175
	 * @see #NewInstance(Feature)
176
	 */
177
	public static FeatureNode<Feature> NewInstance(){
178
		return new FeatureNode<>(TermType.Feature);
179
	}
180

    
181
	/**
182
	 * Creates a new feature node instance only with the given {@link DefinedTermBase term}
183
	 * (without parent and children). The termType of the feature node is the termType of
184
	 * the given term
185
	 *
186
	 * @param	term	the term assigned to the new feature node
187
	 * @see 			#NewInstance()
188
	 */
189
	public static <T extends DefinedTermBase<T>> FeatureNode<T> NewInstance(T term){
190
	    FeatureNode<T> result = new FeatureNode<>(term.getTermType());
191
	    result.setTerm(term);
192
	    return result;
193
	}
194

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

    
197
	//TODO needed?
198
    @Deprecated
199
    protected FeatureNode(){}
200

    
201
	/**
202
	 * Class constructor: creates a new empty feature node instance.
203
	 */
204
	protected FeatureNode(TermType termType) {
205
	    this.termType = termType;
206
	    IHasTermType.checkTermTypeNull(this);
207
	}
208

    
209
    @Override
210
    public TermType getTermType() {
211
        return termType;
212
    }
213

    
214
//*************************** TREE ************************************/
215

    
216
	public FeatureTree<T> getFeatureTree() {
217
		return featureTree;
218
	}
219

    
220
	protected void setFeatureTree(FeatureTree<T> featureTree) {
221
		checkTermType(featureTree);
222
	    this.featureTree = featureTree;
223
	}
224

    
225
//** ********************** FEATURE ******************************/
226

    
227
	/**
228
     * Returns the {@link Feature feature} <i>this</i> feature node is based on.
229
     */
230
    public T getTerm() {
231
        return feature;
232
    }
233
    /**
234
     * @see #getFeature()
235
     */
236
    public void setTerm(T term) {
237
        checkTermType(term);
238
        this.feature = term;
239
    }
240

    
241
//** ********************** PARENT ******************************/
242

    
243
	/**
244
	 * Returns the feature node <i>this</i> feature node is a child of.
245
	 *
246
	 * @see	#getChildNodes()
247
	 */
248
	@Override
249
    public FeatureNode<T> getParent() {
250
		return parent;
251
	}
252
	/**
253
	 * Assigns the given feature node as the parent of <i>this</i> feature node.
254
	 * Due to bidirectionality this method must also add <i>this</i> feature node
255
	 * to the list of children of the given parent.
256
	 *
257
	 * @param	parent	the feature node to be set as parent
258
	 * @see				#getParent()
259
	 */
260
	protected void setParent(FeatureNode<T> parent) {
261
		checkTermType(parent);
262
	    this.parent = parent;
263
	}
264

    
265
//** ********************** CHILDREN ******************************/
266

    
267

    
268
	/**
269
	 * @deprecated for internal use only.
270
	 */
271
	//see #4278 , #4200
272
	@Deprecated
273
    protected void setSortIndex(Integer sortIndex) {
274
		this.sortIndex = sortIndex;
275
	}
276

    
277
	/**
278
	 * Returns the (ordered) list of feature nodes which are children nodes of
279
	 * <i>this</i> feature node.
280
	 */
281
	@Override
282
    public List<FeatureNode<T>> getChildNodes() {
283
	    return children;
284
	}
285

    
286
	/**
287
	 * Adds the given feature node at the end of the list of children of
288
	 * <i>this</i> feature node. Due to bidirectionality this method must also
289
	 * assign <i>this</i> feature node as the parent of the given child.
290
	 *
291
	 * @param	child	the feature node to be added
292
	 * @see				#getChildNodes()
293
	 * @see				#setChildren(List)
294
	 * @see				#addChild(FeatureNode, int)
295
	 * @see				#removeChild(FeatureNode)
296
	 * @see				#removeChild(int)
297
	 */
298
	public void addChild(FeatureNode<T> child){
299
		addChild(child, children.size());
300
	}
301
	/**
302
	 * Inserts the given feature node in the list of children of <i>this</i> feature node
303
	 * at the given (index + 1) position. If the given index is out of bounds
304
	 * an exception will arise.<BR>
305
	 * Due to bidirectionality this method must also assign <i>this</i> feature node
306
	 * as the parent of the given child.
307
	 *
308
	 * @param	child	the feature node to be added
309
	 * @param	index	the integer indicating the position at which the child
310
	 * 					should be added
311
	 * @see				#getChildNodes()
312
	 * @see				#setChildren(List)
313
	 * @see				#addChild(FeatureNode)
314
	 * @see				#removeChild(FeatureNode)
315
	 * @see				#removeChild(int)
316
	 */
317
	public void addChild(FeatureNode<T> child, int index){
318
	    checkTermType(child);
319
	    List<FeatureNode<T>> children = this.getChildNodes();
320
		if (index < 0 || index > children.size() + 1){
321
			throw new IndexOutOfBoundsException("Wrong index: " + index);
322
		}
323
		if (child.getParent() != null){
324
			child.getParent().removeChild(child);
325
		}
326
		child.setParent(this);
327
		child.setFeatureTree(this.getFeatureTree());
328
		children.add(index, child);
329
		//TODO workaround (see sortIndex doc)
330
		for(int i = 0; i < children.size(); i++){
331
			children.get(i).setSortIndex(i);
332
		}
333
		child.setSortIndex(index);
334
	}
335

    
336

    
337
    /**
338
	 * Removes the given feature node from the list of {@link #getChildNodes() children}
339
	 * of <i>this</i> feature node.
340
	 *
341
	 * @param  child	the feature node which should be removed
342
	 * @see     		#getChildNodes()
343
	 * @see				#addChild(FeatureNode, int)
344
	 * @see				#addChild(FeatureNode)
345
	 * @see				#removeChild(int)
346
	 */
347
	public void removeChild(FeatureNode<T> child){
348

    
349
	    int index = children.indexOf(child);
350
		if (index >= 0){
351
			removeChild(index);
352
		}
353
	}
354
	/**
355
	 * Removes the feature node placed at the given (index + 1) position from
356
	 * the list of {@link #getChildNodes() children} of <i>this</i> feature node.
357
	 * If the given index is out of bounds no child will be removed.
358
	 *
359
	 * @param  index	the integer indicating the position of the feature node to
360
	 * 					be removed
361
	 * @see     		#getChildNodes()
362
	 * @see				#addChild(FeatureNode, int)
363
	 * @see				#addChild(FeatureNode)
364
	 * @see				#removeChild(FeatureNode)
365
	 */
366
	public void removeChild(int index){
367
	   FeatureNode<T> child = children.get(index);
368
	   if (child != null){
369
			children.remove(index);
370
			child.setParent(null);
371
			child.setFeatureTree(null);
372
			//TODO workaround (see sortIndex doc)
373
			for(int i = 0; i < children.size(); i++){
374
				FeatureNode<T> childAt = children.get(i);
375
				childAt.setSortIndex(i);
376
			}
377
			child.setSortIndex(null);
378
		}
379
	}
380

    
381
	/**
382
	 * Returns the feature node placed at the given (childIndex + 1) position
383
	 * within the list of {@link #getChildNodes() children} of <i>this</i> feature node.
384
	 * If the given index is out of bounds no child will be returned.
385
	 *
386
	 * @param  childIndex	the integer indicating the position of the feature node
387
	 * @see     			#getChildNodes()
388
	 * @see					#addChild(FeatureNode, int)
389
	 * @see					#removeChild(int)
390
	 */
391
	public FeatureNode<T> getChildAt(int childIndex) {
392
	    return children.get(childIndex);
393
	}
394

    
395
	/**
396
	 * Returns the number of children nodes of <i>this</i> feature node.
397
	 *
398
	 * @see	#getChildNodes()
399
	 */
400
	@Transient
401
	public int getChildCount() {
402
		return children.size();
403
	}
404

    
405
	/**
406
	 * Returns the integer indicating the position of the given feature node
407
	 * within the list of {@link #getChildNodes() children} of <i>this</i> feature node.
408
	 * If the list does not contain this node then -1 will be returned.
409
	 *
410
	 * @param  node	the feature node the position of which is being searched
411
	 * @see			#addChild(FeatureNode, int)
412
	 * @see			#removeChild(int)
413
	 */
414
	public int getIndex(FeatureNode<T> node) {
415
	    if (! children.contains(node)){
416
			return -1;
417
		}else{
418
			return children.indexOf(node);
419
		}
420
	}
421

    
422
	/**
423
	 * Returns the boolean value indicating if <i>this</i> feature node has
424
	 * children (false) or not (true). A node without children is at the
425
	 * bottommost level of a tree and is called a leaf.
426
	 *
427
	 * @see	#getChildNodes()
428
	 * @see	#getChildCount()
429
	 */
430
	@Transient
431
	public boolean isLeaf() {
432
		return children.size() < 1;
433
	}
434

    
435
	/**
436
	 * Whether <code>this</code> node is the root node of the associated {@link FeatureTree feature tree}.
437
	 *
438
	 * @return <code>true</code> if <code>this</code> is the feature trees root node, <code>false</code> if not
439
	 */
440
	@Transient
441
	public boolean isRoot(){
442
		if(getFeatureTree() != null){
443
			return this.equals(getFeatureTree().getRoot());
444
		}
445
		return false;
446
	}
447

    
448
	/**
449
	 * Returns the set of {@link State states} implying rendering the
450
	 * concerned {@link Feature feature} applicable.
451
	 * If at least one state is present in this set, in a given description
452
	 * the {@link Feature feature} in <i>this</i> feature node is inapplicable
453
	 * unless any of the listed controlling states is present in the parent
454
	 * {@link Feature feature} description element {@link CategoricalData
455
	 * categoricalData}.
456
	 * This attribute is not equivalent to onlyApplicableIf in SDD as it is
457
	 * attached directly to the child feature rather than the parent, which
458
	 * allow having different applicable states for each child feature.
459
	 *
460
	 * @see    #addApplicableState(State)
461
	 * @see    #removeApplicableState(State)
462
	 */
463
	public Set<State> getOnlyApplicableIf() {
464
		return onlyApplicableIf;
465
	}
466

    
467
	/**
468
	 * Adds an existing {@link State applicable state} to the set of
469
	 * {@link #getOnlyApplicableIf() applicable states} described in
470
	 * <i>this</i> feature node.<BR>
471
	 *
472
	 * @param applicableState	the applicable state to be added to <i>this</i> feature node
473
	 * @see    	   								#getApplicableState()
474
	 */
475
	public void addApplicableState(State applicableState) {
476
		this.onlyApplicableIf.add(applicableState);
477
	}
478

    
479
	/**
480
	 * Removes one element from the set of
481
	 * {@link #getOnlyApplicableIf() applicable states} described in
482
	 * <i>this</i> feature node.<BR>
483
	 *
484
	 * @param  applicableState   the applicable state which should be removed
485
	 * @see    	   								#getApplicableState()
486
	 * @see     		  						#addApplicableState(State)
487
	 */
488
	public void removeApplicableState(State applicableState) {
489
		this.onlyApplicableIf.remove(applicableState);
490
	}
491

    
492
	/**
493
	 * Returns the set of {@link State states} implying rendering the
494
	 * concerned {@link Feature feature} inapplicable.
495
	 * If at least one {@link State inapplicable state} is defined in the set,
496
	 * in a given description the {@link Feature feature} attribute of
497
	 * <i>this</i> feature node is inapplicable when any of the listed
498
	 * controlling states is present.
499
	 * This attribute is not equivalent to inapplicableIf in SDD as it is
500
	 * attached directly to the child feature rather than the parent, which
501
	 * allow having different inapplicability rules for each child feature.
502
	 *
503
	 * @see    #addInapplicableState(State)
504
	 * @see    #removeInapplicableState(State)
505
	 */
506
	public Set<State> getInapplicableIf() {
507
		return inapplicableIf;
508
	}
509

    
510
	/**
511
	 * Adds an existing {@link State inapplicable state} to the set of
512
	 * {@link #getInapplicableIf() inapplicable states} described in
513
	 * <i>this</i> feature node.<BR>
514
	 *
515
	 * @param inapplicableState	the inapplicable state to be added to <i>this</i> feature node
516
	 * @see    	   								#getInapplicableState()
517
	 */
518
	public void addInapplicableState(State inapplicableState) {
519
		this.inapplicableIf.add(inapplicableState);
520
	}
521

    
522
	/**
523
	 * Removes one element from the set of
524
	 * {@link #getInapplicableIf() inapplicable states} described in
525
	 * <i>this</i> feature node.<BR>
526
	 *
527
	 * @param  inapplicableState   the inapplicable state which should be removed
528
	 * @see    	   								#getInapplicableState()
529
	 * @see     		  						#addInapplicableState(State)
530
	 */
531
	public void removeInapplicableState(State inapplicableState) {
532
		this.inapplicableIf.remove(inapplicableState);
533
	}
534

    
535
//	//** ********************** QUESTIONS ******************************/
536
//
537
//	/**
538
//	 * Returns the {@link Representation question} formulation that
539
//	 * corresponds to <i>this</i> feature node and the corresponding
540
//	 * {@link Feature feature} in case it is part of a
541
//	 * {@link PolytomousKey polytomous key}.
542
//	 */
543
//	public Set<Representation> getQuestions() {
544
//		return this.questions;
545
//	}
546
//
547
//	public void addQuestion(Representation question) {
548
//		this.questions.add(question);
549
//	}
550
//
551
//	public void removeQuestion(Representation question) {
552
//		this.questions.remove(question);
553
//	}
554
//
555
//	@Transient
556
//	public Representation getQuestion(Language lang) {
557
//		for (Representation question : questions){
558
//			Language reprLanguage = question.getLanguage();
559
//			if (reprLanguage != null && reprLanguage.equals(lang)){
560
//				return question;
561
//			}
562
//		}
563
//		return null;
564
//	}
565

    
566
	/**
567
     * Throws {@link IllegalArgumentException} if the given
568
     * term has not the same term type as this term or if term type is null.
569
     * @param term
570
     */
571
    private void checkTermType(IHasTermType term) {
572
        IHasTermType.checkTermTypes(term, this);
573
    }
574

    
575
	/**
576
	 * Returns all terms that are contained in this node or a child node
577
	 *
578
	 * @param featureNode
579
	 * @param features
580
	 * @return
581
	 */
582
	@Transient
583
	public Set<T> getDistinctFeaturesRecursive(Set<T> features){
584
		T term = this.getTerm();
585

    
586
		if(term!=null){
587
		    features.add(term);
588
		}
589

    
590
		for(FeatureNode<T> childNode : this.getChildNodes()){
591
			features.addAll(childNode.getDistinctFeaturesRecursive(features));
592
		}
593

    
594
		return features;
595
	}
596

    
597
	public FeatureNode<T> cloneDescendants(){
598
		FeatureNode<T> clone = (FeatureNode<T>)this.clone();
599
		FeatureNode<T> childClone;
600

    
601
		for(FeatureNode<T> childNode : this.getChildNodes()){
602
			childClone = (FeatureNode<T>) childNode.clone();
603
			for (FeatureNode<T> childChild:childNode.getChildNodes()){
604
				childClone.addChild(childChild.cloneDescendants());
605
			}
606
			clone.addChild(childClone);
607

    
608
		}
609
		return clone;
610
	}
611

    
612
//*********************** CLONE ********************************************************/
613

    
614
	/**
615
	 * Clones <i>this</i> FeatureNode. This is a shortcut that enables to create
616
	 * a new instance that differs only slightly from <i>this</i> FeatureNode by
617
	 * modifying only some of the attributes.
618
	 * The parent, the feature and the featureTree are the are the same as for the original feature node
619
	 * the children are removed
620
	 *
621
	 * @see eu.etaxonomy.cdm.model.common.VersionableEntity#clone()
622
	 * @see java.lang.Object#clone()
623
	 */
624
	@Override
625
	public Object clone() {
626
		FeatureNode<T> result;
627
		try {
628
			result = (FeatureNode<T>)super.clone();
629
			result.children = new ArrayList<>();
630
			return result;
631
		}catch (CloneNotSupportedException e) {
632
			logger.warn("Object does not implement cloneable");
633
			e.printStackTrace();
634
			return null;
635
		}
636

    
637

    
638

    
639
	}
640

    
641
// ********************** TREE NODE METHODS ******************************/
642

    
643
	@Override
644
	public String treeIndex() {
645
		return this.treeIndex;
646
	}    @Override
647
    public String treeIndexLike() {
648
        return treeIndex + "%";
649
    }
650
    @Override
651
    public String treeIndexWc() {
652
        return treeIndex + "*";
653
    }
654

    
655
	@Override
656
	@Deprecated
657
	public void setTreeIndex(String newTreeIndex) {
658
		this.treeIndex = newTreeIndex;
659
	}
660

    
661

    
662
	@Override
663
	@Deprecated
664
	public int treeId() {
665
		if (this.featureTree == null){
666
			return -1;
667
		}else{
668
			return this.featureTree.getId();
669
		}
670
	}
671

    
672
	private void updateSortIndex(){
673
	 // TODO workaround (see sortIndex doc)
674
        for (int i = 0; i < children.size(); i++) {
675
            children.get(i).setSortIndex(i);
676
        }
677
	}
678

    
679
	public void removeNullValueFromChildren(){
680
	    HHH_9751_Util.removeAllNull(children);
681
	    updateSortIndex();
682
	}
683

    
684

    
685
}
(11-11/37)