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