Merge branch 'hotfix/3.12.4' into develop
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / taxon / TaxonBase.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.taxon;
11
12 import java.lang.reflect.Method;
13 import java.util.List;
14
15 import javax.persistence.Entity;
16 import javax.persistence.FetchType;
17 import javax.persistence.ManyToOne;
18 import javax.persistence.Transient;
19 import javax.validation.constraints.NotNull;
20 import javax.xml.bind.annotation.XmlAccessType;
21 import javax.xml.bind.annotation.XmlAccessorType;
22 import javax.xml.bind.annotation.XmlAttribute;
23 import javax.xml.bind.annotation.XmlElement;
24 import javax.xml.bind.annotation.XmlIDREF;
25 import javax.xml.bind.annotation.XmlSchemaType;
26 import javax.xml.bind.annotation.XmlType;
27
28 import org.apache.log4j.Logger;
29 import org.hibernate.annotations.Cascade;
30 import org.hibernate.annotations.CascadeType;
31 import org.hibernate.annotations.Index;
32 import org.hibernate.annotations.Table;
33 import org.hibernate.envers.Audited;
34 import org.hibernate.search.annotations.ClassBridge;
35 import org.hibernate.search.annotations.ClassBridges;
36 import org.hibernate.search.annotations.IndexedEmbedded;
37 import org.hibernate.search.annotations.Store;
38
39 import eu.etaxonomy.cdm.hibernate.search.AcceptedTaxonBridge;
40 import eu.etaxonomy.cdm.hibernate.search.ClassInfoBridge;
41 import eu.etaxonomy.cdm.model.common.IPublishable;
42 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
43 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
44 import eu.etaxonomy.cdm.model.name.Rank;
45 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
46 import eu.etaxonomy.cdm.model.reference.Reference;
47 import eu.etaxonomy.cdm.strategy.cache.TaggedText;
48 import eu.etaxonomy.cdm.strategy.cache.taxon.ITaxonCacheStrategy;
49 import eu.etaxonomy.cdm.validation.Level2;
50 import eu.etaxonomy.cdm.validation.Level3;
51 import eu.etaxonomy.cdm.validation.annotation.TaxonNameCannotBeAcceptedAndSynonym;
52
53 /**
54 * The upmost (abstract) class for the use of a {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name} in a {@link eu.etaxonomy.cdm.model.reference.Reference reference}
55 * or within a taxonomic view/treatment either as a {@link Taxon taxon}
56 * ("accepted" respectively "correct" name) or as a (junior) {@link Synonym synonym}.
57 * Within a taxonomic view/treatment or a reference a taxon name can be used
58 * only in one of both described meanings. The reference using the taxon name
59 * is generally cited with "sec." (secundum, sensu). For instance:
60 * "<i>Juncus longirostris</i> Kuvaev sec. Kirschner, J. et al. 2002".
61 * <P>
62 * This class corresponds to: <ul>
63 * <li> TaxonConcept according to the TDWG ontology
64 * <li> TaxonConcept according to the TCS
65 * </ul>
66 *
67 * @author m.doering
68 * @created 08-Nov-2007 13:06:56
69 */
70 @XmlAccessorType(XmlAccessType.FIELD)
71 @XmlType(name = "TaxonBase", propOrder = {
72 "name",
73 "sec",
74 "doubtful",
75 "appendedPhrase",
76 "useNameCache",
77 "publish"
78 })
79 @Entity
80 @Audited
81 //@PreFilter("hasPermission(filterObject, 'edit')")
82 @Table(appliesTo="TaxonBase", indexes = { @Index(name = "taxonBaseTitleCacheIndex", columnNames = { "titleCache" }) })
83 @TaxonNameCannotBeAcceptedAndSynonym(groups = Level3.class)
84 @ClassBridges({
85 @ClassBridge(name="classInfo",
86 index = org.hibernate.search.annotations.Index.YES,
87 store = Store.YES,
88 impl = ClassInfoBridge.class),
89 @ClassBridge(name="accTaxon", // TODO rename to acceptedTaxon, since we are usually not using abbreviations for field names
90 index = org.hibernate.search.annotations.Index.YES,
91 store = Store.YES,
92 impl = AcceptedTaxonBridge.class),
93 @ClassBridge(impl = eu.etaxonomy.cdm.hibernate.search.NomenclaturalSortOrderBrigde.class)
94 })
95 public abstract class TaxonBase<S extends ITaxonCacheStrategy> extends IdentifiableEntity<S> implements IPublishable, Cloneable {
96 private static final long serialVersionUID = -3589185949928938529L;
97 private static final Logger logger = Logger.getLogger(TaxonBase.class);
98
99 private static Method methodTaxonNameAddTaxonBase;
100
101 static {
102 try {
103 methodTaxonNameAddTaxonBase = TaxonNameBase.class.getDeclaredMethod("addTaxonBase", TaxonBase.class);
104 methodTaxonNameAddTaxonBase.setAccessible(true);
105 } catch (Exception e) {
106 logger.error(e);
107 for(StackTraceElement ste : e.getStackTrace()) {
108 logger.error(ste);
109 }
110 }
111 }
112
113 //The assignment to the Taxon or to the Synonym class is not definitive
114 @XmlAttribute(name = "isDoubtful")
115 private boolean doubtful;
116
117
118 @XmlElement(name = "Name", required = true)
119 @XmlIDREF
120 @XmlSchemaType(name = "IDREF")
121 @ManyToOne(fetch = FetchType.LAZY)
122 @IndexedEmbedded(includeEmbeddedObjectId=true)
123 @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
124 @NotNull(groups = Level2.class)
125 private TaxonNameBase<?,?> name;
126
127 // The concept reference
128 @XmlElement(name = "Sec")
129 @XmlIDREF
130 @XmlSchemaType(name = "IDREF")
131 @ManyToOne(fetch = FetchType.LAZY)
132 @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
133 @NotNull(groups = Level2.class)
134 @IndexedEmbedded
135 private Reference<?> sec;
136
137
138 @XmlElement(name = "AppendedPhrase")
139 private String appendedPhrase;
140
141 @XmlAttribute(name= "UseNameCache")
142 private boolean useNameCache = false;
143
144 @XmlAttribute(name = "publish")
145 private boolean publish = true;
146
147
148 // ************* CONSTRUCTORS *************/
149 /**
150 * Class constructor: creates a new empty (abstract) taxon.
151 *
152 * @see #TaxonBase(TaxonNameBase, Reference)
153 */
154 protected TaxonBase(){
155 super();
156 }
157
158 /**
159 * Class constructor: creates a new (abstract) taxon with the
160 * {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
161 * using it.
162 *
163 * @param taxonNameBase the taxon name used
164 * @param sec the reference using the taxon name
165 * @see #TaxonBase()
166 */
167 protected TaxonBase(TaxonNameBase taxonNameBase, Reference sec){
168 super();
169 if (taxonNameBase != null){
170 this.invokeSetMethod(methodTaxonNameAddTaxonBase, taxonNameBase);
171 }
172 this.setSec(sec);
173 }
174
175 //********* METHODS **************************************/
176
177 /**
178 * Generates and returns the string with the full scientific name (including
179 * authorship) of the {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name} used in <i>this</i>
180 * (abstract) taxon as well as the title of the {@link eu.etaxonomy.cdm.model.reference.Reference reference} using
181 * this taxon name. This string may be stored in the inherited
182 * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache} attribute.
183 * This method overrides the generic and inherited generateTitle() method
184 * from {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity IdentifiableEntity}.
185 *
186 * @return the string with the full scientific name of the taxon name
187 * and with the title of the reference involved in <i>this</i> (abstract) taxon
188 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
189 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache()
190 */
191 // @Override
192 // public String generateTitle() {
193 // String title;
194 // if (name != null && name.getTitleCache() != null){
195 // title = name.getTitleCache() + " sec. ";
196 // if (sec != null){
197 // title += sec.getTitleCache();
198 // }else{
199 // title += "???";
200 // }
201 // }else{
202 // title = this.toString();
203 // }
204 // return title;
205 // }
206
207 @Transient
208 public List<TaggedText> getTaggedTitle(){
209 return getCacheStrategy().getTaggedTitle(this);
210 }
211
212
213
214 /**
215 * Returns the {@link TaxonNameBase taxon name} used in <i>this</i> (abstract) taxon.
216 */
217 public TaxonNameBase getName(){
218 return this.name;
219 }
220
221 public void setName(TaxonNameBase name) {
222 if (this.name != null){
223 this.name.getTaxonBases().remove(this);
224 }
225 if(name != null) {
226 name.getTaxonBases().add(this);
227 }
228 this.name = name;
229 }
230
231 /**
232 * Returns the {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical group} of the
233 * {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name} used in <i>this</i> (abstract) taxon.
234 */
235 @Transient
236 public HomotypicalGroup getHomotypicGroup(){
237 if (this.getName() == null){
238 return null;
239 }else{
240 return this.getName().getHomotypicalGroup();
241 }
242 }
243
244 /**
245 * Returns the boolean value indicating whether the assignment of <i>this</i>
246 * (abstract) taxon to the {@link Taxon Taxon} or to the {@link Synonym Synonym} class
247 * is definitive (false) or not (true). If this flag is set the use of <i>this</i> (abstract)
248 * taxon as an "accepted/correct" name or as a (junior) "synonym" might
249 * still change in the course of taxonomical working process.
250 */
251 public boolean isDoubtful(){
252 return this.doubtful;
253 }
254 /**
255 * @see #isDoubtful()
256 */
257 public void setDoubtful(boolean doubtful){
258 this.doubtful = doubtful;
259 }
260
261
262 /**
263 * Returns the boolean value indicating if this taxon should be withheld (<code>publish=false</code>) or not
264 * (<code>publish=true</code>) during any publication process to the general public.
265 * This publish flag implementation is preliminary and may be replaced by a more general
266 * implementation of READ rights in future.<BR>
267 * The default value is <code>true</code>.
268 */
269 @Override
270 public boolean isPublish() {
271 return publish;
272 }
273
274 @Override
275 public void setPublish(boolean publish) {
276 this.publish = publish;
277 }
278
279 /**
280 * Returns the {@link eu.etaxonomy.cdm.model.reference.Reference reference} of <i>this</i> (abstract) taxon.
281 * This is the reference or the treatment using the {@link TaxonNameBase taxon name}
282 * in <i>this</i> (abstract) taxon.
283 */
284 public Reference getSec() {
285 return sec;
286 }
287
288 /**
289 * @see #getSec()
290 */
291 public void setSec(Reference sec) {
292 this.sec = sec;
293 }
294
295
296
297 /**
298 * An appended phrase is a phrase that is added to the {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name}
299 * 's title cache to be used just in this taxon. E.g. the phrase "sensu latu" may be added
300 * to the name to describe this taxon more precisely.
301 * If {@link #isUseNameCache()}
302 * @return the appendedPhrase
303 */
304 public String getAppendedPhrase() {
305 return appendedPhrase;
306 }
307
308 /**
309 * @param appendedPhrase the appendedPhrase to set
310 */
311 public void setAppendedPhrase(String appendedPhrase) {
312 this.appendedPhrase = appendedPhrase;
313 }
314
315 /**
316 * @return the useNameCache
317 */
318 public boolean isUseNameCache() {
319 return useNameCache;
320 }
321
322 /**
323 * @param useNameCache the useNameCache to set
324 */
325 public void setUseNameCache(boolean useNameCache) {
326 this.useNameCache = useNameCache;
327 }
328
329 @Transient
330 public abstract boolean isOrphaned();
331
332
333 /**
334 * @return
335 */
336 @Transient
337 public Rank getNullSafeRank() {
338 return name == null ? null : name.getRank();
339 }
340
341 //*********************** CLONE ********************************************************/
342
343 /**
344 * Clones <i>this</i> taxon. This is a shortcut that enables to create
345 * a new instance with empty taxon name and sec reference.
346 *
347 * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
348 * @see java.lang.Object#clone()
349 */
350 @Override
351 public Object clone() {
352 TaxonBase result;
353 try {
354 result = (TaxonBase)super.clone();
355 result.setSec(null);
356
357 return result;
358 } catch (CloneNotSupportedException e) {
359 logger.warn("Object does not implement cloneable");
360 e.printStackTrace();
361 return null;
362 }
363
364
365 }
366
367
368 }