Project

General

Profile

Download (24.9 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.OrderColumn;
27
import javax.persistence.Transient;
28
import javax.validation.constraints.NotNull;
29
import javax.validation.constraints.Size;
30
import javax.xml.bind.annotation.XmlAccessType;
31
import javax.xml.bind.annotation.XmlAccessorType;
32
import javax.xml.bind.annotation.XmlElement;
33
import javax.xml.bind.annotation.XmlElementWrapper;
34
import javax.xml.bind.annotation.XmlTransient;
35
import javax.xml.bind.annotation.XmlType;
36
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
37

    
38
import org.apache.log4j.Logger;
39
import org.hibernate.annotations.Cascade;
40
import org.hibernate.annotations.CascadeType;
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.BotanicalName;
55
import eu.etaxonomy.cdm.model.name.NonViralName;
56
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
57
import eu.etaxonomy.cdm.model.reference.Reference;
58
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
59
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
60
import eu.etaxonomy.cdm.strategy.match.Match;
61
import eu.etaxonomy.cdm.strategy.match.Match.ReplaceMode;
62
import eu.etaxonomy.cdm.strategy.match.MatchMode;
63
import eu.etaxonomy.cdm.strategy.merge.Merge;
64
import eu.etaxonomy.cdm.strategy.merge.MergeMode;
65
import eu.etaxonomy.cdm.validation.Level2;
66

    
67
/**
68
 * 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.
69
 * All subclasses inherit the ability to store additional properties that are stored as {@link Extension Extensions}, basically a string value with a type term.
70
 * Any number of right statements can be attached as well as multiple {@link OriginalSourceBase} objects.
71
 * 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).
72
 * A Taxon for example that was taken from 2 sources like FaunaEuropaea and IPNI would have two originalSource objects.
73
 * 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.
74
 *
75
 * @author m.doering
76
 * @version 1.0
77
 * @created 08-Nov-2007 13:06:27
78
 */
79
@XmlAccessorType(XmlAccessType.FIELD)
80
@XmlType(name = "IdentifiableEntity", propOrder = {
81
    "lsid",
82
    "titleCache",
83
    "protectedTitleCache",
84
    "credits",
85
    "extensions",
86
    "identifiers",
87
    "rights",
88
    "sources"
89
})
90
@Audited
91
@MappedSuperclass
92
public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrategy> extends AnnotatableEntity
93
        implements IIdentifiableEntity /*, ISourceable<IdentifiableSource> */ {
94
    private static final long serialVersionUID = -5610995424730659058L;
95
    private static final Logger logger = Logger.getLogger(IdentifiableEntity.class);
96

    
97
    @XmlTransient
98
    public static final boolean PROTECTED = true;
99
    @XmlTransient
100
    public static final boolean NOT_PROTECTED = false;
101

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

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

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

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

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

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

    
151
    @XmlElementWrapper(name = "Identifiers", nillable = true)
152
    @XmlElement(name = "Identifier")
153
    @OrderColumn(name="sortIndex")
154
    @OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
155
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
156
    @Merge(MergeMode.ADD_CLONE)
157
    @NotNull
158
    private List<Identifier> identifiers = new ArrayList<Identifier>();
159

    
160
    @XmlElementWrapper(name = "Sources", nillable = true)
161
    @XmlElement(name = "IdentifiableSource")
162
    @OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
163
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
164
    @Merge(MergeMode.ADD_CLONE)
165
    @NotNull
166
    private Set<IdentifiableSource> sources = new HashSet<IdentifiableSource>();
167

    
168
    @XmlTransient
169
    @Transient
170
    protected S cacheStrategy;
171

    
172
    protected IdentifiableEntity(){
173
        initListener();
174
    }
175

    
176
    @Override
177
    public void initListener(){
178
        PropertyChangeListener listener = new PropertyChangeListener() {
179
            @Override
180
            public void propertyChange(PropertyChangeEvent ev) {
181
                if (! "titleCache".equals(ev.getPropertyName()) && !"cacheStrategy".equals(ev.getPropertyName()) && ! isProtectedTitleCache()){
182
                    titleCache = null;
183
                }
184
            }
185
        };
186
        addPropertyChangeListener(listener);
187
    }
188

    
189
    /**
190
     * By default, we expect most cdm objects to be abstract things
191
     * i.e. unable to return a data representation.
192
     *
193
     * Specific subclasses (e.g. Sequence) can override if necessary.
194
     */
195
    @Override
196
    public byte[] getData() {
197
        return null;
198
    }
199

    
200
//******************************** CACHE *****************************************************/
201

    
202
    // @Transient  - must not be transient, since this property needs to to be included in all serializations produced by the remote layer
203
    @Override
204
    public String getTitleCache(){
205
        if (protectedTitleCache){
206
            return this.titleCache;
207
        }
208
        // is title dirty, i.e. equal NULL?
209
        if (titleCache == null){
210
            this.titleCache = generateTitle();
211
            this.titleCache = getTruncatedCache(this.titleCache) ;
212
        }
213
        return titleCache;
214
    }
215

    
216
    @Deprecated
217
    @Override
218
    public void setTitleCache(String titleCache){
219
    	//TODO shouldn't we call setTitleCache(String, boolean),but is this conformant with Java Bean Specification?
220
    	this.titleCache = getTruncatedCache(titleCache);
221
    }
222

    
223
    @Override
224
    public void setTitleCache(String titleCache, boolean protectCache){
225
        titleCache = getTruncatedCache(titleCache);
226
        this.titleCache = titleCache;
227
        this.protectedTitleCache = protectCache;
228
    }
229

    
230
    /**
231
     * @param cache
232
     * @return
233
     */
234
    @Transient
235
    protected String getTruncatedCache(String cache) {
236
        int maxLength = 800;
237
    	if (cache != null && cache.length() > maxLength){
238
            logger.warn("Truncation of cache: " + this.toString() + "/" + cache);
239
            cache = cache.substring(0, maxLength - 4) + "...";   //TODO do we need -4 or is -3 enough
240
        }
241
        return cache;
242
    }
243

    
244
//**************************************************************************************
245

    
246
    @Override
247
    public LSID getLsid(){
248
        return this.lsid;
249
    }
250
    @Override
251
    public void setLsid(LSID lsid){
252
        this.lsid = lsid;
253
    }
254
    @Override
255
    public Set<Rights> getRights() {
256
        if(rights == null) {
257
            this.rights = new HashSet<Rights>();
258
        }
259
        return this.rights;
260
    }
261

    
262
    @Override
263
    public void addRights(Rights right){
264
        getRights().add(right);
265
    }
266
    @Override
267
    public void removeRights(Rights right){
268
        getRights().remove(right);
269
    }
270

    
271

    
272
    @Override
273
    public List<Credit> getCredits() {
274
        if(credits == null) {
275
            this.credits = new ArrayList<Credit>();
276
        }
277
        return this.credits;
278
    }
279

    
280
    @Override
281
    public Credit getCredits(Integer index){
282
        return getCredits().get(index);
283
    }
284

    
285
    @Override
286
    public void addCredit(Credit credit){
287
        getCredits().add(credit);
288
    }
289

    
290

    
291
    @Override
292
    public void addCredit(Credit credit, int index){
293
        getCredits().add(index, credit);
294
    }
295

    
296
    @Override
297
    public void removeCredit(Credit credit){
298
        getCredits().remove(credit);
299
    }
300

    
301
    @Override
302
    public void removeCredit(int index){
303
        getCredits().remove(index);
304
    }
305

    
306
    /* (non-Javadoc)
307
     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getIdentifiers()
308
     */
309
    @Override
310
    public List<Identifier> getIdentifiers(){
311
        if(this.identifiers == null) {
312
            this.identifiers = new ArrayList<Identifier>();
313
        }
314
        return this.identifiers;
315
    }
316
    /**
317
     * @param type
318
     * @return a set of identifier value strings
319
     */
320
    public Set<String> getIdentifiers(DefinedTerm type){
321
       return getIdentifiers(type.getUuid());
322
    }
323
    /**
324
     * @param identifierTypeUuid
325
     * @return a set of identifier value strings
326
     */
327
    public Set<String> getIdentifiers(UUID identifierTypeUuid){
328
        Set<String> result = new HashSet<String>();
329
        for (Identifier identifier : getIdentifiers()){
330
            if (identifier.getType().getUuid().equals(identifierTypeUuid)){
331
                result.add(identifier.getIdentifier());
332
            }
333
        }
334
        return result;
335
    }
336

    
337
    @Override
338
    public Identifier addIdentifier(String identifier, DefinedTerm identifierType){
339
    	Identifier result = Identifier.NewInstance(this, identifier, identifierType);
340
    	return result;
341
    }
342

    
343
     @Override
344
    public void addIdentifier(int index, Identifier identifier){
345
        if (identifier != null){
346
        	if (identifier.getIdentifiedObj() != null && ! identifier.getIdentifiedObj().equals(this)){
347
        		identifier.getIdentifiedObj().removeIdentifier(identifier);
348
        	}
349
        	identifier.setIdentifiedObj(this);
350
        	//deduplication
351
        	int oldIndex = getIdentifiers().indexOf(identifier);
352
        	if(oldIndex > -1){
353
        		getIdentifiers().remove(identifier);
354
        		if (oldIndex < index){
355
        			index--;
356
        		}
357
        	}
358
        	getIdentifiers().add(index, identifier);
359
        }
360
    }
361

    
362
    @Override
363
    public void addIdentifier(Identifier identifier){
364
        addIdentifier(getIdentifiers().size(), identifier);
365
    }
366

    
367
    @Override
368
    public void removeIdentifier(Identifier identifier){
369
        if (identifier != null){
370
        	identifier.setIdentifiedObj(null);
371
            getIdentifiers().remove(identifier);
372
        }
373
    }
374
    @Override
375
    public void removeIdentifier(int index){
376
    	getIdentifiers().remove(index);
377
    }
378

    
379
    @Override
380
    public Set<Extension> getExtensions(){
381
        if(extensions == null) {
382
            this.extensions = new HashSet<Extension>();
383
        }
384
        return this.extensions;
385
    }
386
    /**
387
     * @param type
388
     * @return a Set of extension value strings
389
     */
390
    public Set<String> getExtensions(ExtensionType type){
391
       return getExtensions(type.getUuid());
392
    }
393
    /**
394
     * @param extensionTypeUuid
395
     * @return a Set of extension value strings
396
     */
397
    public Set<String> getExtensions(UUID extensionTypeUuid){
398
        Set<String> result = new HashSet<String>();
399
        for (Extension extension : getExtensions()){
400
            if (extension.getType().getUuid().equals(extensionTypeUuid)){
401
                result.add(extension.getValue());
402
            }
403
        }
404
        return result;
405
    }
406

    
407
    public void addExtension(String value, ExtensionType extensionType){
408
        Extension.NewInstance(this, value, extensionType);
409
    }
410

    
411
    @Override
412
    public void addExtension(Extension extension){
413
        if (extension != null){
414
            extension.setExtendedObj(this);
415
            getExtensions().add(extension);
416
        }
417
    }
418
    @Override
419
    public void removeExtension(Extension extension){
420
        if (extension != null){
421
            extension.setExtendedObj(null);
422
            getExtensions().remove(extension);
423
        }
424
    }
425

    
426
    @Override
427
    public boolean isProtectedTitleCache() {
428
        return protectedTitleCache;
429
    }
430

    
431
    @Override
432
    public void setProtectedTitleCache(boolean protectedTitleCache) {
433
        this.protectedTitleCache = protectedTitleCache;
434
    }
435

    
436
    @Override
437
    public Set<IdentifiableSource> getSources() {
438
        if(sources == null) {
439
            this.sources = new HashSet<IdentifiableSource>();
440
        }
441
        return this.sources;
442
    }
443

    
444
    @Override
445
    public void addSource(IdentifiableSource source) {
446
        if (source != null){
447
            IdentifiableEntity<?> oldSourcedObj = source.getSourcedObj();
448
            if (oldSourcedObj != null && oldSourcedObj != this){
449
                oldSourcedObj.getSources().remove(source);
450
            }
451
            getSources().add(source);
452
            source.setSourcedObj(this);
453
        }
454
    }
455

    
456
    @Override
457
    public void addSources(Set<IdentifiableSource> sources) {
458
        if (sources != null){
459
        	for (IdentifiableSource source: sources){
460
	            IdentifiableEntity<?> oldSourcedObj = source.getSourcedObj();
461
	            if (oldSourcedObj != null && oldSourcedObj != this){
462
	                oldSourcedObj.getSources().remove(source);
463
	            }
464
	            getSources().add(source);
465
	            source.setSourcedObj(this);
466
        	}
467
        }
468
    }
469

    
470
    @Override
471
    public void removeSources() {
472
       this.sources.clear();
473
    }
474

    
475
    @Override
476
    public IdentifiableSource addSource(OriginalSourceType type, String id, String idNamespace, Reference citation, String microCitation) {
477
        if (id == null && idNamespace == null && citation == null && microCitation == null){
478
            return null;
479
        }
480
        IdentifiableSource source = IdentifiableSource.NewInstance(type, id, idNamespace, citation, microCitation);
481
        addSource(source);
482
        return source;
483
    }
484

    
485

    
486
    @Override
487
    public IdentifiableSource addImportSource(String id, String idNamespace, Reference<?> citation, String microCitation) {
488
        if (id == null && idNamespace == null && citation == null && microCitation == null){
489
            return null;
490
        }
491
        IdentifiableSource source = IdentifiableSource.NewInstance(OriginalSourceType.Import, id, idNamespace, citation, microCitation);
492
        addSource(source);
493
        return source;
494
    }
495

    
496

    
497
    @Override
498
    public void removeSource(IdentifiableSource source) {
499
        getSources().remove(source);
500
    }
501

    
502
//******************************** TO STRING *****************************************************/
503

    
504
    /* (non-Javadoc)
505
     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#toString()
506
     */
507
     @Override
508
    public String toString() {
509
        String result;
510
        if (titleCache == null){
511
            result = super.toString();
512
        }else{
513
            result = this.titleCache;
514
        }
515
        return result;
516
    }
517

    
518

    
519
    public int compareTo(IdentifiableEntity identifiableEntity) {
520

    
521
         int result = 0;
522

    
523
         if (identifiableEntity == null) {
524
             throw new NullPointerException("Cannot compare to null.");
525
         }
526

    
527
         // First, compare the name cache.
528
         // TODO: Avoid using instanceof operator
529
         // Use Class.getDeclaredMethod() instead to find out whether class has getNameCache() method?
530

    
531
         String specifiedNameCache = "";
532
         String thisNameCache = "";
533
         String specifiedTitleCache = "";
534
         String thisTitleCache = "";
535
         String specifiedReferenceTitleCache = "";
536
         String thisReferenceTitleCache = "";
537
         String thisGenusString = "";
538
         String specifiedGenusString = "";
539
         int thisrank_order = 0;
540
         final String HYBRID_SIGN = "\u00D7";
541
         final String QUOT_SIGN = "[\\u02BA\\u0022\\u0022]";
542
         //TODO we can remove all the deproxies here except for the first one
543
         identifiableEntity = HibernateProxyHelper.deproxy(identifiableEntity, IdentifiableEntity.class);
544
         if(identifiableEntity instanceof NonViralName) {
545
             specifiedNameCache = HibernateProxyHelper.deproxy(identifiableEntity, NonViralName.class).getNameCache();
546
             specifiedTitleCache = identifiableEntity.getTitleCache();
547
            if (identifiableEntity instanceof BotanicalName){
548
            	 if (((BotanicalName)identifiableEntity).isAutonym()){
549
            		 boolean isProtected = false;
550
            		 String oldNameCache = ((BotanicalName) identifiableEntity).getNameCache();
551
            		 if ( ((BotanicalName)identifiableEntity).isProtectedNameCache()){
552
            			 isProtected = true;
553
            		 }
554
            		 ((BotanicalName)identifiableEntity).setProtectedNameCache(false);
555
            		 ((BotanicalName)identifiableEntity).setNameCache(null, false);
556
            		 specifiedNameCache = ((BotanicalName) identifiableEntity).getNameCache();
557
            		 ((BotanicalName)identifiableEntity).setNameCache(oldNameCache, isProtected);
558

    
559
            	 }
560
             }
561

    
562
         } else if(identifiableEntity instanceof TaxonBase) {
563
             TaxonBase taxonBase = HibernateProxyHelper.deproxy(identifiableEntity, TaxonBase.class);
564

    
565
             TaxonNameBase<?,?> taxonNameBase = taxonBase.getName();
566

    
567

    
568
             NonViralName nonViralName = HibernateProxyHelper.deproxy(taxonNameBase, NonViralName.class);
569
             specifiedNameCache = nonViralName.getNameCache();
570
             specifiedTitleCache = taxonNameBase.getTitleCache();
571

    
572
             specifiedReferenceTitleCache = ((TaxonBase)identifiableEntity).getSec().getTitleCache();
573
             Reference reference = taxonBase.getSec();
574
             if (reference != null) {
575
                 reference = HibernateProxyHelper.deproxy(reference, Reference.class);
576
                 specifiedReferenceTitleCache = reference.getTitleCache();
577
             }
578
         }
579

    
580
         if(this.isInstanceOf(NonViralName.class)) {
581
             thisNameCache = HibernateProxyHelper.deproxy(this, NonViralName.class).getNameCache();
582
             thisTitleCache = getTitleCache();
583

    
584
             if (this instanceof BotanicalName){
585
            	 if (((BotanicalName)this).isAutonym()){
586
            		 boolean isProtected = false;
587
            		 String oldNameCache = ((BotanicalName) this).getNameCache();
588
            		 if ( ((BotanicalName)this).isProtectedNameCache()){
589
            			 isProtected = true;
590
            		 }
591
            		 ((BotanicalName)this).setProtectedNameCache(false);
592
            		 ((BotanicalName)this).setNameCache(null, false);
593
            		 thisNameCache = ((BotanicalName) this).getNameCache();
594
            		 ((BotanicalName)this).setNameCache(oldNameCache, isProtected);
595
            	 }
596
             }
597
         } else if(this.isInstanceOf(TaxonBase.class)) {
598
             TaxonNameBase<?,?> taxonNameBase= HibernateProxyHelper.deproxy(this, TaxonBase.class).getName();
599
             NonViralName nonViralName = HibernateProxyHelper.deproxy(taxonNameBase, NonViralName.class);
600
             thisNameCache = nonViralName.getNameCache();
601
             thisTitleCache = taxonNameBase.getTitleCache();
602
             thisReferenceTitleCache = ((TaxonBase)this).getSec().getTitleCache();
603
             thisGenusString = nonViralName.getGenusOrUninomial();
604
         }
605

    
606
         // Compare name cache of taxon names
607

    
608

    
609

    
610
         if (!specifiedNameCache.equals("") && !thisNameCache.equals("")) {
611

    
612
        	 thisNameCache = thisNameCache.replaceAll(HYBRID_SIGN, "");
613
        	 thisNameCache = thisNameCache.replaceAll(QUOT_SIGN, "");
614

    
615

    
616
        	 specifiedNameCache = specifiedNameCache.replaceAll(HYBRID_SIGN, "");
617
        	 specifiedNameCache = specifiedNameCache.replaceAll(QUOT_SIGN, "");
618

    
619

    
620
             result = thisNameCache.compareTo(specifiedNameCache);
621
         }
622

    
623
         // Compare title cache of taxon names
624

    
625
         if ((result == 0) && (!specifiedTitleCache.equals("") || !thisTitleCache.equals(""))) {
626
        	 thisTitleCache = thisTitleCache.replaceAll(HYBRID_SIGN, "");
627
        	 thisTitleCache = thisTitleCache.replaceAll(QUOT_SIGN, "");
628

    
629
        	 specifiedTitleCache = specifiedTitleCache.replaceAll(HYBRID_SIGN, "");
630
        	 specifiedTitleCache = specifiedTitleCache.replaceAll(QUOT_SIGN, "");
631
             result = thisTitleCache.compareTo(specifiedTitleCache);
632
         }
633

    
634
         // Compare title cache of taxon references
635

    
636
         if ((result == 0) && (!specifiedReferenceTitleCache.equals("") || !thisReferenceTitleCache.equals(""))) {
637
             result = thisReferenceTitleCache.compareTo(specifiedReferenceTitleCache);
638
         }
639

    
640
         return result;
641
     }
642

    
643
    /**
644
     * Returns the {@link eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy cache strategy} used to generate
645
     * several strings corresponding to <i>this</i> identifiable entity
646
     * (in particular taxon name caches and author strings).
647
     *
648
     * @return  the cache strategy used for <i>this</i> identifiable entity
649
     * @see     eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy
650
     */
651
    public S getCacheStrategy() {
652
        return this.cacheStrategy;
653
    }
654
    /**
655
     * @see 	#getCacheStrategy()
656
     */
657

    
658
    public void setCacheStrategy(S cacheStrategy) {
659
        this.cacheStrategy = cacheStrategy;
660
    }
661

    
662
    @Override
663
    public String generateTitle() {
664
        if (getCacheStrategy() == null){
665
            //logger.warn("No CacheStrategy defined for "+ this.getClass() + ": " + this.getUuid());
666
            return this.getClass() + ": " + this.getUuid();
667
        }else{
668
            return getCacheStrategy().getTitleCache(this);
669
        }
670
    }
671

    
672
//****************** CLONE ************************************************/
673

    
674
    @Override
675
    public Object clone() throws CloneNotSupportedException{
676
        IdentifiableEntity<?> result = (IdentifiableEntity<?>)super.clone();
677

    
678
        //Extensions
679
        result.extensions = new HashSet<Extension>();
680
        for (Extension extension : getExtensions() ){
681
            Extension newExtension = (Extension)extension.clone();
682
            result.addExtension(newExtension);
683
        }
684

    
685
        //Identifier
686
        result.identifiers = new ArrayList<Identifier>();
687
        for (Identifier<?> identifier : getIdentifiers() ){
688
        	Identifier<?> newIdentifier = (Identifier<?>)identifier.clone();
689
            result.addIdentifier(newIdentifier);
690
        }
691

    
692
        //OriginalSources
693
        result.sources = new HashSet<IdentifiableSource>();
694
        for (IdentifiableSource source : getSources()){
695
            IdentifiableSource newSource = (IdentifiableSource)source.clone();
696
            result.addSource(newSource);
697
        }
698

    
699
        //Rights
700
        result.rights = new HashSet<Rights>();
701
        for(Rights rights : getRights()) {
702
            result.addRights(rights);
703
        }
704

    
705

    
706
        //Credits
707
        result.credits = new ArrayList<Credit>();
708
        for(Credit credit : getCredits()) {
709
            result.addCredit(credit);
710
        }
711

    
712
        //no changes to: lsid, titleCache, protectedTitleCache
713

    
714
        //empty titleCache
715
        if (! protectedTitleCache){
716
            result.titleCache = null;
717
        }
718

    
719
        result.initListener();
720
        return result;
721
    }
722

    
723

    
724
}
(36-36/72)