Optimize ActionLog and Paginated util

This commit is contained in:
Luck 2020-05-11 21:00:42 +01:00
parent a1c91b9007
commit 9984d4be42
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
12 changed files with 203 additions and 123 deletions

View File

@ -100,6 +100,7 @@ public interface PlayerAdapter<T> {
default @NonNull CachedPermissionData getPermissionData(@NonNull T player) { default @NonNull CachedPermissionData getPermissionData(@NonNull T player) {
return getUser(player).getCachedData().getPermissionData(getQueryOptions(player)); return getUser(player).getCachedData().getPermissionData(getQueryOptions(player));
} }
/** /**
* Gets the current {@link CachedMetaData} for the {@code player}, * Gets the current {@link CachedMetaData} for the {@code player},
* using their {@link #getQueryOptions(Object) active query options}. * using their {@link #getQueryOptions(Object) active query options}.

View File

@ -0,0 +1,96 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.common.actionlog;
import net.luckperms.api.actionlog.Action;
import java.util.Comparator;
import java.util.Optional;
final class ActionComparator implements Comparator<Action> {
public static final Comparator<Action> INSTANCE = new ActionComparator();
private ActionComparator() {
}
@Override
public int compare(Action o1, Action o2) {
int cmp = o1.getTimestamp().compareTo(o2.getTimestamp());
if (cmp != 0) {
return cmp;
}
Action.Source o1Source = o1.getSource();
Action.Source o2Source = o2.getSource();
cmp = o1Source.getUniqueId().compareTo(o2Source.getUniqueId());
if (cmp != 0) {
return cmp;
}
cmp = o1Source.getName().compareTo(o2Source.getName());
if (cmp != 0) {
return cmp;
}
Action.Target o1Target = o1.getTarget();
Action.Target o2Target = o2.getTarget();
cmp = o1Target.getType().compareTo(o2Target.getType());
if (cmp != 0) {
return cmp;
}
cmp = o1Target.getType().compareTo(o2Target.getType());
if (cmp != 0) {
return cmp;
}
cmp = compareOptionals(o1Target.getUniqueId(), o2Target.getUniqueId());
if (cmp != 0) {
return cmp;
}
cmp = o1Source.getName().compareTo(o2Source.getName());
if (cmp != 0) {
return cmp;
}
return o1.getDescription().compareTo(o2.getDescription());
}
@SuppressWarnings({"OptionalUsedAsFieldOrParameterType", "OptionalIsPresent"})
private static <T extends Comparable<T>> int compareOptionals(Optional<T> a, Optional<T> b) {
if (!a.isPresent()) {
return b.isPresent() ? -1 : 0;
} else if (!b.isPresent()) {
return 1;
} else {
return a.get().compareTo(b.get());
}
}
}

View File

@ -25,36 +25,33 @@
package me.lucko.luckperms.common.actionlog; package me.lucko.luckperms.common.actionlog;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.ImmutableSortedSet;
import me.lucko.luckperms.common.util.ImmutableCollectors; import me.lucko.luckperms.common.util.ImmutableCollectors;
import net.luckperms.api.actionlog.Action; import net.luckperms.api.actionlog.Action;
import java.util.Comparator; import java.util.ArrayList;
import java.util.List;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeSet;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors;
public class Log { public class Log {
private static Log empty = null; private static final Log EMPTY = new Log(ImmutableList.of());
public static Builder builder() { public static Builder builder() {
return new Builder(); return new Builder();
} }
public static synchronized Log empty() { public static Log empty() {
if (empty == null) { return EMPTY;
empty = builder().build();
}
return empty;
} }
private final SortedSet<LoggedAction> content; private final SortedSet<LoggedAction> content;
public Log(SortedSet<LoggedAction> content) { Log(List<LoggedAction> content) {
this.content = ImmutableSortedSet.copyOfSorted(content); this.content = ImmutableSortedSet.copyOf(content);
} }
public SortedSet<LoggedAction> getContent() { public SortedSet<LoggedAction> getContent() {
@ -64,38 +61,38 @@ public class Log {
public SortedSet<LoggedAction> getContent(UUID actor) { public SortedSet<LoggedAction> getContent(UUID actor) {
return this.content.stream() return this.content.stream()
.filter(e -> e.getSource().getUniqueId().equals(actor)) .filter(e -> e.getSource().getUniqueId().equals(actor))
.collect(Collectors.toCollection(TreeSet::new)); .collect(ImmutableCollectors.toSortedSet());
} }
public SortedSet<LoggedAction> getUserHistory(UUID uniqueId) { public SortedSet<LoggedAction> getUserHistory(UUID uniqueId) {
return this.content.stream() return this.content.stream()
.filter(e -> e.getTarget().getType() == Action.Target.Type.USER) .filter(e -> e.getTarget().getType() == Action.Target.Type.USER)
.filter(e -> e.getTarget().getUniqueId().isPresent() && e.getTarget().getUniqueId().get().equals(uniqueId)) .filter(e -> e.getTarget().getUniqueId().isPresent() && e.getTarget().getUniqueId().get().equals(uniqueId))
.collect(ImmutableCollectors.toSortedSet(Comparator.naturalOrder())); .collect(ImmutableCollectors.toSortedSet());
} }
public SortedSet<LoggedAction> getGroupHistory(String name) { public SortedSet<LoggedAction> getGroupHistory(String name) {
return this.content.stream() return this.content.stream()
.filter(e -> e.getTarget().getType() == Action.Target.Type.GROUP) .filter(e -> e.getTarget().getType() == Action.Target.Type.GROUP)
.filter(e -> e.getTarget().getName().equals(name)) .filter(e -> e.getTarget().getName().equals(name))
.collect(ImmutableCollectors.toSortedSet(Comparator.naturalOrder())); .collect(ImmutableCollectors.toSortedSet());
} }
public SortedSet<LoggedAction> getTrackHistory(String name) { public SortedSet<LoggedAction> getTrackHistory(String name) {
return this.content.stream() return this.content.stream()
.filter(e -> e.getTarget().getType() == Action.Target.Type.TRACK) .filter(e -> e.getTarget().getType() == Action.Target.Type.TRACK)
.filter(e -> e.getTarget().getName().equals(name)) .filter(e -> e.getTarget().getName().equals(name))
.collect(ImmutableCollectors.toSortedSet(Comparator.naturalOrder())); .collect(ImmutableCollectors.toSortedSet());
} }
public SortedSet<LoggedAction> getSearch(String query) { public SortedSet<LoggedAction> getSearch(String query) {
return this.content.stream() return this.content.stream()
.filter(e -> e.matchesSearch(query)) .filter(e -> e.matchesSearch(query))
.collect(ImmutableCollectors.toSortedSet(Comparator.naturalOrder())); .collect(ImmutableCollectors.toSortedSet());
} }
public static class Builder { public static class Builder {
private final SortedSet<LoggedAction> content = new TreeSet<>(); private final List<LoggedAction> content = new ArrayList<>();
public Builder add(LoggedAction e) { public Builder add(LoggedAction e) {
this.content.add(e); this.content.add(e);
@ -103,6 +100,9 @@ public class Log {
} }
public Log build() { public Log build() {
if (this.content.isEmpty()) {
return EMPTY;
}
return new Log(this.content); return new Log(this.content);
} }
} }

View File

@ -45,7 +45,6 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
@ -58,15 +57,6 @@ import java.util.UUID;
*/ */
public class LoggedAction implements Action { public class LoggedAction implements Action {
private static final Comparator<Action> COMPARATOR = Comparator
.<Action>comparingLong(a -> a.getTimestamp().getEpochSecond())
.thenComparing(a -> a.getSource().getUniqueId())
.thenComparing(a -> a.getSource().getName(), String.CASE_INSENSITIVE_ORDER)
.thenComparing(a -> a.getTarget().getType())
.thenComparing(e -> e.getTarget().getUniqueId().map(UUID::toString).orElse(""))
.thenComparing(a -> a.getTarget().getName(), String.CASE_INSENSITIVE_ORDER)
.thenComparing(Action::getDescription);
/** /**
* Creates a new log entry builder * Creates a new log entry builder
* *
@ -131,7 +121,7 @@ public class LoggedAction implements Action {
@Override @Override
public int compareTo(@NonNull Action other) { public int compareTo(@NonNull Action other) {
Objects.requireNonNull(other, "other"); Objects.requireNonNull(other, "other");
return COMPARATOR.compare(this, other); return ActionComparator.INSTANCE.compare(this, other);
} }
public boolean matchesSearch(String query) { public boolean matchesSearch(String query) {
@ -171,13 +161,7 @@ public class LoggedAction implements Action {
@Override @Override
public int hashCode() { public int hashCode() {
final int PRIME = 59; return Objects.hash(getTimestamp(), getSource(), getTarget(), getDescription());
int result = 1;
result = result * PRIME + getTimestamp().hashCode();
result = result * PRIME + getSource().hashCode();
result = result * PRIME + getTarget().hashCode();
result = result * PRIME + getDescription().hashCode();
return result;
} }
private static final class SourceImpl implements Source { private static final class SourceImpl implements Source {

View File

@ -43,11 +43,7 @@ import me.lucko.luckperms.common.util.DurationFormatter;
import me.lucko.luckperms.common.util.Paginated; import me.lucko.luckperms.common.util.Paginated;
import me.lucko.luckperms.common.util.Predicates; import me.lucko.luckperms.common.util.Predicates;
import net.luckperms.api.actionlog.Action;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.SortedMap;
public class LogGroupHistory extends ChildCommand<Log> { public class LogGroupHistory extends ChildCommand<Log> {
private static final int ENTRIES_PER_PAGE = 10; private static final int ENTRIES_PER_PAGE = 10;
@ -90,18 +86,18 @@ public class LogGroupHistory extends ChildCommand<Log> {
return CommandResult.INVALID_ARGS; return CommandResult.INVALID_ARGS;
} }
SortedMap<Integer, LoggedAction> entries = log.getPage(page, ENTRIES_PER_PAGE); List<Paginated.Entry<LoggedAction>> entries = log.getPage(page, ENTRIES_PER_PAGE);
String name = ((Action) entries.values().stream().findAny().get()).getTarget().getName(); String name = entries.stream().findAny().get().value().getTarget().getName();
Message.LOG_HISTORY_GROUP_HEADER.send(sender, name, page, maxPage); Message.LOG_HISTORY_GROUP_HEADER.send(sender, name, page, maxPage);
for (Map.Entry<Integer, LoggedAction> e : entries.entrySet()) { for (Paginated.Entry<LoggedAction> e : entries) {
Message.LOG_ENTRY.send(sender, Message.LOG_ENTRY.send(sender,
e.getKey(), e.position(),
DurationFormatter.CONCISE_LOW_ACCURACY.format(e.getValue().getDurationSince()), DurationFormatter.CONCISE_LOW_ACCURACY.format(e.value().getDurationSince()),
e.getValue().getSourceFriendlyString(), e.value().getSourceFriendlyString(),
Character.toString(LoggedAction.getTypeCharacter(((Action) e.getValue()).getTarget().getType())), Character.toString(LoggedAction.getTypeCharacter(e.value().getTarget().getType())),
e.getValue().getTargetFriendlyString(), e.value().getTargetFriendlyString(),
e.getValue().getDescription() e.value().getDescription()
); );
} }

View File

@ -40,11 +40,7 @@ import me.lucko.luckperms.common.util.DurationFormatter;
import me.lucko.luckperms.common.util.Paginated; import me.lucko.luckperms.common.util.Paginated;
import me.lucko.luckperms.common.util.Predicates; import me.lucko.luckperms.common.util.Predicates;
import net.luckperms.api.actionlog.Action;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.UUID; import java.util.UUID;
public class LogRecent extends ChildCommand<Log> { public class LogRecent extends ChildCommand<Log> {
@ -95,9 +91,9 @@ public class LogRecent extends ChildCommand<Log> {
return CommandResult.INVALID_ARGS; return CommandResult.INVALID_ARGS;
} }
SortedMap<Integer, LoggedAction> entries = log.getPage(page, ENTRIES_PER_PAGE); List<Paginated.Entry<LoggedAction>> entries = log.getPage(page, ENTRIES_PER_PAGE);
if (specificUser) { if (specificUser) {
String name = ((Action) entries.values().stream().findAny().get()).getSource().getName(); String name = entries.stream().findAny().get().value().getSource().getName();
if (name.contains("@")) { if (name.contains("@")) {
name = name.split("@")[0]; name = name.split("@")[0];
} }
@ -106,14 +102,14 @@ public class LogRecent extends ChildCommand<Log> {
Message.LOG_RECENT_HEADER.send(sender, page, maxPage); Message.LOG_RECENT_HEADER.send(sender, page, maxPage);
} }
for (Map.Entry<Integer, LoggedAction> e : entries.entrySet()) { for (Paginated.Entry<LoggedAction> e : entries) {
Message.LOG_ENTRY.send(sender, Message.LOG_ENTRY.send(sender,
e.getKey(), e.position(),
DurationFormatter.CONCISE_LOW_ACCURACY.format(e.getValue().getDurationSince()), DurationFormatter.CONCISE_LOW_ACCURACY.format(e.value().getDurationSince()),
e.getValue().getSourceFriendlyString(), e.value().getSourceFriendlyString(),
Character.toString(LoggedAction.getTypeCharacter(((Action) e.getValue()).getTarget().getType())), Character.toString(LoggedAction.getTypeCharacter(e.value().getTarget().getType())),
e.getValue().getTargetFriendlyString(), e.value().getTargetFriendlyString(),
e.getValue().getDescription() e.value().getDescription()
); );
} }
return CommandResult.SUCCESS; return CommandResult.SUCCESS;

View File

@ -39,11 +39,7 @@ import me.lucko.luckperms.common.util.DurationFormatter;
import me.lucko.luckperms.common.util.Paginated; import me.lucko.luckperms.common.util.Paginated;
import me.lucko.luckperms.common.util.Predicates; import me.lucko.luckperms.common.util.Predicates;
import net.luckperms.api.actionlog.Action;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.SortedMap;
public class LogSearch extends ChildCommand<Log> { public class LogSearch extends ChildCommand<Log> {
private static final int ENTRIES_PER_PAGE = 10; private static final int ENTRIES_PER_PAGE = 10;
@ -90,17 +86,17 @@ public class LogSearch extends ChildCommand<Log> {
return CommandResult.INVALID_ARGS; return CommandResult.INVALID_ARGS;
} }
SortedMap<Integer, LoggedAction> entries = log.getPage(page, ENTRIES_PER_PAGE); List<Paginated.Entry<LoggedAction>> entries = log.getPage(page, ENTRIES_PER_PAGE);
Message.LOG_SEARCH_HEADER.send(sender, query, page, maxPage); Message.LOG_SEARCH_HEADER.send(sender, query, page, maxPage);
for (Map.Entry<Integer, LoggedAction> e : entries.entrySet()) { for (Paginated.Entry<LoggedAction> e : entries) {
Message.LOG_ENTRY.send(sender, Message.LOG_ENTRY.send(sender,
e.getKey(), e.position(),
DurationFormatter.CONCISE_LOW_ACCURACY.format(e.getValue().getDurationSince()), DurationFormatter.CONCISE_LOW_ACCURACY.format(e.value().getDurationSince()),
e.getValue().getSourceFriendlyString(), e.value().getSourceFriendlyString(),
Character.toString(LoggedAction.getTypeCharacter(((Action) e.getValue()).getTarget().getType())), Character.toString(LoggedAction.getTypeCharacter(e.value().getTarget().getType())),
e.getValue().getTargetFriendlyString(), e.value().getTargetFriendlyString(),
e.getValue().getDescription() e.value().getDescription()
); );
} }

View File

@ -43,11 +43,7 @@ import me.lucko.luckperms.common.util.DurationFormatter;
import me.lucko.luckperms.common.util.Paginated; import me.lucko.luckperms.common.util.Paginated;
import me.lucko.luckperms.common.util.Predicates; import me.lucko.luckperms.common.util.Predicates;
import net.luckperms.api.actionlog.Action;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.SortedMap;
public class LogTrackHistory extends ChildCommand<Log> { public class LogTrackHistory extends ChildCommand<Log> {
private static final int ENTRIES_PER_PAGE = 10; private static final int ENTRIES_PER_PAGE = 10;
@ -90,18 +86,18 @@ public class LogTrackHistory extends ChildCommand<Log> {
return CommandResult.INVALID_ARGS; return CommandResult.INVALID_ARGS;
} }
SortedMap<Integer, LoggedAction> entries = log.getPage(page, ENTRIES_PER_PAGE); List<Paginated.Entry<LoggedAction>> entries = log.getPage(page, ENTRIES_PER_PAGE);
String name = ((Action) entries.values().stream().findAny().get()).getTarget().getName(); String name = entries.stream().findAny().get().value().getTarget().getName();
Message.LOG_HISTORY_TRACK_HEADER.send(sender, name, page, maxPage); Message.LOG_HISTORY_TRACK_HEADER.send(sender, name, page, maxPage);
for (Map.Entry<Integer, LoggedAction> e : entries.entrySet()) { for (Paginated.Entry<LoggedAction> e : entries) {
Message.LOG_ENTRY.send(sender, Message.LOG_ENTRY.send(sender,
e.getKey(), e.position(),
DurationFormatter.CONCISE_LOW_ACCURACY.format(e.getValue().getDurationSince()), DurationFormatter.CONCISE_LOW_ACCURACY.format(e.value().getDurationSince()),
e.getValue().getSourceFriendlyString(), e.value().getSourceFriendlyString(),
Character.toString(LoggedAction.getTypeCharacter(((Action) e.getValue()).getTarget().getType())), Character.toString(LoggedAction.getTypeCharacter(e.value().getTarget().getType())),
e.getValue().getTargetFriendlyString(), e.value().getTargetFriendlyString(),
e.getValue().getDescription() e.value().getDescription()
); );
} }

View File

@ -40,11 +40,7 @@ import me.lucko.luckperms.common.util.DurationFormatter;
import me.lucko.luckperms.common.util.Paginated; import me.lucko.luckperms.common.util.Paginated;
import me.lucko.luckperms.common.util.Predicates; import me.lucko.luckperms.common.util.Predicates;
import net.luckperms.api.actionlog.Action;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.UUID; import java.util.UUID;
public class LogUserHistory extends ChildCommand<Log> { public class LogUserHistory extends ChildCommand<Log> {
@ -83,18 +79,18 @@ public class LogUserHistory extends ChildCommand<Log> {
return CommandResult.INVALID_ARGS; return CommandResult.INVALID_ARGS;
} }
SortedMap<Integer, LoggedAction> entries = log.getPage(page, ENTRIES_PER_PAGE); List<Paginated.Entry<LoggedAction>> entries = log.getPage(page, ENTRIES_PER_PAGE);
String name = ((Action) entries.values().stream().findAny().get()).getTarget().getName(); String name = entries.stream().findAny().get().value().getTarget().getName();
Message.LOG_HISTORY_USER_HEADER.send(sender, name, page, maxPage); Message.LOG_HISTORY_USER_HEADER.send(sender, name, page, maxPage);
for (Map.Entry<Integer, LoggedAction> e : entries.entrySet()) { for (Paginated.Entry<LoggedAction> e : entries) {
Message.LOG_ENTRY.send(sender, Message.LOG_ENTRY.send(sender,
e.getKey(), e.position(),
DurationFormatter.CONCISE_LOW_ACCURACY.format(e.getValue().getDurationSince()), DurationFormatter.CONCISE_LOW_ACCURACY.format(e.value().getDurationSince()),
e.getValue().getSourceFriendlyString(), e.value().getSourceFriendlyString(),
Character.toString(LoggedAction.getTypeCharacter(((Action) e.getValue()).getTarget().getType())), Character.toString(LoggedAction.getTypeCharacter(e.value().getTarget().getType())),
e.getValue().getTargetFriendlyString(), e.value().getTargetFriendlyString(),
e.getValue().getDescription() e.value().getDescription()
); );
} }

View File

@ -45,7 +45,7 @@ import java.util.stream.Collectors;
public interface Sender { public interface Sender {
/** The uuid used by the console sender. */ /** The uuid used by the console sender. */
UUID CONSOLE_UUID = UUID.fromString("00000000-0000-0000-0000-000000000000"); UUID CONSOLE_UUID = new UUID(0, 0); // 00000000-0000-0000-0000-000000000000
/** The name used by the console sender. */ /** The name used by the console sender. */
String CONSOLE_NAME = "Console"; String CONSOLE_NAME = "Console";

View File

@ -72,6 +72,15 @@ public final class ImmutableCollectors {
); );
} }
public static <E extends Comparable<? super E>> Collector<E, ?, ImmutableSortedSet<E>> toSortedSet() {
return Collector.of(
ImmutableSortedSet::<E>naturalOrder,
ImmutableSortedSet.Builder::add,
(l, r) -> l.addAll(r.build()),
ImmutableSortedSet.Builder::build
);
}
public static <E> Collector<E, ?, ImmutableSortedSet<E>> toSortedSet(Comparator<? super E> comparator) { public static <E> Collector<E, ?, ImmutableSortedSet<E>> toSortedSet(Comparator<? super E> comparator) {
return Collector.of( return Collector.of(
() -> new ImmutableSortedSet.Builder<E>(comparator), () -> new ImmutableSortedSet.Builder<E>(comparator),

View File

@ -27,11 +27,9 @@ package me.lucko.luckperms.common.util;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.stream.Stream;
/** /**
* A simple pagination utility * A simple pagination utility
@ -45,10 +43,6 @@ public class Paginated<T> {
this.content = ImmutableList.copyOf(content); this.content = ImmutableList.copyOf(content);
} }
public Paginated(Stream<T> content) {
this.content = content.collect(ImmutableCollectors.toList());
}
public List<T> getContent() { public List<T> getContent() {
return this.content; return this.content;
} }
@ -57,32 +51,48 @@ public class Paginated<T> {
return (int) Math.ceil((double) this.content.size() / (double) entriesPerPage); return (int) Math.ceil((double) this.content.size() / (double) entriesPerPage);
} }
public SortedMap<Integer, T> getPage(int pageNo, int entries) { public List<Entry<T>> getPage(int pageNo, int pageSize) {
if (pageNo < 1) { if (pageNo < 1) {
throw new IllegalArgumentException("pageNo cannot be less than 1: " + pageNo); throw new IllegalArgumentException("pageNo cannot be less than 1: " + pageNo);
} }
int minimumEntries = ((pageNo * entries) - entries) + 1; int first = ((pageNo - 1) * pageSize);
if (this.content.size() < minimumEntries) { if (this.content.size() <= first) {
throw new IllegalStateException("Content does not contain that many elements. " + throw new IllegalStateException("Content does not contain that many elements. (requested page: " + pageNo +
"Requested: " + minimumEntries + ", Size: " + this.content.size()); ", page size: " + pageSize + ", page first index: " + first + ", content size: " + this.content.size() + ")");
} }
final SortedMap<Integer, T> out = new TreeMap<>(); int last = first + pageSize - 1;
List<Entry<T>> out = new ArrayList<>(pageSize);
final int max = minimumEntries + entries - 1; for (int i = first; i <= last && i < this.content.size(); i++) {
int index = 0; out.add(new Entry<>(i + 1, this.content.get(i)));
for (T e : this.content) {
index++;
if (index >= minimumEntries) {
out.put(index, e);
}
if (index == max) {
break;
}
} }
return out; return out;
} }
public static final class Entry<T> {
private final int position;
private final T value;
public Entry(int position, T value) {
this.position = position;
this.value = value;
}
public int position() {
return this.position;
}
public T value() {
return this.value;
}
@Override
public String toString() {
return this.position + ": " + this.value;
}
}
} }