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 org.gbif.api.util.VocabularyUtils;
017
018import java.util.Optional;
019
020import javax.annotation.Nullable;
021
022import org.apache.commons.lang3.StringUtils;
023
024import io.swagger.v3.oas.annotations.ExternalDocumentation;
025import io.swagger.v3.oas.annotations.media.Schema;
026
027/**
028 * Enumeration of the set of licenses GBIF supports for applying to a dataset. The license provides a standardised way
029 * to define appropriate uses of a dataset.
030 * </br>
031 * GBIF's recommended best practice is to use the most recent license version, which for CC-BY and CC-BY-NC is 4.0.
032 * This is in line with the recommendation from Creative Commons.
033 * The ordinal number in the Enum implicitly defines the level of restriction, see LicenseTest.
034 *
035 * @see <a href="https://creativecommons.org/faq/#why-should-i-use-the-latest-version-of-the-creative-commons-licenses">Creative
036 * Commons recommendation</a>
037 * @see <a href="http://www.gbif.org/terms/licences">GBIF Licensing</a>
038 */
039@Schema(
040  externalDocs = @ExternalDocumentation(
041    description = "API call to retrieve all official values.",
042    url = "https://api.gbif.org/v1/enumeration/basic/License"
043  )
044)
045public enum License {
046
047  /**
048   * Creative Commons Zero / Public Domain version 1.0. Technically a waiver, not a license.
049   *
050   * @see <a href="http://creativecommons.org/publicdomain/zero/1.0/legalcode">legal document</a>
051   */
052  CC0_1_0("Public Domain (CC0 1.0)", "http://creativecommons.org/publicdomain/zero/1.0/legalcode"),
053  /**
054   * Creative Commons Attribution version 4.0.
055   *
056   * @see <a href="http://creativecommons.org/licenses/by/4.0/legalcode">legal document</a>
057   */
058  CC_BY_4_0("Creative Commons Attribution (CC-BY) 4.0", "http://creativecommons.org/licenses/by/4.0/legalcode"),
059  /**
060   * Creative Commons Attribution-NonCommercial version 4.0.
061   *
062   * @see <a href="http://creativecommons.org/licenses/by-nc/4.0/legalcode">legal document</a>
063   */
064  CC_BY_NC_4_0("Creative Commons Attribution Non Commercial (CC-BY-NC) 4.0", "http://creativecommons.org/licenses/by-nc/4.0/legalcode"),
065  /**
066   * No license has been specified.
067   */
068  UNSPECIFIED(null, null),
069  /**
070   * A license not supported by GBIF.
071   */
072  UNSUPPORTED(null, null);
073
074  private final String licenseTitle;
075  private final String licenseUrl;
076
077  /**
078   * Get an {@link License} from its name as String.
079   *
080   * @return instance of Optional, never null
081   */
082  public static Optional<License> fromString(String license) {
083    return VocabularyUtils.lookup(license, License.class);
084  }
085
086  /**
087   * Lookup a License by either its a) legal code URL or b) human readable summary URL, with HTTP or HTTPS.
088   * For any parsing see LicenseParser in GBIF parsers project.
089   *
090   * @param licenseUrl the case insensitive URL for the license.
091   *
092   * @return instance of Optional, never null
093   */
094  public static Optional<License> fromLicenseUrl(String licenseUrl) {
095    if (StringUtils.isNotEmpty(licenseUrl)) {
096      licenseUrl = licenseUrl.trim().toLowerCase();
097      licenseUrl = StringUtils.removeEnd(licenseUrl, "/");
098      licenseUrl = licenseUrl.replace("https://", "http://");
099
100      // do lookup by legal code URL or human readable summary URL (excluding "/legalcode")
101      for (License license : License.values()) {
102        if (license.getLicenseUrl() != null && (licenseUrl.equals(license.getLicenseUrl())
103                || license.getLicenseUrl().startsWith(licenseUrl))) {
104          return Optional.of(license);
105        }
106      }
107    }
108    return Optional.empty();
109  }
110
111  /**
112   * Get the most restrictive license between the 2 provided licenses.
113   * If one or the two licenses are null or not concrete, this method returns the fallback License.
114   *
115   * @param fallback License to return if one or the two licenses are null or not concrete
116   *
117   * @return the most restrictive License or the fallback License
118   */
119  public static License getMostRestrictive(License license1, License license2, License fallback) {
120    if (license1 == null || license2 == null || !license1.isConcrete() || !license2.isConcrete()) {
121      return fallback;
122    }
123    return (license1.compareTo(license2) > 0) ? license1 : license2;
124  }
125
126  License(@Nullable String licenseTitle, @Nullable String licenseUrl) {
127    this.licenseTitle = licenseTitle;
128    // ensure license information is kept in lowercase internally
129    if (licenseUrl != null) {
130      this.licenseUrl = licenseUrl.toLowerCase();
131    } else {
132      this.licenseUrl = null;
133    }
134  }
135
136  /**
137   * @return the License URL
138   */
139  public String getLicenseUrl() {
140    return licenseUrl;
141  }
142
143  /**
144   * @return the License title
145   */
146  public String getLicenseTitle() {
147    return licenseTitle;
148  }
149
150  /**
151   * Indicates if a license is a concrete license (true) or an abstracted license (false) like
152   * UNSPECIFIED or UNSUPPORTED.
153   * @return the license if concrete or not
154   */
155  public boolean isConcrete() {
156    return licenseUrl != null;
157  }
158}