This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
commit 982b7136345f6bf7cfa0b271eb9299e42b16b07a Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Thu Sep 9 10:38:42 2021 +0200 Generalize `SortByComparator`. --- .../sis/internal/sql/feature/FeatureStream.java | 9 +- .../sis/internal/storage/query/FeatureQuery.java | 4 +- .../internal/storage/query/SortByComparator.java | 148 ++++++++++++++++++--- 3 files changed, 132 insertions(+), 29 deletions(-) diff --git a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/FeatureStream.java b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/FeatureStream.java index f7f734d..af89400 100644 --- a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/FeatureStream.java +++ b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/FeatureStream.java @@ -243,12 +243,9 @@ final class FeatureStream extends DeferredStream<Feature> { if (isPagined() || hasComparator) { return delegate().sorted(comparator); } - if (sort == null && comparator instanceof SortBy<?>) { - sort = (SortBy<? super Feature>) comparator; - return this; - } - if (sort instanceof SortByComparator && comparator instanceof SortByComparator) { - sort = new SortByComparator((SortByComparator) sort, (SortByComparator) comparator); + final SortBy<? super Feature> c = SortByComparator.concatenate(sort, comparator); + if (c != null) { + sort = c; return this; } hasComparator = true; diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/FeatureQuery.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/FeatureQuery.java index 32926a5..67baf3b 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/FeatureQuery.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/FeatureQuery.java @@ -254,8 +254,8 @@ public class FeatureQuery extends Query implements Cloneable { @SafeVarargs public final void setSortBy(final SortProperty<Feature>... properties) { SortBy<Feature> sortBy = null; - if (properties != null && properties.length != 0) { - sortBy = new SortByComparator(properties); + if (properties != null) { + sortBy = SortByComparator.create(properties); } setSortBy(sortBy); } diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/SortByComparator.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/SortByComparator.java index 6d4a010..37aeaee 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/SortByComparator.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/SortByComparator.java @@ -16,31 +16,34 @@ */ package org.apache.sis.internal.storage.query; +import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.LinkedHashMap; +import java.util.Comparator; import java.io.Serializable; -import org.apache.sis.util.ArraysExt; import org.apache.sis.util.ArgumentChecks; import org.apache.sis.internal.util.UnmodifiableArrayList; // Branch-dependent imports -import org.opengis.feature.Feature; import org.opengis.filter.SortBy; import org.opengis.filter.SortProperty; +import org.opengis.filter.ValueReference; /** * Comparator sorting features using an array of {@link SortProperty} elements applied in order. - * This is restricted to comparator of {@link Feature} instances for now because this is the only - * comparator that we currently need, and it makes {@linkplain #SortByComparator(SortByComparator, - * SortByComparator) concatenations} type-safe. We may generalize in the future if needed. * * @author Johann Sorel (Geomatys) * @author Martin Desruisseaux (Geomatys) * @version 1.1 - * @since 1.0 + * + * @param <R> the type of resources (typically {@code Feature}) to sort. + * + * @since 1.0 * @module */ -public final class SortByComparator implements SortBy<Feature>, Serializable { +public final class SortByComparator<R> implements SortBy<R>, Serializable { /** * For cross-version compatibility. */ @@ -51,28 +54,109 @@ public final class SortByComparator implements SortBy<Feature>, Serializable { * * @see #getSortProperties() */ - private final SortProperty<Feature>[] properties; + private final SortProperty<R>[] properties; + + /** + * Creates a new comparator for the given sort expression. + * This is shortcut for the common case where there is a single expression. + * + * @param p the property to wrap in a {@link SortBy} comparator. + */ + @SuppressWarnings({"unchecked","rawtypes"}) // Generic array creation. + private SortByComparator(final SortProperty<R> p) { + ArgumentChecks.ensureNonNullElement("properties", 0, p); + properties = new SortProperty[] {p}; + } + + /** + * Creates a new comparator with the values of given map. + * The map is used for removing duplicated expressions. + */ + @SuppressWarnings({"unchecked","rawtypes"}) // Generic array creation. + private SortByComparator(final Map<?, SortProperty<R>> merged) { + properties = merged.values().toArray(new SortProperty[merged.size()]); + } /** * Creates a new comparator for the given sort expressions. - * It is caller responsibility to ensure that the given array is non-empty. + * + * @param <R> the type of resources (typically {@code Feature}) to sort. + * @param properties the sort order. + * @return the comparator, or {@code null} if the given array is empty. + */ + public static <R> SortByComparator<R> create(final SortProperty<R>[] properties) { + switch (properties.length) { + case 0: return null; + case 1: return new SortByComparator<>(properties[0]); + } + final Map<ValueReference<? super R, ?>, SortProperty<R>> merged = new LinkedHashMap<>(); + addAll(Arrays.asList(properties), merged); + return new SortByComparator<>(merged); + } + + /** + * Creates a new comparator as the concatenation of the two given comparators. + * The first comparator is used first, and if two resources are equal then the + * second comparator is used. + * + * @param <R> the type of resources (typically {@code Feature}) to sort. + * @param sort the first "sort by" to use, or {@code null} if none. + * @param comparator the second "sort by" to use. + * @return concatenation of the two comparators. */ - SortByComparator(SortProperty<Feature>[] properties) { - properties = properties.clone(); - this.properties = properties; - for (int i=0; i < properties.length; i++) { - ArgumentChecks.ensureNonNullElement("properties", i, properties[i]); + @SuppressWarnings("unchecked") + public static <R> SortBy<? super R> concatenate(final SortBy<? super R> sort, final Comparator<? super R> comparator) { + final SortBy<? super R> other; + if (comparator instanceof SortBy<?>) { + other = (SortBy<? super R>) comparator; + } else if (comparator instanceof SortProperty<?>) { + other = new SortByComparator<>((SortProperty<? super R>) comparator); + } else { + return null; + } + if (sort == null) { + return other; } + /* + * The (SortBy<R>) casts are unsafe — they should be (SortBy<? super R>) — but are okay in this context + * because we create a `SortByComparator` which will only "push" instances of R to the comparators. + * There is no code that "pull" instances of R from the comparators, consequently no code that may + * be surprised to get an instance of a super type of R instead of R. + */ + return concatenate((SortBy<R>) sort, (SortBy<R>) other); } /** * Creates a new comparator as the concatenation of the two given comparators. + * The first comparator is used first, and if two resources are equal then the + * second comparator is used. * - * @param s1 the first "sort by" to concatenate. - * @param s2 the second "sort by" to concatenate. + * @param <R> the type of resources (typically {@code Feature}) to sort. + * @param s1 the first "sort by" to use. + * @param s2 the second "sort by" to use. + * @return concatenation of the two comparators. + */ + public static <R> SortBy<R> concatenate(final SortBy<R> s1, final SortBy<R> s2) { + final Map<ValueReference<? super R, ?>, SortProperty<R>> merged = new LinkedHashMap<>(); + addAll(s1.getSortProperties(), merged); + addAll(s2.getSortProperties(), merged); + return new SortByComparator<>(merged); + } + + /** + * Adds all elements of the {@code properties} list into the {@code merged} map. + * If two {@code SortProperty} instances use the same {@link ValueReference}, + * then only the first occurrence is retained. */ - public SortByComparator(final SortByComparator s1, final SortByComparator s2) { - properties = ArraysExt.concatenate(s1.properties, s2.properties); + private static <R> void addAll(final List<SortProperty<R>> properties, + final Map<ValueReference<? super R, ?>, SortProperty<R>> merged) + { + final int size = properties.size(); + for (int i=0; i<size; i++) { + final SortProperty<R> p = properties.get(i); + ArgumentChecks.ensureNonNullElement("properties", i, p); + merged.putIfAbsent(p.getValueReference(), p); + } } /** @@ -80,7 +164,7 @@ public final class SortByComparator implements SortBy<Feature>, Serializable { * The list shall have a minimum of one element. */ @Override - public List<SortProperty<Feature>> getSortProperties() { + public List<SortProperty<R>> getSortProperties() { return UnmodifiableArrayList.wrap(properties); } @@ -90,11 +174,33 @@ public final class SortByComparator implements SortBy<Feature>, Serializable { * The ordering of null resources or null property values is unspecified. */ @Override - public int compare(final Feature r1, final Feature r2) { - for (final SortProperty<Feature> p : properties) { + public int compare(final R r1, final R r2) { + for (final SortProperty<R> p : properties) { final int c = p.compare(r1, r2); if (c != 0) return c; } return 0; } + + /** + * Returns a comparator as the concatenation of this comparator with the given one. + * This comparator is used first, and if two resources are equal then the other comparator is used. + * + * @param other the other comparator to be used when this comparator considers two resources as equal. + * @return concatenation of this comparator with the given one. + */ + @Override + @SuppressWarnings("unchecked") + public Comparator<R> thenComparing(final Comparator<? super R> other) { + if (other instanceof SortBy<?>) { + /* + * The (SortBy<R>) cast is unsafe — it should be (SortBy<? super R>) — but it is okay in this context + * because we create a `SortByComparator` which will only "push" instances of R to the `other` comparator. + * There is no code that "pull" instances of R from the `other` comparator, consequently no code that may + * be surprised to get an instance of a super type of R instead of R. + */ + return concatenate(this, (SortBy<R>) other); + } + return SortBy.super.thenComparing(other); + } }