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);
+    }
 }

Reply via email to