PlotSquared/Core/src/main/java/com/plotsquared/core/util/query/PlotQuery.java

461 lines
15 KiB
Java

/*
* PlotSquared, a land and world management plugin for Minecraft.
* Copyright (C) IntellectualSites <https://intellectualsites.com>
* Copyright (C) IntellectualSites team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.plotsquared.core.util.query;
import com.google.common.base.Preconditions;
import com.plotsquared.core.PlotSquared;
import com.plotsquared.core.player.PlotPlayer;
import com.plotsquared.core.plot.Plot;
import com.plotsquared.core.plot.PlotArea;
import com.plotsquared.core.plot.Rating;
import com.plotsquared.core.plot.flag.implementations.DoneFlag;
import com.plotsquared.core.plot.world.PlotAreaManager;
import com.plotsquared.core.util.MathMan;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* This represents a plot query, and can be used to
* search for plots matching certain criteria.
* <p>
* The queries can be reused as no results are stored
* in the query itself
*/
public final class PlotQuery implements Iterable<Plot> {
private final Collection<PlotFilter> filters = new LinkedList<>();
private final PlotAreaManager plotAreaManager;
private PlotProvider plotProvider;
private SortingStrategy sortingStrategy = SortingStrategy.NO_SORTING;
private PlotArea priorityArea;
private Comparator<Plot> plotComparator;
private PlotQuery(final @NonNull PlotAreaManager plotAreaManager) {
this.plotAreaManager = plotAreaManager;
this.plotProvider = new GlobalPlotProvider(plotAreaManager);
}
/**
* Create a new plot query instance
*
* @return New query
*/
public static PlotQuery newQuery() {
return new PlotQuery(PlotSquared.get().getPlotAreaManager());
}
/**
* Query for plots in a single area
*
* @param area Area
* @return The query instance
*/
public @NonNull PlotQuery inArea(final @NonNull PlotArea area) {
Preconditions.checkNotNull(area, "Area may not be null");
this.plotProvider = new AreaLimitedPlotProvider(Collections.singletonList(area));
return this;
}
/**
* Query for plots in all areas in a world
*
* @param world World name
* @return The query instance
*/
public @NonNull PlotQuery inWorld(final @NonNull String world) {
Preconditions.checkNotNull(world, "World may not be null");
this.plotProvider = new AreaLimitedPlotProvider(this.plotAreaManager.getPlotAreasSet(world));
return this;
}
/**
* Query for plots in specific areas
*
* @param areas Plot areas
* @return The query instance
*/
public @NonNull PlotQuery inAreas(final @NonNull Collection<PlotArea> areas) {
Preconditions.checkNotNull(areas, "Areas may not be null");
Preconditions.checkState(!areas.isEmpty(), "At least one area must be provided");
this.plotProvider = new AreaLimitedPlotProvider(Collections.unmodifiableCollection(areas));
return this;
}
/**
* Query for expired plots
*
* @return The query instance
*/
public @NonNull PlotQuery expiredPlots() {
this.plotProvider = new ExpiredPlotProvider();
return this;
}
/**
* Query for all plots
*
* @return The query instance
*/
public @NonNull PlotQuery allPlots() {
this.plotProvider = new GlobalPlotProvider(this.plotAreaManager);
return this;
}
/**
* Don't query at all
*
* @return The query instance
*/
public @NonNull PlotQuery noPlots() {
this.plotProvider = new NullProvider();
return this;
}
/**
* Query for plots based on a search term
*
* @param searchTerm search term to use (uuid, plotID, username)
* @return The query instance
*/
public @NonNull PlotQuery plotsBySearch(final @NonNull String searchTerm) {
Preconditions.checkNotNull(searchTerm, "Search term may not be null");
this.plotProvider = new SearchPlotProvider(searchTerm);
return this;
}
/**
* Query with a pre-defined result
*
* @param plot to return when Query is searched
* @return The query instance
*/
public @NonNull PlotQuery withPlot(final @NonNull Plot plot) {
Preconditions.checkNotNull(plot, "Plot may not be null");
this.plotProvider = new FixedPlotProvider(plot);
return this;
}
/**
* Query for base plots only
*
* @return The query instance
*/
public @NonNull PlotQuery whereBasePlot() {
return this.addFilter(new PredicateFilter(Plot::isBasePlot));
}
/**
* Query for plots owned by a specific player
*
* @param owner Owner UUID
* @return The query instance
*/
public @NonNull PlotQuery ownedBy(final @NonNull UUID owner) {
Preconditions.checkNotNull(owner, "Owner may not be null");
return this.addFilter(new OwnerFilter(owner));
}
/**
* Query for plots owned by a specific player
*
* @param owner Owner
* @return The query instance
*/
public @NonNull PlotQuery ownedBy(final @NonNull PlotPlayer<?> owner) {
Preconditions.checkNotNull(owner, "Owner may not be null");
return this.addFilter(new OwnerFilter(owner.getUUID()));
}
/**
* Query for base plots where one of the merged plots is owned by a specific player
*
* @param owner Owner UUID
* @return The query instance
* @since 6.1.0
*/
public @NonNull PlotQuery ownersInclude(final @NonNull UUID owner) {
Preconditions.checkNotNull(owner, "Owner may not be null");
return this.addFilter(new OwnersIncludeFilter(owner));
}
/**
* Query for base plots where one of the merged plots is owned by a specific player
*
* @param owner Owner
* @return The query instance
* @since 6.1.0
*/
public @NonNull PlotQuery ownersInclude(final @NonNull PlotPlayer<?> owner) {
Preconditions.checkNotNull(owner, "Owner may not be null");
return this.addFilter(new OwnersIncludeFilter(owner.getUUID()));
}
/**
* Query only for plots that have an owner
*
* @return The query instance
* @since 7.2.1
*/
public @NonNull PlotQuery hasOwner() {
return this.addFilter(new HasOwnerFilter());
}
/**
* Query for plots with a specific alias
*
* @param alias Plot alias
* @return The query instance
*/
public @NonNull PlotQuery withAlias(final @NonNull String alias) {
Preconditions.checkNotNull(alias, "Alias may not be null");
return this.addFilter(new AliasFilter(alias));
}
/**
* Query for plots with a specific member (added/trusted/owner)
*
* @param member Member UUID
* @return The query instance
*/
public @NonNull PlotQuery withMember(final @NonNull UUID member) {
Preconditions.checkNotNull(member, "Member may not be null");
return this.addFilter(new MemberFilter(member));
}
/**
* Query for plots that passes a given predicate
*
* @param predicate Predicate
* @return The query instance
*/
public @NonNull PlotQuery thatPasses(final @NonNull Predicate<Plot> predicate) {
Preconditions.checkNotNull(predicate, "Predicate may not be null");
return this.addFilter(new PredicateFilter(predicate));
}
/**
* Specify the sorting strategy that will decide how to
* sort the results. This only matters if you use {@link #asList()}
*
* @param strategy Strategy
* @return The query instance
*/
public @NonNull PlotQuery withSortingStrategy(final @NonNull SortingStrategy strategy) {
Preconditions.checkNotNull(strategy, "Strategy may not be null");
this.sortingStrategy = strategy;
return this;
}
/**
* Use a custom comparator to sort the results
*
* @param comparator Comparator
* @return The query instance
*/
public @NonNull PlotQuery sorted(final @NonNull Comparator<Plot> comparator) {
Preconditions.checkNotNull(comparator, "Comparator may not be null");
this.sortingStrategy = SortingStrategy.COMPARATOR;
this.plotComparator = comparator;
return this;
}
/**
* Defines the area around which plots may be sorted, depending on the
* sorting strategy
*
* @param plotArea Plot area
* @return The query instance
*/
public @NonNull PlotQuery relativeToArea(final @NonNull PlotArea plotArea) {
Preconditions.checkNotNull(plotArea, "Area may not be null");
this.priorityArea = plotArea;
return this;
}
/**
* Get all plots that match the given criteria
*
* @return Matching plots
*/
public @NonNull Stream<Plot> asStream() {
return this.asList().stream();
}
/**
* Get all plots that match the given criteria
*
* @return Matching plots as a mutable
*/
public @NonNull List<Plot> asList() {
final List<Plot> result;
if (this.filters.isEmpty()) {
result = new ArrayList<>(this.plotProvider.getPlots());
} else {
final Collection<Plot> plots = this.plotProvider.getPlots();
result = new ArrayList<>(plots.size());
outer:
for (final Plot plot : plots) {
for (final PlotFilter filter : this.filters) {
if (!filter.accepts(plot)) {
continue outer;
}
}
result.add(plot);
}
}
if (this.sortingStrategy == SortingStrategy.NO_SORTING) {
return result;
} else if (this.sortingStrategy == SortingStrategy.SORT_BY_TEMP) {
return PlotSquared.get().sortPlotsByTemp(result);
} else if (this.sortingStrategy == SortingStrategy.SORT_BY_DONE) {
result.sort((a, b) -> {
String va = a.getFlag(DoneFlag.class);
String vb = b.getFlag(DoneFlag.class);
if (MathMan.isInteger(va)) {
if (MathMan.isInteger(vb)) {
return Integer.parseInt(vb) - Integer.parseInt(va);
}
return -1;
}
return 1;
});
} else if (this.sortingStrategy == SortingStrategy.SORT_BY_RATING) {
result.sort((p1, p2) -> {
double v1 = 0;
int p1s = p1.getSettings().getRatings().size();
int p2s = p2.getRatings().size();
if (!p1.getSettings().getRatings().isEmpty()) {
v1 = p1.getRatings().values().stream().mapToDouble(Rating::getAverageRating)
.map(av -> av * av).sum();
v1 /= p1s;
v1 += p1s;
}
double v2 = 0;
if (!p2.getSettings().getRatings().isEmpty()) {
for (Map.Entry<UUID, Rating> entry : p2.getRatings().entrySet()) {
double av = entry.getValue().getAverageRating();
v2 += av * av;
}
v2 /= p2s;
v2 += p2s;
}
if (v2 == v1 && v2 != 0) {
return p2s - p1s;
}
return (int) Math.signum(v2 - v1);
});
} else if (this.sortingStrategy == SortingStrategy.SORT_BY_CREATION) {
return PlotSquared.get().sortPlots(result, PlotSquared.SortType.CREATION_DATE, this.priorityArea);
} else if (this.sortingStrategy == SortingStrategy.COMPARATOR) {
result.sort(this.plotComparator);
}
return result;
}
/**
* Get all plots that match the given criteria
*
* @return Matching plots as a mutable set
*/
public @NonNull Set<Plot> asSet() {
return new HashSet<>(this.asList());
}
/**
* Get all plots that match the given criteria
* in the form of a {@link PaginatedPlotResult}
*
* @param pageSize The size of the pages. Must be positive.
* @return Paginated plot result
*/
public @NonNull PaginatedPlotResult getPaginated(final int pageSize) {
Preconditions.checkState(pageSize > 0, "Page size must be greater than 0");
return new PaginatedPlotResult(this.asList(), pageSize);
}
/**
* Get all plots that match the given criteria
*
* @return Matching plots as an immutable collection
*/
public @NonNull Collection<Plot> asCollection() {
return this.asList();
}
/**
* Get the amount of plots contained in the query result
*
* @return Result count
*/
public int count() {
return this.asList().size();
}
/**
* Get whether any provided plot matches the given filters.
* If no plot was provided, false will be returned.
*
* @return {@code true} if any provided plot matches the filters.
*/
public boolean anyMatch() {
if (this.filters.isEmpty()) {
return !this.plotProvider.getPlots().isEmpty();
} else {
final Collection<Plot> plots = this.plotProvider.getPlots();
outer:
for (final Plot plot : plots) {
// a plot must pass all filters to match the criteria
for (final PlotFilter filter : this.filters) {
if (!filter.accepts(plot)) {
continue outer;
}
}
return true; // a plot passed all filters, so we have a match
}
return false;
}
}
@NonNull
private PlotQuery addFilter(final @NonNull PlotFilter filter) {
this.filters.add(filter);
return this;
}
@NonNull
@Override
public Iterator<Plot> iterator() {
return this.asCollection().iterator();
}
}