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