Project

General

Profile

Download (22 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.occurrence;
11

    
12
import java.net.URI;
13
import java.util.ArrayList;
14
import java.util.Collection;
15
import java.util.HashMap;
16
import java.util.HashSet;
17
import java.util.Map;
18
import java.util.Set;
19

    
20
import javax.persistence.Column;
21
import javax.persistence.Entity;
22
import javax.persistence.FetchType;
23
import javax.persistence.Index;
24
import javax.persistence.Inheritance;
25
import javax.persistence.InheritanceType;
26
import javax.persistence.ManyToMany;
27
import javax.persistence.ManyToOne;
28
import javax.persistence.MapKeyJoinColumn;
29
import javax.persistence.OneToMany;
30
import javax.persistence.Table;
31
import javax.persistence.Transient;
32
import javax.validation.constraints.NotNull;
33
import javax.xml.bind.annotation.XmlAccessType;
34
import javax.xml.bind.annotation.XmlAccessorType;
35
import javax.xml.bind.annotation.XmlAttribute;
36
import javax.xml.bind.annotation.XmlElement;
37
import javax.xml.bind.annotation.XmlElementWrapper;
38
import javax.xml.bind.annotation.XmlIDREF;
39
import javax.xml.bind.annotation.XmlRootElement;
40
import javax.xml.bind.annotation.XmlSchemaType;
41
import javax.xml.bind.annotation.XmlType;
42
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
43

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

    
58
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
59
import eu.etaxonomy.cdm.hibernate.search.StripHtmlBridge;
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
    @Type(type="uriUserType")
189
    private URI preferredStableUri;
190

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

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

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

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

    
231

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

    
236

    
237
//********************************** CONSTRUCTOR *********************************/
238

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

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

    
251

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

    
257

    
258
//************************* GETTER / SETTER ***********************/
259

    
260

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

    
270

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

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

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

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

    
313

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

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

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

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

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

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

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

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

    
424

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

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

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

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

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

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

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

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

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

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

    
479

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

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

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

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

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

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

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

    
536

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

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

    
552

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

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

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

    
602
//******************** CLONE **********************************************/
603

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

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

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

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

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

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

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