You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2021/09/09 14:11:26 UTC
[sis] 01/03: Generalize `SortByComparator`.
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 <ma...@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);
+ }
}