add name relationship type "original spelling for" #2874
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / name / NameRelationshipType.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.name;
11
12
13 import java.util.HashMap;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.UUID;
17
18 import javax.persistence.Entity;
19 import javax.persistence.Transient;
20 import javax.xml.bind.annotation.XmlAccessType;
21 import javax.xml.bind.annotation.XmlAccessorType;
22 import javax.xml.bind.annotation.XmlType;
23
24 import org.apache.commons.lang.StringUtils;
25 import org.apache.log4j.Logger;
26 import org.hibernate.envers.Audited;
27 import org.hibernate.search.annotations.Indexed;
28
29 import eu.etaxonomy.cdm.common.CdmUtils;
30 import eu.etaxonomy.cdm.model.common.DefinedTermBase;
31 import eu.etaxonomy.cdm.model.common.RelationshipTermBase;
32 import eu.etaxonomy.cdm.model.common.TermType;
33 import eu.etaxonomy.cdm.model.common.TermVocabulary;
34
35 /**
36 * The class representing the categories of {@link NameRelationship taxon name relationships} between
37 * two {@link TaxonNameBase taxon names}. These name relationship types are
38 * based on the concrete {@link NomenclaturalCode nomenclatural code} governing
39 * the taxon names involved in the name relationship or on decisions taken by
40 * the competent authorities; they do not depend on the use made of these
41 * taxon names in a particular reference or in a particular taxonomic treatment.
42 * Most relationships are to be understood as 'is .... of': for instance
43 * <i>Linum radiola</i> L. is a replaced synonym of <i>Radiola linoides</i> Roth or
44 * <i>Astragalus rhizanthus</i> Boiss. is a later homonym of
45 * <i>Astragalus rhizanthus</i> Royle.
46 * <P>
47 * A standard (ordered) list of name relationship type instances will be
48 * automatically created as the project starts. But this class allows to extend
49 * this standard list by creating new instances of additional name relationship
50 * types if needed.
51 * <P>
52 * This class corresponds partially to: <ul>
53 * <li> TaxonRelationshipTerm and NomenclaturalNoteTypeTerm according to the TDWG ontology
54 * <li> RelationshipType and NomenclaturalNoteType according to the TCS
55 * </ul>
56 *
57 * @author m.doering
58 * @version 1.0
59 * @created 08-Nov-2007 13:06:38
60 */
61 @XmlAccessorType(XmlAccessType.FIELD)
62 @XmlType(name = "NameRelationshipType")
63 @Entity
64 @Indexed(index = "eu.etaxonomy.cdm.model.common.DefinedTermBase")
65 @Audited
66 public class NameRelationshipType extends RelationshipTermBase<NameRelationshipType> {
67 static Logger logger = Logger.getLogger(NameRelationshipType.class);
68
69 private static final UUID uuidOrthographicVariant = UUID.fromString("eeaea868-c4c1-497f-b9fe-52c9fc4aca53");
70 private static final UUID uuidMisspelling = UUID.fromString("c6f9afcb-8287-4a2b-a6f6-4da3a073d5de");
71 private static final UUID uuidEmendation = UUID.fromString("6e23ad45-3f2a-462b-ad87-d2389cd6e26c");
72 private static final UUID uuidLaterHomonym = UUID.fromString("80f06f65-58e0-4209-b811-cb40ad7220a6");
73 private static final UUID uuidTreatedAsLaterHomonym = UUID.fromString("2990a884-3302-4c8b-90b2-dfd31aaa2778");
74 private static final UUID uuidAlternativeName = UUID.fromString("049c6358-1094-4765-9fae-c9972a0e7780");
75 private static final UUID uuidBasionym = UUID.fromString("25792738-98de-4762-bac1-8c156faded4a");
76 private static final UUID uuidReplacedSynonym = UUID.fromString("71c67c38-d162-445b-b0c2-7aba56106696");
77 private static final UUID uuidConservedAgainst = UUID.fromString("e6439f95-bcac-4ebb-a8b5-69fa5ce79e6a");
78 private static final UUID uuidValidatedByName = UUID.fromString("a176c9ad-b4c2-4c57-addd-90373f8270eb");
79 private static final UUID uuidLaterValidatedByName = UUID.fromString("a25ee4c1-863a-4dab-9499-290bf9b89639");
80 private static final UUID uuidBlockingNameFor = UUID.fromString("1dab357f-2e12-4511-97a4-e5153589e6a6");
81
82 protected static Map<UUID, NameRelationshipType> termMap = null;
83
84 protected static NameRelationshipType findTermByUuid(UUID uuid){
85 if (termMap == null){
86 return null;
87 }
88 return (NameRelationshipType)termMap.get(uuid);
89 }
90
91
92 // ************* CONSTRUCTORS *************/
93 /**
94 * Class constructor: creates a new empty name relationship type instance.
95 *
96 * @see #NameRelationshipType(String, String, String, boolean, boolean)
97 */
98 public NameRelationshipType() {
99 }
100
101 /**
102 * Class constructor: creates an additional name relationship type
103 * instance with a description, a label, a label abbreviation and the flags
104 * indicating whether <i>this</i> new name relationship type is symmetric and/or
105 * transitive.
106 *
107 * @param term the string (in the default language) describing the
108 * new name relationship type to be created
109 * @param label the string identifying the new name relationship
110 * type to be created
111 * @param labelAbbrev the string identifying (in abbreviated form) the
112 * new name relationship type to be created
113 * @param symmetric the boolean indicating whether the new name
114 * relationship type to be created is symmetric
115 * @param transitive the boolean indicating whether the new name
116 * relationship type to be created is transitive
117 * @see #NameRelationshipType()
118 */
119 public NameRelationshipType(String term, String label, String labelAbbrev, boolean symmetric, boolean transitive) {
120 super(TermType.NameRelationshipType, term, label, labelAbbrev, symmetric, transitive);
121 }
122
123
124
125 //************************** METHODS ********************************
126
127 /* (non-Javadoc)
128 * @see eu.etaxonomy.cdm.model.common.DefinedTermBase#resetTerms()
129 */
130 @Override
131 public void resetTerms(){
132 termMap = null;
133 }
134
135 // TODO this method should be moved to consistency proof classes
136 /**
137 * Returns the boolean value indicating whether the nomenclatural status
138 * type of the {@link eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedFrom() first taxon name}
139 * involved in a name relationship with <i>this</i> name relationship type should
140 * be "invalid" (true) or not (false). Returns false if <i>this</i> name
141 * relationship status type is null.
142 *
143 * @see #isLegitimateType()
144 * @see #isIllegitimateType()
145 * @see NomenclaturalStatusType#isInvalidType()
146 * @see eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedFrom()
147 */
148 @Transient
149 public boolean isInvalidType(){
150 if (this.equals(VALIDATED_BY_NAME()) ||
151 this.equals(LATER_VALIDATED_BY_NAME())
152 ){
153 return true;
154 }else{
155 return false;
156 }
157 }
158
159 // TODO this method should be moved to consistency proof classes
160 /**
161 * Returns the boolean value indicating whether the nomenclatural status
162 * type of the {@link eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedFrom() first taxon name}
163 * involved in a name relationship with <i>this</i> name relationship type should
164 * be "legitimate" (true) or not (false). Returns false if <i>this</i> name
165 * relationship status type is null.
166 *
167 * @see #isInvalidType()
168 * @see #isIllegitimateType()
169 * @see NomenclaturalStatusType#isLegitimateType()
170 * @see eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedFrom()
171 */
172 @Transient
173 public boolean isLegitimateType(){
174 if (this.equals(BASIONYM()) ||
175 this.equals(REPLACED_SYNONYM()) ||
176 this.equals(ALTERNATIVE_NAME()) ||
177 this.equals(CONSERVED_AGAINST())
178 ){
179 return true;
180 }else{
181 return false;
182 }
183 }
184
185 // TODO this method should be moved to consistency proof classes
186 /**
187 * Returns the boolean value indicating whether the nomenclatural status
188 * type of the {@link eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedFrom() first taxon name}
189 * involved in a name relationship with <i>this</i> name relationship type should
190 * be "illegitimate" (true) or not (false). Returns false if <i>this</i> name
191 * relationship status type is null.
192 *
193 * @see #isInvalidType()
194 * @see #isLegitimateType()
195 * @see NomenclaturalStatusType#isIllegitimateType()
196 * @see eu.etaxonomy.cdm.model.common.RelationshipBase#getRelatedFrom()
197 */
198 @Transient
199 public boolean isIllegitimateType(){
200 //TODO: implement isX method. Maybe as persistent class attribute?
201 //TODO: RejectedInFavour,
202 if (this.equals(LATER_HOMONYM()) ||
203 this.equals(TREATED_AS_LATER_HOMONYM())
204 ){
205 return true;
206 }else{
207 return false;
208 }
209 }
210
211 @Transient
212 public boolean isBasionymRelation(){
213 if (BASIONYM() == null){
214 throw new IllegalStateException("NameRelationships have not been initialized yet. Please initialize DefinedTerms first");
215 }
216 return this.equals(BASIONYM());
217 }
218
219 @Transient
220 public boolean isReplacedSynonymRelation(){
221 if (REPLACED_SYNONYM() == null){
222 throw new IllegalStateException("NameRelationships have not been initialized yet. Please initialize DefinedTerms first");
223 }
224 return this.equals(REPLACED_SYNONYM());
225 }
226
227
228 /**
229 * Returns the "orthographic variant" name relationship type. The first
230 * {@link TaxonNameBase taxon name} involved in such a relationship is an
231 * orthographic variant of the second taxon name. The two {@link TaxonNameBase taxon names}
232 * involved in such a relationship must have the same {@link NonViralName#getAuthorshipCache() authorship}
233 * and {@link Rank rank}, belong to the same {@link HomotypicalGroup homotypical group} and their name parts
234 * must be almost identical (so one usually does not differentiate them).<BR>
235 * For instance <i>Angelica silvestris</i> L. is an orthographic variant of
236 * <i>Angelica sylvestris</i> L.<BR>
237 * This type is symmetric and transitive but usually orthographic variant relationships should be organized
238 * in a star schema with the correct variant in the middle and other variants pointing to it.
239 */
240 public static final NameRelationshipType ORTHOGRAPHIC_VARIANT(){
241 return findTermByUuid(uuidOrthographicVariant);
242 }
243 /**
244 * Returns the "misspelling" name relationship type. The first
245 * {@link TaxonNameBase taxon name} involved in such a relationship is a
246 * misspelling of the second taxon name. The two {@link TaxonNameBase taxon names}
247 * involved in such a relationship must have the same {@link NonViralName#getAuthorshipCache() authorship}
248 * and {@link Rank rank}, belong to the same {@link HomotypicalGroup homotypical group} and their name parts
249 * must be almost identical (so one usually does not differentiate them).<BR>
250 * For instance <i>Anhelica silvestris</i> L. is a misspelling of
251 * <i>Angelica silvestris</i> L.<BR>
252 * A misspelling is always accicentally (not on purpose). Therefore misspellings are a
253 * subset of {@link #ORTHOGRAPHIC_VARIANT orthographic variants} and are complementary to
254 * emendations. A misspelling is always an {@link #ORTHOGRAPHIC_VARIANT orthographic variant}, too.
255 * This type is symmetric and transitive but usually the misspelling relationships should be organized
256 * in a star schema with the correct variant in the middle and the misspellings pointing to it.
257 */
258 public static final NameRelationshipType MISSPELLING(){
259 return findTermByUuid(uuidMisspelling);
260 }
261 /**
262 * Returns the "emendation" name relationship type. The first
263 * {@link TaxonNameBase taxon name} involved in such a relationship is a
264 * misspelling of the second taxon name. The two {@link TaxonNameBase taxon names}
265 * involved in such a relationship must have the same {@link NonViralName#getAuthorshipCache() authorship}
266 * and {@link Rank rank}, belong to the same {@link HomotypicalGroup homotypical group} and their name parts
267 * must be almost identical (so one usually does not differentiate them).<BR>
268 * For instance <i>Angelica silvestris</i> L. is a emendation of
269 * <i>Angelica sylvestris</i> L.<BR>
270 * The name corrected by an emendation has originally been used on purpose (not accidentially)
271 * Therefore emendations are a subset of {@link #ORTHOGRAPHIC_VARIANT orthographic variants} and are
272 * complementary to {@link #MISSPELLING missepllings}. An emendation is always an
273 * {@link #ORTHOGRAPHIC_VARIANT orthographic variant}, too.<BR>
274 * This type is symmetric and transitive but usually the misspelling relationships should be organized
275 * in a star schema with the correct variant in the middle and the misspellings pointing to it.
276 */
277 public static final NameRelationshipType EMENDATION(){
278 return findTermByUuid(uuidEmendation);
279 }
280 /**
281 * Returns the "later homonym" name relationship type. The first
282 * {@link TaxonNameBase taxon name} involved in such a relationship should
283 * have been published after the second taxon name. The two {@link TaxonNameBase taxon names}
284 * involved in such a relationship must belong to different
285 * {@link HomotypicalGroup homotypical groups}, have in general different
286 * {@link NonViralName#getAuthorshipCache() authorship} and their name parts (excluding infraspecific
287 * {@link Rank ranks}) must be (almost) identical, so one could be mistaken for
288 * the other one. The first taxon name is "illegitimate" and the second one
289 * is "legitimate" (this corresponds to "invalid" and "valid" in case of
290 * {@link ZoologicalName zoological names}).<BR>
291 * For instance <i>Astragalus rhizanthus</i> Boiss. is a later homonym of
292 * <i>Astragalus rhizanthus</i> Royle.<BR>
293 * This type is not symmetric but transitive.
294 *
295 * @see NomenclaturalStatusType#isIllegitimateType()
296 * @see NomenclaturalStatusType#isLegitimateType()
297 */
298 public static final NameRelationshipType LATER_HOMONYM(){
299 return findTermByUuid(uuidLaterHomonym);
300 }
301 /**
302 * Returns the "treated as later homonym" name relationship type. The first
303 * {@link TaxonNameBase taxon name} involved in such a relationship is
304 * treated as an homonym although it has been published before the second
305 * taxon name. The two taxon names involved must belong to different
306 * {@link HomotypicalGroup homotypical groups} and their name parts (excluding
307 * {@link Rank#isInfraSpecific() infraspecific ranks} and {@link NonViralName#getAuthorshipCache() authorship}) must be
308 * almost identical (so one could be mistaken for the other). The first
309 * taxon name is "illegitimate" and the second one is "legitimate" (this
310 * corresponds to "invalid" and "valid" in case of {@link ZoologicalName zoological names}).<BR>
311 * This type is not symmetric but transitive.
312 *
313 * @see #LATER_HOMONYM()
314 * @see NomenclaturalStatusType#isIllegitimateType()
315 * @see NomenclaturalStatusType#isLegitimateType()
316 */
317 public static final NameRelationshipType TREATED_AS_LATER_HOMONYM(){
318 return findTermByUuid(uuidTreatedAsLaterHomonym);
319 }
320 /**
321 * Returns the "alternative name" name relationship type. Both {@link TaxonNameBase taxon names}
322 * involved in such a relationship are family names. The first one is a
323 * classical name long in use, in some cases, even before 1753 and is considered as
324 * {@link NomenclaturalStatusType#VALID() valid} and also {@link NomenclaturalStatusType#isLegitimateType() legitimate}
325 * although it does not follow the rules for family names (see Article 18 of
326 * the ICBN). An alternative name is typified by the type of the name
327 * it is alternative to (so both must belong to the same
328 * {@link HomotypicalGroup homotypical group}).<BR>
329 * For instance <i>Cruciferae</i> Adans is an alternative name to
330 * <i>Brassicaceae</i> Lindl.<BR>
331 * This type is neither symmetric nor transitive.
332 */
333 public static final NameRelationshipType ALTERNATIVE_NAME(){
334 return findTermByUuid(uuidAlternativeName);
335 }
336 /**
337 * Returns the "basionym" name relationship type. The first {@link TaxonNameBase taxon name}
338 * involved in such a relationship is the "basionym" of the second taxon
339 * name. Both taxon names belong to the same {@link HomotypicalGroup homotypical group}).
340 * The basionym is the epithet-bringing taxon name (first taxon name
341 * ever validly published given to the same {@link Rank#isInfraGeneric() infrageneric}
342 * taxon, the epithet of which is the same as in the second taxon name
343 * originated through a reclassification).<BR>
344 * According to the ICBN the author of the basionym must be mentioned in the
345 * later taxon name (by placing it in parentheses before the authority of
346 * the new combination). For instance <i>Pinus abies</i> L. is the basionym of
347 * <i>Picea abies</i> (L.) H. Karst.<BR>
348 * This type is neither symmetric nor transitive.
349 */
350 public static final NameRelationshipType BASIONYM(){
351 return findTermByUuid(uuidBasionym);
352 }
353 /**
354 * Returns the "replaced synonym" name relationship type. The first
355 * {@link TaxonNameBase taxon name} involved in such a relationship is the
356 * "replaced synonym" of the second taxon name. Both taxon names belong to
357 * the same {@link HomotypicalGroup homotypical group}. The replaced synonym is the
358 * first taxon name ever validly published given to the same
359 * {@link Rank#isInfraGeneric() infrageneric} taxon that is either itself a
360 * "later homonym" or the epithet of which could not be used in the new
361 * taxon name originated through a reclassification. A new epithet must be
362 * proposed if the use of the original epithet leads to an already existing
363 * taxon name (for another taxon) or in botany to autonyms (since the ICBN
364 * does not allow such names where epithet and genus name are the same).<BR>
365 * For instance <i>Spartium biflorum</i> Desf. is the replaced synonym of
366 * of <i>Cytisus fontanesii</i> Spach ("novum" taxon name) because at the time
367 * of reclassification a taxon name <i>Cytisus biflorum</i> had been already
368 * published by L'H�r.<BR>
369 * This type is neither symmetric nor transitive.
370 *
371 * @see #BASIONYM()
372 * @see #LATER_HOMONYM()
373 * @see NomenclaturalStatusType#NOVUM()
374 */
375 public static final NameRelationshipType REPLACED_SYNONYM(){
376 return findTermByUuid(uuidReplacedSynonym);
377 }
378 /**
379 * Returns the "conserved against" name relationship type. Both {@link TaxonNameBase taxon names}
380 * involved in such a relationship belong to the same {@link HomotypicalGroup homotypical group}.
381 * Competent authorities decided, regardless of the general
382 * nomenclatural rules, to handle the first one as the "legitimate"
383 * one and the second taxon name as "illegitimate" (this corresponds to
384 * "valid" and "invalid" in case of {@link ZoologicalName zoological names}).<BR>
385 * For instance <i>Cephaloziella</i> (Spruce) Schiffn. is conserved against
386 * <i>Dichiton</i> Mont.<BR>
387 * This type is neither symmetric nor transitive.
388 *
389 * @see NomenclaturalStatusType#CONSERVED()
390 * @see NomenclaturalStatusType#REJECTED()
391 * @see NomenclaturalStatusType#isLegitimateType()
392 * @see NomenclaturalStatusType#isIllegitimateType()
393 */
394 public static final NameRelationshipType CONSERVED_AGAINST(){
395 return findTermByUuid(uuidConservedAgainst);
396 }
397 /**
398 * Returns the "validated by name" name relationship type. The two
399 * {@link TaxonNameBase taxon names} involved in such a relationship were published
400 * in order to define the same taxonomical group but the first
401 * (earlier) taxon name was invalidly published whereas the second (later)
402 * taxon name is the one which was validly published for the first time.<BR>
403 * This type is neither symmetric nor transitive.
404 *
405 * @see NomenclaturalStatusType#isInvalidType()
406 * @see NomenclaturalStatusType#VALID()
407 */
408 public static final NameRelationshipType VALIDATED_BY_NAME(){
409 return findTermByUuid(uuidValidatedByName);
410 }
411 /**
412 * Returns the "later validated by name" name relationship type. The two
413 * {@link TaxonNameBase taxon names} involved in such a relationship were published
414 * in order to define the same taxonomical group but the first
415 * (earlier) taxon name was invalidly published whereas the second (later)
416 * taxon name is the one which was validly published for the first time.<BR>
417 * This type is neither symmetric nor transitive.
418 *
419 * @see NomenclaturalStatusType#isInvalidType()
420 * @see NomenclaturalStatusType#VALID()
421 */
422 public static final NameRelationshipType LATER_VALIDATED_BY_NAME(){
423 return findTermByUuid(uuidLaterValidatedByName);
424 }
425 /**
426 * Returns the "blocking name" name relationship type. The first
427 * {@link TaxonNameBase taxon name} involved in such a relationship is the
428 * "blocking name" for the second taxon name. Both taxon names belong to
429 * different {@link HomotypicalGroup homotypical groups}). The blocking taxon name is the
430 * {@link Rank#isInfraGeneric() infrageneric} taxon name, already published at the time of
431 * reclassification, which makes illegitim (because of homonymy) the use of
432 * the epithet in the second taxon name originated through a reclassification.
433 * Therefore a "replaced synonym" name relationship arises.<BR>
434 * For instance <i>Cytisus biflorum</i> L'H�r. is the blocking name for
435 * <i>Cytisus fontanesii</i> Spach ("novum" taxon name) when reclassifying
436 * <i>Spartium biflorum</i> Desf. from <i>Spartium</i> to <i>Cytisus</i>.<BR>
437 * This type is neither symmetric nor transitive.
438 *
439 * @see #REPLACED_SYNONYM()
440 * @see #LATER_HOMONYM()
441 * @see NomenclaturalStatusType#NOVUM()
442 */
443 public static final NameRelationshipType BLOCKING_NAME_FOR(){
444 return findTermByUuid(uuidBlockingNameFor);
445 }
446
447 @Override
448 protected void setDefaultTerms(TermVocabulary<NameRelationshipType> termVocabulary) {
449 termMap = new HashMap<UUID, NameRelationshipType>();
450 for (NameRelationshipType term : termVocabulary.getTerms()){
451 termMap.put(term.getUuid(), term);
452 }
453 }
454
455 @Override
456 public NameRelationshipType readCsvLine(Class<NameRelationshipType> termClass, List<String> csvLine, Map<UUID,DefinedTermBase> terms) {
457 NameRelationshipType result = super.readCsvLine(termClass, csvLine, terms);
458 String kindOfString = csvLine.get(10).trim();
459 if (StringUtils.isNotBlank(kindOfString)){
460 UUID uuidKindOf = UUID.fromString(kindOfString);
461 DefinedTermBase kindOf = terms.get(uuidKindOf);
462 result.setKindOf((NameRelationshipType)kindOf);
463 }
464 return result;
465 }
466 }