1 /*
2 * Copyright 2008-2020 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.springframework.data.domain;
17
18 import java.util.List;
19 import java.util.function.Function;
20
21 import org.springframework.lang.Nullable;
22
23 /**
24 * Basic {@code Page} implementation.
25 *
26 * @param <T> the type of which the page consists.
27 * @author Oliver Gierke
28 * @author Mark Paluch
29 */
30 public class PageImpl<T> extends Chunk<T> implements Page<T> {
31
32 private static final long serialVersionUID = 867755909294344406L;
33
34 private final long total;
35
36 /**
37 * Constructor of {@code PageImpl}.
38 *
39 * @param content the content of this page, must not be {@literal null}.
40 * @param pageable the paging information, must not be {@literal null}.
41 * @param total the total amount of items available. The total might be adapted considering the length of the content
42 * given, if it is going to be the content of the last page. This is in place to mitigate inconsistencies.
43 */
44 public PageImpl(List<T> content, Pageable pageable, long total) {
45
46 super(content, pageable);
47
48 this.total = pageable.toOptional().filter(it -> !content.isEmpty())//
49 .filter(it -> it.getOffset() + it.getPageSize() > total)//
50 .map(it -> it.getOffset() + content.size())//
51 .orElse(total);
52 }
53
54 /**
55 * Creates a new {@link PageImpl} with the given content. This will result in the created {@link Page} being identical
56 * to the entire {@link List}.
57 *
58 * @param content must not be {@literal null}.
59 */
60 public PageImpl(List<T> content) {
61 this(content, Pageable.unpaged(), null == content ? 0 : content.size());
62 }
63
64 /*
65 * (non-Javadoc)
66 * @see org.springframework.data.domain.Page#getTotalPages()
67 */
68 @Override
69 public int getTotalPages() {
70 return getSize() == 0 ? 1 : (int) Math.ceil((double) total / (double) getSize());
71 }
72
73 /*
74 * (non-Javadoc)
75 * @see org.springframework.data.domain.Page#getTotalElements()
76 */
77 @Override
78 public long getTotalElements() {
79 return total;
80 }
81
82 /*
83 * (non-Javadoc)
84 * @see org.springframework.data.domain.Slice#hasNext()
85 */
86 @Override
87 public boolean hasNext() {
88 return getNumber() + 1 < getTotalPages();
89 }
90
91 /*
92 * (non-Javadoc)
93 * @see org.springframework.data.domain.Slice#isLast()
94 */
95 @Override
96 public boolean isLast() {
97 return !hasNext();
98 }
99
100 /*
101 * (non-Javadoc)
102 * @see org.springframework.data.domain.Slice#transform(org.springframework.core.convert.converter.Converter)
103 */
104 @Override
105 public <U> Page<U> map(Function<? super T, ? extends U> converter) {
106 return new PageImpl<>(getConvertedContent(converter), getPageable(), total);
107 }
108
109 /*
110 * (non-Javadoc)
111 * @see java.lang.Object#toString()
112 */
113 @Override
114 public String toString() {
115
116 String contentType = "UNKNOWN";
117 List<T> content = getContent();
118
119 if (!content.isEmpty() && content.get(0) != null) {
120 contentType = content.get(0).getClass().getName();
121 }
122
123 return String.format("Page %s of %d containing %s instances", getNumber() + 1, getTotalPages(), contentType);
124 }
125
126 /*
127 * (non-Javadoc)
128 * @see java.lang.Object#equals(java.lang.Object)
129 */
130 @Override
131 public boolean equals(@Nullable Object obj) {
132
133 if (this == obj) {
134 return true;
135 }
136
137 if (!(obj instanceof PageImpl<?>)) {
138 return false;
139 }
140
141 PageImpl<?> that = (PageImpl<?>) obj;
142
143 return this.total == that.total && super.equals(obj);
144 }
145
146 /*
147 * (non-Javadoc)
148 * @see java.lang.Object#hashCode()
149 */
150 @Override
151 public int hashCode() {
152
153 int result = 17;
154
155 result += 31 * (int) (total ^ total >>> 32);
156 result += 31 * super.hashCode();
157
158 return result;
159 }
160 }
161