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

434 lines
14 KiB
Java
Raw Normal View History

2020-05-23 22:20:57 +02:00
/*
* _____ _ _ _____ _
* | __ \| | | | / ____| | |
* | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| |
* | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` |
* | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| |
* |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_|
* | |
* |_|
* PlotSquared plot management system for Minecraft
* Copyright (C) 2020 IntellectualSites
*
* 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 <http://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;
2020-07-10 17:32:07 +02:00
import com.plotsquared.core.plot.world.PlotAreaManager;
import com.plotsquared.core.util.MathMan;
2020-07-14 18:49:40 +02:00
import javax.annotation.Nonnull;
2020-05-23 22:20:57 +02:00
import java.util.ArrayList;
2020-05-23 22:20:57 +02:00
import java.util.Collection;
import java.util.Collections;
2020-05-24 00:27:38 +02:00
import java.util.Comparator;
import java.util.HashSet;
2020-05-23 22:20:57 +02:00
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
2020-05-23 22:20:57 +02:00
import java.util.Set;
import java.util.UUID;
2020-07-08 15:09:25 +02:00
import java.util.function.Consumer;
2020-05-23 22:20:57 +02:00
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 {
private final Collection<PlotFilter> filters = new LinkedList<>();
2020-07-10 17:32:07 +02:00
private final PlotAreaManager plotAreaManager;
private PlotProvider plotProvider;
private SortingStrategy sortingStrategy = SortingStrategy.NO_SORTING;
private PlotArea priorityArea;
2020-05-24 00:27:38 +02:00
private Comparator<Plot> plotComparator;
2020-05-23 22:20:57 +02:00
2020-07-14 18:49:40 +02:00
private PlotQuery(@Nonnull final PlotAreaManager plotAreaManager) {
2020-07-10 17:32:07 +02:00
this.plotAreaManager = plotAreaManager;
this.plotProvider = new GlobalPlotProvider(plotAreaManager);
2020-05-23 22:20:57 +02:00
}
/**
* Create a new plot query instance
*
* @return New query
*/
public static PlotQuery newQuery() {
2020-07-10 17:32:07 +02:00
return new PlotQuery(PlotSquared.get().getPlotAreaManager());
2020-05-23 22:20:57 +02:00
}
/**
* Query for plots in a single area
*
* @param area Area
* @return The query instance
*/
2020-07-14 18:49:40 +02:00
@Nonnull public PlotQuery inArea(@Nonnull final PlotArea area) {
2020-05-23 22:20:57 +02:00
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
*/
2020-07-14 18:49:40 +02:00
@Nonnull public PlotQuery inWorld(@Nonnull final String world) {
2020-05-23 22:20:57 +02:00
Preconditions.checkNotNull(world, "World may not be null");
2020-07-10 17:32:07 +02:00
this.plotProvider = new AreaLimitedPlotProvider(this.plotAreaManager.getPlotAreasSet(world));
2020-05-23 22:20:57 +02:00
return this;
}
/**
* Query for plots in specific areas
*
* @param areas Plot areas
* @return The query instance
*/
2020-07-14 18:49:40 +02:00
@Nonnull public PlotQuery inAreas(@Nonnull final Collection<PlotArea> areas) {
2020-05-23 22:20:57 +02:00
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
*/
2020-07-14 18:49:40 +02:00
@Nonnull public PlotQuery expiredPlots() {
2020-05-24 00:08:52 +02:00
this.plotProvider = new ExpiredPlotProvider();
return this;
}
/**
* Query for all plots
*
* @return The query instance
*/
2020-07-14 18:49:40 +02:00
@Nonnull public PlotQuery allPlots() {
2020-07-10 17:32:07 +02:00
this.plotProvider = new GlobalPlotProvider(this.plotAreaManager);
return this;
}
/**
* Don't query at all
*
* @return The query instance
*/
2020-07-14 18:49:40 +02:00
@Nonnull public PlotQuery noPlots() {
this.plotProvider = new NullProvider();
return this;
}
/**
* Query for plots based on a search term
*
* @return The query instance
*/
2020-07-14 18:49:40 +02:00
@Nonnull public PlotQuery plotsBySearch(@Nonnull final String searchTerm) {
Preconditions.checkNotNull(searchTerm, "Search term may not be null");
this.plotProvider = new SearchPlotProvider(searchTerm);
return this;
}
2020-05-24 00:27:38 +02:00
/**
* Query with a pre-defined result
*
* @return The query instance
*/
2020-07-14 18:49:40 +02:00
@Nonnull public PlotQuery withPlot(@Nonnull final Plot plot) {
2020-05-24 00:27:38 +02:00
Preconditions.checkNotNull(plot, "Plot may not be null");
this.plotProvider = new FixedPlotProvider(plot);
return this;
}
2020-05-23 22:20:57 +02:00
/**
* Query for base plots only
*
* @return The query instance
*/
2020-07-14 18:49:40 +02:00
@Nonnull public PlotQuery whereBasePlot() {
2020-05-23 22:20:57 +02:00
return this.addFilter(new PredicateFilter(Plot::isBasePlot));
}
/**
* Query for plots owned by a specific player
*
* @param owner Owner UUID
* @return The query instance
*/
2020-07-14 18:49:40 +02:00
@Nonnull public PlotQuery ownedBy(@Nonnull final UUID owner) {
2020-05-23 22:20:57 +02:00
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
*/
2020-07-14 18:49:40 +02:00
@Nonnull public PlotQuery ownedBy(@Nonnull final PlotPlayer owner) {
2020-05-23 22:20:57 +02:00
Preconditions.checkNotNull(owner, "Owner may not be null");
return this.addFilter(new OwnerFilter(owner.getUUID()));
}
/**
* Query for plots with a specific alias
*
* @param alias Plot alias
* @return The query instance
*/
2020-07-14 18:49:40 +02:00
@Nonnull public PlotQuery withAlias(@Nonnull final String alias) {
2020-05-23 22:20:57 +02:00
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
*/
2020-07-14 18:49:40 +02:00
@Nonnull public PlotQuery withMember(@Nonnull final UUID member) {
2020-05-23 22:20:57 +02:00
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
*/
2020-07-14 18:49:40 +02:00
@Nonnull public PlotQuery thatPasses(@Nonnull final Predicate<Plot> predicate) {
2020-05-23 22:20:57 +02:00
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
*/
2020-07-14 18:49:40 +02:00
@Nonnull public PlotQuery withSortingStrategy(@Nonnull final SortingStrategy strategy) {
Preconditions.checkNotNull(strategy, "Strategy may not be null");
this.sortingStrategy = strategy;
return this;
}
2020-05-24 00:27:38 +02:00
/**
* Use a custom comparator to sort the results
*
* @param comparator Comparator
* @return The query instance
*/
2020-07-14 18:49:40 +02:00
@Nonnull public PlotQuery sorted(@Nonnull final Comparator<Plot> comparator) {
2020-05-24 00:27:38 +02:00
Preconditions.checkNotNull(comparator, "Comparator may not be null");
this.sortingStrategy = SortingStrategy.COMPARATOR;
this.plotComparator = comparator;
return this;
}
2020-05-24 00:02:08 +02:00
/**
* Defines the area around which plots may be sorted, depending on the
* sorting strategy
*
* @param plotArea Plot area
* @return The query instance
*/
2020-07-14 18:49:40 +02:00
@Nonnull public PlotQuery relativeToArea(@Nonnull final PlotArea plotArea) {
Preconditions.checkNotNull(plotArea, "Area may not be null");
this.priorityArea = plotArea;
return this;
}
2020-05-23 22:20:57 +02:00
/**
* Get all plots that match the given criteria
*
* @return Matching plots
*/
2020-07-14 18:49:40 +02:00
@Nonnull public Stream<Plot> asStream() {
return this.asList().stream();
2020-05-23 22:20:57 +02:00
}
/**
* Get all plots that match the given criteria
*
2020-05-24 00:14:33 +02:00
* @return Matching plots as a mutable
2020-05-23 22:20:57 +02:00
*/
2020-07-14 18:49:40 +02:00
@Nonnull public 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());
2020-05-24 20:18:02 +02:00
outer: for (final Plot plot : plots) {
for (final PlotFilter filter : this.filters) {
2020-05-24 20:18:02 +02:00
if (!filter.accepts(plot)) {
continue outer;
}
}
2020-05-24 20:18:02 +02:00
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);
2020-05-24 00:27:38 +02:00
} else if (this.sortingStrategy == SortingStrategy.COMPARATOR) {
result.sort(this.plotComparator);
}
return result;
2020-05-23 22:20:57 +02:00
}
/**
* Get all plots that match the given criteria
*
2020-05-24 00:14:33 +02:00
* @return Matching plots as a mutable set
2020-05-23 22:20:57 +02:00
*/
2020-07-14 18:49:40 +02:00
@Nonnull public Set<Plot> asSet() {
return new HashSet<>(this.asList());
2020-05-23 22:20:57 +02:00
}
/**
* 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
*/
2020-07-14 18:49:40 +02:00
@Nonnull public PaginatedPlotResult getPaginated(final int pageSize) {
2020-05-23 22:20:57 +02:00
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
*/
2020-07-14 18:49:40 +02:00
@Nonnull public Collection<Plot> asCollection() {
2020-05-23 22:20:57 +02:00
return this.asList();
2020-07-08 15:09:25 +02:00
}
/**
* Perform an action on each plot returned by the query
*
* @param consumer Plot consumer
*/
2020-07-14 18:49:40 +02:00
public void forEach(@Nonnull final Consumer<Plot> consumer) {
2020-07-08 15:09:25 +02:00
Preconditions.checkNotNull(consumer, "Consumer may not be null");
this.asCollection().forEach(consumer);
}
/**
* Get the amount of plots contained in the query result
*
* @return Result count
*/
public int count() {
return this.asList().size();
2020-05-23 22:20:57 +02:00
}
/**
* Get whether any provided plot matches the given filters.
* If no plot was provided, false will be returned.
*
* @return 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;
}
}
2020-07-14 18:49:40 +02:00
@Nonnull private PlotQuery addFilter(@Nonnull final PlotFilter filter) {
2020-05-23 22:20:57 +02:00
this.filters.add(filter);
return this;
}
}