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