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.model.common.paging;
015
016import java.util.ArrayList;
017import java.util.List;
018import java.util.Objects;
019import java.util.StringJoiner;
020
021import javax.annotation.Nullable;
022
023import io.swagger.v3.oas.annotations.media.Schema;
024
025/**
026 * Paging response bean.
027 */
028public class PagingResponse<T> extends PageableBase {
029
030  @Schema(
031    description = "True if this page of search results is the final page."
032  )
033  private Boolean endOfRecords;
034
035  @Schema(
036    description = "The total number of records returned by the search."
037  )
038  private Long count;
039
040  @Schema(
041    description = "Search results."
042  )
043  private List<T> results = new ArrayList<>();
044
045  /**
046   * Default constructor with default paging values.
047   */
048  public PagingResponse() {
049  }
050
051  public PagingResponse(Pageable page) {
052    super(page);
053  }
054
055  public PagingResponse(Pageable page, Long count) {
056    super(page);
057    this.count = count;
058  }
059
060  public PagingResponse(long offset, int limit) {
061    super(offset, limit);
062  }
063
064  public PagingResponse(Pageable page, Long count, List<T> results) {
065    super(page);
066    this.results = results;
067    this.count = count;
068  }
069
070  public PagingResponse(long offset, int limit, Long count) {
071    super(offset, limit);
072    this.count = count;
073  }
074
075  public PagingResponse(long offset, int limit, Long count, List<T> results) {
076    super(offset, limit);
077    this.results = results;
078    this.count = count;
079  }
080
081  /**
082   * Gets the count of total results of search operation.
083   */
084  @Nullable
085  public Long getCount() {
086    return count;
087  }
088
089  /**
090   * Sets the total count for all results, not only the ones in this response page.
091   * The method will not set endOfRecords automatically.
092   */
093  public void setCount(Long count) {
094    this.count = count;
095  }
096
097  /**
098   * Gets the list of results.
099   * The type of element of the result are defined by the parameter class type T.
100   *
101   * @return the results list.
102   */
103  public List<T> getResults() {
104    return results;
105  }
106
107  /**
108   * Sets the list of results for the response.
109   * This method will not modify the endOfRecords flag.
110   */
111  public void setResults(List<T> results) {
112    Objects.requireNonNull(results, "results can't be null");
113    this.results = results;
114  }
115
116  /**
117   * Flag indicating whether more records do exist.
118   * If the property has never been manually initialised, the flag is determined automatically.
119   * If the total count is set it is used to determine the return value.
120   * If only the result is given, we consider a result size equal to limit as an indication that there are (potentially
121   * at least) more results.
122   *
123   * @return true if all records have been returned otherwise null.
124   */
125  public boolean isEndOfRecords() {
126    if (endOfRecords == null) {
127      // automatically determine end
128      if (count != null) {
129        return count <= offset + limit;
130      } else if (results != null) {
131        return results.size() < limit;
132      } else {
133        // what should this be if we dont have any content?
134        return false;
135      }
136    }
137    return endOfRecords;
138  }
139
140  /**
141   * Manually sets the end of records flag.
142   * Setting the flag to true or false deactivates the automatic calculation in #isEndOfRecords().
143   */
144  public void setEndOfRecords(boolean endOfRecords) {
145    this.endOfRecords = endOfRecords;
146  }
147
148  @Override
149  public boolean equals(Object o) {
150    if (this == o) {
151      return true;
152    }
153    if (o == null || getClass() != o.getClass()) {
154      return false;
155    }
156    if (!super.equals(o)) {
157      return false;
158    }
159    PagingResponse<?> that = (PagingResponse<?>) o;
160    return Objects.equals(endOfRecords, that.endOfRecords) &&
161      Objects.equals(count, that.count) &&
162      Objects.equals(results, that.results);
163  }
164
165  @Override
166  public int hashCode() {
167    return Objects.hash(super.hashCode(), endOfRecords, count, results);
168  }
169
170  @Override
171  public String toString() {
172    return new StringJoiner(", ", PagingResponse.class.getSimpleName() + "[", "]")
173      .add("count=" + count)
174      .add("results=" + results)
175      .add("offset=" + offset)
176      .add("limit=" + limit)
177      .toString();
178  }
179}