Project

General

Profile

Download (25.7 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 SourceableEntity<IdentifiableSource>
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 IdentifiableSource addSource(OriginalSourceType type, String id, String idNamespace, Reference citation, String microCitation) {
484
//        if (id == null && idNamespace == null && citation == null && microCitation == null){
485
//            return null;
486
//        }
487
//        IdentifiableSource source = IdentifiableSource.NewInstance(type, id, idNamespace, citation, microCitation);
488
//        addSource(source);
489
//        return source;
490
//    }
491
//
492
//
493
//    @Override
494
//    public IdentifiableSource addImportSource(String id, String idNamespace, Reference<?> citation, String microCitation) {
495
//        if (id == null && idNamespace == null && citation == null && microCitation == null){
496
//            return null;
497
//        }
498
//        IdentifiableSource source = IdentifiableSource.NewInstance(OriginalSourceType.Import, id, idNamespace, citation, microCitation);
499
//        addSource(source);
500
//        return source;
501
//    }
502
//
503
//
504
//    @Override
505
//    public void removeSource(IdentifiableSource source) {
506
//        getSources().remove(source);
507
//    }
508

    
509
    //
510
    @Override
511
    public void removeSources() {
512
       this.sources.clear();
513
    }
514

    
515
    @Override
516
    protected IdentifiableSource sourceInstance(OriginalSourceType type, String id, String idNamespace,
517
            Reference<?> citation, String microCitation) {
518
        return IdentifiableSource.NewInstance(type, id, idNamespace, citation, microCitation);
519
    }
520

    
521

    
522
//******************************** TO STRING *****************************************************/
523

    
524
     @Override
525
    public String toString() {
526
        String result;
527
        if (titleCache == null){
528
            result = super.toString();
529
        }else{
530
            result = this.titleCache;
531
        }
532
        return result;
533
    }
534

    
535
    public int compareTo(IdentifiableEntity identifiableEntity) {
536

    
537
         int result = 0;
538

    
539
         if (identifiableEntity == null) {
540
             throw new NullPointerException("Cannot compare to null.");
541
         }
542

    
543
         // First, compare the name cache.
544
         // TODO: Avoid using instanceof operator
545
         // Use Class.getDeclaredMethod() instead to find out whether class has getNameCache() method?
546

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

    
575
            	 }
576
             }
577

    
578
         } else if(identifiableEntity instanceof TaxonBase) {
579
             TaxonBase taxonBase = HibernateProxyHelper.deproxy(identifiableEntity, TaxonBase.class);
580

    
581
             TaxonNameBase<?,?> taxonNameBase = taxonBase.getName();
582

    
583

    
584
             NonViralName nonViralName = HibernateProxyHelper.deproxy(taxonNameBase, NonViralName.class);
585
             specifiedNameCache = nonViralName.getNameCache();
586
             specifiedTitleCache = taxonNameBase.getTitleCache();
587

    
588
             specifiedReferenceTitleCache = ((TaxonBase)identifiableEntity).getSec().getTitleCache();
589
             Reference reference = taxonBase.getSec();
590
             if (reference != null) {
591
                 reference = HibernateProxyHelper.deproxy(reference, Reference.class);
592
                 specifiedReferenceTitleCache = reference.getTitleCache();
593
             }
594
         }
595

    
596
         if(this.isInstanceOf(NonViralName.class)) {
597
             thisNameCache = HibernateProxyHelper.deproxy(this, NonViralName.class).getNameCache();
598
             thisTitleCache = getTitleCache();
599

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

    
622
         // Compare name cache of taxon names
623

    
624

    
625

    
626
         if (!specifiedNameCache.equals("") && !thisNameCache.equals("")) {
627

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

    
631

    
632
        	 specifiedNameCache = specifiedNameCache.replaceAll(HYBRID_SIGN, "");
633
        	 specifiedNameCache = specifiedNameCache.replaceAll(QUOT_SIGN, "");
634

    
635

    
636
             result = thisNameCache.compareTo(specifiedNameCache);
637
         }
638

    
639
         // Compare title cache of taxon names
640

    
641
         if ((result == 0) && (!specifiedTitleCache.equals("") || !thisTitleCache.equals(""))) {
642
        	 thisTitleCache = thisTitleCache.replaceAll(HYBRID_SIGN, "");
643
        	 thisTitleCache = thisTitleCache.replaceAll(QUOT_SIGN, "");
644

    
645
        	 specifiedTitleCache = specifiedTitleCache.replaceAll(HYBRID_SIGN, "");
646
        	 specifiedTitleCache = specifiedTitleCache.replaceAll(QUOT_SIGN, "");
647
             result = thisTitleCache.compareTo(specifiedTitleCache);
648
         }
649

    
650
         // Compare title cache of taxon references
651

    
652
         if ((result == 0) && (!specifiedReferenceTitleCache.equals("") || !thisReferenceTitleCache.equals(""))) {
653
             result = thisReferenceTitleCache.compareTo(specifiedReferenceTitleCache);
654
         }
655

    
656
         return result;
657
     }
658

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

    
674
    public void setCacheStrategy(S cacheStrategy) {
675
        this.cacheStrategy = cacheStrategy;
676
    }
677

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

    
688
//****************** CLONE ************************************************/
689

    
690
    @Override
691
    public Object clone() throws CloneNotSupportedException{
692
        IdentifiableEntity<?> result = (IdentifiableEntity<?>)super.clone();
693

    
694
        //Extensions
695
        result.extensions = new HashSet<Extension>();
696
        for (Extension extension : getExtensions() ){
697
            Extension newExtension = (Extension)extension.clone();
698
            result.addExtension(newExtension);
699
        }
700

    
701
        //Identifier
702
        result.identifiers = new ArrayList<Identifier>();
703
        for (Identifier<?> identifier : getIdentifiers() ){
704
        	Identifier<?> newIdentifier = (Identifier<?>)identifier.clone();
705
            result.addIdentifier(newIdentifier);
706
        }
707

    
708
        //OriginalSources
709
        result.sources = new HashSet<IdentifiableSource>();
710
        for (IdentifiableSource source : getSources()){
711
            IdentifiableSource newSource = (IdentifiableSource)source.clone();
712
            result.addSource(newSource);
713
        }
714

    
715
        //Rights
716
        result.rights = new HashSet<Rights>();
717
        for(Rights rights : getRights()) {
718
            result.addRights(rights);
719
        }
720

    
721

    
722
        //Credits
723
        result.credits = new ArrayList<Credit>();
724
        for(Credit credit : getCredits()) {
725
            result.addCredit(credit);
726
        }
727

    
728
        //no changes to: lsid, titleCache, protectedTitleCache
729

    
730
        //empty titleCache
731
        if (! protectedTitleCache){
732
            result.titleCache = null;
733
        }
734

    
735
        result.initListener();
736
        return result;
737
    }
738

    
739

    
740
}
(36-36/73)