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
|
2020-08-15 14:59:29 +02:00
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
2020-05-23 22:20:57 +02:00
|
|
|
*/
|
|
|
|
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;
|
2020-05-23 23:58:24 +02:00
|
|
|
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;
|
2020-05-23 23:58:24 +02:00
|
|
|
import com.plotsquared.core.util.MathMan;
|
2020-05-23 22:20:57 +02:00
|
|
|
|
2020-07-17 17:24:45 +02:00
|
|
|
import javax.annotation.Nonnull;
|
2020-05-23 23:58:24 +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;
|
2020-05-23 23:58:24 +02:00
|
|
|
import java.util.HashSet;
|
2020-07-18 16:18:23 +02:00
|
|
|
import java.util.Iterator;
|
2020-05-23 22:20:57 +02:00
|
|
|
import java.util.LinkedList;
|
|
|
|
import java.util.List;
|
2020-05-23 23:58:24 +02:00
|
|
|
import java.util.Map;
|
2020-05-23 22:20:57 +02:00
|
|
|
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
|
|
|
|
*/
|
2020-07-18 16:18:23 +02:00
|
|
|
public final class PlotQuery implements Iterable<Plot> {
|
2020-05-23 22:20:57 +02:00
|
|
|
|
|
|
|
private final Collection<PlotFilter> filters = new LinkedList<>();
|
2020-07-10 17:32:07 +02:00
|
|
|
private final PlotAreaManager plotAreaManager;
|
|
|
|
private PlotProvider plotProvider;
|
2020-05-23 23:58:24 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-05-23 23:58:24 +02:00
|
|
|
/**
|
|
|
|
* 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();
|
2020-05-23 23:58:24 +02:00
|
|
|
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);
|
2020-05-23 23:58:24 +02:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Don't query at all
|
|
|
|
*
|
|
|
|
* @return The query instance
|
|
|
|
*/
|
2020-07-14 18:49:40 +02:00
|
|
|
@Nonnull public PlotQuery noPlots() {
|
2020-05-23 23:58:24 +02:00
|
|
|
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) {
|
2020-05-23 23:58:24 +02:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2020-05-23 23:58:24 +02:00
|
|
|
/**
|
|
|
|
* 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) {
|
2020-05-23 23:58:24 +02:00
|
|
|
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) {
|
2020-05-23 23:58:24 +02:00
|
|
|
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() {
|
2020-05-23 23:58:24 +02:00
|
|
|
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() {
|
2020-05-23 23:58:24 +02:00
|
|
|
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) {
|
2020-05-23 23:58:24 +02:00
|
|
|
for (final PlotFilter filter : this.filters) {
|
2020-05-24 20:18:02 +02:00
|
|
|
if (!filter.accepts(plot)) {
|
|
|
|
continue outer;
|
2020-05-23 23:58:24 +02:00
|
|
|
}
|
|
|
|
}
|
2020-05-24 20:18:02 +02:00
|
|
|
result.add(plot);
|
2020-05-23 23:58:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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);
|
2020-05-23 23:58:24 +02:00
|
|
|
}
|
|
|
|
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() {
|
2020-05-23 23:58:24 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
}
|
|
|
|
|
2020-06-29 13:48:18 +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;
|
|
|
|
}
|
|
|
|
|
2020-07-19 14:34:42 +02:00
|
|
|
@Nonnull @Override public Iterator<Plot> iterator() {
|
2020-07-18 16:18:23 +02:00
|
|
|
return this.asCollection().iterator();
|
|
|
|
}
|
2020-05-23 22:20:57 +02:00
|
|
|
|
|
|
|
}
|