Project

General

Profile

Download (22.2 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
package eu.etaxonomy.cdm.model.occurrence;
10

    
11
import java.util.ArrayList;
12
import java.util.Collection;
13
import java.util.HashMap;
14
import java.util.HashSet;
15
import java.util.Map;
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.Index;
22
import javax.persistence.Inheritance;
23
import javax.persistence.InheritanceType;
24
import javax.persistence.ManyToMany;
25
import javax.persistence.ManyToOne;
26
import javax.persistence.MapKeyJoinColumn;
27
import javax.persistence.OneToMany;
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
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
41

    
42
import org.apache.log4j.Logger;
43
import org.hibernate.annotations.Cascade;
44
import org.hibernate.annotations.CascadeType;
45
import org.hibernate.annotations.Type;
46
import org.hibernate.envers.Audited;
47
import org.hibernate.search.annotations.Analyze;
48
import org.hibernate.search.annotations.Field;
49
import org.hibernate.search.annotations.FieldBridge;
50
import org.hibernate.search.annotations.Fields;
51
import org.hibernate.search.annotations.IndexedEmbedded;
52
import org.hibernate.search.annotations.SortableField;
53
import org.hibernate.search.annotations.Store;
54
import org.hibernate.search.bridge.builtin.BooleanBridge;
55

    
56
import eu.etaxonomy.cdm.common.URI;
57
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
58
import eu.etaxonomy.cdm.hibernate.search.StripHtmlBridge;
59
import eu.etaxonomy.cdm.hibernate.search.UriBridge;
60
import eu.etaxonomy.cdm.jaxb.FormattedTextAdapter;
61
import eu.etaxonomy.cdm.jaxb.MultilanguageTextAdapter;
62
import eu.etaxonomy.cdm.model.common.IIntextReferenceTarget;
63
import eu.etaxonomy.cdm.model.common.IMultiLanguageTextHolder;
64
import eu.etaxonomy.cdm.model.common.IPublishable;
65
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
66
import eu.etaxonomy.cdm.model.common.Language;
67
import eu.etaxonomy.cdm.model.common.LanguageString;
68
import eu.etaxonomy.cdm.model.common.MultilanguageText;
69
import eu.etaxonomy.cdm.model.description.DescriptionBase;
70
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
71
import eu.etaxonomy.cdm.model.description.IDescribable;
72
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
73
import eu.etaxonomy.cdm.model.description.TaxonDescription;
74
import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
75
import eu.etaxonomy.cdm.model.term.DefinedTerm;
76
import eu.etaxonomy.cdm.model.term.TermType;
77
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
78
import eu.etaxonomy.cdm.strategy.match.Match;
79
import eu.etaxonomy.cdm.strategy.match.Match.ReplaceMode;
80
import eu.etaxonomy.cdm.strategy.match.MatchMode;
81

    
82
/**
83
 * type figures are observations with at least a figure object in media
84
 * @author m.doering
85
 * @since 08-Nov-2007 13:06:41
86
 */
87
@XmlAccessorType(XmlAccessType.FIELD)
88
@XmlType(name = "SpecimenOrObservationBase", propOrder = {
89
    "recordBasis",
90
    "identityCache",
91
    "protectedIdentityCache",
92
    "publish",
93
    "preferredStableUri",
94
    "sex",
95
    "lifeStage",
96
    "kindOfUnit",
97
    "individualCount",
98
    "definition",
99
    "descriptions",
100
    "determinations",
101
    "derivationEvents"
102
})
103
@XmlRootElement(name = "SpecimenOrObservationBase")
104
@Entity
105
@Audited
106
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
107
@Table(name="SpecimenOrObservationBase", indexes = { @Index(name = "specimenOrObservationBaseTitleCacheIndex", columnList = "titleCache"),
108
        @Index(name = "specimenOrObservationBaseIdentityCacheIndex", columnList = "identityCache") })
109
public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCacheStrategy<?>>
110
                extends IdentifiableEntity<S>
111
                implements IMultiLanguageTextHolder, IIntextReferenceTarget, IDescribable<DescriptionBase<S>>, IPublishable  {
112

    
113
    private static final long serialVersionUID = 6932680139334408031L;
114
    private static final Logger logger = Logger.getLogger(SpecimenOrObservationBase.class);
115

    
116
    /**
117
     * An indication of what the unit record describes.
118
     *
119
     * NOTE: The name of the attribute was chosen against the common naming conventions of the CDM
120
     * as it is well known in common standards like ABCD and DarwinCore. According to CDM naming
121
     * conventions it would be specimenOrObservationType.
122
     *
123
     * @see ABCD: DataSets/DataSet/Units/Unit/RecordBasis
124
     * @see Darwin Core: http://wiki.tdwg.org/twiki/bin/view/DarwinCore/BasisOfRecord
125
     */
126
    @XmlAttribute(name ="RecordBasis")
127
    @Column(name="recordBasis")
128
    @NotNull
129
    @Type(type = "eu.etaxonomy.cdm.hibernate.EnumUserType",
130
        parameters = {@org.hibernate.annotations.Parameter(name = "enumClass", value = "eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType")}
131
    )
132
    @Audited
133
    private SpecimenOrObservationType recordBasis;
134

    
135

    
136
    @XmlElementWrapper(name = "Descriptions")
137
    @XmlElement(name = "Description")
138
    @OneToMany(mappedBy="describedSpecimenOrObservation", fetch = FetchType.LAZY)
139
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
140
    @NotNull
141
    private Set<DescriptionBase<S>> descriptions = new HashSet<>();
142

    
143

    
144
    @XmlElementWrapper(name = "Determinations")
145
    @XmlElement(name = "Determination")
146
    @OneToMany(mappedBy="identifiedUnit", orphanRemoval=true)
147
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
148
    @IndexedEmbedded(depth = 2)
149
    @NotNull
150
    private Set<DeterminationEvent> determinations = new HashSet<>();
151

    
152
    @XmlElement(name = "Sex")
153
    @XmlIDREF
154
    @XmlSchemaType(name = "IDREF")
155
    @ManyToOne(fetch = FetchType.LAZY)
156
    private DefinedTerm sex;
157

    
158
    @XmlElement(name = "LifeStage")
159
    @XmlIDREF
160
    @XmlSchemaType(name = "IDREF")
161
    @ManyToOne(fetch = FetchType.LAZY)
162
    private DefinedTerm lifeStage;
163

    
164
    /**
165
     * Part(s) of organism or class of materials represented by this unit.
166
     * Example: fruits, seeds, tissue, gDNA, leaves
167
     *
168
     * @see ABCD: DataSets/DataSet/Units/Unit/KindOfUnit
169
     * @see TermType#KindOfUnit
170
     */
171
    @XmlElement(name = "KindOfUnit")
172
    @XmlIDREF
173
    @XmlSchemaType(name = "IDREF")
174
    @ManyToOne(fetch = FetchType.LAZY)
175
//    @IndexedEmbedded(depth=1)
176
    private DefinedTerm kindOfUnit;
177

    
178
    @XmlElement(name = "IndividualCount")
179
    @Field(analyze = Analyze.NO)
180
    private String individualCount;
181

    
182
    /**
183
     * The preferred stable identifier (URI) as discussed in
184
     * {@link  https://dev.e-taxonomy.eu/redmine/issues/5606}
185
     */
186
    @XmlElement(name = "PreferredStableUri")
187
    @Field(analyze = Analyze.NO)
188
    @FieldBridge(impl = UriBridge.class)
189
    @Type(type="uriUserType")
190
    private URI preferredStableUri;
191

    
192
    // the verbatim description of this occurrence. Free text usable when no atomised data is available.
193
    // in conjunction with titleCache which serves as the "citation" string for this object
194
    @XmlElement(name = "Description")
195
    @XmlJavaTypeAdapter(MultilanguageTextAdapter.class)
196
    @OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
197
    @MapKeyJoinColumn(name="definition_mapkey_id")
198
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE, CascadeType.DELETE})
199
    @IndexedEmbedded
200
    @NotNull
201
    protected Map<Language,LanguageString> definition = new HashMap<>();
202

    
203
    // events that created derivedUnits from this unit
204
    @XmlElementWrapper(name = "DerivationEvents")
205
    @XmlElement(name = "DerivationEvent")
206
    @XmlIDREF
207
    @XmlSchemaType(name = "IDREF")
208
    @ManyToMany(fetch=FetchType.LAZY)
209
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE, CascadeType.DELETE})
210
    @NotNull
211
    protected Set<DerivationEvent> derivationEvents = new HashSet<>();
212

    
213
    @XmlAttribute(name = "publish")
214
    @Field(analyze = Analyze.NO)
215
    @FieldBridge(impl=BooleanBridge.class)
216
    private boolean publish = true;
217

    
218
    @XmlElement(name = "IdentityCache", required = false)
219
    @XmlJavaTypeAdapter(FormattedTextAdapter.class)
220
    @Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.ALL)
221
//    @NotEmpty(groups = Level2.class) // implictly NotNull
222
    @Fields({
223
        @Field(store=Store.YES),
224
        //  If the field is only needed for sorting and nothing else, you may configure it as
225
        //  un-indexed and un-stored, thus avoid unnecessary index growth.
226
        @Field(name = "identityCache__sort", analyze = Analyze.NO, store=Store.NO, index = org.hibernate.search.annotations.Index.NO)
227
    })
228
    @SortableField(forField = "identityCache__sort")
229
    @FieldBridge(impl=StripHtmlBridge.class)
230
    private String identityCache;
231

    
232

    
233
    //if true identityCache will not be automatically generated/updated
234
    @XmlElement(name = "ProtectedIdentityCache")
235
    private boolean protectedIdentityCache;
236

    
237

    
238
//********************************** CONSTRUCTOR *********************************/
239

    
240
    //for hibernate use only
241
    @Deprecated
242
    protected SpecimenOrObservationBase(){
243
        super();
244
    }
245

    
246
    protected SpecimenOrObservationBase(SpecimenOrObservationType recordBasis) {
247
        super();
248
        if (recordBasis == null){ throw new IllegalArgumentException("RecordBasis must not be null");}
249
        this.recordBasis = recordBasis;
250
    }
251

    
252

    
253
    /**
254
     * Subclasses should implement setting the default cache strate
255
     */
256
    protected abstract void initDefaultCacheStrategy();
257

    
258

    
259
//************************* GETTER / SETTER ***********************/
260

    
261

    
262
    /**@see #recordBasis */
263
    public SpecimenOrObservationType getRecordBasis() {
264
        return recordBasis;
265
    }
266
    /**@see #recordBasis */
267
    public void setRecordBasis(SpecimenOrObservationType recordBasis) {
268
        this.recordBasis = recordBasis;
269
    }
270

    
271

    
272
    /**
273
     * @return the identityCache
274
     */
275
    public String getIdentityCache() {
276
        return identityCache;
277
    }
278
    /**
279
     * @Deprecated For special use only.
280
     * Use {@link #setIdentityCache(String, boolean)} instead
281
     */
282
    @Deprecated
283
    public void setIdentityCache(String identityCache) {
284
        this.identityCache = identityCache;
285
    }
286

    
287
    public void setIdentityCache(String identityCache, boolean isProtected) {
288
        this.protectedIdentityCache = isProtected;
289
        setIdentityCache(identityCache);
290
    }
291

    
292
    /**
293
     * @return the protectedIdentityCache
294
     */
295
    public boolean isProtectedIdentityCache() {
296
        return protectedIdentityCache;
297
    }
298
    /**
299
     * @param protectedIdentityCache the protectedIdentityCache to set
300
     */
301
    public void setProtectedIdentityCache(boolean protectedIdentityCache) {
302
        this.protectedIdentityCache = protectedIdentityCache;
303
    }
304

    
305
    /**@see #preferredStableUri */
306
    public URI getPreferredStableUri() {
307
        return preferredStableUri;
308
    }
309
    /**@see #preferredStableUri */
310
    public void setPreferredStableUri(URI preferredStableUri) {
311
        this.preferredStableUri = preferredStableUri;
312
    }
313

    
314

    
315
    /**
316
     * Returns the boolean value indicating if this specimen or observation should be withheld
317
     * (<code>publish=false</code>) or not (<code>publish=true</code>) during any publication
318
     * process to the general public.
319
     * This publish flag implementation is preliminary and may be replaced by a more general
320
     * implementation of READ rights in future.<BR>
321
     * The default value is <code>true</code>.
322
     */
323
    @Override
324
    public boolean isPublish() {
325
        return publish;
326
    }
327

    
328
    /**
329
     * @see #isPublish()
330
     * @param publish
331
     */
332
    @Override
333
    public void setPublish(boolean publish) {
334
        this.publish = publish;
335
    }
336

    
337
    /**
338
     * The descriptions this specimen or observation is part of.<BR>
339
     * A specimen can not only have it's own {@link SpecimenDescription specimen description }
340
     * but can also be part of a {@link TaxonDescription taxon description} or a
341
     * {@link TaxonNameDescription taxon name description}.<BR>
342
     * @see #getSpecimenDescriptions()
343
     * @return
344
     */
345
    @Override
346
    public Set<DescriptionBase<S>> getDescriptions() {
347
        if(descriptions == null) {
348
            this.descriptions = new HashSet<>();
349
        }
350
        return this.descriptions;
351
    }
352

    
353
    /**
354
     * Returns the {@link SpecimenDescription specimen descriptions} this specimen is part of.
355
     * @see #getDescriptions()
356
     * @return
357
     */
358
    @Transient
359
    public Set<SpecimenDescription> getSpecimenDescriptions() {
360
        return getSpecimenDescriptions(true);
361
    }
362

    
363
    /**
364
     * Returns the {@link SpecimenDescription specimen descriptions} this specimen is part of.
365
     * @see #getDescriptions()
366
     * @return
367
     */
368
    @Transient
369
    public Set<SpecimenDescription> getSpecimenDescriptions(boolean includeImageGallery) {
370
        Set<SpecimenDescription> specimenDescriptions = new HashSet<>();
371
        for (DescriptionBase<?> descriptionBase : getDescriptions()){
372
            if (descriptionBase.isInstanceOf(SpecimenDescription.class)){
373
                if (includeImageGallery || descriptionBase.isImageGallery() == false){
374
                    specimenDescriptions.add(descriptionBase.deproxy(descriptionBase, SpecimenDescription.class));
375
                }
376

    
377
            }
378
        }
379
        return specimenDescriptions;
380
    }
381
    /**
382
     * Returns the {@link SpecimenDescription specimen descriptions} which act as an image gallery
383
     * and which this specimen is part of.
384
     * @see #getDescriptions()
385
     * @return
386
     */
387
    @Transient
388
    public Set<SpecimenDescription> getSpecimenDescriptionImageGallery() {
389
        Set<SpecimenDescription> specimenDescriptions = new HashSet<>();
390
        for (DescriptionBase<?> descriptionBase : getDescriptions()){
391
            if (descriptionBase.isInstanceOf(SpecimenDescription.class)){
392
                if (descriptionBase.isImageGallery() == true){
393
                    specimenDescriptions.add(descriptionBase.deproxy(descriptionBase, SpecimenDescription.class));
394
                }
395
            }
396
        }
397
        return specimenDescriptions;
398
    }
399

    
400
    /**
401
     * Adds a new description to this specimen or observation
402
     * @param description
403
     */
404
    @Override
405
    public void addDescription(DescriptionBase description) {
406
        if (description.getDescribedSpecimenOrObservation() != null){
407
            description.getDescribedSpecimenOrObservation().removeDescription(description);
408
        }
409
        descriptions.add(description);
410
        description.setDescribedSpecimenOrObservation(this);
411
    }
412

    
413
    /**
414
     * Removes a specimen from a description (removes a description from this specimen)
415
     * @param description
416
     */
417
    @Override
418
    public void removeDescription(DescriptionBase description) {
419
        boolean existed = descriptions.remove(description);
420
        if (existed){
421
            description.setDescribedSpecimenOrObservation(null);
422
        }
423
    }
424

    
425

    
426
    public Set<DerivationEvent> getDerivationEvents() {
427
        if(derivationEvents == null) {
428
            this.derivationEvents = new HashSet<>();
429
        }
430
        return this.derivationEvents;
431
    }
432

    
433
    public void addDerivationEvent(DerivationEvent derivationEvent) {
434
        if (! this.derivationEvents.contains(derivationEvent)){
435
            this.derivationEvents.add(derivationEvent);
436
            derivationEvent.addOriginal(this);
437
        }
438
    }
439

    
440
    public void removeDerivationEvent(DerivationEvent derivationEvent) {
441
        if (this.derivationEvents.contains(derivationEvent)){
442
            this.derivationEvents.remove(derivationEvent);
443
            derivationEvent.removeOriginal(this);
444
        }
445
    }
446

    
447
    public Set<DeterminationEvent> getDeterminations() {
448
        if(determinations == null) {
449
            this.determinations = new HashSet<>();
450
        }
451
        return this.determinations;
452
    }
453

    
454
    public void addDetermination(DeterminationEvent determination) {
455
        // FIXME bidirectional integrity. Use protected Determination setter
456
        this.determinations.add(determination);
457
    }
458

    
459
    public void removeDetermination(DeterminationEvent determination) {
460
        // FIXME bidirectional integrity. Use protected Determination setter
461
        this.determinations.remove(determination);
462
    }
463

    
464
    public DefinedTerm getSex() {
465
        return sex;
466
    }
467

    
468
    public void setSex(DefinedTerm sex) {
469
        this.sex = sex;
470
    }
471

    
472
    public DefinedTerm getLifeStage() {
473
        return lifeStage;
474
    }
475

    
476
    public void setLifeStage(DefinedTerm lifeStage) {
477
        this.lifeStage = lifeStage;
478
    }
479

    
480

    
481
    /**
482
     * @see #kindOfUnit
483
     * @return
484
     */
485
    public DefinedTerm getKindOfUnit() {
486
        return kindOfUnit;
487
    }
488

    
489
    /**
490
     * @see #kindOfUnit
491
     * @param kindOfUnit
492
     */
493
    public void setKindOfUnit(DefinedTerm kindOfUnit) {
494
        this.kindOfUnit = kindOfUnit;
495
    }
496

    
497
    public String getIndividualCount() {
498
        return individualCount;
499
    }
500

    
501
    public void setIndividualCount(String individualCount) {
502
        this.individualCount = individualCount;
503
    }
504

    
505
    public Map<Language,LanguageString> getDefinition(){
506
        return this.definition;
507
    }
508

    
509
    /**
510
     * adds the {@link LanguageString description} to the {@link MultilanguageText multilanguage text}
511
     * used to define <i>this</i> specimen or observation.
512
     *
513
     * @param description	the languageString in with the title string and the given language
514
     *
515
     * @see    	   		#getDefinition()
516
     * @see    	   		#putDefinition(Language, String)
517
     */
518
    public void putDefinition(LanguageString description){
519
        this.definition.put(description.getLanguage(),description);
520
    }
521

    
522
    /**
523
     * Creates a {@link LanguageString language string} based on the given text string
524
     * and the given {@link Language language} and adds it to the {@link MultilanguageText multilanguage text}
525
     * used to define <i>this</i> specimen or observation.
526
     *
527
     * @param language	the language in which the title string is formulated
528
     * @param text		the definition in a particular language
529
     *
530
     * @see    	   		#getDefinition()
531
     * @see    	   		#putDefinition(LanguageString)
532
     */
533
    public void putDefinition(Language language, String text){
534
        this.definition.put(language, LanguageString.NewInstance(text, language));
535
    }
536

    
537

    
538
    public void removeDefinition(Language lang){
539
        this.definition.remove(lang);
540
    }
541

    
542
    /**
543
     * for derived units get the single next higher parental/original unit.
544
     * If multiple original units exist throw error
545
     * @return
546
     */
547
    @Transient
548
    public SpecimenOrObservationBase getOriginalUnit(){
549
        logger.warn("GetOriginalUnit not yet implemented");
550
        return null;
551
    }
552

    
553

    
554
    public boolean hasCharacterData() {
555
        Set<DescriptionBase<S>> descriptions = this.getDescriptions();
556
        for (DescriptionBase<?> descriptionBase : descriptions) {
557
            if (descriptionBase.isInstanceOf(SpecimenDescription.class)) {
558
                SpecimenDescription specimenDescription = HibernateProxyHelper.deproxy(descriptionBase, SpecimenDescription.class);
559
                Set<DescriptionElementBase> elements = specimenDescription.getElements();
560
                for (DescriptionElementBase descriptionElementBase : elements) {
561
                    if (descriptionElementBase.isCharacterData()){
562
                        return true;
563
                    }
564
                }
565
            }
566
        }
567
        return false;
568
    }
569

    
570
    /**
571
     * Returns a list of all description items which
572
     * @return
573
     */
574
    @Transient
575
    public Collection<DescriptionElementBase> characterData() {
576
        Collection<DescriptionElementBase> states = new ArrayList<>();
577
        Set<DescriptionBase<S>> descriptions = this.getDescriptions();
578
        for (DescriptionBase<?> descriptionBase : descriptions) {
579
            if (descriptionBase.isInstanceOf(SpecimenDescription.class)) {
580
                SpecimenDescription specimenDescription = HibernateProxyHelper.deproxy(descriptionBase, SpecimenDescription.class);
581
                Set<DescriptionElementBase> elements = specimenDescription.getElements();
582
                for (DescriptionElementBase descriptionElementBase : elements) {
583
                    if(descriptionElementBase.isCharacterData()){
584
                        states.add(descriptionElementBase);
585
                    }
586
                }
587
            }
588
        }
589
        return states;
590
    }
591

    
592
    public Collection<DerivedUnit> collectDerivedUnits() {
593
        Collection<DerivedUnit> derivedUnits = new ArrayList<>();
594
        for (DerivationEvent derivationEvent : getDerivationEvents()) {
595
            for (DerivedUnit derivative : derivationEvent.getDerivatives()) {
596
                derivedUnits.add(derivative);
597
                derivedUnits.addAll(derivative.collectDerivedUnits());
598
            }
599
        }
600
        return derivedUnits;
601
    }
602

    
603
//******************** CLONE **********************************************/
604

    
605
    @Override
606
    public SpecimenOrObservationBase<S> clone() throws CloneNotSupportedException {
607
        SpecimenOrObservationBase<S> result = (SpecimenOrObservationBase<S>)super.clone();
608

    
609
        //defininion (description, languageString)
610
        result.definition = cloneLanguageString(this.definition);
611

    
612
        //sex
613
        result.setSex(this.sex);
614
        //life stage
615
        result.setLifeStage(this.lifeStage);
616

    
617
        result.descriptions = new HashSet<>();
618
        //Descriptions
619
        for(DescriptionBase<S> description : this.descriptions) {
620
            result.addDescription(description.clone());
621
        }
622

    
623
        result.determinations = new HashSet<>();
624
        for(DeterminationEvent determination : this.determinations) {
625
            result.addDetermination(determination.clone());
626
        }
627

    
628
        result.derivationEvents = new HashSet<>();
629
        //DerivationEvent
630
        for(DerivationEvent derivationEvent : this.derivationEvents) {
631
            //TODO should we clone this?
632
            result.addDerivationEvent(derivationEvent);
633
        }
634

    
635
        //no changes to: individualCount
636
        return result;
637
    }
638
}
(11-11/14)