461 lines
15 KiB
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();
|
|
}
|
|
|
|
}
|