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.lang.annotation.Annotation;
019import java.util.Arrays;
020import java.util.List;
021import java.util.Map;
022import java.util.regex.Pattern;
023import java.util.stream.Collectors;
024import java.util.stream.Stream;
025
026/**
027 * Utility methods for {@link Term}
028 */
029public class Terms {
030
031  private static final Pattern NULL_PATTERN = Pattern.compile("^\\s*(\\\\N|\\\\?NULL)\\s*$");
032  private static final List<Term> VOCABULARY_BACKED_TERMS = findVocabularyBackedTerms();
033
034  /**
035   * static utility class
036   */
037  private Terms() {
038  }
039
040  /**
041   * Tries various terms in given order until it finds a non empty value.
042   *
043   * @return value or null
044   */
045  public static String getValueOfFirst(Map<Term, String> record, Term... terms) {
046    for (Term t : terms) {
047      if (record.containsKey(t)) {
048        String val = clean(record.get(t));
049        if (val != null) {
050          return val;
051        }
052      }
053    }
054    return null;
055  }
056
057  private static String clean(String str) {
058    if (isTermValueBlank(str)) {
059      return null;
060    }
061    str = str.trim();
062    if (str.isEmpty()) {
063      return null;
064    }
065    return str;
066  }
067
068  /**
069   * Check if the value provided should be considered "blank" in the context of a {@link Term} value.
070   */
071  public static boolean isTermValueBlank(String termValue) {
072    return termValue == null || termValue.isEmpty() || NULL_PATTERN.matcher(termValue).find();
073  }
074
075  /**
076   * Returns all the {@link Term} that are annotated with {@link Vocabulary}.
077   */
078  public static List<Term> getVocabularyBackedTerms() {
079    return VOCABULARY_BACKED_TERMS;
080  }
081
082  private static List<Term> findVocabularyBackedTerms() {
083    return Stream.of(getTerms(AcefTerm.class),
084                     getTerms(AcTerm.class),
085                     getTerms(DcTerm.class),
086                     getTerms(DwcaTerm.class),
087                     getTerms(DwcTerm.class),
088                     getTerms(GadmTerm.class),
089                     getTerms(GbifInternalTerm.class),
090                     getTerms(GbifTerm.class),
091                     getTerms(IucnTerm.class),
092                     getTerms(XmpRightsTerm.class),
093                     getTerms(XmpTerm.class),
094                     getTerms(GbifTerm.class))
095      .flatMap(Arrays::stream)
096      .filter(Terms::isVocabulary)
097      .collect(Collectors.toList());
098  }
099
100  private static <T extends Term> Term[] getTerms(Class<T> term) {
101    return term.getEnumConstants();
102  }
103
104  /**
105   * @return true if the term is a handled/annotated as Vocabulary.
106   */
107  public static boolean isVocabulary(Term term) {
108    return term instanceof Enum && hasTermAnnotation(term, Vocabulary.class);
109  }
110
111  private static boolean hasTermAnnotation(Term term, Class<? extends Annotation> annotation) {
112    try {
113      return term.getClass().getField(((Enum<?>) term).name()).isAnnotationPresent(annotation);
114    } catch (NoSuchFieldException ex) {
115      throw new IllegalArgumentException(ex);
116    }
117  }
118}