001/* 002 * Licensed under the Apache License, Version 2.0 (the "License"); 003 * you may not use this file except in compliance with the License. 004 * You may obtain a copy of the License at 005 * 006 * http://www.apache.org/licenses/LICENSE-2.0 007 * 008 * Unless required by applicable law or agreed to in writing, software 009 * distributed under the License is distributed on an "AS IS" BASIS, 010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 011 * See the License for the specific language governing permissions and 012 * limitations under the License. 013 */ 014package org.gbif.api.vocabulary; 015 016import java.util.Arrays; 017import java.util.Collections; 018import java.util.HashMap; 019import java.util.HashSet; 020import java.util.List; 021import java.util.Map; 022import java.util.Set; 023 024import io.swagger.v3.oas.annotations.ExternalDocumentation; 025import io.swagger.v3.oas.annotations.media.Schema; 026 027/** 028 * An ordered taxonomic rank enumeration with the most frequently used values. 029 * Several static methods, lists, sets and maps are provided to help with ordering and lookup from strings. 030 * 031 * @see <a href="http://rs.gbif.org/vocabulary/gbif/rank.xml">rs.gbif.org vocabulary</a> 032 */ 033@Schema( 034 externalDocs = @ExternalDocumentation( 035 description = "Darwin Core definition", 036 url = "https://rs.tdwg.org/dwc/terms/taxonRank" 037 ) 038) 039@SuppressWarnings("unused") 040public enum Rank { 041 042 DOMAIN("dom."), 043 044 SUPERKINGDOM("superreg."), 045 046 KINGDOM("reg."), 047 048 SUBKINGDOM("subreg."), 049 050 INFRAKINGDOM("infrareg."), 051 052 SUPERPHYLUM("superphyl."), 053 054 PHYLUM("phyl."), 055 056 SUBPHYLUM("subphyl."), 057 058 INFRAPHYLUM("infraphyl."), 059 060 SUPERCLASS("supercl."), 061 062 CLASS("cl."), 063 064 SUBCLASS("subcl."), 065 066 INFRACLASS("infracl."), 067 068 PARVCLASS("parvcl."), 069 070 SUPERLEGION("superleg."), 071 072 /** 073 * Sometimes used in zoology, e.g. for birds and mammals 074 */ 075 LEGION("leg."), 076 077 SUBLEGION("subleg."), 078 079 INFRALEGION("infraleg."), 080 081 SUPERCOHORT("supercohort"), 082 083 /** 084 * Sometimes used in zoology, e.g. for birds and mammals 085 */ 086 COHORT("cohort"), 087 088 SUBCOHORT("subcohort"), 089 090 INFRACOHORT("infracohort"), 091 092 MAGNORDER("magnord."), 093 094 SUPERORDER("superord."), 095 096 GRANDORDER("grandord."), 097 098 ORDER("ord."), 099 100 SUBORDER("subord."), 101 102 INFRAORDER("infraord."), 103 104 PARVORDER("parvord."), 105 106 SUPERFAMILY("superfam."), 107 108 FAMILY("fam."), 109 110 SUBFAMILY("subfam."), 111 112 INFRAFAMILY("infrafam."), 113 114 SUPERTRIBE("supertrib."), 115 116 TRIBE("trib."), 117 118 SUBTRIBE("subtrib."), 119 120 INFRATRIBE("infratrib."), 121 122 /** 123 * Used for any other unspecific rank above genera. 124 */ 125 SUPRAGENERIC_NAME("supragen."), 126 127 GENUS("gen."), 128 129 SUBGENUS("subgen."), 130 131 INFRAGENUS("infragen."), 132 133 SECTION("sect."), 134 135 SUBSECTION("subsect."), 136 137 SERIES("ser."), 138 139 SUBSERIES("subser."), 140 141 /** 142 * used for any other unspecific rank below genera and above species. 143 */ 144 INFRAGENERIC_NAME("infrageneric"), 145 146 /** 147 * A loosely defined group of species. 148 * Zoology: Aggregate - a group of species, other than a subgenus, within a genus. An aggregate may be denoted by a group name interpolated in parentheses. 149 * The Berlin/MoreTax model notes: [these] aren't taxonomic ranks but cirumscriptions because on the one hand they are necessary for the concatenation 150 * of the fullname and on the other hand they are necessary for distinguishing the aggregate or species group from the microspecies. 151 */ 152 SPECIES_AGGREGATE("agg."), 153 154 SPECIES("sp."), 155 156 /** 157 * used for any other unspecific rank below species. 158 */ 159 INFRASPECIFIC_NAME("infrasp."), 160 161 /** 162 * The term grex has been coined to expand botanical nomenclature to describe hybrids of orchids. 163 * Grex names are one of the three categories of plant names governed by the International Code of Nomenclature for Cultivated Plants 164 * Within a grex the Groups category can be used to refer to plants by their shared characteristics (rather than by their parentage), 165 * and individual orchid plants can be selected (and propagated) and named as cultivars 166 * https://en.wikipedia.org/wiki/Grex_(horticulture) 167 */ 168 GREX("grex"), 169 170 SUBSPECIES("subsp."), 171 172 /** 173 * Rank in use from the code for cultivated plants. 174 * It does not use a classic rank marker but indicated the Group rank after the actual groups name 175 * For example Rhododendron boothii Mishmiense Group 176 * or Primula Border Auricula Group 177 * 178 * Sometimes authors also used the words "sort", "type", "selections" or "hybrids" instead of Group which is not legal according to the code. 179 */ 180 CULTIVAR_GROUP, 181 182 /** 183 * A group of cultivars. These can be roughly comparable to cultivar groups, but convarieties, unlike cultivar groups, 184 * do not necessarily contain named varieties, and convarieties are members of traditional "Linnaean" ranks. 185 * The ICNCP replaced this term with the term cultivar-group, and convarieties should not be used in modern cultivated plant taxonomy. 186 * 187 * From Spooner et al., Horticultural Reviews 28 (2003): 1-60 188 */ 189 CONVARIETY("convar."), 190 191 /** 192 * used also for any other unspecific rank below subspecies. 193 */ 194 INFRASUBSPECIFIC_NAME("infrasubsp."), 195 196 /** 197 * Botanical legacy rank 198 */ 199 200 PROLES("prol."), 201 202 /** 203 * Botanical legacy rank 204 */ 205 RACE("race"), 206 207 /** 208 * Zoological legacy rank 209 */ 210 NATIO("natio"), 211 212 /** 213 * Zoological legacy rank 214 */ 215 ABERRATION("ab."), 216 217 /** 218 * Zoological legacy rank 219 */ 220 MORPH("morph"), 221 222 VARIETY("var."), 223 224 SUBVARIETY("subvar."), 225 226 FORM("f."), 227 228 SUBFORM("subf."), 229 230 /** 231 * Microbial rank based on pathogenic reactions in one or more hosts. 232 * For recommendations on designating pathovars and use of designations when reviving names see 233 * Dye et al. (1980) Standards for naming pathovars of phytopathogenic bacteria and a list of pathovar names and pathotype strains. 234 * Rev. Plant Pathol. 59:153–168. 235 * See <a href="http://www.ncbi.nlm.nih.gov/books/NBK8812/table/A844/?report=objectonly">Bacteriological Code</a> 236 * See <a href="http://www.isppweb.org/about_tppb_naming.asp">International Standards for Naming Pathovars of Phytopathogenic Bacteria</a> 237 * See <a href="http://sipav.org/main/jpp/index.php/jpp/article/view/682">Demystifying the nomenclature of bacterial plant pathogens</a> 238 * See <a href="http://link.springer.com/chapter/10.1007/978-94-009-3555-6_171">Problems with the Pathovar Concept</a> 239 * For example Pseudomonas syringae pv. lachrymans 240 */ 241 PATHOVAR("pv."), 242 243 /** 244 * Microbial rank based on biochemical or physiological properties. 245 * See <a href="http://www.ncbi.nlm.nih.gov/books/NBK8812/table/A844/?report=objectonly">Bacteriological Code</a> 246 * For example Francisella tularensis biovar tularensis 247 */ 248 BIOVAR("biovar"), 249 250 /** 251 * Microbial rank based on production or amount of production of a particular chemical. 252 * See <a href="http://www.ncbi.nlm.nih.gov/books/NBK8812/table/A844/?report=objectonly">Bacteriological Code</a> 253 * For example Vibrio alginolyticus chemovar iophagus 254 */ 255 CHEMOVAR("chemovar"), 256 257 /** 258 * Microbial rank based on morphological characterislics. 259 * See <a href="http://www.ncbi.nlm.nih.gov/books/NBK8812/table/A844/?report=objectonly">Bacteriological Code</a> 260 * For example Acinetobacter junii morphovar I 261 */ 262 MORPHOVAR("morphovar"), 263 264 /** 265 * Microbial infrasubspecific rank based on reactions to bacteriophage. 266 * See <a href="http://www.ncbi.nlm.nih.gov/books/NBK8812/table/A844/?report=objectonly">Bacteriological Code</a> 267 * For example Staphyloccocus aureus phagovar 42D 268 */ 269 PHAGOVAR("phagovar"), 270 271 /** 272 * Microbial infrasubspecific rank based on antigenic characteristics. 273 * See <a href="http://www.ncbi.nlm.nih.gov/books/NBK8812/table/A844/?report=objectonly">Bacteriological Code</a> 274 * For example Salmonella enterica serovar Dublin 275 */ 276 SEROVAR("serovar"), 277 278 /** 279 * Microbial infrasubspecific rank based on chemical constitution. 280 * See <a href="http://www.ncbi.nlm.nih.gov/books/NBK8812/table/A844/?report=objectonly">Bacteriological Code</a> 281 * For example Thymus vulgaris ct. geraniol 282 */ 283 CHEMOFORM("chemoform"), 284 285 /** 286 * Microbial infrasubspecific rank. 287 * A parasitic, symbiotic, or commensal microorganism distinguished primarily by adaptation to a particular host or habitat. 288 * Named preferably by the scientific name of the host in the genitive. 289 * See <a href="http://www.ncbi.nlm.nih.gov/books/NBK8812/table/A844/?report=objectonly">Bacteriological Code</a> 290 * For example Puccinia graminis f. sp. avenae 291 */ 292 FORMA_SPECIALIS("f.sp."), 293 294 CULTIVAR("cv."), 295 296 /** 297 * A microbial strain. 298 */ 299 STRAIN("strain"), 300 301 /** 302 * Any other rank we cannot map to this enumeration 303 */ 304 OTHER, 305 306 UNRANKED; 307 308 /** 309 * All main Linnean ranks ordered. 310 */ 311 public static final List<Rank> LINNEAN_RANKS = Collections.unmodifiableList( 312 Arrays.asList( 313 KINGDOM, 314 PHYLUM, 315 CLASS, 316 ORDER, 317 FAMILY, 318 GENUS, 319 SPECIES 320 )); 321 322 /** 323 * An ordered list of all ranks that appear in Darwin Core with their own term. 324 */ 325 public static final List<Rank> DWC_RANKS = Collections.unmodifiableList( 326 Arrays.asList( 327 KINGDOM, 328 PHYLUM, 329 CLASS, 330 ORDER, 331 FAMILY, 332 GENUS, 333 SUBGENUS, 334 SPECIES 335 )); 336 337 /** 338 * A set of ranks which cannot clearly be compared to any other rank as they represent rank "ranges". 339 * For example a subgeneric rank is anything below genus, 340 * so one cannot say if its higher or lower than a species for example. 341 */ 342 private static final Set<Rank> UNCOMPARABLE_RANKS = Collections.unmodifiableSet( 343 new HashSet<>(Arrays.asList( 344 INFRAGENERIC_NAME, 345 INFRASPECIFIC_NAME, 346 INFRASUBSPECIFIC_NAME, 347 OTHER, 348 UNRANKED 349 ))); 350 351 private static final Set<Rank> LEGACY_RANKS = Collections.unmodifiableSet( 352 new HashSet<>(Arrays.asList( 353 MORPH, 354 ABERRATION, 355 NATIO, 356 RACE, 357 PROLES, 358 CONVARIETY 359 ))); 360 361 private static final Map<Rank, NomenclaturalCode> RANK2CODE; 362 363 static { 364 Map<Rank, NomenclaturalCode> rank2code = new HashMap<>(); 365 rank2code.put(PARVCLASS, NomenclaturalCode.ZOOLOGICAL); 366 rank2code.put(MAGNORDER, NomenclaturalCode.ZOOLOGICAL); 367 rank2code.put(GRANDORDER, NomenclaturalCode.ZOOLOGICAL); 368 rank2code.put(PARVORDER, NomenclaturalCode.ZOOLOGICAL); 369 rank2code.put(SUPERLEGION, NomenclaturalCode.ZOOLOGICAL); 370 rank2code.put(LEGION, NomenclaturalCode.ZOOLOGICAL); 371 rank2code.put(SUBLEGION, NomenclaturalCode.ZOOLOGICAL); 372 rank2code.put(INFRALEGION, NomenclaturalCode.ZOOLOGICAL); 373 rank2code.put(SUPERCOHORT, NomenclaturalCode.ZOOLOGICAL); 374 rank2code.put(COHORT, NomenclaturalCode.ZOOLOGICAL); 375 rank2code.put(SUBCOHORT, NomenclaturalCode.ZOOLOGICAL); 376 rank2code.put(INFRACOHORT, NomenclaturalCode.ZOOLOGICAL); 377 rank2code.put(MORPH, NomenclaturalCode.ZOOLOGICAL); 378 rank2code.put(ABERRATION, NomenclaturalCode.ZOOLOGICAL); 379 rank2code.put(NATIO, NomenclaturalCode.ZOOLOGICAL); 380 381 rank2code.put(RACE, NomenclaturalCode.BOTANICAL); 382 rank2code.put(PROLES, NomenclaturalCode.BOTANICAL); 383 rank2code.put(SECTION, NomenclaturalCode.BOTANICAL); 384 rank2code.put(SUBSECTION, NomenclaturalCode.BOTANICAL); 385 rank2code.put(SERIES, NomenclaturalCode.BOTANICAL); 386 rank2code.put(SUBSERIES, NomenclaturalCode.BOTANICAL); 387 388 rank2code.put(CULTIVAR, NomenclaturalCode.CULTIVARS); 389 rank2code.put(CULTIVAR_GROUP, NomenclaturalCode.CULTIVARS); 390 rank2code.put(CONVARIETY, NomenclaturalCode.CULTIVARS); 391 rank2code.put(GREX, NomenclaturalCode.CULTIVARS); 392 393 rank2code.put(PATHOVAR, NomenclaturalCode.BACTERIAL); 394 rank2code.put(BIOVAR, NomenclaturalCode.BACTERIAL); 395 rank2code.put(CHEMOVAR, NomenclaturalCode.BACTERIAL); 396 rank2code.put(MORPHOVAR, NomenclaturalCode.BACTERIAL); 397 rank2code.put(PHAGOVAR, NomenclaturalCode.BACTERIAL); 398 rank2code.put(SEROVAR, NomenclaturalCode.BACTERIAL); 399 rank2code.put(CHEMOFORM, NomenclaturalCode.BACTERIAL); 400 rank2code.put(FORMA_SPECIALIS, NomenclaturalCode.BACTERIAL); 401 402 RANK2CODE = Collections.unmodifiableMap(rank2code); 403 } 404 405 private final String marker; 406 407 Rank() { 408 this(null); 409 } 410 411 Rank(String marker) { 412 this.marker = marker; 413 } 414 415 public String getMarker() { 416 return marker; 417 } 418 419 /** 420 * @return true for infraspecific ranks. 421 */ 422 public boolean isInfraspecific() { 423 return this != SPECIES && isSpeciesOrBelow(); 424 } 425 426 /** 427 * @return true for infra subspecific ranks. 428 */ 429 public boolean isInfrasubspecific() { 430 return ordinal() > SUBSPECIES.ordinal() && notOtherOrUnknown(); 431 } 432 433 /** 434 * @return true for rank is below genus. Also incluse species and infraspecific ranks 435 */ 436 public boolean isInfrageneric() { 437 return ordinal() > GENUS.ordinal() && notOtherOrUnknown(); 438 } 439 440 /** 441 * @return true for real infrageneric ranks with an infragenericEpithet below genus and above species aggregate. 442 */ 443 public boolean isInfragenericStrictly() { 444 return isInfrageneric() && ordinal() < SPECIES_AGGREGATE.ordinal(); 445 } 446 447 /** 448 * True for all mayor Linnéan ranks, ie kingdom,phylum,class,order,family,genus and species. 449 */ 450 public boolean isLinnean() { 451 for (Rank r : LINNEAN_RANKS) { 452 if (r == this) { 453 return true; 454 } 455 } 456 return false; 457 } 458 459 public boolean isSpeciesOrBelow() { 460 return ordinal() >= SPECIES.ordinal() && notOtherOrUnknown(); 461 } 462 463 public boolean isSpeciesAggregateOrBelow() { 464 return ordinal() >= SPECIES_AGGREGATE.ordinal() && notOtherOrUnknown(); 465 } 466 467 public boolean notOtherOrUnknown() { 468 return this != OTHER && this != UNRANKED; 469 } 470 471 /** 472 * @return true if the rank is above genus. 473 */ 474 public boolean isSuprageneric() { 475 return ordinal() < GENUS.ordinal(); 476 } 477 478 /** 479 * @return true if the rank is above rank species. 480 */ 481 public boolean isSupraspecific() { 482 return ordinal() < SPECIES.ordinal(); 483 } 484 485 /** 486 * True for names of informal ranks that represent a range of ranks really and therefore cannot safely be compared to 487 * other ranks in all cases. 488 * Example ranks are INFRASPECIFIC_NAME or INFRAGENERIC_NAME 489 * 490 * @return true if uncomparable 491 */ 492 public boolean isUncomparable() { 493 return UNCOMPARABLE_RANKS.contains(this); 494 } 495 496 /** 497 * @return true if the rank is considered a legacy rank not used anymore in current nomenclature. 498 */ 499 public boolean isLegacy() { 500 return LEGACY_RANKS.contains(this); 501 } 502 503 /** 504 * @return the nomenclatural code if the rank is restricted to just one code or null otherwise 505 */ 506 public NomenclaturalCode isRestrictedToCode() { 507 return RANK2CODE.get(this); 508 } 509 510 /** 511 * @return true if this rank is higher than the given other 512 */ 513 public boolean higherThan(Rank other) { 514 return ordinal() < other.ordinal(); 515 } 516}