Merge branch 'release/3.8.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 java.util.HashSet;
13 import java.util.Set;
14
15 import javax.persistence.Entity;
16 import javax.persistence.FetchType;
17 import javax.persistence.OneToMany;
18 import javax.persistence.Transient;
19 import javax.validation.Valid;
20 import javax.validation.constraints.NotNull;
21 import javax.xml.bind.annotation.XmlAccessType;
22 import javax.xml.bind.annotation.XmlAccessorType;
23 import javax.xml.bind.annotation.XmlElement;
24 import javax.xml.bind.annotation.XmlElementWrapper;
25 import javax.xml.bind.annotation.XmlIDREF;
26 import javax.xml.bind.annotation.XmlRootElement;
27 import javax.xml.bind.annotation.XmlSchemaType;
28 import javax.xml.bind.annotation.XmlType;
29
30 import org.apache.log4j.Logger;
31 import org.hibernate.annotations.Cascade;
32 import org.hibernate.annotations.CascadeType;
33 import org.hibernate.envers.Audited;
34 import org.hibernate.search.annotations.Indexed;
35 import org.hibernate.validator.constraints.NotEmpty;
36 import org.springframework.beans.factory.annotation.Configurable;
37
38 import eu.etaxonomy.cdm.model.common.IRelated;
39 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
40 import eu.etaxonomy.cdm.model.reference.Reference;
41 import eu.etaxonomy.cdm.strategy.cache.taxon.ITaxonCacheStrategy;
42 import eu.etaxonomy.cdm.strategy.cache.taxon.TaxonBaseDefaultCacheStrategy;
43 import eu.etaxonomy.cdm.validation.Level2;
44
45 /**
46 * The class for synonyms: these are {@link TaxonBase taxa} the {@link name.TaxonNameBase taxon names}
47 * of which are not used by the {@link TaxonBase#getSec() reference} to designate a real
48 * taxon but are mentioned as taxon names that were oder are used by some other
49 * unspecified references to designate (at least to some extent) the same
50 * particular real taxon. Synonyms that are involved in no
51 * {@link SynonymRelationship synonym relationship} are actually meaningless.<BR>
52 * Splitting taxa in "accepted/correct" and "synonyms"
53 * makes it easier to handle particular relationships between
54 * ("accepted/correct") {@link Taxon taxa} on the one hand and between ("synonym") taxa
55 * and ("accepted/correct") taxa on the other.
56 *
57 * @author m.doering
58 * @version 1.0
59 * @created 08-Nov-2007 13:06:55
60 */
61 @XmlAccessorType(XmlAccessType.FIELD)
62 @XmlType(name = "Synonym", propOrder = {
63 "synonymRelations"
64 })
65 @XmlRootElement(name = "Synonym")
66 @Entity
67 @Indexed(index = "eu.etaxonomy.cdm.model.taxon.TaxonBase")
68 @Audited
69 @Configurable
70 public class Synonym extends TaxonBase<ITaxonCacheStrategy<Synonym>> implements IRelated<SynonymRelationship>{
71 private static final long serialVersionUID = -454067515022159757L;
72
73 @SuppressWarnings("unused")
74 private static final Logger logger = Logger.getLogger(Synonym.class);
75
76 // Don't need the synonym relations here since they are stored at taxon side?
77 @XmlElementWrapper(name = "SynonymRelations")
78 @XmlElement(name = "SynonymRelationship")
79 @XmlIDREF
80 @XmlSchemaType(name = "IDREF")
81 @OneToMany(mappedBy="relatedFrom", fetch=FetchType.LAZY, orphanRemoval=true)
82 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
83 @NotNull
84 @NotEmpty(groups = Level2.class,message="{eu.etaxonomy.cdm.model.taxon.Synonym.noOrphanedSynonyms.message}")
85 @Valid
86 private Set<SynonymRelationship> synonymRelations = new HashSet<SynonymRelationship>();
87
88 // ************* CONSTRUCTORS *************/
89 /**
90 * Class constructor: creates a new empty synonym instance.
91 *
92 * @see #Synonym(TaxonNameBase, Reference)
93 */
94 //TODO should be private, but still produces Spring init errors
95 public Synonym(){
96 this.cacheStrategy = new TaxonBaseDefaultCacheStrategy<Synonym>();
97 }
98
99 /**
100 * Class constructor: creates a new synonym instance with
101 * the {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
102 * using it as a synonym and not as an ("accepted/correct") {@link Taxon taxon}.
103 *
104 * @param taxonNameBase the taxon name used
105 * @param sec the reference using the taxon name
106 * @see Synonym#Synonym(TaxonNameBase, Reference)
107 */
108 public Synonym(TaxonNameBase taxonNameBase, Reference sec){
109 super(taxonNameBase, sec);
110 this.cacheStrategy = new TaxonBaseDefaultCacheStrategy<Synonym>();
111 }
112
113 //********* METHODS **************************************/
114
115 /**
116 * Creates a new synonym instance with
117 * the {@link eu.etaxonomy.cdm.model.name.TaxonNameBase taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
118 * using it as a synonym and not as an ("accepted/correct") {@link Taxon taxon}.
119 *
120 * @param taxonNameBase the taxon name used
121 * @param sec the reference using the taxon name
122 * @see #Synonym(TaxonNameBase, Reference)
123 */
124 public static Synonym NewInstance(TaxonNameBase taxonName, Reference sec){
125 Synonym result = new Synonym(taxonName, sec);
126 return result;
127 }
128
129 /**
130 * Returns the set of all {@link SynonymRelationship synonym relationships}
131 * in which <i>this</i> synonym is involved. <i>This</i> synonym can only
132 * be the source within these synonym relationships.
133 *
134 * @see #addSynonymRelation(SynonymRelationship)
135 * @see #addRelationship(SynonymRelationship)
136 * @see #removeSynonymRelation(SynonymRelationship)
137 */
138 public Set<SynonymRelationship> getSynonymRelations() {
139 if(synonymRelations == null) {
140 this.synonymRelations = new HashSet<SynonymRelationship>();
141 }
142 return synonymRelations;
143 }
144
145 /**
146 * @see #getSynonymRelations()
147 */
148 protected void setSynonymRelations(Set<SynonymRelationship> synonymRelations) {
149 this.synonymRelations = synonymRelations;
150 }
151
152 /**
153 * Adds an existing {@link SynonymRelationship synonym relationship} to the set of
154 * {@link #getSynonymRelations() synonym relationships} assigned to <i>this</i> synonym. If
155 * the source of the synonym relationship does not match with <i>this</i>
156 * synonym no addition will be carried out.<BR>
157 * This methods does the same as the {@link #addRelationship() addRelationship} method.
158 *
159 * @param synonymRelation the synonym relationship to be added to <i>this</i> synonym's
160 * synonym relationships set
161 * @see #addRelationship(SynonymRelationship)
162 * @see #getSynonymRelations()
163 * @see #removeSynonymRelation(SynonymRelationship)
164 */
165 protected void addSynonymRelation(SynonymRelationship synonymRelation) {
166 this.synonymRelations.add(synonymRelation);
167 }
168 /**
169 * Removes one element from the set of {@link SynonymRelationship synonym relationships} assigned
170 * to <i>this</i> synonym. Due to bidirectionality the given
171 * synonym relationship will also be removed from the set of synonym
172 * relationships assigned to the {@link Taxon#getSynonymRelations() taxon} involved in the
173 * relationship. Furthermore the content of
174 * the {@link SynonymRelationship#getAcceptedTaxon() accepted taxon} attribute and of the
175 * {@link SynonymRelationship#getSynonym() synonym} attribute within the synonym relationship
176 * itself will be set to "null".
177 *
178 * @param synonymRelation the synonym relationship which should be deleted
179 * @see #getSynonymRelations()
180 * @see #addRelationship(SynonymRelationship)
181 */
182 public void removeSynonymRelation(SynonymRelationship synonymRelation) {
183 synonymRelation.setSynonym(null);
184 Taxon taxon = synonymRelation.getAcceptedTaxon();
185 if (taxon != null){
186 synonymRelation.setAcceptedTaxon(null);
187 taxon.removeSynonymRelation(synonymRelation);
188 }
189 this.synonymRelations.remove(synonymRelation);
190 }
191
192
193 /**
194 * Adds an existing {@link SynonymRelationship synonym relationship} to the set of
195 * {@link #getSynonymRelations() synonym relationships} assigned to <i>this</i> synonym. If
196 * the source of the synonym relationship does not match with <i>this</i>
197 * synonym no addition will be carried out.<BR>
198 * This methods does the same as the {@link #addSynonymRelation(SynonymRelationship) addSynonymRelation} method.
199 *
200 * @param synonymRelation the synonym relationship to be added to <i>this</i> synonym's
201 * synonym relationships set
202 * @see #addSynonymRelation(SynonymRelationship)
203 * @see #getSynonymRelations()
204 * @see #removeSynonymRelation(SynonymRelationship)
205 */
206 /* (non-Javadoc)
207 * @see eu.etaxonomy.cdm.model.common.IRelated#addRelationship(eu.etaxonomy.cdm.model.common.RelationshipBase)
208 */
209 @Override
210 public void addRelationship(SynonymRelationship rel){
211 addSynonymRelation(rel);
212 }
213
214
215 /**
216 * Returns the set of all ("accepted/correct") {@link Taxon taxa} involved in the same
217 * {@link SynonymRelationship synonym relationships} as <i>this</i> synonym.
218 * Each taxon is the target and <i>this</i> synonym is the source of a {@link SynonymRelationship synonym relationship}
219 * belonging to the {@link #getSynonymRelations() set of synonym relationships} assigned to
220 * <i>this</i> synonym. For a particular synonym there are more than one
221 * ("accepted/correct") taxa only if the {@link SynonymRelationship#isProParte() "is pro parte" flag}
222 * of the corresponding {@link SynonymRelationship synonym relationships} is set.
223 *
224 * @see #getSynonymRelations()
225 * @see #getRelationType(Taxon)
226 * @see SynonymRelationship#isProParte()
227 */
228 @Transient
229 public Set<Taxon> getAcceptedTaxa() {
230 Set<Taxon>taxa=new HashSet<Taxon>();
231 for (SynonymRelationship rel:getSynonymRelations()){
232 taxa.add(rel.getAcceptedTaxon());
233 }
234 return taxa;
235 }
236
237 /**
238 * Returns true if <i>this</i> is a synonym of the given taxon.
239 *
240 * @param taxon the taxon to check synonym for
241 * @return true if <i>this</i> is a ynonms of the given taxon
242 *
243 * @see #getAcceptedTaxa()
244 */
245 @Transient
246 public boolean isSynonymOf(Taxon taxon){
247 return getAcceptedTaxa().contains(taxon);
248 }
249
250 @Override
251 @Transient
252 public boolean isOrphaned() {
253 return false;
254 }
255 /**
256 * Returns the set of {@link SynonymRelationshipType synonym relationship types} of the
257 * {@link SynonymRelationship synonym relationships} where the {@link SynonymRelationship#getSynonym() synonym}
258 * is <i>this</i> synonym and the {@link SynonymRelationship#getAcceptedTaxon() taxon}
259 * is the given one. "Null" is returned if the given taxon is "null" or if
260 * no synonym relationship exists from <i>this</i> synonym to the
261 * given taxon.
262 *
263 * @param taxon the ("accepted/correct") taxon which a synonym relationship
264 * from <i>this</i> synonym should point to
265 * @see #getSynonymRelations()
266 * @see #getAcceptedTaxa()
267 */
268 public Set<SynonymRelationshipType> getRelationType(Taxon taxon){
269 Set<SynonymRelationshipType> result = new HashSet<SynonymRelationshipType>();
270 if (taxon == null ){
271 return result;
272 }
273 for (SynonymRelationship rel : getSynonymRelations()){
274 Taxon acceptedTaxon = rel.getAcceptedTaxon();
275 if (taxon.equals(acceptedTaxon)){
276 result.add(rel.getType());
277 }
278 }
279 return result;
280 }
281
282 /**
283 * Replaces ALL accepted taxa of this synonym by the new accepted taxon.
284 * The citation information (citation /microcitation) of the synonym relationship
285 * is kept.
286 * @param newAcceptedTaxon
287 * the new accepted taxon
288 * @param relType
289 * if not <code>null</code> the relationship type is changed to relType
290 * @param copyCitationInfo
291 * if true the citation and the microcitation of relationship
292 * is not changed.
293 * @param citation
294 * if copyCitationInfo is <code>false</code> this citation is set
295 * to the synonym relationship.
296 * @param microCitation
297 * if copyCitationInfo is <code>false</code> this micro citation is set
298 * to the synonym relationship.
299
300 * @param acceptedTaxon
301 */
302 public void replaceAcceptedTaxon(Taxon newAcceptedTaxon, SynonymRelationshipType relType, boolean copyCitationInfo, Reference citation, String microCitation) {
303 Set<SynonymRelationship> rels = new HashSet<SynonymRelationship>();
304 rels.addAll(this.getSynonymRelations()); //avoid concurrent modification exception
305
306 for (SynonymRelationship rel : rels){
307 Taxon oldAcceptedTaxon = rel.getAcceptedTaxon();
308 Synonym syn = rel.getSynonym();
309
310 oldAcceptedTaxon.removeSynonym(rel.getSynonym(), false);
311
312 SynonymRelationship newRel = (SynonymRelationship)rel.clone();
313 newRel.setAcceptedTaxon(newAcceptedTaxon);
314 newAcceptedTaxon.getSynonymRelations().add(newRel);
315 newRel.setSynonym(syn);
316 syn.addSynonymRelation(newRel);
317
318 newRel.setType(relType);
319 }
320 }
321 //*********************** CLONE ********************************************************/
322
323 @Override
324 public Object clone() {
325 Synonym result;
326 result = (Synonym)super.clone();
327
328 result.setSynonymRelations(new HashSet<SynonymRelationship>());
329
330 for (SynonymRelationship synRelationship : this.getSynonymRelations()){
331 SynonymRelationship newRelationship = (SynonymRelationship)synRelationship.clone();
332 newRelationship.setRelatedFrom(result);
333 result.synonymRelations.add(newRelationship);
334 }
335 return result;
336
337 }
338
339
340 }