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