Project

General

Profile

Download (22.4 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.common;
11

    
12

    
13
import java.beans.PropertyChangeEvent;
14
import java.beans.PropertyChangeListener;
15
import java.util.ArrayList;
16
import java.util.HashSet;
17
import java.util.List;
18
import java.util.Set;
19
import java.util.UUID;
20

    
21
import javax.persistence.Column;
22
import javax.persistence.Embedded;
23
import javax.persistence.FetchType;
24
import javax.persistence.MappedSuperclass;
25
import javax.persistence.OneToMany;
26
import javax.persistence.Transient;
27
import javax.validation.constraints.NotNull;
28
import javax.validation.constraints.Size;
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.XmlTransient;
34
import javax.xml.bind.annotation.XmlType;
35
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
36

    
37
import org.apache.log4j.Logger;
38
import org.hibernate.annotations.Cascade;
39
import org.hibernate.annotations.CascadeType;
40
import org.hibernate.annotations.IndexColumn;
41
import org.hibernate.envers.Audited;
42
import org.hibernate.search.annotations.Analyze;
43
import org.hibernate.search.annotations.Field;
44
import org.hibernate.search.annotations.FieldBridge;
45
import org.hibernate.search.annotations.Fields;
46
import org.hibernate.search.annotations.Store;
47
import org.hibernate.validator.constraints.NotEmpty;
48

    
49
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
50
import eu.etaxonomy.cdm.hibernate.search.StripHtmlBridge;
51
import eu.etaxonomy.cdm.jaxb.FormattedTextAdapter;
52
import eu.etaxonomy.cdm.jaxb.LSIDAdapter;
53
import eu.etaxonomy.cdm.model.media.Rights;
54
import eu.etaxonomy.cdm.model.name.NonViralName;
55
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
56
import eu.etaxonomy.cdm.model.reference.Reference;
57
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
58
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
59
import eu.etaxonomy.cdm.strategy.match.Match;
60
import eu.etaxonomy.cdm.strategy.match.Match.ReplaceMode;
61
import eu.etaxonomy.cdm.strategy.match.MatchMode;
62
import eu.etaxonomy.cdm.strategy.merge.Merge;
63
import eu.etaxonomy.cdm.strategy.merge.MergeMode;
64
import eu.etaxonomy.cdm.validation.Level2;
65

    
66
/**
67
 * Superclass for the primary CDM classes that can be referenced from outside via LSIDs and contain a simple generated title string as a label for human reading.
68
 * All subclasses inherit the ability to store additional properties that are stored as {@link Extension Extensions}, basically a string value with a type term.
69
 * Any number of right statements can be attached as well as multiple {@link OriginalSourceBase} objects.
70
 * Original sources carry a reference to the source, an ID within that source and the original title/label of this object as it was used in that source (originalNameString).
71
 * A Taxon for example that was taken from 2 sources like FaunaEuropaea and IPNI would have two originalSource objects.
72
 * The originalSource representing that taxon as it was found in IPNI would contain IPNI as the reference, the IPNI id of the taxon and the name of the taxon exactly as it was used in IPNI.
73
 *
74
 * @author m.doering
75
 * @version 1.0
76
 * @created 08-Nov-2007 13:06:27
77
 */
78
@XmlAccessorType(XmlAccessType.FIELD)
79
@XmlType(name = "IdentifiableEntity", propOrder = {
80
    "lsid",
81
    "titleCache",
82
    "protectedTitleCache",
83
    "rights",
84
    "extensions",
85
    "credits",
86
    "sources"
87
})
88
@Audited
89
@MappedSuperclass
90
public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrategy> extends AnnotatableEntity
91
        implements IIdentifiableEntity /*, ISourceable<IdentifiableSource> */ {
92
    private static final long serialVersionUID = -5610995424730659058L;
93
    private static final Logger logger = Logger.getLogger(IdentifiableEntity.class);
94

    
95
    @XmlTransient
96
    public static final boolean PROTECTED = true;
97
    @XmlTransient
98
    public static final boolean NOT_PROTECTED = false;
99

    
100
    @XmlElement(name = "LSID", type = String.class)
101
    @XmlJavaTypeAdapter(LSIDAdapter.class)
102
    @Embedded
103
    private LSID lsid;
104

    
105
    @XmlElement(name = "TitleCache", required = true)
106
    @XmlJavaTypeAdapter(FormattedTextAdapter.class)
107
    @Column(length=255, name="titleCache")
108
    @Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.ALL)
109
    @NotEmpty(groups = Level2.class) // implictly NotNull
110
    @Size(max = 800)  //see #1592
111
    @Fields({
112
        @Field(store=Store.YES),
113
        @Field(name = "titleCache__sort", analyze = Analyze.NO, store=Store.YES)
114
    })
115
    @FieldBridge(impl=StripHtmlBridge.class)
116
    protected String titleCache;
117

    
118
    //if true titleCache will not be automatically generated/updated
119
    @XmlElement(name = "ProtectedTitleCache")
120
    protected boolean protectedTitleCache;
121

    
122
    @XmlElementWrapper(name = "Rights", nillable = true)
123
    @XmlElement(name = "Rights")
124
    @OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
125
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
126
    //TODO
127
    @Merge(MergeMode.ADD_CLONE)
128
    @NotNull
129
    private Set<Rights> rights = new HashSet<Rights>();
130

    
131
    @XmlElementWrapper(name = "Credits", nillable = true)
132
    @XmlElement(name = "Credit")
133
    @IndexColumn(name="sortIndex", base = 0)
134
    @OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
135
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
136
    //TODO
137
    @Merge(MergeMode.ADD_CLONE)
138
    @NotNull
139
    private List<Credit> credits = new ArrayList<Credit>();
140

    
141
    @XmlElementWrapper(name = "Extensions", nillable = true)
142
    @XmlElement(name = "Extension")
143
    @OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
144
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
145
    @Merge(MergeMode.ADD_CLONE)
146
    @NotNull
147
    private Set<Extension> extensions = new HashSet<Extension>();
148

    
149
    @XmlElementWrapper(name = "Sources", nillable = true)
150
    @XmlElement(name = "IdentifiableSource")
151
    @OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
152
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
153
    @Merge(MergeMode.ADD_CLONE)
154
    @NotNull
155
    private Set<IdentifiableSource> sources = new HashSet<IdentifiableSource>();
156

    
157
    @XmlTransient
158
    @Transient
159
    protected S cacheStrategy;
160

    
161
    protected IdentifiableEntity(){
162
        initListener();
163
    }
164

    
165
    protected void initListener(){
166
        PropertyChangeListener listener = new PropertyChangeListener() {
167
            @Override
168
            public void propertyChange(PropertyChangeEvent e) {
169
                if (!e.getPropertyName().equals("titleCache") && !e.getPropertyName().equals("cacheStrategy") && ! isProtectedTitleCache()){
170
                    titleCache = null;
171
                }
172
            }
173
        };
174
        addPropertyChangeListener(listener);
175
    }
176

    
177
    /**
178
     * By default, we expect most cdm objects to be abstract things
179
     * i.e. unable to return a data representation.
180
     *
181
     * Specific subclasses (e.g. Sequence) can override if necessary.
182
     */
183
    @Override
184
    public byte[] getData() {
185
        return null;
186
    }
187

    
188
//******************************** CACHE *****************************************************/
189

    
190

    
191
    /* (non-Javadoc)
192
     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getTitleCache()
193
     */
194
    // @Transient  - must not be transient, since this property needs to to be included in all serializations produced by the remote layer
195
    @Override
196
    public String getTitleCache(){
197
        if (protectedTitleCache){
198
            return this.titleCache;
199
        }
200
        // is title dirty, i.e. equal NULL?
201
        if (titleCache == null){
202
            this.titleCache = generateTitle();
203
            this.titleCache = getTruncatedCache(this.titleCache) ;
204
        }
205
        return titleCache;
206
    }
207

    
208
    /**
209
     * The titleCache will be regenerated from scratch if not protected
210
     * @return <code>true</code> if title cache was regenerated, <code>false</code> otherwise
211
     */
212
    protected boolean regenerateTitleCache() {
213
        if (!protectedTitleCache) {
214
            this.titleCache = null;
215
            getTitleCache();
216
        }
217
        return protectedTitleCache;
218
    }
219
    /* (non-Javadoc)
220
     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#setTitleCache(java.lang.String)
221
     */
222
    @Override
223
    public void setTitleCache(String titleCache){
224
        this.titleCache = titleCache;
225
    }
226

    
227
    /* (non-Javadoc)
228
     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#setTitleCache(java.lang.String, boolean)
229
     */
230
    @Override
231
    public void setTitleCache(String titleCache, boolean protectCache){
232
        titleCache = getTruncatedCache(titleCache);
233
        this.titleCache = titleCache;
234
        this.protectedTitleCache = protectCache;
235
    }
236

    
237

    
238
    /**
239
     * @param cache
240
     * @return
241
     */
242
    @Transient
243
    protected String getTruncatedCache(String cache) {
244
        if (cache != null && cache.length() > 1023){
245
            logger.warn("Truncation of cache: " + this.toString() + "/" + cache);
246
            cache = cache.substring(0, 1020) + "...";
247
        }
248
        return cache;
249
    }
250

    
251
//**************************************************************************************
252

    
253
    /* (non-Javadoc)
254
     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getLsid()
255
     */
256
    @Override
257
    public LSID getLsid(){
258
        return this.lsid;
259
    }
260
    /* (non-Javadoc)
261
     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#setLsid(java.lang.String)
262
     */
263
    @Override
264
    public void setLsid(LSID lsid){
265
        this.lsid = lsid;
266
    }
267
    /* (non-Javadoc)
268
     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getRights()
269
     */
270
    @Override
271
    public Set<Rights> getRights() {
272
        if(rights == null) {
273
            this.rights = new HashSet<Rights>();
274
        }
275
        return this.rights;
276
    }
277

    
278
    /* (non-Javadoc)
279
     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addRights(eu.etaxonomy.cdm.model.media.Rights)
280
     */
281
    @Override
282
    public void addRights(Rights right){
283
        getRights().add(right);
284
    }
285
    /* (non-Javadoc)
286
     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeRights(eu.etaxonomy.cdm.model.media.Rights)
287
     */
288
    @Override
289
    public void removeRights(Rights right){
290
        getRights().remove(right);
291
    }
292

    
293

    
294
    @Override
295
    public List<Credit> getCredits() {
296
        if(credits == null) {
297
            this.credits = new ArrayList<Credit>();
298
        }
299
        return this.credits;
300
    }
301

    
302
    /* (non-Javadoc)
303
     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getCredits(int)
304
     */
305
    @Override
306
    public Credit getCredits(Integer index){
307
        return getCredits().get(index);
308
    }
309

    
310
    /* (non-Javadoc)
311
     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addCredit(eu.etaxonomy.cdm.model.common.Credit)
312
     */
313
    @Override
314
    public void addCredit(Credit credit){
315
        getCredits().add(credit);
316
    }
317

    
318

    
319
    /* (non-Javadoc)
320
     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addCredit(eu.etaxonomy.cdm.model.common.Credit, int)
321
     */
322
    @Override
323
    public void addCredit(Credit credit, int index){
324
        getCredits().add(index, credit);
325
    }
326

    
327
    /* (non-Javadoc)
328
     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeCredit(eu.etaxonomy.cdm.model.common.Credit)
329
     */
330
    @Override
331
    public void removeCredit(Credit credit){
332
        getCredits().remove(credit);
333
    }
334

    
335
    /* (non-Javadoc)
336
     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeCredit(int)
337
     */
338
    @Override
339
    public void removeCredit(int index){
340
        getCredits().remove(index);
341
    }
342

    
343

    
344
    /* (non-Javadoc)
345
     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getExtensions()
346
     */
347
    @Override
348
    public Set<Extension> getExtensions(){
349
        if(extensions == null) {
350
            this.extensions = new HashSet<Extension>();
351
        }
352
        return this.extensions;
353
    }
354
    /**
355
     * @param type
356
     * @return a Set of extension value strings
357
     */
358
    public Set<String> getExtensions(ExtensionType type){
359
       return getExtensions(type.getUuid());
360
    }
361
    /**
362
     * @param extensionTypeUuid
363
     * @return a Set of extension value strings
364
     */
365
    public Set<String> getExtensions(UUID extensionTypeUuid){
366
        Set<String> result = new HashSet<String>();
367
        for (Extension extension : getExtensions()){
368
            if (extension.getType().getUuid().equals(extensionTypeUuid)){
369
                result.add(extension.getValue());
370
            }
371
        }
372
        return result;
373
    }
374

    
375
    public void addExtension(String value, ExtensionType extensionType){
376
        Extension.NewInstance(this, value, extensionType);
377
    }
378

    
379
    /* (non-Javadoc)
380
     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addExtension(eu.etaxonomy.cdm.model.common.Extension)
381
     */
382
    @Override
383
    public void addExtension(Extension extension){
384
        if (extension != null){
385
            extension.setExtendedObj(this);
386
            getExtensions().add(extension);
387
        }
388
    }
389
    /* (non-Javadoc)
390
     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeExtension(eu.etaxonomy.cdm.model.common.Extension)
391
     */
392
    @Override
393
    public void removeExtension(Extension extension){
394
        if (extension != null){
395
            extension.setExtendedObj(null);
396
            getExtensions().remove(extension);
397
        }
398
    }
399

    
400

    
401
    /* (non-Javadoc)
402
     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#isProtectedTitleCache()
403
     */
404
    @Override
405
    public boolean isProtectedTitleCache() {
406
        return protectedTitleCache;
407
    }
408

    
409
    /* (non-Javadoc)
410
     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#setProtectedTitleCache(boolean)
411
     */
412
    @Override
413
    public void setProtectedTitleCache(boolean protectedTitleCache) {
414
        this.protectedTitleCache = protectedTitleCache;
415
    }
416

    
417
    /* (non-Javadoc)
418
     * @see eu.etaxonomy.cdm.model.common.ISourceable#getSources()
419
     */
420
    @Override
421
    public Set<IdentifiableSource> getSources() {
422
        if(sources == null) {
423
            this.sources = new HashSet<IdentifiableSource>();
424
        }
425
        return this.sources;
426
    }
427

    
428
    /* (non-Javadoc)
429
     * @see eu.etaxonomy.cdm.model.common.ISourceable#addSource(eu.etaxonomy.cdm.model.common.OriginalSourceBase)
430
     */
431
    @Override
432
    public void addSource(IdentifiableSource source) {
433
        if (source != null){
434
            IdentifiableEntity oldSourcedObj = source.getSourcedObj();
435
            if (oldSourcedObj != null && oldSourcedObj != this){
436
                oldSourcedObj.getSources().remove(source);
437
            }
438
            getSources().add(source);
439
            source.setSourcedObj(this);
440
        }
441
    }
442

    
443
    /* (non-Javadoc)
444
     * @see eu.etaxonomy.cdm.model.common.ISourceable#addSource(java.lang.String, java.lang.String, eu.etaxonomy.cdm.model.reference.Reference, java.lang.String)
445
     */
446
    @Override
447
    public IdentifiableSource addSource(OriginalSourceType type, String id, String idNamespace, Reference citation, String microCitation) {
448
        if (id == null && idNamespace == null && citation == null && microCitation == null){
449
            return null;
450
        }
451
        IdentifiableSource source = IdentifiableSource.NewInstance(type, id, idNamespace, citation, microCitation);
452
        addSource(source);
453
        return source;
454
    }
455
    
456
    
457
    /* (non-Javadoc)
458
     * @see eu.etaxonomy.cdm.model.common.ISourceable#addImportSource(java.lang.String, java.lang.String, eu.etaxonomy.cdm.model.reference.Reference, java.lang.String)
459
     */
460
    @Override
461
    public IdentifiableSource addImportSource(String id, String idNamespace, Reference<?> citation, String microCitation) {
462
        if (id == null && idNamespace == null && citation == null && microCitation == null){
463
            return null;
464
        }
465
        IdentifiableSource source = IdentifiableSource.NewInstance(OriginalSourceType.Import, id, idNamespace, citation, microCitation);
466
        addSource(source);
467
        return source;
468
    }
469

    
470
     /* (non-Javadoc)
471
     * @see eu.etaxonomy.cdm.model.common.ISourceable#removeSource(eu.etaxonomy.cdm.model.common.IOriginalSource)
472
     */
473
    @Override
474
    public void removeSource(IdentifiableSource source) {
475
        getSources().remove(source);
476
    }
477

    
478
//******************************** TO STRING *****************************************************/
479

    
480
    /* (non-Javadoc)
481
     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#toString()
482
     */
483
     @Override
484
    public String toString() {
485
        String result;
486
        if (titleCache == null){
487
            result = super.toString();
488
        }else{
489
            result = this.titleCache;
490
        }
491
        return result;
492
    }
493

    
494

    
495
    public int compareTo(IdentifiableEntity identifiableEntity) {
496

    
497
         int result = 0;
498

    
499
         if (identifiableEntity == null) {
500
             throw new NullPointerException("Cannot compare to null.");
501
         }
502

    
503
         // First, compare the name cache.
504
         // TODO: Avoid using instanceof operator
505
         // Use Class.getDeclaredMethod() instead to find out whether class has getNameCache() method?
506

    
507
         String specifiedNameCache = "";
508
         String thisNameCache = "";
509
         String specifiedTitleCache = "";
510
         String thisTitleCache = "";
511
         String specifiedReferenceTitleCache = "";
512
         String thisReferenceTitleCache = "";
513
         String thisGenusString = "";
514
         String specifiedGenusString = "";
515
         int thisrank_order = 0;
516

    
517
         if(identifiableEntity instanceof NonViralName) {
518
             specifiedNameCache = HibernateProxyHelper.deproxy(identifiableEntity, NonViralName.class).getNameCache();
519
             specifiedTitleCache = identifiableEntity.getTitleCache();
520

    
521
         } else if(identifiableEntity instanceof TaxonBase) {
522
             TaxonBase taxonBase = HibernateProxyHelper.deproxy(identifiableEntity, TaxonBase.class);
523

    
524
             TaxonNameBase<?,?> taxonNameBase = taxonBase.getName();
525

    
526

    
527
             NonViralName nonViralName = HibernateProxyHelper.deproxy(taxonNameBase, NonViralName.class);
528
             specifiedNameCache = nonViralName.getNameCache();
529
             specifiedTitleCache = taxonNameBase.getTitleCache();
530

    
531
             specifiedReferenceTitleCache = ((TaxonBase)identifiableEntity).getSec().getTitleCache();
532
             Reference reference = taxonBase.getSec();
533
             if (reference != null) {
534
                 reference = HibernateProxyHelper.deproxy(reference, Reference.class);
535
                 specifiedReferenceTitleCache = reference.getTitleCache();
536
             }
537
         }
538

    
539
         if(this instanceof NonViralName) {
540
             thisNameCache = HibernateProxyHelper.deproxy(this, NonViralName.class).getNameCache();
541
             thisTitleCache = getTitleCache();
542
         } else if(this instanceof TaxonBase) {
543
             TaxonNameBase<?,?> taxonNameBase= HibernateProxyHelper.deproxy(this, TaxonBase.class).getName();
544
             NonViralName nonViralName = HibernateProxyHelper.deproxy(taxonNameBase, NonViralName.class);
545
             thisNameCache = nonViralName.getNameCache();
546
             thisTitleCache = taxonNameBase.getTitleCache();
547
             thisReferenceTitleCache = getTitleCache();
548
             thisGenusString = nonViralName.getGenusOrUninomial();
549
         }
550

    
551
         // Compare name cache of taxon names
552

    
553
         if (!specifiedNameCache.equals("") && !thisNameCache.equals("")) {
554
             result = thisNameCache.compareTo(specifiedNameCache);
555
         }
556

    
557
         // Compare title cache of taxon names
558

    
559
         if ((result == 0) && (!specifiedTitleCache.equals("") || !thisTitleCache.equals(""))) {
560
             result = thisTitleCache.compareTo(specifiedTitleCache);
561
         }
562

    
563
         // Compare title cache of taxon references
564

    
565
         if ((result == 0) && (!specifiedReferenceTitleCache.equals("") || !thisReferenceTitleCache.equals(""))) {
566
             result = thisReferenceTitleCache.compareTo(specifiedReferenceTitleCache);
567
         }
568

    
569
         return result;
570
     }
571

    
572
        /**
573
         * Returns the {@link eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy cache strategy} used to generate
574
         * several strings corresponding to <i>this</i> identifiable entity
575
         * (in particular taxon name caches and author strings).
576
         *
577
         * @return  the cache strategy used for <i>this</i> identifiable entity
578
         * @see     eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy
579
         */
580
        public S getCacheStrategy() {
581
            return this.cacheStrategy;
582
        }
583
        /**
584
         * @see 	#getCacheStrategy()
585
         */
586

    
587
        public void setCacheStrategy(S cacheStrategy) {
588
            this.cacheStrategy = cacheStrategy;
589
        }
590

    
591
        @Override
592
        public String generateTitle() {
593
            if (cacheStrategy == null){
594
                //logger.warn("No CacheStrategy defined for "+ this.getClass() + ": " + this.getUuid());
595
                return this.getClass() + ": " + this.getUuid();
596
            }else{
597
                return cacheStrategy.getTitleCache(this);
598
            }
599
        }
600

    
601
//****************** CLONE ************************************************/
602

    
603
    /* (non-Javadoc)
604
     * @see eu.etaxonomy.cdm.model.common.AnnotatableEntity#clone()
605
     */
606
    @Override
607
    public Object clone() throws CloneNotSupportedException{
608
        IdentifiableEntity result = (IdentifiableEntity)super.clone();
609

    
610
        //Extensions
611
        result.extensions = new HashSet<Extension>();
612
        for (Extension extension : getExtensions() ){
613
            Extension newExtension = (Extension)extension.clone();
614
            result.addExtension(newExtension);
615
        }
616

    
617
        //OriginalSources
618
        result.sources = new HashSet<IdentifiableSource>();
619
        for (IdentifiableSource source : getSources()){
620
            IdentifiableSource newSource = (IdentifiableSource)source.clone();
621
            result.addSource(newSource);
622
        }
623

    
624
        //Rights
625
        result.rights = new HashSet<Rights>();
626
        for(Rights rights : getRights()) {
627
            result.addRights(rights);
628
        }
629

    
630

    
631
        //Credits
632
        result.credits = new ArrayList<Credit>();
633
        for(Credit credit : getCredits()) {
634
            result.addCredit(credit);
635
        }
636

    
637
        //no changes to: lsid, titleCache, protectedTitleCache
638

    
639
        //empty titleCache
640
        if (! protectedTitleCache){
641
            result.titleCache = null;
642
        }
643

    
644
        result.initListener();
645
        return result;
646
    }
647

    
648

    
649
}
(35-35/70)