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