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.util;
015
016import javax.annotation.Nullable;
017import javax.validation.constraints.NotNull;
018
019import io.swagger.v3.oas.annotations.media.Schema;
020
021/** Simplified version of guava's {@code Range}. */
022public class Range<T extends Comparable<? super T>> {
023
024  /** Lower bound. Unbound if it is {@code null}. */
025  @Schema(description = "The lower bound.  Unbound if null/unspecified.")
026  private final T from;
027
028  /** Upper bound. Unbound if it is {@code null}. */
029  @Schema(description = "The upper bound. Unbound if null/unspecified.")
030  private final T to;
031
032  /**
033   * Create a range with bounds {@code from} and {@code to}. Use factory method instead.
034   *
035   * @throws IllegalArgumentException if {@code from} is greater than {@code to}
036   */
037  private Range(T from, T to) {
038    if (from != null && to != null && from.compareTo(to) > 0) {
039      throw new IllegalArgumentException(String.format("Invalid range: (%s,%s)", from, to));
040    }
041    this.from = from;
042    this.to = to;
043  }
044
045  /** Factory method. */
046  public static <T extends Comparable<? super T>> Range<T> closed(T from, T to) {
047    return new Range<>(from, to);
048  }
049
050  /** Returns {@code true} if this range has a lower endpoint. */
051  public boolean hasLowerBound() {
052    return from != null;
053  }
054
055  /** Returns {@code true} if this range has an upper endpoint. */
056  public boolean hasUpperBound() {
057    return to != null;
058  }
059
060  /** Returns {@code true} if {@code value} is within the bounds of this range. */
061  public boolean contains(@NotNull T value) {
062    return from.compareTo(value) <= 0 && to.compareTo(value) >= 0;
063  }
064
065  /**
066   * Returns {@code true} if the bounds of {@code other} do not extend outside the bounds of this
067   * range.
068   */
069  public boolean encloses(@NotNull Range<T> other) {
070    return from.compareTo(other.from) <= 0 && to.compareTo(other.to) >= 0;
071  }
072
073  @Nullable
074  public T lowerEndpoint() {
075    return from;
076  }
077
078  @Nullable
079  public T upperEndpoint() {
080    return to;
081  }
082
083  @Override
084  public String toString() {
085    String lower = "*";
086    if (this.lowerEndpoint() != null) {
087      lower = this.lowerEndpoint().toString();
088    }
089
090    String upper = "*";
091    if (this.upperEndpoint() != null) {
092      upper = this.upperEndpoint().toString();
093    }
094
095    return lower + "," + upper;
096  }
097}