Merge branch 'release/4.13.0'
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / taxon / Synonym.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 javax.persistence.Entity;
13 import javax.persistence.FetchType;
14 import javax.persistence.ManyToOne;
15 import javax.persistence.Transient;
16 import javax.xml.bind.annotation.XmlAccessType;
17 import javax.xml.bind.annotation.XmlAccessorType;
18 import javax.xml.bind.annotation.XmlElement;
19 import javax.xml.bind.annotation.XmlIDREF;
20 import javax.xml.bind.annotation.XmlRootElement;
21 import javax.xml.bind.annotation.XmlSchemaType;
22 import javax.xml.bind.annotation.XmlType;
23
24 import org.apache.log4j.Logger;
25 import org.hibernate.annotations.Cascade;
26 import org.hibernate.annotations.CascadeType;
27 import org.hibernate.envers.Audited;
28 import org.hibernate.search.annotations.ContainedIn;
29 import org.hibernate.search.annotations.Indexed;
30 import org.springframework.beans.factory.annotation.Configurable;
31
32 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
33 import eu.etaxonomy.cdm.model.name.ITaxonNameBase;
34 import eu.etaxonomy.cdm.model.name.TaxonName;
35 import eu.etaxonomy.cdm.model.reference.Reference;
36 import eu.etaxonomy.cdm.strategy.cache.taxon.ITaxonCacheStrategy;
37 import eu.etaxonomy.cdm.strategy.cache.taxon.TaxonBaseDefaultCacheStrategy;
38 import eu.etaxonomy.cdm.validation.Level3;
39 import eu.etaxonomy.cdm.validation.annotation.HomotypicSynonymsShouldBelongToGroup;
40
41 /**
42 * The class for synonyms: these are {@link TaxonBase taxa} the {@link name.TaxonName taxon names}
43 * of which are not used by the {@link TaxonBase#getSec() reference} to designate a real
44 * taxon but are mentioned as taxon names that were oder are used by some other
45 * unspecified references to designate (at least to some extent) the same
46 * particular real taxon. Synonyms that are {@link #getAcceptedTaxon() attached} to an accepted {@link Taxon taxon}
47 * are actually meaningless.<BR>
48 * Splitting taxa in "accepted/valid" and "synonyms"
49 * makes it easier to handle particular relationships between
50 * ("accepted/valid") {@link Taxon taxa} on the one hand and ("synonym") taxa
51 * on the other.
52 *
53 * @author m.doering
54 * @created 08-Nov-2007 13:06:55
55 */
56 @XmlAccessorType(XmlAccessType.FIELD)
57 @XmlType(name = "Synonym", propOrder = {
58 "acceptedTaxon",
59 "type",
60 "proParte",
61 "partial"
62 })
63 @XmlRootElement(name = "Synonym")
64 @Entity
65 @Indexed(index = "eu.etaxonomy.cdm.model.taxon.TaxonBase")
66 @Audited
67 @Configurable
68 @HomotypicSynonymsShouldBelongToGroup(groups = Level3.class)
69 public class Synonym extends TaxonBase<ITaxonCacheStrategy<Synonym>> {
70 private static final long serialVersionUID = 6977221584815363620L;
71
72
73 @SuppressWarnings("unused")
74 private static final Logger logger = Logger.getLogger(Synonym.class);
75
76
77 @XmlElement(name = "acceptedTaxon")
78 @XmlIDREF
79 @XmlSchemaType(name = "IDREF")
80 @ManyToOne(fetch = FetchType.LAZY)
81 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
82 @ContainedIn
83 // @NotEmpty(groups = Level2.class,message="{eu.etaxonomy.cdm.model.taxon.Synonym.noOrphanedSynonyms.message}")
84 // @NotNull(groups = Level2.class)
85 private Taxon acceptedTaxon;
86
87
88 @XmlElement(name = "IsProParte")
89 private boolean proParte = false;
90
91 @XmlElement(name = "IsPartial")
92 private boolean partial = false;
93
94
95 @XmlElement(name = "Type")
96 @XmlIDREF
97 @XmlSchemaType(name = "IDREF")
98 @ManyToOne(fetch=FetchType.EAGER)
99 private SynonymType type;
100
101 //************************************* FACTORY ****************************/
102 /**
103 * @see #NewInstance(TaxonName, Reference)
104 * @param taxonName
105 * @param sec
106 * @return
107 */
108 public static Synonym NewInstance(ITaxonNameBase taxonName, Reference sec){
109 return NewInstance(TaxonName.castAndDeproxy(taxonName), sec);
110 }
111
112 /**
113 * Creates a new synonym instance with
114 * the {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
115 * using it as a synonym and not as an ("accepted/correct") {@link Taxon taxon}.
116 *
117 * @param TaxonName the taxon name used
118 * @param sec the reference using the taxon name
119 * @see #Synonym(TaxonName, Reference)
120 */
121 public static Synonym NewInstance(TaxonName taxonName, Reference sec){
122 Synonym result = new Synonym(taxonName, sec, null);
123 return result;
124 }
125
126 public static Synonym NewInstance(TaxonName taxonName, Reference sec, String secDetail){
127 Synonym result = new Synonym(taxonName, sec, secDetail);
128 return result;
129 }
130
131 // ************* CONSTRUCTORS *************/
132 /**
133 * Class constructor: creates a new empty synonym instance.
134 *
135 * @see #Synonym(TaxonName, Reference)
136 */
137 //TODO should be private, but still produces Spring init errors
138 public Synonym(){
139 this.cacheStrategy = new TaxonBaseDefaultCacheStrategy<Synonym>();
140 }
141
142 private Synonym(TaxonName taxonName, Reference sec, String secDetail){
143 super(taxonName, sec, secDetail);
144 this.cacheStrategy = new TaxonBaseDefaultCacheStrategy<Synonym>();
145 }
146
147 //********************** GETTER/SETTER ******************************/
148
149 /**
150 * Returns the "accepted/valid" {@link Taxon taxon}
151 *
152 */
153 public Taxon getAcceptedTaxon() {
154 return this.acceptedTaxon;
155 }
156
157
158 /**
159 * @param acceptedTaxon the acceptedTaxon to set
160 */
161 protected void setAcceptedTaxon(Taxon acceptedTaxon) {
162 if (acceptedTaxon == null){
163 Taxon oldTaxon = this.acceptedTaxon;
164 this.acceptedTaxon = null;
165 oldTaxon.removeSynonym(this);
166 }else{
167 if (this.acceptedTaxon != null){
168 this.acceptedTaxon.removeSynonym(this, false);
169 }
170 this.acceptedTaxon = acceptedTaxon;
171 this.acceptedTaxon.addSynonym(this);
172 checkHomotypic();
173 }
174 }
175
176 /**
177 * Returns "true" if the proParte flag is set.
178 * This indicates that the {@link name.TaxonName taxon name} used as a
179 * {@link Synonym synonym} designated originally a real taxon which later has
180 * been split. In this case the synonym is therefore the synonym of at least
181 * two different ("accepted/valid") {@link Taxon taxa}.
182 */
183 public boolean isProParte() {
184 return proParte;
185 }
186
187 /**
188 * @see #isProParte()
189 */
190 public void setProParte(boolean proParte) {
191 this.proParte = proParte;
192 }
193
194 /**
195 * Returns "true" if the <cod>partial</code> flag is set.
196 * This indicates that the {@link name.TaxonName taxon name} used as <code>this</code>
197 * {@link Synonym synonym} designated originally a real taxon which later has
198 * been lumped together with another one. In this case the
199 * ("accepted/valid") {@link Taxon taxon} has therefore at least
200 * two different synonyms (for the two lumped real taxa).
201 */
202 public boolean isPartial() {
203 return partial;
204 }
205
206 /**
207 * @see #isPartial()
208 */
209 public void setPartial(boolean partial) {
210 this.partial = partial;
211 }
212
213
214 public SynonymType getType() {
215 return type;
216 }
217
218 public void setType(SynonymType type) {
219 this.type = type;
220 checkHomotypic();
221 }
222
223
224 //***************** METHODS **************************/
225 /**
226 * Returns true if <i>this</i> is a synonym of the given taxon.
227 *
228 * @param taxon the taxon to check synonym for
229 * @return true if <i>this</i> is a synonm of the given taxon
230 *
231 * @see #getAcceptedTaxon()
232 */
233 @Transient
234 public boolean isSynonymOf(Taxon taxon){
235 return taxon != null && taxon.equals(this.acceptedTaxon);
236 }
237
238 @Override
239 @Transient
240 public boolean isOrphaned() {
241 return this.acceptedTaxon == null || this.acceptedTaxon.isOrphaned();
242 }
243
244 /**
245 * Checks if the synonym type is homotypic. If it is
246 * the name of <code>this</code> synonym is added to the {@link HomotypicalGroup
247 * homotypic group} of the {@link Taxon accepted taxon}.
248 */
249 private void checkHomotypic() {
250 if (type != null && type.equals(SynonymType.HOMOTYPIC_SYNONYM_OF())
251 && acceptedTaxon != null && acceptedTaxon.getName() != null){
252 acceptedTaxon.getName().getHomotypicalGroup().addTypifiedName(this.getName());
253 }
254 }
255
256 //*********************** CLONE ********************************************************/
257
258 @Override
259 public Object clone() {
260 Synonym result;
261 result = (Synonym)super.clone();
262
263 //no changes to accepted taxon, type, partial, proParte
264
265 return result;
266
267 }
268
269
270 }