Project

General

Profile

Download (22.8 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.term;
11

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

    
18
import javax.persistence.Column;
19
import javax.persistence.Entity;
20
import javax.persistence.FetchType;
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.Transient;
29
import javax.xml.bind.annotation.XmlAccessType;
30
import javax.xml.bind.annotation.XmlAccessorType;
31
import javax.xml.bind.annotation.XmlElement;
32
import javax.xml.bind.annotation.XmlElementWrapper;
33
import javax.xml.bind.annotation.XmlIDREF;
34
import javax.xml.bind.annotation.XmlRootElement;
35
import javax.xml.bind.annotation.XmlSchemaType;
36
import javax.xml.bind.annotation.XmlTransient;
37
import javax.xml.bind.annotation.XmlType;
38

    
39
import org.apache.log4j.Logger;
40
import org.hibernate.annotations.Cascade;
41
import org.hibernate.annotations.CascadeType;
42
import org.hibernate.envers.Audited;
43

    
44
import eu.etaxonomy.cdm.hibernate.HHH_9751_Util;
45
import eu.etaxonomy.cdm.model.common.ITreeNode;
46
import eu.etaxonomy.cdm.model.description.CategoricalData;
47
import eu.etaxonomy.cdm.model.description.Feature;
48
import eu.etaxonomy.cdm.model.description.FeatureState;
49
import eu.etaxonomy.cdm.model.description.State;
50

    
51
/**
52
 * The class for tree nodes within a {@link TermTree feature tree} structure.
53
 * Feature nodes are the elementary components of such a tree since they might
54
 * be related to other nodes as a parent or as a child. A feature node belongs
55
 * at most to one feature tree. It cannot have more than one parent node but
56
 * may have several child nodes. Parent/child relations are bidirectional:
57
 * a node N1 is the parent of a node N2 if and only if the node N2 is a child of
58
 * the node N1.
59
 *
60
 * @author  m.doering
61
 * @since 08-Nov-2007 13:06:16
62
 */
63
@SuppressWarnings("serial")
64
@XmlAccessorType(XmlAccessType.FIELD)
65
@XmlType(name = "TermNode", propOrder = {
66
		"parent",
67
		"treeIndex",
68
		"sortIndex",
69
		"children",
70
		"onlyApplicableIf",
71
		"inapplicableIf",
72
		"onlyApplicableIf_old",
73
		"inapplicableIf_old"
74
})
75
@XmlRootElement(name = "TermNode")
76
@Entity
77
@Audited
78
public class TermNode <T extends DefinedTermBase>
79
            extends TermRelationBase<T, TermNode<T>, TermTree>
80
            implements ITreeNode<TermNode<T>> {
81

    
82
    private static final Logger logger = Logger.getLogger(TermNode.class);
83

    
84
    @XmlElement(name = "Parent")
85
    @XmlIDREF
86
    @XmlSchemaType(name = "IDREF")
87
    @ManyToOne(fetch = FetchType.LAZY, targetEntity=TermNode.class)
88
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
89
	@JoinColumn(name="parent_id")
90
	private TermNode<T> parent;
91

    
92

    
93
    @XmlElement(name = "treeIndex")
94
    @Column(length=255)
95
    private String treeIndex;
96

    
97
    @XmlElementWrapper(name = "Children")
98
    @XmlElement(name = "Child")
99
    //see https://dev.e-taxonomy.eu/trac/ticket/3722
100
    @OrderColumn(name="sortIndex")
101
    @OrderBy("sortIndex")
102
	@OneToMany(fetch = FetchType.LAZY, mappedBy="parent", targetEntity=TermNode.class)
103
	@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
104
	private List<TermNode<T>> children = new ArrayList<>();
105

    
106
    //see https://dev.e-taxonomy.eu/trac/ticket/3722
107
    private Integer sortIndex;
108

    
109
	@XmlElementWrapper(name = "OnlyApplicableIf_old")
110
	@XmlElement(name = "OnlyApplicableIf")
111
	@XmlIDREF
112
	@XmlSchemaType(name="IDREF")
113
	@ManyToMany(fetch = FetchType.LAZY)
114
//	@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})  remove cascade #5755
115
	@JoinTable(name="TermNode_DefinedTermBase_OnlyApplicable")
116
	private final Set<State> onlyApplicableIf_old = new HashSet<>();
117

    
118
	@XmlElementWrapper(name = "InapplicableIf_old")
119
	@XmlElement(name = "InapplicableIf")
120
	@XmlIDREF
121
	@XmlSchemaType(name="IDREF")
122
	@ManyToMany(fetch = FetchType.LAZY)
123
//	@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})  remove cascade #5755
124
	@JoinTable(name="TermNode_DefinedTermBase_InapplicableIf")
125
	private final Set<State> inapplicableIf_old = new HashSet<>();
126

    
127
    @XmlElementWrapper(name = "OnlyApplicableIf")
128
    @XmlElement(name = "OnlyApplicableIf")
129
    @XmlIDREF
130
    @XmlSchemaType(name="IDREF")
131
    @OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
132
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
133
    @JoinTable(name="TermNode_OnlyApplicableIf")
134
    private final Set<FeatureState> onlyApplicableIf = new HashSet<>();
135

    
136
    @XmlElementWrapper(name = "InapplicableIf")
137
    @XmlElement(name = "InapplicableIf")
138
    @XmlIDREF
139
    @XmlSchemaType(name="IDREF")
140
    @OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
141
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
142
    @JoinTable(name="TermNode_InapplicableIf")
143
    private final Set<FeatureState> inapplicableIf = new HashSet<>();
144

    
145
// ***************************** FACTORY *********************************/
146

    
147
	//no factory methods should be provided as TermNodes should only
148
	//be created as children of their parent node (#8257)
149

    
150
// ******************** CONSTRUCTOR ***************************************/
151

    
152
	//TODO needed?
153
    @Deprecated
154
    protected TermNode(){}
155

    
156
	/**
157
	 * Class constructor: creates a new empty feature node instance.
158
	 */
159
	protected TermNode(TermType termType) {
160
	    super(termType);
161
	}
162

    
163
//************************* PARENT ******************************/
164

    
165
	/**
166
	 * Returns the feature node <i>this</i> feature node is a child of.
167
	 *
168
	 * @see	#getChildNodes()
169
	 */
170
	@Override
171
    public TermNode<T> getParent() {
172
		return parent;
173
	}
174
	/**
175
	 * Assigns the given feature node as the parent of <i>this</i> feature node.
176
	 * Due to bidirectionality this method must also add <i>this</i> feature node
177
	 * to the list of children of the given parent.
178
	 *
179
	 * @param	parent	the feature node to be set as parent
180
	 * @see				#getParent()
181
	 */
182
	protected void setParent(TermNode<T> parent) {
183
	    this.parent = parent;
184
	}
185

    
186
//** ********************** CHILDREN ******************************/
187

    
188

    
189
	/**
190
	 * @deprecated for internal use only.
191
	 */
192
	//see #4278 , #4200
193
	@Deprecated
194
    protected void setSortIndex(Integer sortIndex) {
195
		this.sortIndex = sortIndex;
196
	}
197

    
198
	/**
199
     * Returns the (ordered) list of tree nodes which are children nodes of
200
     * <i>this</i> node.
201
	 */
202
	@Override
203
    public List<TermNode<T>> getChildNodes() {
204
	    return children;
205
	}
206

    
207
	/**
208
     * Adds the given term node at the end of the list of children of
209
     * <i>this</i> term node. Due to bidirectionality this method must
210
	 * also assign <i>this</i> feature node as the parent of the given child.
211
	 *
212
	 * @param	child	the feature node to be added
213
	 * @see				#getChildNodes()
214
	 * @see				#setChildren(List)
215
	 * @see				#addChild(TermNode, int)
216
	 * @see				#removeChild(TermNode)
217
	 * @see				#removeChild(int)
218
	 */
219
	public TermNode<T> addChild(TermNode<T> child){
220
		return addChild(child, children.size());
221
	}
222

    
223
	/**
224
     * Creates a new node without a term and adds it to the end of
225
     * the list of children of
226
     * <i>this</i> node. Due to bidirectionality this method must also
227
     * assign <i>this</i> feature node as the parent of the new child.
228
     *
229
     * @return the newly created child node
230
     * @see             #getChildNodes()
231
     * @see             #setChildren(List)
232
     * @see             #removeChild(TermNode)
233
     * @see             #removeChild(int)
234
     */
235
    public TermNode<T> addChild(){
236
        return addChild((T)null, children.size());
237
    }
238

    
239
	/**
240
	 * Creates a new node for the given term and adds it to the end of
241
	 * the list of children of
242
	 * <i>this</i> node. Due to bidirectionality this method must also
243
	 * assign <i>this</i> feature node as the parent of the new child.
244
	 *
245
	 * @param	term	the term to be added
246
	 * @return the newly created child node
247
	 * @see				#getChildNodes()
248
	 * @see				#setChildren(List)
249
	 * @see				#removeChild(TermNode)
250
	 * @see				#removeChild(int)
251
	 */
252
	public TermNode<T> addChild(T term){
253
	    return addChild(term, children.size());
254
	}
255

    
256
    /**
257
     * Creates a new node for the given term and adds it at the
258
     * given (index + 1) position of the list of children of
259
     * <i>this</i> node. Due to bidirectionality this method must also
260
     * assign <i>this</i> feature node as the parent of the new child.
261
     *
262
     * @param   term    the term to be added
263
     * @return the newly created child node
264
     * @see             #getChildNodes()
265
     * @see             #setChildren(List)
266
     * @see             #removeChild(TermNode)
267
     * @see             #removeChild(int)
268
     */
269
	public TermNode<T> addChild(T term, int index){
270
	    TermNode<T> child = new TermNode<>(getTermType());
271
	    if(term!=null){
272
	        child.setTerm(term);
273
	    }
274
	    checkTermType(child);
275

    
276
	    List<TermNode<T>> children = this.getChildNodes();
277
	    if (index < 0 || index > children.size() + 1){
278
	        throw new IndexOutOfBoundsException("Wrong index: " + index);
279
	    }
280
	    child.setParent(this);
281
	    child.setGraph(this.getGraph());
282
	    children.add(index, child);
283
	    //TODO workaround (see sortIndex doc)
284
	    for(int i = 0; i < children.size(); i++){
285
	        children.get(i).setSortIndex(i);
286
	    }
287
	    child.setSortIndex(index);
288
	    return child;
289
	}
290

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

    
327

    
328
    /**
329
	 * Removes the given feature node from the list of {@link #getChildNodes() children}
330
	 * of <i>this</i> feature node.
331
	 *
332
	 * @param  child	the feature node which should be removed
333
	 * @see     		#getChildNodes()
334
	 * @see				#addChild(TermNode, int)
335
	 * @see				#addChild(TermNode)
336
	 * @see				#removeChild(int)
337
	 */
338
	public void removeChild(TermNode<T> child){
339

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

    
372
	/**
373
	 * Returns the feature node placed at the given (childIndex + 1) position
374
	 * within the list of {@link #getChildNodes() children} of <i>this</i> feature node.
375
	 * If the given index is out of bounds no child will be returned.
376
	 *
377
	 * @param  childIndex	the integer indicating the position of the feature node
378
	 * @see     			#getChildNodes()
379
	 * @see					#addChild(TermNode, int)
380
	 * @see					#removeChild(int)
381
	 */
382
	public TermNode<T> getChildAt(int childIndex) {
383
	    return children.get(childIndex);
384
	}
385

    
386
	/**
387
	 * Returns the number of children nodes of <i>this</i> feature node.
388
	 *
389
	 * @see	#getChildNodes()
390
	 */
391
	@Transient
392
	public int getChildCount() {
393
		return children.size();
394
	}
395

    
396
	/**
397
	 * Returns the integer indicating the position of the given feature node
398
	 * within the list of {@link #getChildNodes() children} of <i>this</i> feature node.
399
	 * If the list does not contain this node then -1 will be returned.
400
	 *
401
	 * @param  node	the feature node the position of which is being searched
402
	 * @see			#addChild(TermNode, int)
403
	 * @see			#removeChild(int)
404
	 */
405
	public int getIndex(TermNode<T> node) {
406
	    if (! children.contains(node)){
407
			return -1;
408
		}else{
409
			return children.indexOf(node);
410
		}
411
	}
412

    
413
	/**
414
	 * Returns the boolean value indicating if <i>this</i> feature node has
415
	 * children (false) or not (true). A node without children is at the
416
	 * bottommost level of a tree and is called a leaf.
417
	 *
418
	 * @see	#getChildNodes()
419
	 * @see	#getChildCount()
420
	 */
421
	@Transient
422
	public boolean isLeaf() {
423
		return children.size() < 1;
424
	}
425

    
426
	/**
427
	 * Whether <code>this</code> node is the root node of the associated {@link TermTree feature tree}.
428
	 *
429
	 * @return <code>true</code> if <code>this</code> is the feature trees root node, <code>false</code> if not
430
	 */
431
	@Transient
432
	public boolean isRoot(){
433
		if(getGraph() != null){
434
			return this.equals(getGraph().getRoot());
435
		}
436
		return false;
437
	}
438

    
439
// *************************** APPLICABLE IF ********************************/
440

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

    
460
	/**
461
	 * Adds an existing {@link FeatureState applicable state} to the set of
462
	 * {@link #getOnlyApplicableIf() applicable states} described in
463
	 * <i>this</i> feature node.<BR>
464
	 *
465
	 * @param applicableState	the applicable state to be added to <i>this</i> feature node
466
	 * @see #getOnlyApplicableIf()
467
	 */
468
	public void addApplicableState(FeatureState applicableState) {
469
		this.onlyApplicableIf.add(applicableState);
470
	}
471
    public FeatureState addApplicableState(Feature feature, State applicableState) {
472
        FeatureState featureState = FeatureState.NewInstance(feature, applicableState);
473
        addApplicableState(featureState);
474
        return featureState;
475
    }
476

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

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

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

    
520
    public FeatureState addInapplicableState(Feature feature, State inapplicableState) {
521
        FeatureState featureState = FeatureState.NewInstance(feature, inapplicableState);
522
        addInapplicableState(featureState);
523
        return featureState;
524
    }
525

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

    
536
	public void removeInapplicableState(FeatureState inapplicableState) {
537
		this.inapplicableIf.remove(inapplicableState);
538
	}
539

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

    
571

    
572
//*********************** Terms ************************************/
573

    
574
	/**
575
	 * Returns all terms that are contained in this node or a child node
576
	 *
577
	 * @param terms
578
	 * @return
579
	 */
580
	//TODO do we need to pass the terms parameter? Maybe a bit more performant
581
	// but more difficult to handle. We could use this internally but offer
582
	//the method with return value as public
583
	@Transient
584
	public Set<T> getDistinctTermsRecursive(Set<T> terms){
585
		T term = this.getTerm();
586
		if(term != null){
587
		    terms.add(term);
588
		}
589
		for(TermNode<T> childNode : this.getChildNodes()){
590
			terms.addAll(childNode.getDistinctTermsRecursive(terms));
591
		}
592
		return terms;
593
	}
594

    
595
    /**
596
     * Returns all terms that are contained in this node or a child node
597
     * as long as this node or the child nodes are not {@link #isDependend() dependend}
598
     * on higher nodes/feature states.
599
     * @param terms
600
     * @return
601
     */
602
    @Transient
603
    public Set<T> getIndependentTermsRecursive(){
604
        Set<T> terms = new HashSet<>();
605
        if (!isDependend()){
606
            T term = this.getTerm();
607
            if(term != null){
608
                terms.add(term);
609
            }
610
            for(TermNode<T> childNode : this.getChildNodes()){
611
                terms.addAll(childNode.getIndependentTermsRecursive());
612
            }
613
        }
614
        return terms;
615
    }
616

    
617

    
618

    
619
    /**
620
     * @return <code>true</code> if any of the sets {@link #getInapplicableIf() inapplicableIf}
621
     * and {@link #getOnlyApplicableIf() onlyApplicableIf} are not empty
622
     */
623
    @Transient
624
    @XmlTransient
625
    public boolean isDependend() {
626
        return inapplicableIf.size()>0 || onlyApplicableIf.size()>0;
627
    }
628

    
629
    /**
630
     * @return a list of terms which includes first the
631
     * term of this node and then recursively the list
632
     * of all children and grandChildren
633
     */
634
    public Collection<? extends T> asTermListRecursive() {
635
        List<T> result = new ArrayList<>();
636
        T term = this.getTerm();
637
        if(term != null){
638
            result.add(term);
639
        }
640
        for(TermNode<T> childNode : this.getChildNodes()){
641
            result.addAll(childNode.asTermListRecursive());
642
        }
643
        return result;
644
    }
645

    
646
//*********************** CLONE ********************************************************/
647

    
648
	/**
649
	 * Clones <i>this</i> {@link TermNode}. This is a shortcut that enables to create
650
	 * a new instance that differs only slightly from <i>this</i> tree node by
651
	 * modifying only some of the attributes.
652
	 * The parent, the feature and the featureTree are the same as for the original feature node
653
	 * the children are removed
654
	 *
655
	 * @see eu.etaxonomy.cdm.model.common.VersionableEntity#clone()
656
	 * @see java.lang.Object#clone()
657
	 */
658
	@Override
659
	public Object clone() {
660
		TermNode<T> result;
661
		try {
662
			result = (TermNode<T>)super.clone();
663
			result.children = new ArrayList<>();
664
			return result;
665
		}catch (CloneNotSupportedException e) {
666
			logger.warn("Object does not implement cloneable");
667
			e.printStackTrace();
668
			return null;
669
		}
670
	}
671

    
672
    public TermNode<T> cloneDescendants(){
673
        TermNode<T> clone = (TermNode<T>)this.clone();
674
        TermNode<T> childClone;
675

    
676
        for(TermNode<T> childNode : this.getChildNodes()){
677
            childClone = (TermNode<T>) childNode.clone();
678
            for (TermNode<T> childChild:childNode.getChildNodes()){
679
                childClone.addChild(childChild.cloneDescendants());
680
            }
681
            clone.addChild(childClone);
682

    
683
        }
684
        return clone;
685
    }
686

    
687
// ********************** TREE NODE METHODS ******************************/
688

    
689
	@Override
690
	public String treeIndex() {
691
		return this.treeIndex;
692
	}    @Override
693
    public String treeIndexLike() {
694
        return treeIndex + "%";
695
    }
696
    @Override
697
    public String treeIndexWc() {
698
        return treeIndex + "*";
699
    }
700

    
701
	@Override
702
	@Deprecated
703
	public void setTreeIndex(String newTreeIndex) {
704
		this.treeIndex = newTreeIndex;
705
	}
706

    
707

    
708
	@Override
709
	@Deprecated
710
	public int treeId() {
711
		if (this.getGraph() == null){
712
			return -1;
713
		}else{
714
			return this.getGraph().getId();
715
		}
716
	}
717

    
718
	private void updateSortIndex(){
719
	 // TODO workaround (see sortIndex doc)
720
        for (int i = 0; i < children.size(); i++) {
721
            children.get(i).setSortIndex(i);
722
        }
723
	}
724

    
725
	public void removeNullValueFromChildren(){
726
	    HHH_9751_Util.removeAllNull(children);
727
	    updateSortIndex();
728
	}
729
}
(25-25/33)