2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
10 package eu
.etaxonomy
.cdm
.model
.common
;
13 import java
.util
.ArrayList
;
14 import java
.util
.HashSet
;
15 import java
.util
.List
;
18 import javax
.persistence
.Column
;
19 import javax
.persistence
.Embedded
;
20 import javax
.persistence
.FetchType
;
21 import javax
.persistence
.MappedSuperclass
;
22 import javax
.persistence
.OneToMany
;
23 import javax
.persistence
.Transient
;
24 import javax
.xml
.bind
.annotation
.XmlAccessType
;
25 import javax
.xml
.bind
.annotation
.XmlAccessorType
;
26 import javax
.xml
.bind
.annotation
.XmlElement
;
27 import javax
.xml
.bind
.annotation
.XmlElementWrapper
;
28 import javax
.xml
.bind
.annotation
.XmlTransient
;
29 import javax
.xml
.bind
.annotation
.XmlType
;
30 import javax
.xml
.bind
.annotation
.adapters
.XmlJavaTypeAdapter
;
32 import org
.apache
.log4j
.Logger
;
33 import org
.hibernate
.annotations
.Cascade
;
34 import org
.hibernate
.annotations
.CascadeType
;
35 import org
.hibernate
.annotations
.IndexColumn
;
36 import org
.hibernate
.search
.annotations
.Field
;
37 import org
.hibernate
.search
.annotations
.FieldBridge
;
38 import org
.hibernate
.search
.annotations
.Fields
;
40 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
41 import eu
.etaxonomy
.cdm
.hibernate
.StripHtmlBridge
;
42 import eu
.etaxonomy
.cdm
.jaxb
.FormattedTextAdapter
;
43 import eu
.etaxonomy
.cdm
.jaxb
.LSIDAdapter
;
44 import eu
.etaxonomy
.cdm
.model
.media
.Rights
;
45 import eu
.etaxonomy
.cdm
.model
.name
.NonViralName
;
46 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
47 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
48 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
51 * 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.
52 * All subclasses inherit the ability to store additional properties that are stored as {@link Extension Extensions}, basically a string value with a type term.
53 * Any number of right statements can be attached as well as multiple {@link OriginalSource} objects.
54 * 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).
55 * A Taxon for example that was taken from 2 sources like FaunaEuropaea and IPNI would have two originalSource objects.
56 * 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.
60 * @created 08-Nov-2007 13:06:27
62 @XmlAccessorType(XmlAccessType
.FIELD
)
63 @XmlType(name
= "IdentifiableEntity", propOrder
= {
66 "protectedTitleCache",
73 public abstract class IdentifiableEntity
<S
extends IIdentifiableEntityCacheStrategy
> extends AnnotatableEntity
74 implements ISourceable
, IIdentifiableEntity
, Comparable
<IdentifiableEntity
> {
75 private static final long serialVersionUID
= -5610995424730659058L;
76 private static final Logger logger
= Logger
.getLogger(IdentifiableEntity
.class);
79 public static final boolean PROTECTED
= true;
81 public static final boolean NOT_PROTECTED
= false;
83 @XmlElement(name
= "LSID", type
= String
.class)
84 @XmlJavaTypeAdapter(LSIDAdapter
.class)
88 @XmlElement(name
= "TitleCache", required
= true)
89 @XmlJavaTypeAdapter(FormattedTextAdapter
.class)
90 @Column(length
=255, name
="titleCache")
91 @Fields({@Field(index
= org
.hibernate
.search
.annotations
.Index
.TOKENIZED
),
92 @Field(name
= "titleCache_forSort", index
= org
.hibernate
.search
.annotations
.Index
.UN_TOKENIZED
)
94 @FieldBridge(impl
=StripHtmlBridge
.class)
95 private String titleCache
;
97 //if true titleCache will not be automatically generated/updated
98 @XmlElement(name
= "ProtectedTitleCache")
99 private boolean protectedTitleCache
;
101 @XmlElementWrapper(name
= "Rights")
102 @XmlElement(name
= "Rights")
103 @OneToMany(fetch
= FetchType
.LAZY
)
104 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
})
105 private Set
<Rights
> rights
= new HashSet
<Rights
>();
107 @XmlElementWrapper(name
= "Credits")
108 @XmlElement(name
= "Credit")
109 @IndexColumn(name
="sortIndex", base
= 0)
110 @OneToMany(fetch
= FetchType
.LAZY
)
111 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
})
112 private List
<Credit
> credits
= new ArrayList
<Credit
>();
114 @XmlElementWrapper(name
= "Extensions")
115 @XmlElement(name
= "Extension")
116 @OneToMany(fetch
= FetchType
.LAZY
)
117 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
})
118 private Set
<Extension
> extensions
= new HashSet
<Extension
>();
120 @XmlElementWrapper(name
= "Sources")
121 @XmlElement(name
= "OriginalSource")
122 @OneToMany(fetch
= FetchType
.LAZY
)
123 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
})
124 private Set
<OriginalSource
> sources
= new HashSet
<OriginalSource
>();
128 protected S cacheStrategy
;
131 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getLsid()
133 public LSID
getLsid(){
137 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#setLsid(java.lang.String)
139 public void setLsid(LSID lsid
){
144 * By default, we expect most cdm objects to be abstract things
145 * i.e. unable to return a data representation.
147 * Specific subclasses (e.g. Sequence) can override if necessary.
149 public byte[] getData() {
154 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getTitleCache()
157 public String
getTitleCache(){
158 if (protectedTitleCache
){
159 return this.titleCache
;
161 // is title dirty, i.e. equal NULL?
162 if (titleCache
== null){
163 this.setTitleCache(generateTitle(),protectedTitleCache
) ; //for truncating
168 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#setTitleCache(java.lang.String)
170 public void setTitleCache(String titleCache
){
171 setTitleCache(titleCache
, PROTECTED
);
175 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#setTitleCache(java.lang.String, boolean)
177 public void setTitleCache(String titleCache
, boolean protectCache
){
178 //TODO truncation of title cache
179 if (titleCache
!= null && titleCache
.length() > 254){
180 logger
.warn("Truncation of title cache: " + this.toString() + "/" + titleCache
);
181 titleCache
= titleCache
.substring(0, 251) + "...";
183 this.titleCache
= titleCache
;
184 this.setProtectedTitleCache(protectCache
);
188 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getRights()
190 public Set
<Rights
> getRights() {
195 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addRights(eu.etaxonomy.cdm.model.media.Rights)
197 public void addRights(Rights right
){
198 this.rights
.add(right
);
201 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeRights(eu.etaxonomy.cdm.model.media.Rights)
203 public void removeRights(Rights right
){
204 this.rights
.remove(right
);
209 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getCredits()
211 public List
<Credit
> getCredits() {
216 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getCredits(int)
218 public Credit
getCredits(int index
){
219 return this.credits
.get(index
);
223 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addCredit(eu.etaxonomy.cdm.model.common.Credit)
225 public void addCredit(Credit credit
){
226 this.credits
.add(credit
);
231 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addCredit(eu.etaxonomy.cdm.model.common.Credit, int)
233 public void addCredit(Credit credit
, int index
){
234 this.credits
.add(index
, credit
);
238 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeCredit(eu.etaxonomy.cdm.model.common.Credit)
240 public void removeCredit(Credit credit
){
241 this.credits
.remove(credit
);
245 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeCredit(int)
247 public void removeCredit(int index
){
248 this.credits
.remove(index
);
253 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getExtensions()
255 public Set
<Extension
> getExtensions(){
256 return this.extensions
;
259 public void addExtension(String value
, ExtensionType extensionType
){
260 Extension
.NewInstance(this, value
, extensionType
);
264 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addExtension(eu.etaxonomy.cdm.model.common.Extension)
266 public void addExtension(Extension extension
){
267 if (extension
!= null){
268 extension
.setExtendedObj(this);
269 this.extensions
.add(extension
);
273 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeExtension(eu.etaxonomy.cdm.model.common.Extension)
275 public void removeExtension(Extension extension
){
276 if (extension
!= null){
277 extension
.setExtendedObj(null);
278 this.extensions
.remove(extension
);
284 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#isProtectedTitleCache()
286 public boolean isProtectedTitleCache() {
287 return protectedTitleCache
;
291 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#setProtectedTitleCache(boolean)
293 public void setProtectedTitleCache(boolean protectedTitleCache
) {
294 this.protectedTitleCache
= protectedTitleCache
;
298 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getSources()
300 public Set
<OriginalSource
> getSources() {
304 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addSource(eu.etaxonomy.cdm.model.common.OriginalSource)
306 public void addSource(OriginalSource source
) {
308 IdentifiableEntity oldSourcedObj
= source
.getSourcedObj();
309 if (oldSourcedObj
!= null && oldSourcedObj
!= this){
310 oldSourcedObj
.getSources().remove(source
);
312 this.sources
.add(source
);
313 source
.setSourcedObj(this);
317 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeSource(eu.etaxonomy.cdm.model.common.OriginalSource)
319 public void removeSource(OriginalSource source
) {
320 this.sources
.remove(source
);
324 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#toString()
327 public String
toString() {
329 if (titleCache
== null){
330 result
= super.toString();
332 result
= this.titleCache
;
337 public int compareTo(IdentifiableEntity identifiableEntity
) {
341 if (identifiableEntity
== null) {
342 throw new NullPointerException("Cannot compare to null.");
345 // First, compare the name cache.
346 // TODO: Avoid using instanceof operator
347 // Use Class.getDeclaredMethod() instead to find out whether class has getNameCache() method?
349 String specifiedNameCache
= "";
350 String thisNameCache
= "";
351 String specifiedTitleCache
= "";
352 String thisTitleCache
= "";
353 String specifiedReferenceTitleCache
= "";
354 String thisReferenceTitleCache
= "";
356 if(identifiableEntity
instanceof NonViralName
) {
357 specifiedNameCache
= HibernateProxyHelper
.deproxy(identifiableEntity
, NonViralName
.class).getNameCache();
358 specifiedTitleCache
= identifiableEntity
.getTitleCache();
360 } else if(identifiableEntity
instanceof TaxonBase
) {
361 TaxonBase taxonBase
= HibernateProxyHelper
.deproxy(identifiableEntity
, TaxonBase
.class);
363 TaxonNameBase
<?
,?
> taxonNameBase
= taxonBase
.getName();
364 specifiedNameCache
= HibernateProxyHelper
.deproxy(taxonNameBase
, NonViralName
.class).getNameCache();
365 specifiedTitleCache
= taxonNameBase
.getTitleCache();
367 //specifiedReferenceTitleCache = ((TaxonBase)identifiableEntity).getSec().getTitleCache();
368 // ReferenceBase referenceBase = taxonBase.getSec();
369 // if (referenceBase != null) {
370 // FIXME: HibernateProxyHelper.deproxy(referenceBase, ReferenceBase.class) throws exception
371 // referenceBase = HibernateProxyHelper.deproxy(referenceBase, ReferenceBase.class);
372 // specifiedReferenceTitleCache = referenceBase.getTitleCache();
376 if(this instanceof NonViralName
) {
377 thisNameCache
= HibernateProxyHelper
.deproxy(this, NonViralName
.class).getNameCache();
378 thisTitleCache
= getTitleCache();
379 } else if(this instanceof TaxonBase
) {
380 TaxonNameBase
<?
,?
> taxonNameBase
= HibernateProxyHelper
.deproxy(this, TaxonBase
.class).getName();
381 thisNameCache
= HibernateProxyHelper
.deproxy(taxonNameBase
, NonViralName
.class).getNameCache();
382 thisTitleCache
= taxonNameBase
.getTitleCache();
383 thisReferenceTitleCache
= getTitleCache();
386 // Compare name cache of taxon names
388 if (!specifiedNameCache
.equals("") && !thisNameCache
.equals("")) {
389 result
= thisNameCache
.compareTo(specifiedNameCache
);
392 // Compare title cache of taxon names
394 if ((result
== 0) && (!specifiedTitleCache
.equals("") || !thisTitleCache
.equals(""))) {
395 result
= thisTitleCache
.compareTo(specifiedTitleCache
);
398 // Compare title cache of taxon references
400 if ((result
== 0) && (!specifiedReferenceTitleCache
.equals("") || !thisReferenceTitleCache
.equals(""))) {
401 result
= thisReferenceTitleCache
.compareTo(specifiedReferenceTitleCache
);
408 //****************** CLONE ************************************************/
411 * @see eu.etaxonomy.cdm.model.common.AnnotatableEntity#clone()
414 public Object
clone() throws CloneNotSupportedException
{
415 IdentifiableEntity result
= (IdentifiableEntity
)super.clone();
418 result
.extensions
= new HashSet
<Extension
>();
419 for (Extension extension
: this.extensions
){
420 Extension newExtension
= (Extension
)extension
.clone();
421 result
.addExtension(newExtension
);
425 result
.sources
= new HashSet
<OriginalSource
>();
426 for (OriginalSource originalSource
: this.sources
){
427 OriginalSource newSource
= (OriginalSource
)originalSource
.clone();
428 result
.addSource(newSource
);
432 result
.rights
= new HashSet
<Rights
>();
433 for(Rights rights
: this.rights
) {
434 result
.addRights(rights
);
437 //result.setLsid(lsid);
438 //result.setTitleCache(titleCache);
439 //result.setProtectedTitleCache(protectedTitleCache); //must be after setTitleCache
441 //no changes to: lsid, titleCache, protectedTitleCache
444 if (! protectedTitleCache
){
451 * Returns the {@link eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy cache strategy} used to generate
452 * several strings corresponding to <i>this</i> identifiable entity
453 * (in particular taxon name caches and author strings).
455 * @return the cache strategy used for <i>this</i> identifiable entity
456 * @see eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy
458 public S
getCacheStrategy() {
459 return this.cacheStrategy
;
462 * @see #getCacheStrategy()
465 public void setCacheStrategy(S cacheStrategy
) {
466 this.cacheStrategy
= cacheStrategy
;
469 public String
generateTitle() {
470 if (cacheStrategy
== null){
471 logger
.warn("No CacheStrategy defined for "+ this.getClass() + ": " + this.getUuid());
474 return cacheStrategy
.getTitleCache(this);