bugfixing for new model changes (comparable for OrderedTermBase and identifiableEntity)
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / common / IdentifiableEntity.java
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.util.ArrayList;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Set;
17
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;
31
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;
39
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;
49 import eu.etaxonomy.cdm.strategy.match.Match;
50 import eu.etaxonomy.cdm.strategy.match.MatchMode;
51 import eu.etaxonomy.cdm.strategy.match.Match.ReplaceMode;
52 import eu.etaxonomy.cdm.strategy.merge.Merge;
53 import eu.etaxonomy.cdm.strategy.merge.MergeMode;
54
55 /**
56 * 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.
57 * All subclasses inherit the ability to store additional properties that are stored as {@link Extension Extensions}, basically a string value with a type term.
58 * Any number of right statements can be attached as well as multiple {@link OriginalSource} objects.
59 * 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).
60 * A Taxon for example that was taken from 2 sources like FaunaEuropaea and IPNI would have two originalSource objects.
61 * 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.
62 *
63 * @author m.doering
64 * @version 1.0
65 * @created 08-Nov-2007 13:06:27
66 */
67 @XmlAccessorType(XmlAccessType.FIELD)
68 @XmlType(name = "IdentifiableEntity", propOrder = {
69 "lsid",
70 "titleCache",
71 "protectedTitleCache",
72 "rights",
73 "extensions",
74 "credits",
75 "sources"
76 })
77 @MappedSuperclass
78 public abstract class IdentifiableEntity<S extends IIdentifiableEntityCacheStrategy> extends AnnotatableEntity
79 implements ISourceable, IIdentifiableEntity /*, Comparable<IdentifiableEntity> */{
80 private static final long serialVersionUID = -5610995424730659058L;
81 private static final Logger logger = Logger.getLogger(IdentifiableEntity.class);
82
83 @XmlTransient
84 public static final boolean PROTECTED = true;
85 @XmlTransient
86 public static final boolean NOT_PROTECTED = false;
87
88 @XmlElement(name = "LSID", type = String.class)
89 @XmlJavaTypeAdapter(LSIDAdapter.class)
90 @Embedded
91 private LSID lsid;
92
93 @XmlElement(name = "TitleCache", required = true)
94 @XmlJavaTypeAdapter(FormattedTextAdapter.class)
95 @Column(length=255, name="titleCache")
96 @Fields({@Field(index = org.hibernate.search.annotations.Index.TOKENIZED),
97 @Field(name = "titleCache_forSort", index = org.hibernate.search.annotations.Index.UN_TOKENIZED)
98 })
99 @FieldBridge(impl=StripHtmlBridge.class)
100 @Match(value=MatchMode.CACHE, cacheReplaceMode=ReplaceMode.ALL)
101 private String titleCache;
102
103 //if true titleCache will not be automatically generated/updated
104 @XmlElement(name = "ProtectedTitleCache")
105 private boolean protectedTitleCache;
106
107 @XmlElementWrapper(name = "Rights")
108 @XmlElement(name = "Rights")
109 @OneToMany(fetch = FetchType.LAZY)
110 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
111 //TODO
112 @Merge(MergeMode.ADD_CLONE)
113 private Set<Rights> rights = new HashSet<Rights>();
114
115 @XmlElementWrapper(name = "Credits")
116 @XmlElement(name = "Credit")
117 @IndexColumn(name="sortIndex", base = 0)
118 @OneToMany(fetch = FetchType.LAZY)
119 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
120 //TODO
121 @Merge(MergeMode.ADD_CLONE)
122 private List<Credit> credits = new ArrayList<Credit>();
123
124 @XmlElementWrapper(name = "Extensions")
125 @XmlElement(name = "Extension")
126 @OneToMany(fetch = FetchType.LAZY)
127 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
128 @Merge(MergeMode.ADD_CLONE)
129 private Set<Extension> extensions = new HashSet<Extension>();
130
131 @XmlElementWrapper(name = "Sources")
132 @XmlElement(name = "OriginalSource")
133 @OneToMany(fetch = FetchType.LAZY)
134 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
135 @Merge(MergeMode.ADD_CLONE)
136 private Set<OriginalSource> sources = new HashSet<OriginalSource>();
137
138 @XmlTransient
139 @Transient
140 protected S cacheStrategy;
141
142 /* (non-Javadoc)
143 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getLsid()
144 */
145 public LSID getLsid(){
146 return this.lsid;
147 }
148 /* (non-Javadoc)
149 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#setLsid(java.lang.String)
150 */
151 public void setLsid(LSID lsid){
152 this.lsid = lsid;
153 }
154
155 /**
156 * By default, we expect most cdm objects to be abstract things
157 * i.e. unable to return a data representation.
158 *
159 * Specific subclasses (e.g. Sequence) can override if necessary.
160 */
161 public byte[] getData() {
162 return null;
163 }
164
165 /* (non-Javadoc)
166 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getTitleCache()
167 */
168 //@Transient
169 public String getTitleCache(){
170 if (protectedTitleCache){
171 return this.titleCache;
172 }
173 // is title dirty, i.e. equal NULL?
174 if (titleCache == null){
175 this.setTitleCache(generateTitle(),protectedTitleCache) ; //for truncating
176 }
177 return titleCache;
178 }
179 /* (non-Javadoc)
180 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#setTitleCache(java.lang.String)
181 */
182 public void setTitleCache(String titleCache){
183 setTitleCache(titleCache, PROTECTED);
184 }
185
186 /* (non-Javadoc)
187 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#setTitleCache(java.lang.String, boolean)
188 */
189 public void setTitleCache(String titleCache, boolean protectCache){
190 //TODO truncation of title cache
191 if (titleCache != null && titleCache.length() > 254){
192 logger.warn("Truncation of title cache: " + this.toString() + "/" + titleCache);
193 titleCache = titleCache.substring(0, 251) + "...";
194 }
195 this.titleCache = titleCache;
196 this.setProtectedTitleCache(protectCache);
197 }
198
199 /* (non-Javadoc)
200 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getRights()
201 */
202 public Set<Rights> getRights() {
203 return this.rights;
204 }
205
206 /* (non-Javadoc)
207 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addRights(eu.etaxonomy.cdm.model.media.Rights)
208 */
209 public void addRights(Rights right){
210 this.rights.add(right);
211 }
212 /* (non-Javadoc)
213 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeRights(eu.etaxonomy.cdm.model.media.Rights)
214 */
215 public void removeRights(Rights right){
216 this.rights.remove(right);
217 }
218
219
220 /* (non-Javadoc)
221 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getCredits()
222 */
223 public List<Credit> getCredits() {
224 return this.credits;
225 }
226
227 /* (non-Javadoc)
228 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getCredits(int)
229 */
230 public Credit getCredits(int index){
231 return this.credits.get(index);
232 }
233
234 /* (non-Javadoc)
235 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addCredit(eu.etaxonomy.cdm.model.common.Credit)
236 */
237 public void addCredit(Credit credit){
238 this.credits.add(credit);
239 }
240
241
242 /* (non-Javadoc)
243 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addCredit(eu.etaxonomy.cdm.model.common.Credit, int)
244 */
245 public void addCredit(Credit credit, int index){
246 this.credits.add(index, credit);
247 }
248
249 /* (non-Javadoc)
250 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeCredit(eu.etaxonomy.cdm.model.common.Credit)
251 */
252 public void removeCredit(Credit credit){
253 this.credits.remove(credit);
254 }
255
256 /* (non-Javadoc)
257 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeCredit(int)
258 */
259 public void removeCredit(int index){
260 this.credits.remove(index);
261 }
262
263
264 /* (non-Javadoc)
265 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getExtensions()
266 */
267 public Set<Extension> getExtensions(){
268 return this.extensions;
269 }
270
271 public void addExtension(String value, ExtensionType extensionType){
272 Extension.NewInstance(this, value, extensionType);
273 }
274
275 /* (non-Javadoc)
276 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addExtension(eu.etaxonomy.cdm.model.common.Extension)
277 */
278 public void addExtension(Extension extension){
279 if (extension != null){
280 extension.setExtendedObj(this);
281 this.extensions.add(extension);
282 }
283 }
284 /* (non-Javadoc)
285 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeExtension(eu.etaxonomy.cdm.model.common.Extension)
286 */
287 public void removeExtension(Extension extension){
288 if (extension != null){
289 extension.setExtendedObj(null);
290 this.extensions.remove(extension);
291 }
292 }
293
294
295 /* (non-Javadoc)
296 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#isProtectedTitleCache()
297 */
298 public boolean isProtectedTitleCache() {
299 return protectedTitleCache;
300 }
301
302 /* (non-Javadoc)
303 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#setProtectedTitleCache(boolean)
304 */
305 public void setProtectedTitleCache(boolean protectedTitleCache) {
306 this.protectedTitleCache = protectedTitleCache;
307 }
308
309 /* (non-Javadoc)
310 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#getSources()
311 */
312 public Set<OriginalSource> getSources() {
313 return this.sources;
314 }
315 /* (non-Javadoc)
316 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#addSource(eu.etaxonomy.cdm.model.common.OriginalSource)
317 */
318 public void addSource(OriginalSource source) {
319 if (source != null){
320 IdentifiableEntity oldSourcedObj = source.getSourcedObj();
321 if (oldSourcedObj != null && oldSourcedObj != this){
322 oldSourcedObj.getSources().remove(source);
323 }
324 this.sources.add(source);
325 source.setSourcedObj(this);
326 }
327 }
328 /* (non-Javadoc)
329 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#removeSource(eu.etaxonomy.cdm.model.common.OriginalSource)
330 */
331 public void removeSource(OriginalSource source) {
332 this.sources.remove(source);
333 }
334
335 /* (non-Javadoc)
336 * @see eu.etaxonomy.cdm.model.common.IIdentifiableEntity#toString()
337 */
338 @Override
339 public String toString() {
340 String result;
341 if (titleCache == null){
342 result = super.toString();
343 }else{
344 result = this.titleCache;
345 }
346 return result;
347 }
348
349 public int compareTo(IdentifiableEntity identifiableEntity) {
350
351 int result = 0;
352
353 if (identifiableEntity == null) {
354 throw new NullPointerException("Cannot compare to null.");
355 }
356
357 // First, compare the name cache.
358 // TODO: Avoid using instanceof operator
359 // Use Class.getDeclaredMethod() instead to find out whether class has getNameCache() method?
360
361 String specifiedNameCache = "";
362 String thisNameCache = "";
363 String specifiedTitleCache = "";
364 String thisTitleCache = "";
365 String specifiedReferenceTitleCache = "";
366 String thisReferenceTitleCache = "";
367
368 if(identifiableEntity instanceof NonViralName) {
369 specifiedNameCache = HibernateProxyHelper.deproxy(identifiableEntity, NonViralName.class).getNameCache();
370 specifiedTitleCache = identifiableEntity.getTitleCache();
371
372 } else if(identifiableEntity instanceof TaxonBase) {
373 TaxonBase taxonBase = HibernateProxyHelper.deproxy(identifiableEntity, TaxonBase.class);
374
375 TaxonNameBase<?,?> taxonNameBase = taxonBase.getName();
376 specifiedNameCache = HibernateProxyHelper.deproxy(taxonNameBase, NonViralName.class).getNameCache();
377 specifiedTitleCache = taxonNameBase.getTitleCache();
378
379 //specifiedReferenceTitleCache = ((TaxonBase)identifiableEntity).getSec().getTitleCache();
380 // ReferenceBase referenceBase = taxonBase.getSec();
381 // if (referenceBase != null) {
382 // FIXME: HibernateProxyHelper.deproxy(referenceBase, ReferenceBase.class) throws exception
383 // referenceBase = HibernateProxyHelper.deproxy(referenceBase, ReferenceBase.class);
384 // specifiedReferenceTitleCache = referenceBase.getTitleCache();
385 // }
386 }
387
388 if(this instanceof NonViralName) {
389 thisNameCache = HibernateProxyHelper.deproxy(this, NonViralName.class).getNameCache();
390 thisTitleCache = getTitleCache();
391 } else if(this instanceof TaxonBase) {
392 TaxonNameBase<?,?> taxonNameBase= HibernateProxyHelper.deproxy(this, TaxonBase.class).getName();
393 thisNameCache = HibernateProxyHelper.deproxy(taxonNameBase, NonViralName.class).getNameCache();
394 thisTitleCache = taxonNameBase.getTitleCache();
395 thisReferenceTitleCache = getTitleCache();
396 }
397
398 // Compare name cache of taxon names
399
400 if (!specifiedNameCache.equals("") && !thisNameCache.equals("")) {
401 result = thisNameCache.compareTo(specifiedNameCache);
402 }
403
404 // Compare title cache of taxon names
405
406 if ((result == 0) && (!specifiedTitleCache.equals("") || !thisTitleCache.equals(""))) {
407 result = thisTitleCache.compareTo(specifiedTitleCache);
408 }
409
410 // Compare title cache of taxon references
411
412 if ((result == 0) && (!specifiedReferenceTitleCache.equals("") || !thisReferenceTitleCache.equals(""))) {
413 result = thisReferenceTitleCache.compareTo(specifiedReferenceTitleCache);
414 }
415
416 return result;
417 }
418
419 /**
420 * Returns the {@link eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy cache strategy} used to generate
421 * several strings corresponding to <i>this</i> identifiable entity
422 * (in particular taxon name caches and author strings).
423 *
424 * @return the cache strategy used for <i>this</i> identifiable entity
425 * @see eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy
426 */
427 public S getCacheStrategy() {
428 return this.cacheStrategy;
429 }
430 /**
431 * @see #getCacheStrategy()
432 */
433
434 public void setCacheStrategy(S cacheStrategy) {
435 this.cacheStrategy = cacheStrategy;
436 }
437
438 public String generateTitle() {
439 if (cacheStrategy == null){
440 logger.warn("No CacheStrategy defined for "+ this.getClass() + ": " + this.getUuid());
441 return null;
442 }else{
443 return cacheStrategy.getTitleCache(this);
444 }
445 }
446
447 //****************** CLONE ************************************************/
448
449 /* (non-Javadoc)
450 * @see eu.etaxonomy.cdm.model.common.AnnotatableEntity#clone()
451 */
452 @Override
453 public Object clone() throws CloneNotSupportedException{
454 IdentifiableEntity result = (IdentifiableEntity)super.clone();
455
456 //Extensions
457 result.extensions = new HashSet<Extension>();
458 for (Extension extension : this.extensions ){
459 Extension newExtension = (Extension)extension.clone();
460 result.addExtension(newExtension);
461 }
462
463 //OriginalSources
464 result.sources = new HashSet<OriginalSource>();
465 for (OriginalSource originalSource : this.sources){
466 OriginalSource newSource = (OriginalSource)originalSource.clone();
467 result.addSource(newSource);
468 }
469
470 //Rights
471 result.rights = new HashSet<Rights>();
472 for(Rights rights : this.rights) {
473 result.addRights(rights);
474 }
475
476
477 //Rights
478 result.credits = new ArrayList<Credit>();
479 for(Credit credit : this.credits) {
480 result.addCredit(credit);
481 }
482
483
484 //result.setLsid(lsid);
485 //result.setTitleCache(titleCache);
486 //result.setProtectedTitleCache(protectedTitleCache); //must be after setTitleCache
487
488 //no changes to: lsid, titleCache, protectedTitleCache
489
490 //empty titleCache
491 if (! protectedTitleCache){
492 result.titleCache = null;
493 }
494 return result;
495 }
496
497
498 }