Project

General

Profile

Download (25.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.OrderColumn;
27
import javax.persistence.Transient;
28
import javax.validation.constraints.NotNull;
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.envers.Audited;
41
import org.hibernate.search.annotations.Analyze;
42
import org.hibernate.search.annotations.Field;
43
import org.hibernate.search.annotations.FieldBridge;
44
import org.hibernate.search.annotations.Fields;
45
import org.hibernate.search.annotations.Index;
46
import org.hibernate.search.annotations.SortableField;
47
import org.hibernate.search.annotations.Store;
48
import org.hibernate.validator.constraints.NotEmpty;
49

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

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

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

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

    
108
    @XmlElement(name = "TitleCache", required = true)
109
    @XmlJavaTypeAdapter(FormattedTextAdapter.class)
110
    @Column(name="titleCache", length=800) //see #1592
111
    @Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.ALL)
112
    @NotEmpty(groups = Level2.class) // implictly NotNull
113
    @Fields({
114
        @Field(store=Store.YES),
115
        //  If the field is only needed for sorting and nothing else, you may configure it as
116
        //  un-indexed and un-stored, thus avoid unnecessary index growth.
117
        @Field(name = "titleCache__sort", analyze = Analyze.NO, store=Store.NO, index = Index.NO)
118
    })
119
    @SortableField(forField = "titleCache__sort")
120
    @FieldBridge(impl=StripHtmlBridge.class)
121
    protected String titleCache;
122

    
123
    //if true titleCache will not be automatically generated/updated
124
    @XmlElement(name = "ProtectedTitleCache")
125
    protected boolean protectedTitleCache;
126

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

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

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

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

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

    
171
    @XmlTransient
172
    @Transient
173
    protected S cacheStrategy;
174

    
175
    protected IdentifiableEntity(){
176
        initListener();
177
    }
178

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

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

    
203
//******************************** CACHE *****************************************************/
204

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

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

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

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

    
247
//**************************************************************************************
248

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

    
265
    @Override
266
    public void addRights(Rights right){
267
        getRights().add(right);
268
    }
269
    @Override
270
    public void removeRights(Rights right){
271
        getRights().remove(right);
272
    }
273

    
274

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

    
283
    @Override
284
    public Credit getCredits(Integer index){
285
        return getCredits().get(index);
286
    }
287

    
288
    @Override
289
    public void addCredit(Credit credit){
290
        getCredits().add(credit);
291
    }
292

    
293

    
294
    @Override
295
    public void addCredit(Credit credit, int index){
296
        getCredits().add(index, credit);
297
    }
298

    
299
    @Override
300
    public void removeCredit(Credit credit){
301
        getCredits().remove(credit);
302
    }
303

    
304
    @Override
305
    public void removeCredit(int index){
306
        getCredits().remove(index);
307
    }
308

    
309
    @Override
310
    public boolean replaceCredit(Credit newObject, Credit oldObject){
311
        return replaceInList(this.credits, newObject, oldObject);
312
    }
313

    
314

    
315
    @Override
316
    public List<Identifier> getIdentifiers(){
317
        if(this.identifiers == null) {
318
            this.identifiers = new ArrayList<Identifier>();
319
        }
320
        return this.identifiers;
321
    }
322
    /**
323
     * @param type
324
     * @return a set of identifier value strings
325
     */
326
    public Set<String> getIdentifiers(DefinedTerm type){
327
       return getIdentifiers(type.getUuid());
328
    }
329
    /**
330
     * @param identifierTypeUuid
331
     * @return a set of identifier value strings
332
     */
333
    public Set<String> getIdentifiers(UUID identifierTypeUuid){
334
        Set<String> result = new HashSet<String>();
335
        for (Identifier identifier : getIdentifiers()){
336
            if (identifier.getType().getUuid().equals(identifierTypeUuid)){
337
                result.add(identifier.getIdentifier());
338
            }
339
        }
340
        return result;
341
    }
342

    
343
    @Override
344
    public Identifier addIdentifier(String identifier, DefinedTerm identifierType){
345
    	Identifier result = Identifier.NewInstance(this, identifier, identifierType);
346
    	return result;
347
    }
348

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

    
368
    @Override
369
    public void addIdentifier(Identifier identifier){
370
        addIdentifier(getIdentifiers().size(), identifier);
371
    }
372

    
373
    @Override
374
    public void removeIdentifier(Identifier identifier){
375
        if (identifier != null){
376
        	identifier.setIdentifiedObj(null);
377
            getIdentifiers().remove(identifier);
378
        }
379
    }
380
    @Override
381
    public void removeIdentifier(int index){
382
    	getIdentifiers().remove(index);
383
    }
384

    
385
    @Override
386
    public boolean replaceIdentifier(Identifier newObject, Identifier oldObject){
387
        return replaceInList(this.identifiers, newObject, oldObject);
388
    }
389

    
390

    
391
    @Override
392
    public Set<Extension> getExtensions(){
393
        if(extensions == null) {
394
            this.extensions = new HashSet<Extension>();
395
        }
396
        return this.extensions;
397
    }
398
    /**
399
     * @param type
400
     * @return a Set of extension value strings
401
     */
402
    public Set<String> getExtensions(ExtensionType type){
403
       return getExtensions(type.getUuid());
404
    }
405
    /**
406
     * @param extensionTypeUuid
407
     * @return a Set of extension value strings
408
     */
409
    public Set<String> getExtensions(UUID extensionTypeUuid){
410
        Set<String> result = new HashSet<String>();
411
        for (Extension extension : getExtensions()){
412
            if (extension.getType().getUuid().equals(extensionTypeUuid)){
413
                result.add(extension.getValue());
414
            }
415
        }
416
        return result;
417
    }
418

    
419
    public void addExtension(String value, ExtensionType extensionType){
420
        Extension.NewInstance(this, value, extensionType);
421
    }
422

    
423
    @Override
424
    public void addExtension(Extension extension){
425
        if (extension != null){
426
            extension.setExtendedObj(this);
427
            getExtensions().add(extension);
428
        }
429
    }
430
    @Override
431
    public void removeExtension(Extension extension){
432
        if (extension != null){
433
            extension.setExtendedObj(null);
434
            getExtensions().remove(extension);
435
        }
436
    }
437

    
438
    @Override
439
    public boolean isProtectedTitleCache() {
440
        return protectedTitleCache;
441
    }
442

    
443
    @Override
444
    public void setProtectedTitleCache(boolean protectedTitleCache) {
445
        this.protectedTitleCache = protectedTitleCache;
446
    }
447

    
448
    @Override
449
    public Set<IdentifiableSource> getSources() {
450
        if(sources == null) {
451
            this.sources = new HashSet<IdentifiableSource>();
452
        }
453
        return this.sources;
454
    }
455

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

    
468
    @Override
469
    public void addSources(Set<IdentifiableSource> sources) {
470
        if (sources != null){
471
        	for (IdentifiableSource source: sources){
472
	            IdentifiableEntity<?> oldSourcedObj = source.getSourcedObj();
473
	            if (oldSourcedObj != null && oldSourcedObj != this){
474
	                oldSourcedObj.getSources().remove(source);
475
	            }
476
	            getSources().add(source);
477
	            source.setSourcedObj(this);
478
        	}
479
        }
480
    }
481

    
482
    @Override
483
    public void removeSources() {
484
       this.sources.clear();
485
    }
486

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

    
497

    
498
    @Override
499
    public IdentifiableSource addImportSource(String id, String idNamespace, Reference<?> citation, String microCitation) {
500
        if (id == null && idNamespace == null && citation == null && microCitation == null){
501
            return null;
502
        }
503
        IdentifiableSource source = IdentifiableSource.NewInstance(OriginalSourceType.Import, id, idNamespace, citation, microCitation);
504
        addSource(source);
505
        return source;
506
    }
507

    
508

    
509
    @Override
510
    public void removeSource(IdentifiableSource source) {
511
        getSources().remove(source);
512
    }
513

    
514
//******************************** TO STRING *****************************************************/
515

    
516
    /* (non-Javadoc)
517
     * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#toString()
518
     */
519
     @Override
520
    public String toString() {
521
        String result;
522
        if (titleCache == null){
523
            result = super.toString();
524
        }else{
525
            result = this.titleCache;
526
        }
527
        return result;
528
    }
529

    
530

    
531
    public int compareTo(IdentifiableEntity identifiableEntity) {
532

    
533
         int result = 0;
534

    
535
         if (identifiableEntity == null) {
536
             throw new NullPointerException("Cannot compare to null.");
537
         }
538

    
539
         // First, compare the name cache.
540
         // TODO: Avoid using instanceof operator
541
         // Use Class.getDeclaredMethod() instead to find out whether class has getNameCache() method?
542

    
543
         String specifiedNameCache = "";
544
         String thisNameCache = "";
545
         String specifiedTitleCache = "";
546
         String thisTitleCache = "";
547
         String specifiedReferenceTitleCache = "";
548
         String thisReferenceTitleCache = "";
549
         String thisGenusString = "";
550
         String specifiedGenusString = "";
551
         int thisrank_order = 0;
552
         final String HYBRID_SIGN = "\u00D7";
553
         final String QUOT_SIGN = "[\\u02BA\\u0022\\u0022]";
554
         //TODO we can remove all the deproxies here except for the first one
555
         identifiableEntity = HibernateProxyHelper.deproxy(identifiableEntity, IdentifiableEntity.class);
556
         if(identifiableEntity instanceof NonViralName) {
557
             specifiedNameCache = HibernateProxyHelper.deproxy(identifiableEntity, NonViralName.class).getNameCache();
558
             specifiedTitleCache = identifiableEntity.getTitleCache();
559
            if (identifiableEntity instanceof BotanicalName){
560
            	 if (((BotanicalName)identifiableEntity).isAutonym()){
561
            		 boolean isProtected = false;
562
            		 String oldNameCache = ((BotanicalName) identifiableEntity).getNameCache();
563
            		 if ( ((BotanicalName)identifiableEntity).isProtectedNameCache()){
564
            			 isProtected = true;
565
            		 }
566
            		 ((BotanicalName)identifiableEntity).setProtectedNameCache(false);
567
            		 ((BotanicalName)identifiableEntity).setNameCache(null, false);
568
            		 specifiedNameCache = ((BotanicalName) identifiableEntity).getNameCache();
569
            		 ((BotanicalName)identifiableEntity).setNameCache(oldNameCache, isProtected);
570

    
571
            	 }
572
             }
573

    
574
         } else if(identifiableEntity instanceof TaxonBase) {
575
             TaxonBase taxonBase = HibernateProxyHelper.deproxy(identifiableEntity, TaxonBase.class);
576

    
577
             TaxonNameBase<?,?> taxonNameBase = taxonBase.getName();
578

    
579

    
580
             NonViralName nonViralName = HibernateProxyHelper.deproxy(taxonNameBase, NonViralName.class);
581
             specifiedNameCache = nonViralName.getNameCache();
582
             specifiedTitleCache = taxonNameBase.getTitleCache();
583

    
584
             specifiedReferenceTitleCache = ((TaxonBase)identifiableEntity).getSec().getTitleCache();
585
             Reference reference = taxonBase.getSec();
586
             if (reference != null) {
587
                 reference = HibernateProxyHelper.deproxy(reference, Reference.class);
588
                 specifiedReferenceTitleCache = reference.getTitleCache();
589
             }
590
         }
591

    
592
         if(this.isInstanceOf(NonViralName.class)) {
593
             thisNameCache = HibernateProxyHelper.deproxy(this, NonViralName.class).getNameCache();
594
             thisTitleCache = getTitleCache();
595

    
596
             if (this instanceof BotanicalName){
597
            	 if (((BotanicalName)this).isAutonym()){
598
            		 boolean isProtected = false;
599
            		 String oldNameCache = ((BotanicalName) this).getNameCache();
600
            		 if ( ((BotanicalName)this).isProtectedNameCache()){
601
            			 isProtected = true;
602
            		 }
603
            		 ((BotanicalName)this).setProtectedNameCache(false);
604
            		 ((BotanicalName)this).setNameCache(null, false);
605
            		 thisNameCache = ((BotanicalName) this).getNameCache();
606
            		 ((BotanicalName)this).setNameCache(oldNameCache, isProtected);
607
            	 }
608
             }
609
         } else if(this.isInstanceOf(TaxonBase.class)) {
610
             TaxonNameBase<?,?> taxonNameBase= HibernateProxyHelper.deproxy(this, TaxonBase.class).getName();
611
             NonViralName nonViralName = HibernateProxyHelper.deproxy(taxonNameBase, NonViralName.class);
612
             thisNameCache = nonViralName.getNameCache();
613
             thisTitleCache = taxonNameBase.getTitleCache();
614
             thisReferenceTitleCache = ((TaxonBase)this).getSec().getTitleCache();
615
             thisGenusString = nonViralName.getGenusOrUninomial();
616
         }
617

    
618
         // Compare name cache of taxon names
619

    
620

    
621

    
622
         if (!specifiedNameCache.equals("") && !thisNameCache.equals("")) {
623

    
624
        	 thisNameCache = thisNameCache.replaceAll(HYBRID_SIGN, "");
625
        	 thisNameCache = thisNameCache.replaceAll(QUOT_SIGN, "");
626

    
627

    
628
        	 specifiedNameCache = specifiedNameCache.replaceAll(HYBRID_SIGN, "");
629
        	 specifiedNameCache = specifiedNameCache.replaceAll(QUOT_SIGN, "");
630

    
631

    
632
             result = thisNameCache.compareTo(specifiedNameCache);
633
         }
634

    
635
         // Compare title cache of taxon names
636

    
637
         if ((result == 0) && (!specifiedTitleCache.equals("") || !thisTitleCache.equals(""))) {
638
        	 thisTitleCache = thisTitleCache.replaceAll(HYBRID_SIGN, "");
639
        	 thisTitleCache = thisTitleCache.replaceAll(QUOT_SIGN, "");
640

    
641
        	 specifiedTitleCache = specifiedTitleCache.replaceAll(HYBRID_SIGN, "");
642
        	 specifiedTitleCache = specifiedTitleCache.replaceAll(QUOT_SIGN, "");
643
             result = thisTitleCache.compareTo(specifiedTitleCache);
644
         }
645

    
646
         // Compare title cache of taxon references
647

    
648
         if ((result == 0) && (!specifiedReferenceTitleCache.equals("") || !thisReferenceTitleCache.equals(""))) {
649
             result = thisReferenceTitleCache.compareTo(specifiedReferenceTitleCache);
650
         }
651

    
652
         return result;
653
     }
654

    
655
    /**
656
     * Returns the {@link eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy cache strategy} used to generate
657
     * several strings corresponding to <i>this</i> identifiable entity
658
     * (in particular taxon name caches and author strings).
659
     *
660
     * @return  the cache strategy used for <i>this</i> identifiable entity
661
     * @see     eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy
662
     */
663
    public S getCacheStrategy() {
664
        return this.cacheStrategy;
665
    }
666
    /**
667
     * @see 	#getCacheStrategy()
668
     */
669

    
670
    public void setCacheStrategy(S cacheStrategy) {
671
        this.cacheStrategy = cacheStrategy;
672
    }
673

    
674
    @Override
675
    public String generateTitle() {
676
        if (getCacheStrategy() == null){
677
            //logger.warn("No CacheStrategy defined for "+ this.getClass() + ": " + this.getUuid());
678
            return this.getClass() + ": " + this.getUuid();
679
        }else{
680
            return getCacheStrategy().getTitleCache(this);
681
        }
682
    }
683

    
684
//****************** CLONE ************************************************/
685

    
686
    @Override
687
    public Object clone() throws CloneNotSupportedException{
688
        IdentifiableEntity<?> result = (IdentifiableEntity<?>)super.clone();
689

    
690
        //Extensions
691
        result.extensions = new HashSet<Extension>();
692
        for (Extension extension : getExtensions() ){
693
            Extension newExtension = (Extension)extension.clone();
694
            result.addExtension(newExtension);
695
        }
696

    
697
        //Identifier
698
        result.identifiers = new ArrayList<Identifier>();
699
        for (Identifier<?> identifier : getIdentifiers() ){
700
        	Identifier<?> newIdentifier = (Identifier<?>)identifier.clone();
701
            result.addIdentifier(newIdentifier);
702
        }
703

    
704
        //OriginalSources
705
        result.sources = new HashSet<IdentifiableSource>();
706
        for (IdentifiableSource source : getSources()){
707
            IdentifiableSource newSource = (IdentifiableSource)source.clone();
708
            result.addSource(newSource);
709
        }
710

    
711
        //Rights
712
        result.rights = new HashSet<Rights>();
713
        for(Rights rights : getRights()) {
714
            result.addRights(rights);
715
        }
716

    
717

    
718
        //Credits
719
        result.credits = new ArrayList<Credit>();
720
        for(Credit credit : getCredits()) {
721
            result.addCredit(credit);
722
        }
723

    
724
        //no changes to: lsid, titleCache, protectedTitleCache
725

    
726
        //empty titleCache
727
        if (! protectedTitleCache){
728
            result.titleCache = null;
729
        }
730

    
731
        result.initListener();
732
        return result;
733
    }
734

    
735

    
736
}
(36-36/72)