Merge branch 'release/5.8.0'
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / name / Rank.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 import java.util.HashMap;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.UUID;
16
17 import javax.persistence.Entity;
18 import javax.persistence.Transient;
19 import javax.validation.constraints.NotNull;
20 import javax.xml.bind.annotation.XmlAccessType;
21 import javax.xml.bind.annotation.XmlAccessorType;
22 import javax.xml.bind.annotation.XmlAttribute;
23 import javax.xml.bind.annotation.XmlType;
24
25 import org.apache.commons.lang.StringUtils;
26 import org.apache.log4j.Logger;
27 import org.hibernate.annotations.Type;
28 import org.hibernate.envers.Audited;
29
30 import eu.etaxonomy.cdm.common.CdmUtils;
31 import eu.etaxonomy.cdm.model.common.Language;
32 import eu.etaxonomy.cdm.model.term.DefinedTermBase;
33 import eu.etaxonomy.cdm.model.term.OrderedTermBase;
34 import eu.etaxonomy.cdm.model.term.Representation;
35 import eu.etaxonomy.cdm.model.term.TermType;
36 import eu.etaxonomy.cdm.model.term.TermVocabulary;
37 import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
38
39 /**
40 * The class representing the taxonomical ranks (like "Family", "Genus" or
41 * "Species") used for {@link TaxonName taxon names} across all {@link NomenclaturalCode nomenclatural codes}
42 * for bacteria (ICNB), viruses (ICVCN), plants and fungi (ICBN),
43 * cultivars (ICNCP) and animals (ICZN).
44 * <P>
45 * A standard (ordered) list of taxonomical rank instances will be automatically
46 * created as the project starts. But this class allows to extend this standard
47 * list by creating new instances of additional taxonomical ranks if needed.
48 * <P>
49 * This class corresponds to: <ul>
50 * <li> TaxonRankTerm according to the TDWG ontology
51 * <li> TaxonomicRankEnum according to the TCS
52 * <li> Rank according to the ABCD schema
53 * </ul>
54 *
55 * @author m.doering
56 * @since 08-Nov-2007 13:06:46
57 */
58 @XmlAccessorType(XmlAccessType.FIELD)
59 @XmlType(name = "Rank")
60 @Entity
61 //@Indexed disabled to reduce clutter in indexes, since this type is not used by any search
62 //@Indexed(index = "eu.etaxonomy.cdm.model.term.DefinedTermBase")
63 @Audited
64 public class Rank extends OrderedTermBase<Rank> {
65 private static final long serialVersionUID = -8648081681348758485L;
66 private static final Logger logger = Logger.getLogger(Rank.class);
67
68 private static final UUID uuidEmpire = UUID.fromString("ac470211-1586-4b24-95ca-1038050b618d");
69 private static final UUID uuidDomain = UUID.fromString("ffca6ec8-8b88-417b-a6a0-f7c992aac19b");
70 private static final UUID uuidSuperkingdom = UUID.fromString("64223610-7625-4cfd-83ad-b797bf7f0edd");
71 private static final UUID uuidKingdom = UUID.fromString("fbe7109d-66b3-498c-a697-c6c49c686162");
72 private static final UUID uuidSubkingdom = UUID.fromString("a71bd9d8-f3ab-4083-afb5-d89315d71655");
73 private static final UUID uuidInfrakingdom = UUID.fromString("1e37930c-86cf-44f6-90fd-7822928df260");
74 private static final UUID uuidSuperphylum = UUID.fromString("0d0cecb1-e254-4607-b210-6801e7ecbb04");
75 private static final UUID uuidPhylum = UUID.fromString("773430d2-76b4-438c-b817-97a543a33287");
76 private static final UUID uuidSubphylum = UUID.fromString("23a9b6ff-9408-49c9-bd9e-7a2ca5ab4725");
77 private static final UUID uuidInfraphylum = UUID.fromString("1701de3a-7693-42a5-a2d3-42697f944190");
78 private static final UUID uuidSuperdivision = UUID.fromString("a735a48f-4fc8-49a7-ae0c-6a984f658131");
79 private static final UUID uuidDivision = UUID.fromString("7e56f5cc-123a-4fd1-8cbb-6fd80358b581");
80 private static final UUID uuidSubdivision = UUID.fromString("931c840f-7a6b-4d76-ad38-bfdd77d7b2e8");
81 private static final UUID uuidInfradivision = UUID.fromString("c0ede273-be52-4dee-b411-66ee08d30c94");
82 private static final UUID uuidSuperclass = UUID.fromString("e65b4e1a-21ec-428d-9b9f-e87721ab967c");
83 private static final UUID uuidClass = UUID.fromString("f23d14c4-1d34-4ee6-8b4e-eee2eb9a3daf");
84 private static final UUID uuidSubclass = UUID.fromString("8cb26733-e2f5-46cb-ab5c-f99254f877aa");
85 private static final UUID uuidInfraclass = UUID.fromString("ad23cfda-879a-4021-8629-c54d27caf717");
86 private static final UUID uuidSuperorder = UUID.fromString("c8c67a22-301a-4219-b882-4a49121232ff");
87 private static final UUID uuidOrder = UUID.fromString("b0785a65-c1c1-4eb4-88c7-dbd3df5aaad1");
88 private static final UUID uuidSuborder = UUID.fromString("768ad378-fa85-42ab-b668-763225832f57");
89 private static final UUID uuidInfraorder = UUID.fromString("84099182-a6f5-47d7-8586-33c9e9955a10");
90 private static final UUID uuidSectionZoology = UUID.fromString("691d371e-10d7-43f0-93db-3d7fa1a62c54");
91 private static final UUID uuidSubsectionZoology = UUID.fromString("0ed32d28-adc4-4303-a9ca-68e2acd67e33");
92 private static final UUID uuidSuperfamily = UUID.fromString("2cfa510a-dcea-4a03-b66a-b1528f9b0796");
93 private static final UUID uuidFamily = UUID.fromString("af5f2481-3192-403f-ae65-7c957a0f02b6");
94 private static final UUID uuidSubfamily = UUID.fromString("862526ee-7592-4760-a23a-4ff3641541c5");
95 private static final UUID uuidInfrafamily = UUID.fromString("c3f2e3bb-6eef-4a26-9fb7-b14f4c8c5e4f");
96 private static final UUID uuidSupertribe = UUID.fromString("11e94828-8c61-499b-87d6-1de35ce2c51c");
97 private static final UUID uuidTribe = UUID.fromString("4aa6890b-0363-4899-8d7c-ee0cb78e6166");
98 private static final UUID uuidSubtribe = UUID.fromString("ae41ecc5-5165-4126-9d24-79939ae5d822");
99 private static final UUID uuidInfratribe = UUID.fromString("1ec02e8f-f2b7-4c65-af9f-b436b34c79a3");
100 private static final UUID uuidSupragenericTaxon = UUID.fromString("1fdc0b93-c354-441a-8406-091e0303ff5c");
101 public static final UUID uuidGenus = UUID.fromString("1b11c34c-48a8-4efa-98d5-84f7f66ef43a");
102 private static final UUID uuidSubgenus = UUID.fromString("78786e16-2a70-48af-a608-494023b91904");
103 private static final UUID uuidInfragenus = UUID.fromString("a9972969-82cd-4d54-b693-a096422f13fa");
104 private static final UUID uuidSectionBotany = UUID.fromString("3edff68f-8527-49b5-bf91-7e4398bb975c");
105 private static final UUID uuidSubsectionBotany = UUID.fromString("d20f5b61-d463-4448-8f8a-c1ff1f262f59");
106 private static final UUID uuidSeries = UUID.fromString("d7381ecf-48f8-429b-9c54-f461656978cd");
107 private static final UUID uuidSubseries = UUID.fromString("80c9a263-f4db-4a13-b6c2-b7fec1aa1200");
108 private static final UUID uuidSpeciesAggregate = UUID.fromString("1ecae058-4217-4f75-9c27-6d8ba099ac7a");
109 private static final UUID uuidSpeciesGroup = UUID.fromString("d1988a11-292b-46fa-8fb7-bc64ea6d8fc6");
110 public static final UUID uuidInfragenericTaxon = UUID.fromString("41bcc6ac-37d3-4fd4-bb80-3cc5b04298b9");
111 public static final UUID uuidSpecies = UUID.fromString("b301f787-f319-4ccc-a10f-b4ed3b99a86d");
112 private static final UUID uuidSubspecificAggregate = UUID.fromString("72c248b9-027d-4402-b375-dd4f0850c9ad");
113 private static final UUID uuidSubspecies = UUID.fromString("462a7819-8b00-4190-8313-88b5be81fad5");
114 private static final UUID uuidInfraspecies = UUID.fromString("f28ebc9e-bd50-4194-9af1-42f5cb971a2c");
115 private static final UUID uuidNatio = UUID.fromString("965f2f38-7f97-4270-ab5a-1999bf050a22");
116 private static final UUID uuidVariety = UUID.fromString("d5feb6a5-af5c-45ef-9878-bb4f36aaf490");
117 private static final UUID uuidBioVariety = UUID.fromString("a3a364cb-1a92-43fc-a717-3c44980a0991");
118 private static final UUID uuidPathoVariety = UUID.fromString("2f4f4303-a099-47e3-9048-d749d735423b");
119 private static final UUID uuidSubvariety = UUID.fromString("9a83862a-7aee-480c-a98d-4bceaf8712ca");
120 private static final UUID uuidSubsubvariety = UUID.fromString("bff22f84-553a-4429-a4e7-c4b3796c3a18");
121
122 public static final UUID uuidProles = UUID.fromString("8810d1ba-6a34-4ae3-a355-919ccd1cd1a5");
123 public static final UUID uuidRace = UUID.fromString("196dee39-cfd8-4460-8bf0-88b83da27f62");
124 public static final UUID uuidSublusus = UUID.fromString("1fafa596-a8e7-4e62-a378-3cc8cb3627ca");
125
126 private static final UUID uuidConvar = UUID.fromString("2cc740c9-cebb-43c8-9b06-1bef79e6a56a");
127 private static final UUID uuidForm = UUID.fromString("0461281e-458a-47b9-8d41-19a3d39356d5");
128 private static final UUID uuidSpecialForm = UUID.fromString("bed20aee-2f5a-4635-9c02-eff06246d067");
129 private static final UUID uuidSubform = UUID.fromString("47cfc5b0-0fb7-4ceb-b61d-e1dd8de8b569");
130 private static final UUID uuidSubsubform = UUID.fromString("1c8ac389-4349-4ae0-87be-7239f6635068");
131 public static final UUID uuidInfraspecificTaxon = UUID.fromString("eb75c27d-e154-4570-9d96-227b2df60474");
132 private static final UUID uuidCandidate = UUID.fromString("ead9a1f5-dfd4-4de2-9121-70a47accb10b");
133 private static final UUID uuidDenominationClass = UUID.fromString("49bdf74a-2170-40ed-8be2-887a0db517bf");
134 private static final UUID uuidGrex = UUID.fromString("08dcb4ff-ac58-48a3-93af-efb3d836ac84");
135 private static final UUID uuidGraftChimaera = UUID.fromString("6b4063bc-f934-4796-9bf3-0ef3aea5c1cb");
136 private static final UUID uuidCultivarGroup = UUID.fromString("d763e7d3-e7de-4bb1-9d75-225ca6948659");
137 private static final UUID uuidCultivar = UUID.fromString("5e98415b-dc6e-440b-95d6-ea33dbb39ad0");
138 private static final UUID uuidUnknownRank = UUID.fromString("5c4d6755-2cf6-44ca-9220-cccf8881700b");
139
140 //additional (not yet in csv files, but used in single databases)
141 //https://dev.e-taxonomy.eu/redmine/issues/7285
142 public static final UUID uuidCohort = UUID.fromString("3e4dc2fa-79e8-4ee7-b7d8-4c02a18fe555"); //edaphobase
143 public static final UUID uuidHyporder = UUID.fromString("2a1bdf1f-80fc-4846-b4d7-edcbf664f270"); //edaphobase
144 //E+M
145 public static UUID uuidRankCollSpecies = UUID.fromString("e14630ee-9446-4bb4-a7b7-4c3881bc5d94");
146
147 private static Map<String, UUID> idInVocMap = null;
148 private static Map<String, UUID> labelMap = null;
149
150 protected static Map<UUID, Rank> termMap = null;
151
152 //*********************** Factory methods ********************************************/
153
154 // /**
155 // * Creates a new empty rank.
156 // *
157 // * @see #NewInstance(String, String, String)
158 // */
159 // private static Rank NewInstance(){
160 // return new Rank();
161 // }
162
163 /**
164 * Creates an additional rank with a description (in the {@link Language#DEFAULT() default language}),
165 * a label and a label abbreviation.
166 *
167 * @param term the string (in the default language) describing the
168 * new rank to be created
169 * @param label the string identifying the new rank to be created
170 * @param labelAbbrev the string identifying (in abbreviated form) the
171 * new rank to be created
172 * @see #NewInstance()
173 */
174 public static Rank NewInstance(RankClass rankClass, String term, String label, String labelAbbrev){
175 return new Rank(rankClass, term, label, labelAbbrev);
176 }
177
178 /**
179 * The {@link RankClass rank class} of a rank. It is usually needed for correct formatting of a
180 * rank by using e.g. isSupraGeneric(). Prior to v3.3 this was computed by comparison of ranks.
181 */
182 @XmlAttribute(name ="RankClass")
183 @NotNull
184 @Type(type = "eu.etaxonomy.cdm.hibernate.EnumUserType",
185 parameters = {@org.hibernate.annotations.Parameter(name="enumClass", value="eu.etaxonomy.cdm.model.name.RankClass")}
186 )
187 @Audited
188 private RankClass rankClass;
189
190
191 //********************************** Constructor *********************************/
192
193 //for hibernate use only
194 @Deprecated
195 protected Rank() {
196 super(TermType.Rank);
197 }
198
199 /**
200 * Class constructor: creates an additional rank instance with a description
201 * (in the {@link eu.etaxonomy.cdm.model.common.Language#DEFAULT() default language}), a label and a label abbreviation.
202 *
203 * @param term the string (in the default language) describing the
204 * new rank to be created
205 * @param label the string identifying the new rank to be created
206 * @param labelAbbrev the string identifying (in abbreviated form) the
207 * new rank to be created
208 * @see #Rank()
209 */
210 protected Rank(RankClass rankClass, String term, String label, String labelAbbrev) {
211 super(TermType.Rank, term, label, labelAbbrev);
212 this.rankClass = rankClass;
213 }
214
215
216 //********* METHODS **************************************/
217
218 @Override
219 public void resetTerms(){
220 termMap = null;
221 }
222
223
224
225 protected static Rank getTermByUuid(UUID uuid){
226 if (termMap == null || termMap.isEmpty()){
227 return getTermByClassAndUUID(Rank.class, uuid);
228 } else {
229 return termMap.get(uuid);
230 }
231 }
232
233 public static final Rank EMPIRE(){
234 return getTermByUuid(uuidEmpire);
235 }
236 public static final Rank DOMAIN(){
237 return getTermByUuid(uuidDomain);
238 }
239 public static final Rank SUPERKINGDOM(){
240 return getTermByUuid(uuidSuperkingdom);
241 }
242 public static final Rank KINGDOM(){
243 return getTermByUuid(uuidKingdom);
244 }
245 public static final Rank SUBKINGDOM(){
246 return getTermByUuid(uuidSubkingdom);
247 }
248 public static final Rank INFRAKINGDOM(){
249 return getTermByUuid(uuidInfrakingdom);
250 }
251 public static final Rank SUPERPHYLUM(){
252 return getTermByUuid(uuidSuperphylum);
253 }
254 public static final Rank PHYLUM(){
255 return getTermByUuid(uuidPhylum);
256 }
257 public static final Rank SUBPHYLUM(){
258 return getTermByUuid(uuidSubphylum);
259 }
260 public static final Rank INFRAPHYLUM(){
261 return getTermByUuid(uuidInfraphylum);
262 }
263 public static final Rank SUPERDIVISION(){
264 return getTermByUuid(uuidSuperdivision);
265 }
266 public static final Rank DIVISION(){
267 return getTermByUuid(uuidDivision);
268 }
269 public static final Rank SUBDIVISION(){
270 return getTermByUuid(uuidSubdivision);
271 }
272 public static final Rank INFRADIVISION(){
273 return getTermByUuid(uuidInfradivision);
274 }
275 public static final Rank SUPERCLASS(){
276 return getTermByUuid(uuidSuperclass);
277 }
278 public static final Rank CLASS(){
279 return getTermByUuid(uuidClass);
280 }
281 public static final Rank SUBCLASS(){
282 return getTermByUuid(uuidSubclass);
283 }
284 public static final Rank INFRACLASS(){
285 return getTermByUuid(uuidInfraclass);
286 }
287 public static final Rank SUPERORDER(){
288 return getTermByUuid(uuidSuperorder);
289 }
290 public static final Rank ORDER(){
291 return getTermByUuid(uuidOrder);
292 }
293 public static final Rank SUBORDER(){
294 return getTermByUuid(uuidSuborder);
295 }
296 public static final Rank INFRAORDER(){
297 return getTermByUuid(uuidInfraorder);
298 }
299 public static final Rank SUPERFAMILY(){
300 return getTermByUuid(uuidSuperfamily);
301 }
302 public static final Rank FAMILY(){
303 return getTermByUuid(uuidFamily);
304 }
305 public static final Rank SUBFAMILY(){
306 return getTermByUuid(uuidSubfamily);
307 }
308 public static final Rank INFRAFAMILY(){
309 return getTermByUuid(uuidInfrafamily);
310 }
311 public static final Rank SUPERTRIBE(){
312 return getTermByUuid(uuidSupertribe);
313 }
314 public static final Rank TRIBE(){
315 return getTermByUuid(uuidTribe);
316 }
317 public static final Rank SUBTRIBE(){
318 return getTermByUuid(uuidSubtribe);
319 }
320 public static final Rank INFRATRIBE(){
321 return getTermByUuid(uuidInfratribe);
322 }
323 public static final Rank SUPRAGENERICTAXON(){
324 return getTermByUuid(uuidSupragenericTaxon);
325 }
326 public static final Rank GENUS(){
327 return getTermByUuid(uuidGenus);
328 }
329 public static final Rank SUBGENUS(){
330 return getTermByUuid(uuidSubgenus);
331 }
332 public static final Rank INFRAGENUS(){
333 return getTermByUuid(uuidInfragenus);
334 }
335 public static final Rank SECTION_BOTANY(){
336 return getTermByUuid(uuidSectionBotany);
337 }
338 public static final Rank SUBSECTION_BOTANY(){
339 return getTermByUuid(uuidSubsectionBotany);
340 }
341 public static final Rank SECTION_ZOOLOGY(){
342 return getTermByUuid(uuidSectionZoology);
343 }
344 public static final Rank SUBSECTION_ZOOLOGY(){
345 return getTermByUuid(uuidSubsectionZoology);
346 }
347 public static final Rank SERIES(){
348 return getTermByUuid(uuidSeries);
349 }
350 public static final Rank SUBSERIES(){
351 return getTermByUuid(uuidSubseries);
352 }
353 public static final Rank SPECIESAGGREGATE(){
354 return getTermByUuid(uuidSpeciesAggregate);
355 }
356 public static final Rank SPECIESGROUP(){
357 return getTermByUuid(uuidSpeciesGroup);
358 }
359 /**
360 * 'Unranked infrageneric'. An infrageneric rank which is on purpose not further defined.
361 * This sometimes holds for names from the 19th century.
362 */
363 public static final Rank INFRAGENERICTAXON(){
364 return getTermByUuid(uuidInfragenericTaxon);
365 }
366 public static final Rank SPECIES(){
367 return getTermByUuid(uuidSpecies);
368 }
369 public static final Rank SUBSPECIFICAGGREGATE(){
370 return getTermByUuid(uuidSubspecificAggregate);
371 }
372 public static final Rank SUBSPECIES(){
373 return getTermByUuid(uuidSubspecies);
374 }
375 public static final Rank INFRASPECIES(){
376 return getTermByUuid(uuidInfraspecies);
377 }
378 public static final Rank VARIETY(){
379 return getTermByUuid(uuidVariety);
380 }
381 public static final Rank BIOVARIETY(){
382 return getTermByUuid(uuidBioVariety);
383 }
384 public static final Rank PATHOVARIETY(){
385 return getTermByUuid(uuidPathoVariety);
386 }
387 public static final Rank SUBVARIETY(){
388 return getTermByUuid(uuidSubvariety);
389 }
390 public static final Rank SUBSUBVARIETY(){
391 return getTermByUuid(uuidSubsubvariety );
392 }
393 public static final Rank PROLES(){
394 return getTermByUuid(uuidProles);
395 }
396 public static final Rank RACE(){
397 return getTermByUuid(uuidRace);
398 }
399 public static final Rank SUBLUSUS(){
400 return getTermByUuid(uuidSublusus);
401 }
402
403 public static final Rank CONVAR(){
404 return getTermByUuid(uuidConvar);
405 }
406 public static final Rank FORM(){
407 return getTermByUuid(uuidForm);
408 }
409 public static final Rank SPECIALFORM(){
410 return getTermByUuid(uuidSpecialForm);
411 }
412 public static final Rank SUBFORM(){
413 return getTermByUuid(uuidSubform);
414 }
415 public static final Rank SUBSUBFORM(){
416 return getTermByUuid(uuidSubsubform);
417 }
418 /**
419 * 'Unranked infraspecific'. An infraspecific rank which is on purpose not further defined.
420 * This sometimes holds for names from the 19th century.
421 */
422 public static final Rank INFRASPECIFICTAXON(){
423 return getTermByUuid(uuidInfraspecificTaxon);
424 }
425 public static final Rank CANDIDATE(){
426 return getTermByUuid(uuidCandidate);
427 }
428 public static final Rank DENOMINATIONCLASS(){
429 return getTermByUuid(uuidDenominationClass);
430 }
431 public static final Rank GREX(){
432 return getTermByUuid(uuidGrex);
433 }
434 public static final Rank GRAFTCHIMAERA(){
435 return getTermByUuid(uuidGraftChimaera);
436 }
437 public static final Rank CULTIVARGROUP(){
438 return getTermByUuid(uuidCultivarGroup);
439 }
440 public static final Rank CULTIVAR(){
441 return getTermByUuid(uuidCultivar);
442 }
443 public static final Rank UNKNOWN_RANK(){
444 return getTermByUuid(uuidUnknownRank);
445 }
446 public static final Rank NATIO(){
447 return getTermByUuid(uuidNatio);
448 }
449 /**
450 * @see #INFRASPECIFICTAXON()
451 */
452 public static final Rank UNRANKED_INFRASPECIFIC(){
453 return getTermByUuid(uuidInfraspecificTaxon);
454 }
455 /**
456 * @see #INFRAGENERICTAXON()
457 */
458 public static final Rank UNRANKED_INFRAGENERIC(){
459 return getTermByUuid(uuidInfragenericTaxon);
460 }
461
462 // ************************ GETTER / SETTER **********************************/
463
464 public RankClass getRankClass() {
465 return rankClass;
466 }
467
468 public void setRankClass(RankClass rankClass) {
469 this.rankClass = rankClass;
470 }
471
472 // ******************************** METHODS ***************************************/
473
474 /**
475 * Returns the boolean value indicating whether <i>this</i> rank is higher than
476 * the genus rank (true) or not (false). Returns false if <i>this</i> rank is null.
477 *
478 * @see #isGenus()
479 * @see #isInfraGeneric()
480 * @see #isSpecies()
481 * @see #isInfraSpecific()
482 */
483 @Transient
484 public boolean isSupraGeneric(){
485 return this.rankClass.equals(RankClass.Suprageneric); // (this.isHigher(Rank.GENUS()));
486 }
487
488 /**
489 * Returns the boolean value indicating whether <i>this</i> rank is the genus rank
490 * (true) or not (false). Returns false if <i>this</i> rank is null.
491 *
492 * @see #isSupraGeneric()
493 * @see #isInfraGeneric()
494 * @see #isSpecies()
495 * @see #isInfraSpecific()
496 */
497 @Transient
498 public boolean isGenus(){
499 return this.rankClass.equals(RankClass.Genus); // (this.equals(Rank.GENUS()));
500 }
501
502 /**
503 * Returns the boolean value indicating whether <i>this</i> rank is higher than the
504 * species rank and lower than the genus rank (true) or not (false). Species groups or
505 * aggregates are also handled as infrageneric ranks.
506 * Returns false if <i>this</i> rank is null.
507 *
508 * @see #isSupraGeneric()
509 * @see #isGenus()
510 * @see #isInfraGenericButNotSpeciesGroup()
511 * @see #isSpeciesAggregate()
512 * @see #isSpecies()
513 * @see #isInfraSpecific()
514 */
515 @Transient
516 public boolean isInfraGeneric(){
517 return this.rankClass.equals(RankClass.Infrageneric) || this.rankClass.equals(RankClass.SpeciesGroup) ; //(this.isLower(Rank.GENUS()) && this.isHigher(Rank.SPECIES()));
518 }
519
520 /**
521 * Returns the boolean value indicating whether <i>this</i> rank is higher than the
522 * species aggregate/group rank and lower than the genus rank (true) or not (false).
523 * Returns false if <i>this</i> rank is null.
524 *
525 * @see #isSupraGeneric()
526 * @see #isGenus()
527 * @see #isInfraGeneric()
528 * @see #isSpeciesAggregate()
529 * @see #isSpecies()
530 * @see #isInfraSpecific()
531 */
532 @Transient
533 public boolean isInfraGenericButNotSpeciesGroup(){
534 return this.rankClass.equals(RankClass.Infrageneric) ;
535 }
536
537 /**
538 * Returns true if this rank indicates a rank that aggregates species
539 * like species aggregates or species groups, false otherwise.
540 * @return
541 */
542 @Transient
543 public boolean isSpeciesAggregate(){
544 return this.rankClass.equals(RankClass.SpeciesGroup); //(this.equals(Rank.SPECIESAGGREGATE()) || (this.isLower(Rank.SPECIESAGGREGATE()) && this.isHigher(Rank.SPECIES())));
545 }
546
547 /**
548 * Returns the boolean value indicating whether <i>this</i> rank is the species
549 * rank (true) or not (false). Returns false if <i>this</i> rank is null.
550 *
551 * @see #isSupraGeneric()
552 * @see #isGenus()
553 * @see #isInfraGeneric()
554 * @see #isInfraSpecific()
555 */
556 @Transient
557 public boolean isSpecies(){
558 return this.rankClass.equals(RankClass.Species); //(this.equals(Rank.SPECIES()));
559 }
560
561 /**
562 * Returns the boolean value indicating whether <i>this</i> rank is lower than the
563 * species rank (true) or not (false). Returns false if <i>this</i> rank is null.
564 *
565 * @see #isSupraGeneric()
566 * @see #isGenus()
567 * @see #isInfraGeneric()
568 * @see #isSpecies()
569 */
570 @Transient
571 public boolean isInfraSpecific(){
572 return this.rankClass.equals(RankClass.Infraspecific); // (this.isLower(Rank.SPECIES()));
573 }
574
575
576 /**
577 * Returns the rank identified through a label or the identifier within the vocabulary
578 * Preliminary implementation for BotanicalNameParser.
579 *
580 * @param strRank the string identifying the rank
581 * @return the rank
582 */
583 public static Rank getRankByNameOrIdInVoc(String strRank) throws UnknownCdmTypeException{
584 return getRankByNameOrIdInVoc(strRank, false);
585 }
586
587 /**
588 * Returns the rank identified through a label or the identifier within the vocabulary
589 * for a given nomenclatural code.
590 * Preliminary implementation for BotanicalNameParser.
591 *
592 * @param strRank the string identifying the rank
593 * @param nc the nomenclatural code
594 * @return the rank
595 */
596 public static Rank getRankByNameOrIdInVoc(String strRank, NomenclaturalCode nc) throws UnknownCdmTypeException{
597 return getRankByNameOrIdInVoc(strRank, nc, false);
598 }
599
600 // TODO
601 // Preliminary implementation for BotanicalNameParser.
602 // not yet complete
603 /**
604 * Returns the rank identified through a label or the identifier within the vocabulary.
605 * Preliminary implementation for BotanicalNameParser.
606 *
607 * @param strRank the string identifying the rank
608 * @param useUnknown if true the rank UNKNOWN_RANK is returned if the abbrev is
609 * unknown or not yet implemented
610 * @return the rank
611 */
612 public static Rank getRankByNameOrIdInVoc(String strRank, boolean useUnknown) throws UnknownCdmTypeException{
613 try {
614 return getRankByIdInVoc(strRank);
615 } catch (UnknownCdmTypeException e) {
616 return getRankByName(strRank, useUnknown);
617 }
618 }
619
620 // TODO
621 // Preliminary implementation for BotanicalNameParser.
622 // not yet complete
623 /**
624 * Returns the rank identified through a label or the identifier within the vocabulary.
625 * Preliminary implementation for BotanicalNameParser.
626 *
627 * @param strRank the string identifying the rank
628 * @param nc the nomenclatural code
629 * @param useUnknown if true the rank UNKNOWN_RANK is returned if the abbrev is
630 * unknown or not yet implemented
631 * @return the rank
632 */
633 public static Rank getRankByNameOrIdInVoc(String strRank, NomenclaturalCode nc, boolean useUnknown)
634 throws UnknownCdmTypeException{
635 try {
636 return getRankByIdInVoc(strRank, nc);
637 } catch (UnknownCdmTypeException e) {
638 return getRankByName(strRank, nc, useUnknown);
639 }
640 }
641
642 /**
643 * Returns the rank identified through the vocabulary identifier.
644 * Preliminary implementation for BotanicalNameParser.<BR>
645 * Note: For abbrev = "[unranked]" the result is undefined.
646 * It maybe the infrageneric unranked or the infraspecific unranked.
647 * You need to define by context which one is correct.
648 *
649 * @param abbrev the string for the name abbreviation
650 * @return the rank
651 */
652 public static Rank getRankByIdInVoc(String abbrev) throws UnknownCdmTypeException{
653 return getRankByIdInVoc(abbrev, false);
654 }
655
656 /**
657 * Returns the rank identified through an abbreviated name for a given nomenclatural code.
658 * See also {@link #getRankByIdInVoc(String, boolean)}
659 *
660 * @param abbrev the string for the name abbreviation
661 * @param nc the nomenclatural code
662 * @return the rank
663 */
664 public static Rank getRankByIdInVoc(String abbrev, NomenclaturalCode nc) throws UnknownCdmTypeException{
665 return getRankByIdInVoc(abbrev, nc, false);
666 }
667
668 // TODO
669 // Preliminary implementation for BotanicalNameParser.
670 // not yet complete
671 /**
672 * Returns the rank identified through an abbreviated representation.
673 * At the moment it uses the English abbreviations (being Latin because
674 * we do not have Latin representations yet.
675 * TODO
676 * If no according abbreviation is available it throws either an UnknownCdmTypeException
677 * or an #Rank.UNKNOWN() object depending on the useUnknown flag.
678 *
679 * @param idInVoc the string for the name abbreviation
680 * @param useUnknown if true the rank UNKNOWN_RANK is returned if the abbrev is
681 * unknown or not yet existent
682 * @return the rank
683 */
684 public static Rank getRankByIdInVoc(String idInVoc, boolean useUnknown) throws UnknownCdmTypeException{
685 Rank result = null;
686 if (idInVoc == null){
687 throw new NullPointerException("idInVoc is NULL in getRankByIdInVoc");
688 }
689 if (StringUtils.isBlank(idInVoc)){
690 //handle empty idInVoc as unknown
691 idInVoc = "oijas34\u0155";
692 }
693 if (idInVocMap == null){
694 return null;
695 }
696 idInVoc = normalizeSectionAndSubsection(idInVoc);
697 idInVoc = normalizeSubgen(idInVoc);
698 idInVoc = normalizeSpecialForm(idInVoc);
699 idInVoc = normalizeSsp(idInVoc);
700 idInVoc = normalizeForma(idInVoc);
701 UUID uuid = idInVocMap.get(idInVoc);
702 if (uuid != null ){
703 result = getTermByUuid(uuid);
704 }
705 if (result != null){
706 return result;
707 }else {
708 if (idInVoc == null){
709 idInVoc = "(null)";
710 }
711 if (useUnknown){
712 logger.info("Unknown rank name: " + idInVoc + ". Rank 'UNKNOWN_RANK' created instead");
713 return Rank.UNKNOWN_RANK();
714 }else{
715 throw new UnknownCdmTypeException("Unknown rank abbreviation: " + idInVoc);
716 }
717 }
718 }
719
720 private static String normalizeSectionAndSubsection(String idInVoc) {
721 if (idInVoc.equals("sect.")){
722 return "sect.(bot.)";
723 }else if (idInVoc.equals("subsect.")){
724 return "subsect.(bot.)";
725 }
726 return idInVoc;
727 }
728
729 private static String normalizeSubgen(String idInVoc) {
730 if (idInVoc.equals("subgen.")){
731 return "subg.";
732 }
733 return idInVoc;
734 }
735
736 private static String normalizeSpecialForm(String idInVoc) {
737 if (idInVoc.equals("f.sp.") || idInVoc.equals("f. sp.")){
738 return "f.spec.";
739 }
740 return idInVoc;
741 }
742
743 private static String normalizeForma(String idInVoc) {
744 if (idInVoc.equals("forma")){
745 return "f.";
746 }
747 return idInVoc;
748 }
749
750 private static String normalizeSsp(String idInVoc) {
751 if (idInVoc.equals("ssp.") && !idInVocMap.containsKey("ssp.") && idInVocMap.containsKey("subsp.")){
752 return "subsp.";
753 }
754 return idInVoc;
755 }
756
757 // TODO
758 // Preliminary implementation to cover Botany and Zoology.
759 /**
760 * Returns the rank identified through an abbreviated name for a given nomenclatural code.
761 * Preliminary implementation for ICBN and ICZN.
762 * See also {@link #getRankByIdInVoc(String, boolean)}
763
764 *
765 * @param abbrev the string for the name abbreviation
766 * @param nc the nomenclatural code
767 * @param useUnknown if true the rank UNKNOWN_RANK is returned if the abbrev is
768 * unknown or not yet implemented
769 * @return the rank
770 */
771 public static Rank getRankByIdInVoc(String abbrev, NomenclaturalCode nc, boolean useUnknown)
772 throws UnknownCdmTypeException{
773
774 if (nc != null && nc.equals(NomenclaturalCode.ICZN)) {
775 if (abbrev != null){
776 if (abbrev.equalsIgnoreCase("sect.")) {
777 return Rank.SECTION_ZOOLOGY();
778 } else if (abbrev.equalsIgnoreCase("subsect.")) {
779 return Rank.SUBSECTION_ZOOLOGY();
780 }
781 }
782 }else if (nc != null && nc.equals(NomenclaturalCode.ICNAFP)) {
783 if (abbrev != null){
784 if (abbrev.equalsIgnoreCase("sect.")) {
785 return Rank.SECTION_BOTANY();
786 } else if (abbrev.equalsIgnoreCase("subsect.")) {
787 return Rank.SUBSECTION_BOTANY();
788 }
789 }
790 }
791 return getRankByIdInVoc(abbrev, useUnknown);
792 }
793
794 // TODO
795 // Preliminary implementation for BotanicalNameParser.
796 // not yet complete
797 /**
798 * Returns the rank identified through a name.
799 * Preliminary implementation for BotanicalNameParser.
800 *
801 * @param rankName the string for the name of the rank
802 * @return the rank
803 */
804 public static Rank getRankByName(String rankName) throws UnknownCdmTypeException{
805 return getRankByName(rankName, false);
806 }
807
808
809 // TODO
810 // Preliminary implementation for ICBN and ICZN.
811 // not yet complete
812 /**
813 * Returns the rank identified through a name for a given nomenclatural code.
814 * Preliminary implementation for ICBN and ICZN.
815 *
816 * @param rankName the string for the name of the rank
817 * @param nc the nomenclatural code
818 * @return the rank
819 */
820 public static Rank getRankByName(String rankName, NomenclaturalCode nc) throws UnknownCdmTypeException{
821 return getRankByName(rankName, nc, false);
822 }
823
824 /**
825 * Returns the rank identified through a name.
826 * Preliminary implementation for BotanicalNameParser.
827 * TODO At the moment we do not have Latin representations yet.
828 *
829 * @param rankName the string for the name of the rank
830 * @param useUnknown if true the rank UNKNOWN_RANK is returned if the rank name is
831 * unknown or not yet implemented
832 * @return the rank
833 */
834 public static Rank getRankByName(String rankName, boolean useUnknown)
835 throws UnknownCdmTypeException{
836 if (rankName.equalsIgnoreCase("Regnum")){ return Rank.KINGDOM();
837 }else if (rankName.equalsIgnoreCase("Subregnum")){ return Rank.SUBKINGDOM();
838 }else if (rankName.equalsIgnoreCase("Phylum")){ return Rank.PHYLUM();
839 }else if (rankName.equalsIgnoreCase("Subphylum")){ return Rank.SUBPHYLUM();
840 }else if (rankName.equalsIgnoreCase("Divisio")){ return Rank.DIVISION();
841 }else if (rankName.equalsIgnoreCase("Subdivisio")){ return Rank.SUBDIVISION();
842 }else if (rankName.equalsIgnoreCase("Classis")){ return Rank.CLASS();
843 }else if (rankName.equalsIgnoreCase("Subclassis")){ return Rank.SUBCLASS();
844 }else if (rankName.equalsIgnoreCase("Superordo")){ return Rank.SUPERORDER();
845 }else if (rankName.equalsIgnoreCase("Ordo")){ return Rank.ORDER();
846 }else if (rankName.equalsIgnoreCase("Subordo")){ return Rank.SUBORDER();
847 }else if (rankName.equalsIgnoreCase("Familia")){ return Rank.FAMILY();
848 }else if (rankName.equalsIgnoreCase("Subfamilia")){ return Rank.SUBFAMILY();
849 }else if (rankName.equalsIgnoreCase("Tribus")){ return Rank.TRIBE();
850 }else if (rankName.equalsIgnoreCase("Subtribus")){ return Rank.SUBTRIBE();
851 }else if (rankName.equalsIgnoreCase("Genus")){ return Rank.GENUS();
852 }else if (rankName.equalsIgnoreCase("Subgenus")){ return Rank.SUBGENUS();
853 }else if (rankName.equalsIgnoreCase("Sectio")){ return Rank.SECTION_BOTANY();
854 }else if (rankName.equalsIgnoreCase("Subsectio")){ return Rank.SUBSECTION_BOTANY();
855 }else if (rankName.equalsIgnoreCase("Series")){ return Rank.SERIES();
856 }else if (rankName.equalsIgnoreCase("Subseries")){ return Rank.SUBSERIES();
857 }else if (rankName.equalsIgnoreCase("Aggregate")){ return Rank.SPECIESAGGREGATE();
858 }else if (rankName.equalsIgnoreCase("Speciesgroup")){ return Rank.SPECIESGROUP();
859 }else if (rankName.equalsIgnoreCase("Species")){ return Rank.SPECIES();
860 }else if (rankName.equalsIgnoreCase("Subspecies")){ return Rank.SUBSPECIES();
861 }else if (rankName.equalsIgnoreCase("Convarietas")){ return Rank.CONVAR();
862 }else if (rankName.equalsIgnoreCase("Varietas")){ return Rank.VARIETY();
863 }else if (rankName.equalsIgnoreCase("Subvarietas")){ return Rank.SUBVARIETY();
864 }else if (rankName.equalsIgnoreCase("Forma")){ return Rank.FORM();
865 }else if (rankName.equalsIgnoreCase("Subforma")){ return Rank.SUBFORM();
866 }else if (rankName.equalsIgnoreCase("Forma spec.")){ return Rank.SPECIALFORM();
867 }else if (rankName.equalsIgnoreCase("tax.infragen.")){ return Rank.INFRAGENERICTAXON();
868 }else if (rankName.equalsIgnoreCase("tax.infrasp.")){ return Rank.INFRASPECIFICTAXON();
869 // old ranks
870 }else if (rankName.equalsIgnoreCase("proles")){ return Rank.PROLES();
871 }else if (rankName.equalsIgnoreCase("race")){ return Rank.RACE();
872 }else if (rankName.equalsIgnoreCase("sublusus")){ return Rank.SUBLUSUS();
873
874 }else if (rankName.equalsIgnoreCase("taxon")){ return Rank.INFRASPECIFICTAXON(); //to create the name put 'taxon' and the infraspeciesepi to the field unnamed namephrase
875
876 }else{
877 if (rankName == null){
878 rankName = "(null)"; //see NPE above
879 }
880 if (useUnknown){
881 if (logger.isInfoEnabled()){logger.info("Unknown rank name: " + rankName+". Rank 'UNKNOWN_RANK' created instead");}
882 return Rank.UNKNOWN_RANK();
883 }else{
884 throw new UnknownCdmTypeException("Unknown rank name: " + rankName);
885 }
886 }
887 }
888
889 /**
890 * Defines the rank according to the English name.
891 * @param rankName English rank name.
892 * @param nc Defines the handling of the section and subsection ranks. These are in different orders depending on the
893 * nomenclatural code.
894 * @param useUnknown if true, the "Unknown" rank is returned as a placeholder.
895 * @return
896 * @throws UnknownCdmTypeException never thrown if useUnknown is true
897 */
898 public static Rank getRankByEnglishName(String rankName, NomenclaturalCode nc, boolean useUnknown) throws UnknownCdmTypeException{
899 Rank result = null;
900 if (rankName == null){
901 throw new NullPointerException("Abbrev is NULL in getRankByAbbreviation");
902 }
903 if (labelMap == null){
904 return null;
905 }
906 //handle section and subsection (not unique representations)
907 if (rankName.equalsIgnoreCase("Section")){
908 if (nc != null && nc.equals(NomenclaturalCode.ICZN)){ return Rank.SECTION_ZOOLOGY();
909 }else if (nc != null && nc.equals(NomenclaturalCode.ICNAFP)){return Rank.SECTION_BOTANY();
910 }else{
911 String errorWarning = "Section is only defined for ICZN and ICNAFP at the moment but here needed for " + ((nc == null)? "(null)": nc.toString());
912 logger.warn(errorWarning);
913 throw new UnknownCdmTypeException (errorWarning);
914 }
915 }else if (rankName.equalsIgnoreCase("Subsection")){
916 if (nc != null && nc.equals(NomenclaturalCode.ICZN)){ return Rank.SUBSECTION_ZOOLOGY();
917 }else if (nc != null && nc.equals(NomenclaturalCode.ICNAFP)){ return Rank.SUBSECTION_BOTANY();
918 }else{
919 String errorWarning = "Subsection is only defined for ICZN and ICBN at the moment but here needed for " + ((nc == null)? "(null)": nc.toString());
920 logger.warn(errorWarning);
921 throw new UnknownCdmTypeException (errorWarning);
922 }
923 }
924
925 rankName = rankName.toLowerCase();
926
927 UUID uuid = labelMap.get(rankName);
928 if (uuid != null ){
929 result = getTermByUuid(uuid);
930 }
931 if (result != null){
932 return result;
933 }else {
934 if (rankName == null){
935 rankName = "(null)";
936 }
937 if (useUnknown){
938 if (logger.isInfoEnabled()){logger.info("Unknown rank name: " + rankName + ". Rank 'UNKNOWN_RANK' created instead");}
939 return Rank.UNKNOWN_RANK();
940 }else{
941 throw new UnknownCdmTypeException("Unknown rank: " + rankName);
942 }
943 }
944 }
945
946
947 public static Rank getRankByName(String rankName, NomenclaturalCode nc, boolean useUnknown)
948 throws UnknownCdmTypeException {
949
950 if (nc.equals(NomenclaturalCode.ICZN)) {
951 if (rankName.equalsIgnoreCase("Sectio")) { return Rank.SECTION_ZOOLOGY();
952 }else if (rankName.equalsIgnoreCase("Subsectio")) { return Rank.SUBSECTION_ZOOLOGY();
953 }
954 }
955 return getRankByName(rankName, useUnknown);
956 }
957
958 /**
959 * Returns the abbreviated rank name for <i>this</i> rank according to the English representation
960 * abbreviated label.
961 * TODO Needs to be changed to Latin as soon as Latin representations are available.
962 *
963 * @return the abbreviation string for <i>this</i> rank
964 */
965 public String getAbbreviation(){
966 Language language = Language.getLanguageFromUuid(Language.uuidEnglish);
967 String result = this.getRepresentation(language).getAbbreviatedLabel();
968 if (result== null) {
969 logger.warn("Abbreviation for rank " + this.toString() + " not yet implemented");
970 return "no abbreviation available.";
971 }else{
972 return result;
973 }
974 }
975
976 @Transient
977 public String getInfraGenericMarker() throws UnknownCdmTypeException{
978 String result = null;
979 if (! this.isInfraGeneric()){
980 throw new IllegalStateException("An infrageneric marker is only available for an infrageneric rank but was asked for rank: " + this.toString());
981 }else{
982 result = this.getAbbreviation();
983 }
984 if (result == null){
985 throw new UnknownCdmTypeException("Abbreviation for rank unknown: " + this.toString());
986 }
987 return result;
988 }
989
990
991
992 @Override
993 public Rank readCsvLine(Class<Rank> termClass, List<String> csvLine, TermType termType, Map<UUID, DefinedTermBase> terms, boolean abbrevAsId) {
994 Rank rank = super.readCsvLine(termClass, csvLine, termType, terms, abbrevAsId);
995 RankClass rankClass = RankClass.getByKey(csvLine.get(5));
996 assert rankClass != null: "XXXXXXXXXXXXXXXXXXXXX Rank class must not be null: " + csvLine ;
997 rank.setRankClass(rankClass);
998 return rank;
999 }
1000
1001 @Override
1002 protected void setDefaultTerms(TermVocabulary<Rank> termVocabulary) {
1003 termMap = new HashMap<UUID, Rank>();
1004 for (Rank term : termVocabulary.getTerms()){
1005 termMap.put(term.getUuid(), term);
1006 addRank(term);
1007 }
1008 }
1009
1010 // FIXME:Remoting This is a ugly hack and need to be removed
1011 // once the static initialisation is refactored
1012 public static void initDefaultTerms() {
1013 TermVocabulary<Rank> vocabulary = getTermByUuid(uuidKingdom).getVocabulary();
1014 if(idInVocMap != null) {
1015 idInVocMap.clear();
1016 }
1017 (new Rank()).setDefaultTerms(vocabulary);
1018 }
1019
1020 /**
1021 * @param term
1022 */
1023 private void addRank(Rank rank) {
1024 if (rank == null){
1025 logger.warn("rank is NULL");
1026 return;
1027 }
1028 if (rank.getUuid().equals(uuidSectionZoology) || rank.getUuid().equals(uuidSubsectionZoology )){
1029 //sect./subsect. is used for botanical sections, see also #getRankByAbbreviation(String, NomenclaturalCode, boolean)
1030 return;
1031 }
1032 Language lang = Language.DEFAULT(); //TODO should be Latin but at the moment we have only English representations
1033 Representation representation = rank.getRepresentation(lang);
1034 String abbrevLabel = representation.getAbbreviatedLabel();
1035 String label = representation.getLabel();
1036
1037 //initialize maps
1038 if (idInVocMap == null){
1039 idInVocMap = new HashMap<String, UUID>();
1040 }
1041 if (labelMap == null){
1042 labelMap = new HashMap<String, UUID>();
1043 }
1044 labelMap.put(label.toLowerCase(), rank.getUuid());
1045 //add to map
1046 if (StringUtils.isBlank(abbrevLabel)){
1047 if (logger.isDebugEnabled()){logger.info("Abbreviated label for rank is NULL or empty.Can't add rank to abbrevLabel map: " + CdmUtils.Nz(rank.getLabel()));}
1048 }else{
1049 idInVocMap.put(abbrevLabel, rank.getUuid());
1050 }
1051 }
1052
1053
1054 /**
1055 * It is necessary to skip the vocabulary check, otherwise we would have
1056 * problems in some CacheStrategies, due to uninitialized Vocabularies.
1057 *
1058 * @see eu.etaxonomy.cdm.model.term.OrderedTermBase#compareTo(eu.etaxonomy.cdm.model.term.OrderedTermBase)
1059 */
1060 @Override
1061 public int compareTo(Rank orderedTerm) {
1062 return performCompareTo(orderedTerm, true);
1063 }
1064
1065 }