Project

General

Profile

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

    
11
package eu.etaxonomy.cdm.model.taxon;
12

    
13
import java.util.ArrayList;
14
import java.util.HashMap;
15
import java.util.HashSet;
16
import java.util.Iterator;
17
import java.util.List;
18
import java.util.Map;
19
import java.util.Set;
20

    
21
import javax.persistence.Entity;
22
import javax.persistence.FetchType;
23
import javax.persistence.JoinColumn;
24
import javax.persistence.JoinTable;
25
import javax.persistence.ManyToMany;
26
import javax.persistence.ManyToOne;
27
import javax.persistence.MapKeyJoinColumn;
28
import javax.persistence.OneToMany;
29
import javax.persistence.OneToOne;
30
import javax.persistence.Transient;
31
import javax.xml.bind.annotation.XmlAccessType;
32
import javax.xml.bind.annotation.XmlAccessorType;
33
import javax.xml.bind.annotation.XmlElement;
34
import javax.xml.bind.annotation.XmlElementWrapper;
35
import javax.xml.bind.annotation.XmlIDREF;
36
import javax.xml.bind.annotation.XmlRootElement;
37
import javax.xml.bind.annotation.XmlSchemaType;
38
import javax.xml.bind.annotation.XmlType;
39
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
40

    
41
import org.apache.log4j.Logger;
42
import org.hibernate.annotations.Cascade;
43
import org.hibernate.annotations.CascadeType;
44
import org.hibernate.envers.Audited;
45
import org.hibernate.search.annotations.Indexed;
46
import org.hibernate.search.annotations.IndexedEmbedded;
47

    
48
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
49
import eu.etaxonomy.cdm.jaxb.MultilanguageTextAdapter;
50
import eu.etaxonomy.cdm.model.common.IReferencedEntity;
51
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
52
import eu.etaxonomy.cdm.model.common.Language;
53
import eu.etaxonomy.cdm.model.common.LanguageString;
54
import eu.etaxonomy.cdm.model.common.MultilanguageText;
55
import eu.etaxonomy.cdm.model.common.TimePeriod;
56
import eu.etaxonomy.cdm.model.location.NamedArea;
57
import eu.etaxonomy.cdm.model.reference.Reference;
58
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
59

    
60
/**
61
 * @author a.mueller
62
 * @created 31.03.2009
63
 */
64
@XmlAccessorType(XmlAccessType.FIELD)
65
@XmlType(name = "Classification", propOrder = {
66
    "name",
67
    "description",
68
    "rootNode",
69
    "reference",
70
    "microReference",
71
    "timeperiod",
72
    "geoScopes"
73
})
74
@XmlRootElement(name = "Classification")
75
@Entity
76
@Audited
77
@Indexed(index = "eu.etaxonomy.cdm.model.taxon.Classification")
78
public class Classification extends IdentifiableEntity<IIdentifiableEntityCacheStrategy<Classification>> implements IReferencedEntity, ITaxonTreeNode, Cloneable{
79
    private static final long serialVersionUID = -753804821474209635L;
80
    private static final Logger logger = Logger.getLogger(Classification.class);
81

    
82
    @XmlElement(name = "Name")
83
    @OneToOne(fetch = FetchType.LAZY)
84
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
85
    @JoinColumn(name = "name_id", referencedColumnName = "id")
86
    @IndexedEmbedded
87
    private LanguageString name;
88

    
89

    
90
    @XmlElement(name = "rootNode")
91
    @XmlIDREF
92
    @XmlSchemaType(name = "IDREF")
93
    @OneToOne(fetch=FetchType.LAZY)
94
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
95
    private TaxonNode rootNode;
96

    
97
    @XmlElement(name = "reference")
98
    @XmlIDREF
99
    @XmlSchemaType(name = "IDREF")
100
    @ManyToOne(fetch = FetchType.LAZY)
101
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
102
    private Reference<?> reference;
103

    
104
    @XmlElement(name = "microReference")
105
    private String microReference;
106

    
107
	@XmlElement(name = "TimePeriod")
108
    private TimePeriod timeperiod = TimePeriod.NewInstance();
109

    
110
    @XmlElementWrapper( name = "GeoScopes")
111
    @XmlElement( name = "GeoScope")
112
    @XmlIDREF
113
    @XmlSchemaType(name="IDREF")
114
    @ManyToMany(fetch = FetchType.LAZY)
115
    @JoinTable(name="Classification_GeoScope")
116
//    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})  remove cascade #5755
117
    private Set<NamedArea> geoScopes = new HashSet<NamedArea>();
118

    
119
	@XmlElement(name = "Description")
120
	@XmlJavaTypeAdapter(MultilanguageTextAdapter.class)
121
	@OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
122
	@MapKeyJoinColumn(name="description_mapkey_id")
123
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE, CascadeType.DELETE })
124
	@JoinTable(name = "Classification_Description")
125
//	@Field(name="text", store=Store.YES)
126
//    @FieldBridge(impl=MultilanguageTextFieldBridge.class)
127
    private final Map<Language,LanguageString> description = new HashMap<Language,LanguageString>();
128

    
129

    
130

    
131
//	/**
132
//	 * If this classification is an alternative classification for a subclassification in
133
//	 * an other classification(parent view),
134
//	 * the alternativeViewRoot is the connection node from this classification to the parent classification.
135
//	 * It replaces another node in the parent view.
136
//	 */
137
//	private AlternativeViewRoot alternativeViewRoot;
138

    
139
// ********************** FACTORY METHODS *********************************************/
140

    
141
    public static Classification NewInstance(String name){
142
        return NewInstance(name, null, Language.DEFAULT());
143
    }
144

    
145
    public static Classification NewInstance(String name, Language language){
146
        return NewInstance(name, null, language);
147
    }
148

    
149
    public static Classification NewInstance(String name, Reference reference){
150
        return NewInstance(name, reference, Language.DEFAULT());
151
    }
152

    
153
    public static Classification NewInstance(String name, Reference reference, Language language){
154
        return new Classification(name, reference, language);
155
    }
156

    
157
// **************************** CONSTRUCTOR *********************************/
158

    
159
    //for hibernate use only, protected required by Javassist
160
    protected Classification(){super();}
161

    
162
    protected Classification(String name, Reference reference, Language language){
163
        this();
164
        LanguageString langName = LanguageString.NewInstance(name, language);
165
        setName(langName);
166
        setReference(reference);
167
        this.rootNode = new TaxonNode();
168
        rootNode.setClassification(this);
169
    }
170

    
171
//********************** xxxxxxxxxxxxx ******************************************/
172
    /**
173
     * Returns the topmost {@link TaxonNode taxon node} (root node) of <i>this</i>
174
     * classification. The root node does not have any parent and no taxon. Since taxon nodes
175
     * recursively point to their child nodes the complete classification is
176
     * defined by its root node.
177
     */
178
    public TaxonNode getRootNode(){
179
        return rootNode;
180
    }
181

    
182
    public void setRootNode(TaxonNode root){
183
        this.rootNode = root;
184
    }
185

    
186
    @Override
187
    public TaxonNode addChildNode(TaxonNode childNode, Reference citation, String microCitation) {
188
        return addChildNode(childNode, rootNode.getCountChildren(), citation, microCitation);
189
    }
190

    
191
    @Override
192
    public TaxonNode addChildNode(TaxonNode childNode, int index, Reference citation, String microCitation) {
193

    
194
        childNode.setParentTreeNode(this.rootNode, index);
195

    
196
        childNode.setReference(citation);
197
        childNode.setMicroReference(microCitation);
198
//		childNode.setSynonymToBeUsed(synonymToBeUsed);
199

    
200
        return childNode;
201
    }
202

    
203
    @Override
204
    public TaxonNode addChildTaxon(Taxon taxon, Reference citation, String microCitation) {
205
        return addChildTaxon(taxon, rootNode.getCountChildren(), citation, microCitation);
206
    }
207

    
208
    @Override
209
    public TaxonNode addChildTaxon(Taxon taxon, int index, Reference citation, String microCitation) {
210
        return addChildNode(new TaxonNode(taxon), index, citation, microCitation);
211
    }
212

    
213
    @Override
214
    public boolean deleteChildNode(TaxonNode node) {
215
        boolean result = removeChildNode(node);
216

    
217
        if (node.hasTaxon()){
218
            node.getTaxon().removeTaxonNode(node);
219
            node.setTaxon(null);
220
        }
221

    
222
        ArrayList<TaxonNode> childNodes = new ArrayList<TaxonNode>(node.getChildNodes());
223
        for (TaxonNode childNode : childNodes){
224
            if (childNode != null){
225
                node.deleteChildNode(childNode);
226
            }
227
        }
228
        return result;
229
    }
230

    
231
    public boolean deleteChildNode(TaxonNode node, boolean deleteChildren) {
232
        boolean result = removeChildNode(node);
233

    
234
        node.getTaxon().removeTaxonNode(node);
235
        //node.setTaxon(null);
236
        if (deleteChildren){
237
            ArrayList<TaxonNode> childNodes = new ArrayList<TaxonNode>(node.getChildNodes());
238
            for (TaxonNode childNode : childNodes){
239
                node.deleteChildNode(childNode);
240
            }
241
        }
242
        return result;
243
    }
244

    
245
    /**
246
     *
247
     * @param node
248
     * @return
249
     */
250
    protected boolean removeChildNode(TaxonNode node){
251
        boolean result = false;
252
        if(!rootNode.getChildNodes().contains(node)){
253
            throw new IllegalArgumentException("TaxonNode is a not a root node of this classification");
254
        }
255

    
256
        result = rootNode.removeChildNode(node);
257

    
258
        node.setParent(null);
259
        node.setClassification(null);
260

    
261
        return result;
262
    }
263

    
264
    public boolean removeRootNode(){
265
        boolean result = false;
266

    
267
        if (rootNode != null){
268
            this.rootNode.setChildNodes(new ArrayList<TaxonNode>());
269
            this.rootNode.setParent(null);
270
            rootNode = null;
271
            result = true;
272
        }
273
        return result;
274

    
275
    }
276

    
277
    /**
278
     * Appends an existing topmost node to another node of this tree. The existing topmost node becomes
279
     * an ordinary node.
280
     * @param topmostNode
281
     * @param otherNode
282
     * @param ref
283
     * @param microReference
284
     * @throws IllegalArgumentException
285
     */
286
    public void makeTopmostNodeChildOfOtherNode(TaxonNode topmostNode, TaxonNode otherNode, Reference ref, String microReference)
287
                throws IllegalArgumentException{
288
        if (otherNode == null){
289
            throw new NullPointerException("other node must not be null");
290
        }
291
        if (! getChildNodes().contains(topmostNode)){
292
            throw new IllegalArgumentException("root node to be added as child must already be root node within this tree");
293
        }
294
        if (otherNode.getClassification() == null || ! otherNode.getClassification().equals(this)){
295
            throw new IllegalArgumentException("other node must already be node within this tree");
296
        }
297
        if (otherNode.equals(topmostNode)){
298
            throw new IllegalArgumentException("root node and other node must not be the same");
299
        }
300
        otherNode.addChildNode(topmostNode, ref, microReference);
301
    }
302

    
303

    
304
    /**
305
     * Checks if the given taxon is part of <b>this</b> tree.
306
     * @param taxon
307
     * @return
308
     */
309
    public boolean isTaxonInTree(Taxon taxon){
310
        taxon = HibernateProxyHelper.deproxy(taxon, Taxon.class);
311
        return (getNode(taxon) != null);
312
    }
313

    
314
    /**
315
     * Checks if the given taxon is part of <b>this</b> tree. If so the according TaxonNode is returned.
316
     * Otherwise null is returned.
317
     * @param taxon
318
     * @return
319
     */
320
    public TaxonNode getNode(Taxon taxon){
321
        if (taxon == null){
322
            return null;
323
        }
324

    
325
        for (TaxonNode taxonNode: taxon.getTaxonNodes()){
326
        	Classification classification = HibernateProxyHelper.deproxy(taxonNode.getClassification(), Classification.class);
327
            if (classification.equals(this)){
328
                return taxonNode;
329
            }
330
        }
331
        return null;
332
    }
333

    
334
    /**
335
     * Checks if the given taxon is one of the topmost taxa in <b>this</b> tree.
336
     * @param taxon
337
     * @return
338
     */
339
    public boolean isTopmostInTree(Taxon taxon){
340
        return (getTopmostNode(taxon) != null);
341
    }
342

    
343

    
344
    /**
345
     * Checks if the taxon is a direct child of <b>this</b> tree and returns the according node if true.
346
     * Returns null otherwise.
347
     * @param taxon
348
     * @return
349
     */
350
    public TaxonNode getTopmostNode(Taxon taxon){
351
        if (taxon == null){
352
            return null;
353
        }
354
        for (TaxonNode taxonNode: taxon.getTaxonNodes()){
355
            if (taxonNode.getClassification().equals(this)){
356
                if (this.getChildNodes().contains(taxonNode)){
357
                    if (taxonNode.getParent() == null){
358
                        logger.warn("A topmost node should always have the root node as parent but actually has no parent");
359
                    }else if (taxonNode.getParent().getParent() != null){
360
                        logger.warn("The root node should have not parent but actually has one");
361
                    }else if (taxonNode.getParent().getTaxon() != null){
362
                        logger.warn("The root node should have not taxon but actually has one");
363
                    }
364
                    return taxonNode;
365
                }
366
            }
367
        }
368
        return null;
369
    }
370

    
371
    private boolean handleCitationOverwrite(TaxonNode childNode, Reference citation, String microCitation){
372
        if (citation != null){
373
            if (childNode.getReference() != null && ! childNode.getReference().equals(citation)){
374
                logger.warn("ReferenceForParentChildRelation will be overwritten");
375
            }
376
            childNode.setReference(citation);
377
        }
378
        if (microCitation != null){
379
            if (childNode.getMicroReference() != null && ! childNode.getMicroReference().equals(microCitation)){
380
                logger.warn("MicroReferenceForParentChildRelation will be overwritten");
381
            }
382
            childNode.setMicroReference(microCitation);
383
        }
384
        return true;
385
    }
386

    
387
    /**
388
     * Relates two taxa as parent-child nodes within a classification. <BR>
389
     * If the taxa are not yet part of the tree they are added to it.<Br>
390
     * If the child taxon is a topmost node still it is added as child and deleted from the rootNode set.<Br>
391
     * If the child is a child of another parent already an IllegalStateException is thrown because a child can have only
392
     * one parent. <Br>
393
     * If the parent-child relationship between these two taxa already exists nothing is changed. Only
394
     * citation and microcitation are overwritten by the new values if these values are not null.
395
     *
396
     * @param parent
397
     * @param child
398
     * @param citation
399
     * @param microCitation
400
     * @return the childNode
401
     * @throws IllegalStateException If the child is a child of another parent already
402
     */
403
    public TaxonNode addParentChild (Taxon parent, Taxon child, Reference citation, String microCitation)
404
            throws IllegalStateException{
405
        try {
406
            if (parent == null || child == null){
407
                logger.warn("Child or parent taxon is null.");
408
                return null;
409
            }
410
            if (parent == child){
411
                logger.warn("A taxon should never be its own child. Child not added");
412
                return null;
413
            }
414
            TaxonNode parentNode = this.getNode(parent);
415
            TaxonNode childNode = this.getNode(child);
416

    
417
            //if child exists in tree and has a parent
418
            //no multiple parents are allowed in the tree
419
            if (childNode != null && ! childNode.isTopmostNode()){
420
                //...different to the parent taxon  throw exception
421
                if ( !(childNode.getParent().getTaxon().equals(parent) )){
422
                	if (childNode.getParent().getTaxon().getId() != 0 && childNode.getParent().getTaxon().getName().equals(parent.getName())){
423
                		throw new IllegalStateException("The child taxon is already part of the tree but has an other parent taxon than the parent to be added. Child: " + child.toString() + ", new parent:" + parent.toString() + ", old parent: " + childNode.getParent().getTaxon().toString()) ;
424
                	}
425
                    //... same as the parent taxon do nothing but overwriting citation and microCitation
426
                }else{
427
                    handleCitationOverwrite(childNode, citation, microCitation);
428
                    return childNode;
429
                }
430
            }
431

    
432
            //add parent node if not exist
433
            if (parentNode == null){
434
                parentNode = this.addChildTaxon(parent, null, null);
435
            }
436

    
437
            //add child if not exists
438
            if (childNode == null){
439
                childNode = parentNode.addChildTaxon(child, citation, microCitation);
440
            }else{
441
                //child is still topmost node
442
                //TODO test if child is topmostNode otherwise throw IllegalStateException
443
                if (! this.isTopmostInTree(child)){
444
                    //throw new IllegalStateException("Child is not a topmost node but must be");
445
                    if (childNode.getClassification() != null && childNode.getParent() == null){
446
                        logger.warn("Child has no parent and is not a topmost node, child: " + child.getId() + " classification: " + childNode.getClassification().getId());
447
                    }else{
448
                        logger.warn("ChildNode has no classification: " + childNode.getId());
449
                    }
450
                    parentNode.addChildNode(childNode, citation, microCitation);
451
                    if (!parentNode.isTopmostNode()){
452
                        this.addChildNode(parentNode, citation, microCitation);
453
                        logger.warn("parent is added as a topmost node");
454
                    }else{
455
                        logger.warn("parent is already a topmost node");
456
                    }
457
                }else{
458
                    this.makeTopmostNodeChildOfOtherNode(childNode, parentNode, citation, microCitation);
459
                }
460
            }
461
            return childNode;
462
        } catch (IllegalStateException e) {
463
            throw e;
464
        } catch (RuntimeException e){
465
            throw e;
466
        }
467
    }
468

    
469

    
470
    @Override
471
    @Transient
472
    public Reference getCitation() {
473
        return reference;
474
    }
475

    
476
    public LanguageString getName() {
477
        return name;
478
    }
479

    
480
    public void setName(LanguageString name) {
481
        this.name = name;
482
    }
483

    
484
    /**
485
     * Returns a set containing all nodes in this classification.
486
     *
487
     * Caution: Use this method with care. It can be very time and resource consuming and might
488
     * run into OutOfMemoryExceptions for big trees.
489
     *
490
     * @return
491
     */
492
    @Transient
493
    public Set<TaxonNode> getAllNodes() {
494
        Set<TaxonNode> allNodes = new HashSet<TaxonNode>();
495

    
496
        for(TaxonNode rootNode : getChildNodes()){
497
            allNodes.addAll(rootNode.getDescendants());
498
        }
499

    
500
        return allNodes;
501
    }
502

    
503
    @Override
504
    @Transient
505
    public List<TaxonNode> getChildNodes() {
506
        return rootNode.getChildNodes();
507
    }
508

    
509
    @Override
510
    public Reference getReference() {
511
        return reference;
512
    }
513

    
514
    public void setReference(Reference reference) {
515
        this.reference = reference;
516
    }
517

    
518

    
519
    @Override
520
    public String getMicroReference() {
521
        return microReference;
522
    }
523

    
524
    /**
525
     * @param microReference the microReference to set
526
     */
527
    public void setMicroReference(String microReference) {
528
        this.microReference = microReference;
529
    }
530

    
531

    
532
    /**
533
	 * The point in time, the time period or the season for which this description element
534
	 * is valid. A season may be expressed by not filling the year part(s) of the time period.
535
	 */
536
	public TimePeriod getTimeperiod() {
537
		return timeperiod;
538
	}
539

    
540
	/**
541
	 * @see #getTimeperiod()
542
	 */
543
	public void setTimeperiod(TimePeriod timeperiod) {
544
		if (timeperiod == null){
545
			timeperiod = TimePeriod.NewInstance();
546
		}
547
		this.timeperiod = timeperiod;
548
	}
549

    
550

    
551
    /**
552
     * Returns the set of {@link NamedArea named areas} indicating the geospatial
553
     * data where <i>this</i> {@link Classification} is valid.
554
     */
555
    public Set<NamedArea> getGeoScopes(){
556
        return this.geoScopes;
557
    }
558

    
559
    /**
560
     * Adds a {@link NamedArea named area} to the set of {@link #getGeoScopes() named areas}
561
     * delimiting the geospatial area where <i>this</i> {@link Classification} is valid.
562
     *
563
     * @param geoScope	the named area to be additionally assigned to <i>this</i> taxon description
564
     * @see    	   		#getGeoScopes()
565
     */
566
    public void addGeoScope(NamedArea geoScope){
567
        this.geoScopes.add(geoScope);
568
    }
569

    
570
    /**
571
     * Removes one element from the set of {@link #getGeoScopes() named areas} delimiting
572
     * the geospatial area where <i>this</i> {@link Classification} is valid.
573
     *
574
     * @param  geoScope   the named area which should be removed
575
     * @see     		  #getGeoScopes()
576
     * @see     		  #addGeoScope(NamedArea)
577
     */
578
    public void removeGeoScope(NamedArea geoScope){
579
        this.geoScopes.remove(geoScope);
580
    }
581

    
582

    
583
	/**
584
	 * Returns the i18n description used to describe
585
	 * <i>this</i> {@link Classification}. The different {@link LanguageString language strings}
586
	 * contained in the multilanguage text should all have the same meaning.
587
	 */
588
	public Map<Language,LanguageString> getDescription(){
589
		return this.description;
590
	}
591

    
592
	/**
593
	 * Adds a translated {@link LanguageString text in a particular language}
594
	 * to the {@link MultilanguageText description} used to describe
595
	 * <i>this</i> {@link Classification}.
596
	 *
597
	 * @param description	the language string describing the individuals association
598
	 * 						in a particular language
599
	 * @see    	   			#getDescription()
600
	 * @see    	   			#putDescription(Language, String)
601
	 *
602
	 */
603
	public void putDescription(LanguageString description){
604
		this.description.put(description.getLanguage(),description);
605
	}
606
	/**
607
	 * Creates a {@link LanguageString language string} based on the given text string
608
	 * and the given {@link Language language} and adds it to the {@link MultilanguageText multilanguage text}
609
	 * used to describe <i>this</i> {@link Classification}.
610
	 *
611
	 * @param text		the string describing the individuals association
612
	 * 					in a particular language
613
	 * @param language	the language in which the text string is formulated
614
	 * @see    	   		#getDescription()
615
	 * @see    	   		#putDescription(LanguageString)
616
	 */
617
	public void putDescription(Language language, String text){
618
		this.description.put(language, LanguageString.NewInstance(text, language));
619
	}
620
	/**
621
	 * Removes from the {@link MultilanguageText description} used to describe
622
	 * <i>this</i>  {@link Classification} the one {@link LanguageString language string}
623
	 * with the given {@link Language language}.
624
	 *
625
	 * @param  language	the language in which the language string to be removed
626
	 * 					has been formulated
627
	 * @see     		#getDescription()
628
	 */
629
	public void removeDescription(Language language){
630
		this.description.remove(language);
631
	}
632

    
633

    
634
    @Override
635
    public String generateTitle() {
636
        return name.getText();
637
    }
638

    
639
    public int compareTo(Object o) {
640
        return 0;
641
    }
642

    
643

    
644
    @Override
645
    public boolean hasChildNodes() {
646
        return getChildNodes().size() > 0;
647
    }
648

    
649
    //*********************** CLONE ********************************************************/
650
    /**
651
     * Clones <i>this</i> classification. This is a shortcut that enables to create
652
     * a new instance that differs only slightly from <i>this</i> classification by
653
     * modifying only some of the attributes.<BR><BR>
654

    
655
     * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
656
     * @see java.lang.Object#clone()
657
     */
658
    @Override
659
    public Object clone() {
660
        Classification result;
661
        try{
662
            result = (Classification)super.clone();
663
            //result.rootNode.childNodes = new ArrayList<TaxonNode>();
664
            List<TaxonNode> rootNodes = new ArrayList<TaxonNode>();
665
            TaxonNode rootNodeClone;
666

    
667

    
668
            rootNodes.addAll(rootNode.getChildNodes());
669
            TaxonNode rootNode;
670
            Iterator<TaxonNode> iterator = rootNodes.iterator();
671

    
672
            while (iterator.hasNext()){
673
                rootNode = iterator.next();
674
                rootNodeClone = rootNode.cloneDescendants();
675
                rootNodeClone.setClassification(result);
676
                result.addChildNode(rootNodeClone, rootNode.getReference(), rootNode.getMicroReference());
677
                rootNodeClone.setSynonymToBeUsed(rootNode.getSynonymToBeUsed());
678
            }
679

    
680
            //geo-scopes
681
            result.geoScopes = new HashSet<NamedArea>();
682
            for (NamedArea namedArea : getGeoScopes()){
683
                result.geoScopes.add(namedArea);
684
            }
685

    
686
            return result;
687

    
688
        }catch (CloneNotSupportedException e) {
689
            logger.warn("Object does not implement cloneable");
690
            e.printStackTrace();
691
            return null;
692
        }
693

    
694

    
695

    
696

    
697

    
698
    }
699

    
700

    
701
}
(2-2/21)