001package org.gbif.api.util;
002
003import org.gbif.api.model.common.LinneanClassification;
004import org.gbif.api.model.common.LinneanClassificationKeys;
005import org.gbif.api.vocabulary.Rank;
006
007import java.util.LinkedHashMap;
008import javax.annotation.Nullable;
009import javax.validation.constraints.NotNull;
010
011import com.google.common.base.Joiner;
012import com.google.common.base.Strings;
013import com.google.common.collect.Maps;
014
015public class ClassificationUtils {
016
017  private static final Joiner JOINER = Joiner.on(", ").skipNulls();
018
019  /**
020   * Concatenates all higher Linnean taxa into a single dwc:higherClassification string, skipping
021   * null values.
022   *
023   * @param lc the LinneanClassification to join into the higher classification string
024   *
025   * @return the concatenated dwc:higherClassification string
026   */
027  @Nullable
028  public static String getHigherClassification(LinneanClassification lc) {
029    return Strings.emptyToNull(JOINER.join(lc.getKingdom(),
030                                           lc.getPhylum(),
031                                           lc.getClazz(),
032                                           lc.getOrder(),
033                                           lc.getFamily(),
034                                           lc.getGenus()));
035  }
036
037  /**
038   * Gets a higher taxon property by passing the rank of it.
039   * Only Linnean ranks are supported.
040   *
041   * @param lc   the LinneanClassification holding the taxon property to be retrieved
042   * @param rank the higher linnean rank to retrieve
043   *
044   * @return the name of the higher taxon or null if rank doesnt exist
045   */
046  @Nullable
047  public static String getHigherRank(LinneanClassification lc, Rank rank) {
048    if (rank != null) {
049      switch (rank) {
050        case KINGDOM:
051          return lc.getKingdom();
052        case PHYLUM:
053          return lc.getPhylum();
054        case CLASS:
055          return lc.getClazz();
056        case ORDER:
057          return lc.getOrder();
058        case FAMILY:
059          return lc.getFamily();
060        case GENUS:
061          return lc.getGenus();
062        case SUBGENUS:
063          return lc.getSubgenus();
064        case SPECIES:
065          return lc.getSpecies();
066      }
067    }
068    return null;
069  }
070
071  /**
072   * Sets a higher taxon property by passing the rank of it.
073   *
074   * @param lc   the LinneanClassification on which to set the new property
075   * @param rank the higher linnean rank to set
076   * @param name the higher ranks name
077   */
078  public static void setHigherRank(LinneanClassification lc, Rank rank, String name) {
079    if (rank != null) {
080      switch (rank) {
081        case KINGDOM:
082          lc.setKingdom(name);
083          break;
084        case PHYLUM:
085          lc.setPhylum(name);
086          break;
087        case CLASS:
088          lc.setClazz(name);
089          break;
090        case ORDER:
091          lc.setOrder(name);
092          break;
093        case FAMILY:
094          lc.setFamily(name);
095          break;
096        case GENUS:
097          lc.setGenus(name);
098          break;
099        case SUBGENUS:
100          lc.setSubgenus(name);
101          break;
102        case SPECIES:
103          lc.setSpecies(name);
104          break;
105      }
106    }
107  }
108
109  /**
110   * An ordered map with entries for all higher Linnean ranks down to species which are not null.
111   * The map starts with the highest rank, e.g. the kingdom and maps the name usage key to its canonical name.
112   *
113   * @param lc the object that implements both LinneanClassification and LinneanClassificationKeys from which to build
114   *           the map
115   *
116   * @return map of higher ranks
117   */
118  @NotNull
119  public static <T extends LinneanClassification & LinneanClassificationKeys> LinkedHashMap<Integer, String> getHigherClassificationMap(
120    T lc) {
121    return getHigherClassificationBaseMap(lc);
122  }
123
124  /**
125   * An ordered map with entries for all higher Linnean ranks down to the actual direct parent of this usage.
126   * The map starts with the highest rank, e.g. the kingdom and maps the name usage key to its canonical name.
127   * The name usage itself is never included, even though a higher rank might point to the usage itself.
128   *
129   * @param lc the object that implements both LinneanClassification and LinneanClassificationKeys from which to build
130   *           the map
131   *
132   * @return map of higher ranks
133   */
134  @NotNull
135  public static <T extends LinneanClassification & LinneanClassificationKeys> LinkedHashMap<Integer, String> getHigherClassificationMap(
136    T lc, int key, Integer parentKey, String parent) {
137    LinkedHashMap<Integer, String> map = getHigherClassificationBaseMap(lc);
138
139    if (parentKey != null) {
140      map.put(parentKey, parent);
141    }
142    // remove notion to this usage
143    map.remove(key);
144
145    return map;
146  }
147
148  private static <T extends LinneanClassification & LinneanClassificationKeys> LinkedHashMap<Integer, String> getHigherClassificationBaseMap(
149    T lc) {
150    LinkedHashMap<Integer, String> map = Maps.newLinkedHashMap();
151    if (lc.getKingdomKey() != null) {
152      map.put(lc.getKingdomKey(), lc.getKingdom());
153    }
154    if (lc.getPhylumKey() != null) {
155      map.put(lc.getPhylumKey(), lc.getPhylum());
156    }
157    if (lc.getClassKey() != null) {
158      map.put(lc.getClassKey(), lc.getClazz());
159    }
160    if (lc.getOrderKey() != null) {
161      map.put(lc.getOrderKey(), lc.getOrder());
162    }
163    if (lc.getFamilyKey() != null) {
164      map.put(lc.getFamilyKey(), lc.getFamily());
165    }
166    if (lc.getGenusKey() != null) {
167      map.put(lc.getGenusKey(), lc.getGenus());
168    }
169    if (lc.getSpeciesKey() != null) {
170      map.put(lc.getSpeciesKey(), lc.getSpecies());
171    }
172    return map;
173  }
174
175  /**
176   * Gets a higher taxon key by passing the rank of it.
177   * Only Linnean ranks are supported.
178   *
179   * @param lck  the classification that holds the taxon key
180   * @param rank the higher linnean rank to retrieve
181   *
182   * @return the key of the higher taxon or null if rank doesnt exist
183   */
184  @Nullable
185  public static Integer getHigherRankKey(LinneanClassificationKeys lck, Rank rank) {
186    if (rank != null) {
187      switch (rank) {
188        case KINGDOM:
189          return lck.getKingdomKey();
190        case PHYLUM:
191          return lck.getPhylumKey();
192        case CLASS:
193          return lck.getClassKey();
194        case ORDER:
195          return lck.getOrderKey();
196        case FAMILY:
197          return lck.getFamilyKey();
198        case GENUS:
199          return lck.getGenusKey();
200        case SUBGENUS:
201          return lck.getSubgenusKey();
202        case SPECIES:
203          return lck.getSpeciesKey();
204      }
205    }
206    return null;
207  }
208
209  /**
210   * Sets a higher taxon property by passing the rank of it.
211   *
212   * @param lck      the classification on which to set the taxon property
213   * @param rank     the higher rank to set
214   * @param usageKey key of the higher ranks usage
215   */
216  public static void setHigherRankKey(LinneanClassificationKeys lck, Rank rank, Integer usageKey) {
217    if (rank != null) {
218      switch (rank) {
219        case KINGDOM:
220          lck.setKingdomKey(usageKey);
221          break;
222        case PHYLUM:
223          lck.setPhylumKey(usageKey);
224          break;
225        case CLASS:
226          lck.setClassKey(usageKey);
227          break;
228        case ORDER:
229          lck.setOrderKey(usageKey);
230          break;
231        case FAMILY:
232          lck.setFamilyKey(usageKey);
233          break;
234        case GENUS:
235          lck.setGenusKey(usageKey);
236          break;
237        case SUBGENUS:
238          lck.setSubgenusKey(usageKey);
239          break;
240        case SPECIES:
241          lck.setSpeciesKey(usageKey);
242          break;
243      }
244    }
245  }
246
247  /**
248   * Sets a higher taxon property by passing the rank of it.
249   *
250   * @param lc       the object that implements both LinneanClassification and LinneanClassificationKeys on which to set
251   *                 the taxon property
252   * @param rank     the higher linnean rank to set
253   * @param name     the higher ranks name
254   * @param usageKey key of the higher ranks usage
255   */
256  public static <T extends LinneanClassification & LinneanClassificationKeys> void setHigherRank(
257    T lc, Rank rank, String name, Integer usageKey
258  ) {
259    setHigherRank(lc, rank, name);
260    setHigherRankKey(lc, rank, usageKey);
261  }
262
263  /**
264   * @return true if at least one higher rank of the classification is given, i.e. not null or empty
265   */
266  public static boolean hasContent(LinneanClassification lc) {
267    for (Rank qr : Rank.DWC_RANKS) {
268      if (!Strings.isNullOrEmpty(getHigherRank(lc, qr))) {
269        return true;
270      }
271    }
272    return false;
273  }
274
275  /**
276   * Copies all linnean classification based higher taxon keys from one instance to another.
277   */
278  public static void copyLinneanClassificationKeys(LinneanClassificationKeys source, LinneanClassificationKeys target) {
279    for (Rank r : Rank.DWC_RANKS) {
280      setHigherRankKey(target, r, source.getHigherRankKey(r));
281    }
282  }
283
284  /**
285   * Copies all linnean classification based higher taxon names from one instance to another.
286   */
287  public static void copyLinneanClassification(LinneanClassification source, LinneanClassification target) {
288    for (Rank r : Rank.DWC_RANKS) {
289      setHigherRank(target, r, source.getHigherRank(r));
290    }
291  }
292
293  /**
294   * Utility class.
295   */
296  private ClassificationUtils() {
297  }
298}