001/*
002 * Copyright 2021 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.dwc.terms;
017
018import java.io.Serializable;
019import java.net.URI;
020import java.util.ArrayList;
021import java.util.Arrays;
022import java.util.List;
023
024/**
025 * All Darwin Core terms with namespace http://rs.tdwg.org/dwc/terms/ as an
026 * enumeration with alternative term names found sometimes in data.
027 * Old, deprecated terms are kept but marked as such.
028 */
029public enum DwcTerm implements Term, AlternativeNames, Serializable {
030
031  /**
032   * CLASS TERMS
033   * Listed in the order given on the Darwin Core Quick Reference Guide.
034   * @see <a href="http://rs.tdwg.org/dwc/terms/index.htm#theterms">DwC Quick Reference Guide</a>
035   * Location is not on this list because it is a term in the dcterm namespace.
036   */
037  Occurrence(DwcTerm.GROUP_OCCURRENCE, "DarwinCore", "SimpleDarwinCore"),
038  Organism(DwcTerm.GROUP_ORGANISM),
039  MaterialSample(DwcTerm.GROUP_MATERIAL_SAMPLE),
040  Event(DwcTerm.GROUP_EVENT),
041  GeologicalContext(DwcTerm.GROUP_GEOLOGICALCONTEXT),
042  Identification(DwcTerm.GROUP_IDENTIFICATION),
043  Taxon(DwcTerm.GROUP_TAXON),
044  MeasurementOrFact(DwcTerm.GROUP_MEASUREMENTORFACT),
045  ResourceRelationship(DwcTerm.GROUP_RESOURCERELATIONSHIP),
046
047  /**
048   * PROPERTY TERMS
049   * Listed in the order given on the Darwin Core Quick Reference Guide.
050   * @see <a href="http://rs.tdwg.org/dwc/terms/index.htm#theterms">DwC Quick Reference Guide</a>
051   */
052  institutionID(DwcTerm.GROUP_RECORD),
053  collectionID(DwcTerm.GROUP_RECORD),
054  datasetID(DwcTerm.GROUP_RECORD),
055  institutionCode(DwcTerm.GROUP_RECORD),
056  collectionCode(DwcTerm.GROUP_RECORD),
057  datasetName(DwcTerm.GROUP_RECORD),
058  ownerInstitutionCode(DwcTerm.GROUP_RECORD),
059  basisOfRecord(DwcTerm.GROUP_RECORD),
060  informationWithheld(DwcTerm.GROUP_RECORD),
061  dataGeneralizations(DwcTerm.GROUP_RECORD),
062  dynamicProperties(DwcTerm.GROUP_RECORD),
063
064  occurrenceID(DwcTerm.GROUP_OCCURRENCE),
065  catalogNumber(DwcTerm.GROUP_OCCURRENCE, "catalogNumberNumeric"),
066  recordNumber(DwcTerm.GROUP_OCCURRENCE, "collectorNumber"),
067  recordedBy(DwcTerm.GROUP_OCCURRENCE, "collector"),
068  recordedByID(DwcTerm.GROUP_OCCURRENCE, "gbif:recordedByID", "http://rs.gbif.org/terms/1.0/recordedByID"),
069  individualCount(DwcTerm.GROUP_OCCURRENCE),
070  organismQuantity(DwcTerm.GROUP_OCCURRENCE),
071  organismQuantityType(DwcTerm.GROUP_OCCURRENCE),
072  sex(DwcTerm.GROUP_OCCURRENCE),
073  @Vocabulary lifeStage(DwcTerm.GROUP_OCCURRENCE),
074  reproductiveCondition(DwcTerm.GROUP_OCCURRENCE),
075  behavior(DwcTerm.GROUP_OCCURRENCE),
076  @Vocabulary establishmentMeans(DwcTerm.GROUP_OCCURRENCE),
077  @Vocabulary degreeOfEstablishment(DwcTerm.GROUP_OCCURRENCE),
078  @Vocabulary pathway(DwcTerm.GROUP_OCCURRENCE),
079  georeferenceVerificationStatus(DwcTerm.GROUP_OCCURRENCE),
080  occurrenceStatus(DwcTerm.GROUP_OCCURRENCE),
081  preparations(DwcTerm.GROUP_OCCURRENCE),
082  disposition(DwcTerm.GROUP_OCCURRENCE),
083  associatedMedia(DwcTerm.GROUP_OCCURRENCE),
084  associatedOccurrences(DwcTerm.GROUP_OCCURRENCE),
085  associatedReferences(DwcTerm.GROUP_OCCURRENCE),
086  associatedSequences(DwcTerm.GROUP_OCCURRENCE),
087  associatedTaxa(DwcTerm.GROUP_OCCURRENCE),
088  otherCatalogNumbers(DwcTerm.GROUP_OCCURRENCE),
089  occurrenceRemarks(DwcTerm.GROUP_OCCURRENCE),
090
091  organismID(DwcTerm.GROUP_ORGANISM, "individualID"),
092  organismName(DwcTerm.GROUP_ORGANISM),
093  organismScope(DwcTerm.GROUP_ORGANISM),
094  associatedOrganisms(DwcTerm.GROUP_ORGANISM),
095  previousIdentifications(DwcTerm.GROUP_ORGANISM),
096  organismRemarks(DwcTerm.GROUP_ORGANISM),
097
098  materialSampleID(DwcTerm.GROUP_MATERIAL_SAMPLE),
099
100  eventID(DwcTerm.GROUP_EVENT),
101  parentEventID(DwcTerm.GROUP_EVENT),
102  fieldNumber(DwcTerm.GROUP_EVENT),
103  eventDate(DwcTerm.GROUP_EVENT, "earliestDateCollected", "latestDateCollected"),
104  eventTime(DwcTerm.GROUP_EVENT),
105  startDayOfYear(DwcTerm.GROUP_EVENT),
106  endDayOfYear(DwcTerm.GROUP_EVENT),
107  year(DwcTerm.GROUP_EVENT),
108  month(DwcTerm.GROUP_EVENT),
109  day(DwcTerm.GROUP_EVENT),
110  verbatimEventDate(DwcTerm.GROUP_EVENT),
111  habitat(DwcTerm.GROUP_EVENT),
112  samplingProtocol(DwcTerm.GROUP_EVENT),
113  sampleSizeValue(DwcTerm.GROUP_EVENT),
114  sampleSizeUnit(DwcTerm.GROUP_EVENT),
115  samplingEffort(DwcTerm.GROUP_EVENT),
116  fieldNotes(DwcTerm.GROUP_EVENT),
117  eventRemarks(DwcTerm.GROUP_EVENT),
118
119  locationID(DwcTerm.GROUP_LOCATION),
120  higherGeographyID(DwcTerm.GROUP_LOCATION),
121  higherGeography(DwcTerm.GROUP_LOCATION),
122  continent(DwcTerm.GROUP_LOCATION),
123  waterBody(DwcTerm.GROUP_LOCATION),
124  islandGroup(DwcTerm.GROUP_LOCATION),
125  island(DwcTerm.GROUP_LOCATION),
126  country(DwcTerm.GROUP_LOCATION),
127  countryCode(DwcTerm.GROUP_LOCATION),
128  stateProvince(DwcTerm.GROUP_LOCATION, "state", "province"),
129  county(DwcTerm.GROUP_LOCATION),
130  municipality(DwcTerm.GROUP_LOCATION, "city"),
131  locality(DwcTerm.GROUP_LOCATION),
132  verbatimLocality(DwcTerm.GROUP_LOCATION),
133  minimumElevationInMeters(DwcTerm.GROUP_LOCATION),
134  maximumElevationInMeters(DwcTerm.GROUP_LOCATION),
135  verbatimElevation(DwcTerm.GROUP_LOCATION),
136  verticalDatum(DwcTerm.GROUP_LOCATION),
137  minimumDepthInMeters(DwcTerm.GROUP_LOCATION),
138  maximumDepthInMeters(DwcTerm.GROUP_LOCATION),
139  verbatimDepth(DwcTerm.GROUP_LOCATION),
140  minimumDistanceAboveSurfaceInMeters(DwcTerm.GROUP_LOCATION),
141  maximumDistanceAboveSurfaceInMeters(DwcTerm.GROUP_LOCATION),
142  locationAccordingTo(DwcTerm.GROUP_LOCATION),
143  locationRemarks(DwcTerm.GROUP_LOCATION),
144  decimalLatitude(DwcTerm.GROUP_LOCATION, "latitude"),
145  decimalLongitude(DwcTerm.GROUP_LOCATION, "longitude"),
146  geodeticDatum(DwcTerm.GROUP_LOCATION, "datum", "horizontaldatum"),
147  coordinateUncertaintyInMeters(DwcTerm.GROUP_LOCATION),
148  coordinatePrecision(DwcTerm.GROUP_LOCATION),
149  pointRadiusSpatialFit(DwcTerm.GROUP_LOCATION),
150  verbatimCoordinates(DwcTerm.GROUP_LOCATION),
151  verbatimLatitude(DwcTerm.GROUP_LOCATION),
152  verbatimLongitude(DwcTerm.GROUP_LOCATION),
153  verbatimCoordinateSystem(DwcTerm.GROUP_LOCATION),
154  verbatimSRS(DwcTerm.GROUP_LOCATION),
155  footprintWKT(DwcTerm.GROUP_LOCATION),
156  footprintSRS(DwcTerm.GROUP_LOCATION),
157  footprintSpatialFit(DwcTerm.GROUP_LOCATION),
158  georeferencedBy(DwcTerm.GROUP_LOCATION),
159  georeferencedDate(DwcTerm.GROUP_LOCATION),
160  georeferenceProtocol(DwcTerm.GROUP_LOCATION),
161  georeferenceSources(DwcTerm.GROUP_LOCATION),
162  georeferenceRemarks(DwcTerm.GROUP_LOCATION),
163
164  geologicalContextID(DwcTerm.GROUP_GEOLOGICALCONTEXT),
165  earliestEonOrLowestEonothem(DwcTerm.GROUP_GEOLOGICALCONTEXT),
166  latestEonOrHighestEonothem(DwcTerm.GROUP_GEOLOGICALCONTEXT),
167  earliestEraOrLowestErathem(DwcTerm.GROUP_GEOLOGICALCONTEXT),
168  latestEraOrHighestErathem(DwcTerm.GROUP_GEOLOGICALCONTEXT),
169  earliestPeriodOrLowestSystem(DwcTerm.GROUP_GEOLOGICALCONTEXT),
170  latestPeriodOrHighestSystem(DwcTerm.GROUP_GEOLOGICALCONTEXT),
171  earliestEpochOrLowestSeries(DwcTerm.GROUP_GEOLOGICALCONTEXT),
172  latestEpochOrHighestSeries(DwcTerm.GROUP_GEOLOGICALCONTEXT),
173  earliestAgeOrLowestStage(DwcTerm.GROUP_GEOLOGICALCONTEXT),
174  latestAgeOrHighestStage(DwcTerm.GROUP_GEOLOGICALCONTEXT),
175  lowestBiostratigraphicZone(DwcTerm.GROUP_GEOLOGICALCONTEXT),
176  highestBiostratigraphicZone(DwcTerm.GROUP_GEOLOGICALCONTEXT),
177  lithostratigraphicTerms(DwcTerm.GROUP_GEOLOGICALCONTEXT),
178  group(DwcTerm.GROUP_GEOLOGICALCONTEXT),
179  formation(DwcTerm.GROUP_GEOLOGICALCONTEXT),
180  member(DwcTerm.GROUP_GEOLOGICALCONTEXT),
181  bed(DwcTerm.GROUP_GEOLOGICALCONTEXT),
182
183  identificationID(DwcTerm.GROUP_IDENTIFICATION),
184  verbatimIdentification(DwcTerm.GROUP_IDENTIFICATION),
185  identificationQualifier(DwcTerm.GROUP_IDENTIFICATION),
186  typeStatus(DwcTerm.GROUP_IDENTIFICATION),
187  identifiedBy(DwcTerm.GROUP_IDENTIFICATION),
188  identifiedByID(DwcTerm.GROUP_IDENTIFICATION, "gbif:identifiedByID", "http://rs.gbif.org/terms/1.0/identifiedByID"),
189  dateIdentified(DwcTerm.GROUP_IDENTIFICATION),
190  identificationReferences(DwcTerm.GROUP_IDENTIFICATION),
191  identificationVerificationStatus(DwcTerm.GROUP_IDENTIFICATION),
192  identificationRemarks(DwcTerm.GROUP_IDENTIFICATION),
193
194  taxonID(DwcTerm.GROUP_TAXON, "nameUsageID"),
195  scientificNameID(DwcTerm.GROUP_TAXON, "nameID"),
196  acceptedNameUsageID(DwcTerm.GROUP_TAXON, "acceptedTaxonID"),
197  parentNameUsageID(DwcTerm.GROUP_TAXON, "higherNameUsageID", "parentTaxonID"),
198  originalNameUsageID(DwcTerm.GROUP_TAXON, "originalNameID", "basionymID"),
199  nameAccordingToID(DwcTerm.GROUP_TAXON, "taxonAccordingToID"),
200  namePublishedInID(DwcTerm.GROUP_TAXON),
201  taxonConceptID(DwcTerm.GROUP_TAXON),
202  scientificName(DwcTerm.GROUP_TAXON),
203  acceptedNameUsage(DwcTerm.GROUP_TAXON, "acceptedTaxon"),
204  parentNameUsage(DwcTerm.GROUP_TAXON, "parentTaxon", "higherTaxon", "higherNameUsage"),
205  originalNameUsage(DwcTerm.GROUP_TAXON, "originalName", "originalTaxon", "basionym"),
206  nameAccordingTo(DwcTerm.GROUP_TAXON, "taxonAccordingTo"),
207  namePublishedIn(DwcTerm.GROUP_TAXON),
208  namePublishedInYear(DwcTerm.GROUP_TAXON),
209  higherClassification(DwcTerm.GROUP_TAXON),
210  kingdom(DwcTerm.GROUP_TAXON),
211  phylum(DwcTerm.GROUP_TAXON),
212  /**
213   * The taxonomic class.
214   * The real Darwin Core term is class, but as java does not allow this name we use a variation instead.
215   */
216  class_(DwcTerm.GROUP_TAXON, "class"),
217  order(DwcTerm.GROUP_TAXON),
218  family(DwcTerm.GROUP_TAXON),
219  subfamily(DwcTerm.GROUP_TAXON),
220  genus(DwcTerm.GROUP_TAXON),
221  genericName(DwcTerm.GROUP_TAXON, "gbif:genericName", "http://rs.gbif.org/terms/1.0/genericName"),
222  subgenus(DwcTerm.GROUP_TAXON),
223  infragenericEpithet(DwcTerm.GROUP_TAXON),
224  specificEpithet(DwcTerm.GROUP_TAXON),
225  infraspecificEpithet(DwcTerm.GROUP_TAXON),
226  cultivarEpithet(DwcTerm.GROUP_TAXON),
227  taxonRank(DwcTerm.GROUP_TAXON, "rank"),
228  verbatimTaxonRank(DwcTerm.GROUP_TAXON),
229  scientificNameAuthorship(DwcTerm.GROUP_TAXON),
230  vernacularName(DwcTerm.GROUP_TAXON),
231  nomenclaturalCode(DwcTerm.GROUP_TAXON),
232  taxonomicStatus(DwcTerm.GROUP_TAXON),
233  nomenclaturalStatus(DwcTerm.GROUP_TAXON),
234  taxonRemarks(DwcTerm.GROUP_TAXON, "taxonRemark"),
235
236  measurementID(DwcTerm.GROUP_MEASUREMENTORFACT),
237  measurementType(DwcTerm.GROUP_MEASUREMENTORFACT),
238  measurementValue(DwcTerm.GROUP_MEASUREMENTORFACT),
239  measurementAccuracy(DwcTerm.GROUP_MEASUREMENTORFACT),
240  measurementUnit(DwcTerm.GROUP_MEASUREMENTORFACT),
241  measurementDeterminedBy(DwcTerm.GROUP_MEASUREMENTORFACT),
242  measurementDeterminedDate(DwcTerm.GROUP_MEASUREMENTORFACT),
243  measurementMethod(DwcTerm.GROUP_MEASUREMENTORFACT),
244  measurementRemarks(DwcTerm.GROUP_MEASUREMENTORFACT),
245
246  resourceRelationshipID(DwcTerm.GROUP_RESOURCERELATIONSHIP),
247  resourceID(DwcTerm.GROUP_RESOURCERELATIONSHIP),
248  relationshipOfResourceID(DwcTerm.GROUP_RESOURCERELATIONSHIP),
249  relatedResourceID(DwcTerm.GROUP_RESOURCERELATIONSHIP),
250  relationshipOfResource(DwcTerm.GROUP_RESOURCERELATIONSHIP),
251  relationshipAccordingTo(DwcTerm.GROUP_RESOURCERELATIONSHIP),
252  relationshipEstablishedDate(DwcTerm.GROUP_RESOURCERELATIONSHIP),
253  relationshipRemarks(DwcTerm.GROUP_RESOURCERELATIONSHIP);
254
255  private static final String PREFIX = "dwc";
256  private static final String NS = "http://rs.tdwg.org/dwc/terms/";
257  private static final URI NS_URI = URI.create(NS);
258
259  public static final String GROUP_RECORD = "Record";
260  public static final String GROUP_OCCURRENCE = "Occurrence";
261  public static final String GROUP_ORGANISM = "Organism";
262  public static final String GROUP_MATERIAL_SAMPLE = "MaterialSample";
263  public static final String GROUP_EVENT = "Event";
264  public static final String GROUP_LOCATION = "Location";
265  public static final String GROUP_GEOLOGICALCONTEXT = "GeologicalContext";
266  public static final String GROUP_IDENTIFICATION = "Identification";
267  public static final String GROUP_TAXON = "Taxon";
268  public static final String GROUP_MEASUREMENTORFACT = "MeasurementOrFact";
269  public static final String GROUP_RESOURCERELATIONSHIP = "ResourceRelationship";
270
271  /**
272   * Lists all term groups in the order given on the Darwin Core Quick Reference Guide.
273   * @see <a href="http://rs.tdwg.org/dwc/terms/index.htm#theterms">DwC Quick Reference Guide</a>
274   */
275  public static final String[] GROUPS =
276    {GROUP_RECORD, GROUP_OCCURRENCE, GROUP_ORGANISM, GROUP_EVENT, GROUP_LOCATION,
277          GROUP_GEOLOGICALCONTEXT, GROUP_IDENTIFICATION, GROUP_TAXON,
278          GROUP_MEASUREMENTORFACT, GROUP_RESOURCERELATIONSHIP};
279
280  public static final DwcTerm[] TAXONOMIC_TERMS = Arrays.stream(values())
281                                                        .filter(t -> !t.isClass() && t.getGroup().equals(GROUP_TAXON))
282                                                        .toArray(DwcTerm[]::new);
283
284  /**
285   * List of all higher rank terms in dwc, ordered by rank and starting with kingdom.
286   */
287  public static final DwcTerm[] HIGHER_RANKS =
288    {DwcTerm.kingdom, DwcTerm.phylum, DwcTerm.class_, DwcTerm.order,
289        DwcTerm.family, DwcTerm.subfamily, DwcTerm.genus, DwcTerm.subgenus};
290
291  /**
292   * List of all class terms in dwc.
293   */
294  public static final DwcTerm[] CLASS_TERMS = Arrays.stream(values())
295                                                    .filter(DwcTerm::isClass)
296                                                    .toArray(DwcTerm[]::new);
297
298  private final String groupName;
299  public final String normQName;
300  public final String[] normAlts;
301
302  private DwcTerm(String groupName, String... alternatives) {
303    normQName = TermFactory.normaliseTerm(qualifiedName());
304    for (int i = 0; i < alternatives.length; i++) {
305      alternatives[i] = TermFactory.normaliseTerm(alternatives[i]);
306    }
307    normAlts = alternatives;
308    this.groupName = groupName;
309  }
310
311
312  /**
313   * The simple term name without a namespace.
314   * For example scientificName.
315   * @return simple term name
316   */
317  @Override
318  public String simpleName() {
319    if (this == class_) {
320      return "class";
321    }
322    return name();
323  }
324
325  /**
326   * Array of alternative simple names in use for the term.
327   * Often based on older dwc versions.
328   * @return simple term name
329   */
330  @Override
331  public String[] alternativeNames() {
332    return normAlts;
333  }
334
335  /**
336   * The optional group the term is grouped in.
337   * For example Taxon, Identification, etc.
338   */
339  public String getGroup() {
340    return groupName;
341  }
342
343  /**
344   * @return true if the dwc term is defining a class instead of a property, e.g. Taxon
345   */
346  @Override
347  public boolean isClass() {
348    return Character.isUpperCase(simpleName().charAt(0));
349  }
350
351  /**
352   * List all terms that belong to a given group.
353   *
354   * @param group the group to list terms for
355   *
356   * @return the list of dwc terms in the given group
357   */
358  public static List<DwcTerm> listByGroup(String group) {
359    List<DwcTerm> terms = new ArrayList<DwcTerm>();
360    for (DwcTerm t : DwcTerm.values()) {
361      if (t.getGroup().equalsIgnoreCase(group)) {
362        terms.add(t);
363      }
364    }
365    return terms;
366  }
367
368  @Override
369  public String prefix() {
370    return PREFIX;
371  }
372
373  @Override
374  public URI namespace() {
375    return NS_URI;
376  }
377
378  @Override
379  public String toString() {
380    return prefixedName();
381  }
382}