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 fa65ecf05431a6d88d0f7a017822ad99019694ce
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Thu Sep 9 15:05:29 2021 +0200

    Move Query sub-types to public package.
---
 .../sis/internal/filter}/SortByComparator.java     |   2 +-
 .../org/apache/sis/internal/map/SEPortrayer.java   |   2 +-
 .../apache/sis/internal/map/SEPortrayerTest.java   |   2 +-
 .../sis/internal/sql/feature/FeatureStream.java    |   2 +-
 .../org/apache/sis/storage/sql/SQLStoreTest.java   |   2 +-
 .../sis/internal/storage/JoinFeatureSet.java       |   2 +-
 .../sis/internal/storage/query/package-info.java   |  32 -----
 .../storage/query => storage}/CoverageQuery.java   |  28 ++--
 .../storage/query => storage}/CoverageSubset.java  |   6 +-
 .../storage/query => storage}/FeatureQuery.java    | 141 ++++++++++++++-------
 .../java/org/apache/sis/storage/FeatureSet.java    |   1 -
 .../storage/query => storage}/FeatureSubset.java   |  21 ++-
 .../apache/sis/storage/GridCoverageResource.java   |   1 -
 .../main/java/org/apache/sis/storage/Query.java    |   5 +-
 .../query => storage}/CoverageQueryTest.java       |   4 +-
 .../FeatureQueryTest.java}                         |   8 +-
 .../query => storage}/GridResourceMock.java        |   2 +-
 .../apache/sis/test/suite/StorageTestSuite.java    |   4 +-
 18 files changed, 140 insertions(+), 125 deletions(-)

diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/SortByComparator.java
 
b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/SortByComparator.java
similarity index 99%
rename from 
storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/SortByComparator.java
rename to 
core/sis-feature/src/main/java/org/apache/sis/internal/filter/SortByComparator.java
index 37aeaee..3727386 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/SortByComparator.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/SortByComparator.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.internal.storage.query;
+package org.apache.sis.internal.filter;
 
 import java.util.Arrays;
 import java.util.List;
diff --git 
a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/SEPortrayer.java 
b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/SEPortrayer.java
index 19765d1..9b267af 100644
--- 
a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/SEPortrayer.java
+++ 
b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/SEPortrayer.java
@@ -43,7 +43,7 @@ import org.apache.sis.filter.DefaultFilterFactory;
 import org.apache.sis.geometry.Envelopes;
 import org.apache.sis.geometry.GeneralEnvelope;
 import org.apache.sis.internal.feature.AttributeConvention;
-import org.apache.sis.internal.storage.query.FeatureQuery;
+import org.apache.sis.storage.FeatureQuery;
 import org.apache.sis.portrayal.MapItem;
 import org.apache.sis.portrayal.MapLayer;
 import org.apache.sis.portrayal.MapLayers;
diff --git 
a/core/sis-portrayal/src/test/java/org/apache/sis/internal/map/SEPortrayerTest.java
 
b/core/sis-portrayal/src/test/java/org/apache/sis/internal/map/SEPortrayerTest.java
index 8f8b16b..d35ca20 100644
--- 
a/core/sis-portrayal/src/test/java/org/apache/sis/internal/map/SEPortrayerTest.java
+++ 
b/core/sis-portrayal/src/test/java/org/apache/sis/internal/map/SEPortrayerTest.java
@@ -35,7 +35,7 @@ import org.apache.sis.filter.DefaultFilterFactory;
 import org.apache.sis.geometry.GeneralEnvelope;
 import org.apache.sis.internal.feature.AttributeConvention;
 import org.apache.sis.internal.storage.MemoryFeatureSet;
-import org.apache.sis.internal.storage.query.FeatureQuery;
+import org.apache.sis.storage.FeatureQuery;
 import org.apache.sis.portrayal.MapItem;
 import org.apache.sis.portrayal.MapLayer;
 import org.apache.sis.portrayal.MapLayers;
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 af89400..e6f8790 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
@@ -31,7 +31,7 @@ import org.apache.sis.filter.Optimization;
 import org.apache.sis.internal.metadata.sql.SQLBuilder;
 import org.apache.sis.internal.stream.DeferredStream;
 import org.apache.sis.internal.stream.PaginedStream;
-import org.apache.sis.internal.storage.query.SortByComparator;
+import org.apache.sis.internal.filter.SortByComparator;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.util.collection.BackingStoreException;
 import org.apache.sis.util.ArgumentChecks;
diff --git 
a/storage/sis-sqlstore/src/test/java/org/apache/sis/storage/sql/SQLStoreTest.java
 
b/storage/sis-sqlstore/src/test/java/org/apache/sis/storage/sql/SQLStoreTest.java
index d7a810d..2f4c2a9 100644
--- 
a/storage/sis-sqlstore/src/test/java/org/apache/sis/storage/sql/SQLStoreTest.java
+++ 
b/storage/sis-sqlstore/src/test/java/org/apache/sis/storage/sql/SQLStoreTest.java
@@ -26,7 +26,7 @@ import org.apache.sis.storage.FeatureSet;
 import org.apache.sis.storage.StorageConnector;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.filter.DefaultFilterFactory;
-import org.apache.sis.internal.storage.query.FeatureQuery;
+import org.apache.sis.storage.FeatureQuery;
 import org.apache.sis.test.sql.TestDatabase;
 import org.apache.sis.test.TestUtilities;
 import org.apache.sis.test.TestCase;
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/JoinFeatureSet.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/JoinFeatureSet.java
index 0977396..c74e056 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/JoinFeatureSet.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/JoinFeatureSet.java
@@ -29,7 +29,7 @@ import org.apache.sis.feature.FeatureOperations;
 import org.apache.sis.feature.DefaultFeatureType;
 import org.apache.sis.feature.DefaultAssociationRole;
 import org.apache.sis.internal.feature.AttributeConvention;
-import org.apache.sis.internal.storage.query.FeatureQuery;
+import org.apache.sis.storage.FeatureQuery;
 import org.apache.sis.storage.FeatureSet;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.event.StoreListeners;
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/package-info.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/package-info.java
deleted file mode 100644
index a75a1c9..0000000
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/package-info.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Default implementation of query operations.
- *
- * <STRONG>Do not use!</STRONG>
- *
- * This package is for internal use by SIS only. Classes in this package
- * may change in incompatible ways in any future version without notice.
- *
- * @author  Johann Sorel (Geomatys)
- * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
- * @since   1.0
- * @module
- */
-package org.apache.sis.internal.storage.query;
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/CoverageQuery.java
 b/storage/sis-storage/src/main/java/org/apache/sis/storage/CoverageQuery.java
similarity index 90%
rename from 
storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/CoverageQuery.java
rename to 
storage/sis-storage/src/main/java/org/apache/sis/storage/CoverageQuery.java
index 9e9dd1c..c668af6 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/CoverageQuery.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/CoverageQuery.java
@@ -14,10 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.internal.storage.query;
+package org.apache.sis.storage;
 
 import java.util.Arrays;
 import java.util.Objects;
+import java.io.Serializable;
 import java.math.RoundingMode;
 import org.opengis.metadata.extent.GeographicBoundingBox;
 import org.apache.sis.measure.Angle;
@@ -25,15 +26,16 @@ import org.apache.sis.measure.Latitude;
 import org.apache.sis.measure.Longitude;
 import org.apache.sis.measure.AngleFormat;
 import org.apache.sis.coverage.grid.GridGeometry;
-import org.apache.sis.storage.GridCoverageResource;
-import org.apache.sis.storage.Query;
-import org.apache.sis.storage.UnsupportedQueryException;
 import org.apache.sis.util.ArgumentChecks;
 
 
 /**
- * A simple query configuration for coverage resources for requesting a subset 
of the domain and the range.
- * Experimental for now, may move to public API in a future version.
+ * Definition of filtering to apply for fetching a subset of {@link 
GridCoverageResource}.
+ * This query allows requesting a subset of the coverage domain and the range.
+ *
+ * <h2>Optional values</h2>
+ * All aspects of this query are optional and initialized to "none".
+ * Unless otherwise specified, all methods accept a null argument or can 
return a null value, which means "none".
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
@@ -41,7 +43,12 @@ import org.apache.sis.util.ArgumentChecks;
  * @since   1.1
  * @module
  */
-public final class CoverageQuery extends Query implements Cloneable {
+public class CoverageQuery extends Query implements Cloneable, Serializable {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -4296814883807414158L;
+
     /**
      * Desired grid extent and resolution, or {@code null} for reading the 
whole domain.
      */
@@ -115,7 +122,7 @@ public final class CoverageQuery extends Query implements 
Cloneable {
     }
 
     /**
-     * Set a number of additional cells to read on each border of the source 
grid coverage.
+     * Sets a number of additional cells to read on each border of the source 
grid coverage.
      * If non-zero, this property expands the {@linkplain #getDomain() domain} 
to be read
      * by the specified margin.
      *
@@ -158,7 +165,7 @@ public final class CoverageQuery extends Query implements 
Cloneable {
      * @return a view over the given coverage resource containing only the 
given domain and range.
      * @throws UnsupportedQueryException if this query contains filtering 
options not yet supported.
      */
-    public GridCoverageResource execute(final GridCoverageResource source) 
throws UnsupportedQueryException {
+    final GridCoverageResource execute(final GridCoverageResource source) 
throws UnsupportedQueryException {
         ArgumentChecks.ensureNonNull("source", source);
         return new CoverageSubset(source, clone());
     }
@@ -214,7 +221,8 @@ public final class CoverageQuery extends Query implements 
Cloneable {
     }
 
     /**
-     * Returns a textual representation looking like an SQL Select query.
+     * Returns a textual representation of this query for debugging purposes.
+     * The default implementation returns a string that looks like an SQL 
Select query.
      *
      * @return textual representation of this query.
      */
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/CoverageSubset.java
 b/storage/sis-storage/src/main/java/org/apache/sis/storage/CoverageSubset.java
similarity index 97%
rename from 
storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/CoverageSubset.java
rename to 
storage/sis-storage/src/main/java/org/apache/sis/storage/CoverageSubset.java
index eb0a1a4..4ff419f 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/CoverageSubset.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/CoverageSubset.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.internal.storage.query;
+package org.apache.sis.storage;
 
 import java.util.Arrays;
 import java.util.List;
@@ -25,11 +25,7 @@ import org.apache.sis.coverage.grid.GridDerivation;
 import org.apache.sis.coverage.grid.GridRoundingMode;
 import org.apache.sis.coverage.grid.GridClippingMode;
 import org.apache.sis.coverage.grid.DisjointExtentException;
-import org.apache.sis.storage.GridCoverageResource;
 import org.apache.sis.storage.event.StoreListeners;
-import org.apache.sis.storage.DataStoreException;
-import org.apache.sis.storage.DataStoreReferencingException;
-import org.apache.sis.storage.NoSuchDataException;
 import org.apache.sis.internal.storage.Resources;
 import org.apache.sis.internal.storage.AbstractGridResource;
 import org.apache.sis.internal.util.UnmodifiableArrayList;
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/storage/FeatureQuery.java
similarity index 80%
rename from 
storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/FeatureQuery.java
rename to 
storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureQuery.java
index 67baf3b..d34fa2e 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/storage/FeatureQuery.java
@@ -14,23 +14,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.internal.storage.query;
+package org.apache.sis.storage;
 
 import java.util.Arrays;
 import java.util.Map;
-import java.util.List;
 import java.util.LinkedHashMap;
 import java.util.Objects;
+import java.util.OptionalLong;
+import java.io.Serializable;
 import javax.measure.Quantity;
 import javax.measure.quantity.Length;
 import org.opengis.util.GenericName;
 import org.apache.sis.feature.builder.FeatureTypeBuilder;
 import org.apache.sis.feature.builder.PropertyTypeBuilder;
 import org.apache.sis.internal.feature.FeatureExpression;
-import org.apache.sis.internal.util.UnmodifiableArrayList;
+import org.apache.sis.internal.filter.SortByComparator;
 import org.apache.sis.internal.storage.Resources;
-import org.apache.sis.storage.FeatureSet;
-import org.apache.sis.storage.Query;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.Classes;
 import org.apache.sis.util.collection.Containers;
@@ -49,21 +48,40 @@ import org.opengis.filter.InvalidFilterValueException;
 
 
 /**
- * Mimics {@code SQL SELECT} statements using OGC Filter and Expressions.
+ * Definition of filtering to apply for fetching a subset of {@link 
FeatureSet}.
+ * This query mimics {@code SQL SELECT} statements using OGC Filter and 
Expressions.
  * Information stored in this query can be used directly with {@link 
java.util.stream.Stream} API.
  *
+ * <h2>Terminology</h2>
+ * This class uses relational database terminology:
+ * <ul>
+ *   <li>A <cite>selection</cite> is a filter choosing the features instances 
to include in the subset.
+ *       In relational databases, a feature instances are mapped to table 
rows.</li>
+ *   <li>A <cite>projection</cite> (not to be confused with map projection) is 
the set of feature property to keep.
+ *       In relational databases, feature properties are mapped to table 
columns.</li>
+ * </ul>
+ *
+ * <h2>Optional values</h2>
+ * All aspects of this query are optional and initialized to "none".
+ * Unless otherwise specified, all methods accept a null argument or can 
return a null value, which means "none".
+ *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.1
- * @since   1.0
+ * @since   1.1
  * @module
  */
-public class FeatureQuery extends Query implements Cloneable {
+public class FeatureQuery extends Query implements Cloneable, Serializable {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -5841189659773611160L;
+
     /**
      * Sentinel limit value for queries of unlimited length.
-     * This value can be given to {@link #setLimit(long)} or retrieved from 
{@link #getLimit()}.
+     * This value applies to the {@link #limit} field.
      */
-    public static final long UNLIMITED = -1;
+    private static final long UNLIMITED = -1;
 
     /**
      * The properties to retrieve, or {@code null} if all properties shall be 
included in the query.
@@ -87,6 +105,7 @@ public class FeatureQuery extends Query implements Cloneable 
{
 
     /**
      * The number of feature instances to skip from the beginning.
+     * This is zero if there is no instance to skip.
      *
      * @see #getOffset()
      * @see #setOffset(long)
@@ -96,6 +115,7 @@ public class FeatureQuery extends Query implements Cloneable 
{
 
     /**
      * The maximum number of feature instances contained in the {@code 
FeatureSet}.
+     * This is {@link #UNLIMITED} if there is no limit.
      *
      * @see #getLimit()
      * @see #setLimit(long)
@@ -113,7 +133,7 @@ public class FeatureQuery extends Query implements 
Cloneable {
 
     /**
      * Hint used by resources to optimize returned features.
-     * Different stores makes use of vector tiles of different scales.
+     * Different stores make use of vector tiles of different scales.
      * A {@code null} value means to query data at their full resolution.
      *
      * @see #getLinearResolution()
@@ -122,33 +142,54 @@ public class FeatureQuery extends Query implements 
Cloneable {
     private Quantity<Length> linearResolution;
 
     /**
-     * Creates a new query retrieving no property and applying no filter.
+     * Creates a new query applying no filter.
      */
     public FeatureQuery() {
-        selection = Filter.include();
         limit = UNLIMITED;
     }
 
     /**
      * Sets the properties to retrieve, or {@code null} if all properties 
shall be included in the query.
+     * This convenience method wraps the given expression in {@link 
NamedExpression}s without alias and
+     * delegates to {@link #setProjection(NamedExpression...)}.
+     *
+     * @param  properties  properties to retrieve, or {@code null} to retrieve 
all properties.
+     * @throws IllegalArgumentException if a property is duplicated.
+     */
+    @SafeVarargs
+    public final void setProjection(final Expression<? super Feature, ?>... 
properties) {
+        NamedExpression[] wrappers = null;
+        if (properties != null) {
+            wrappers = new NamedExpression[properties.length];
+            for (int i=0; i<wrappers.length; i++) {
+                final Expression<? super Feature, ?> e = properties[i];
+                ArgumentChecks.ensureNonNullElement("properties", i, e);
+                wrappers[i] = new NamedExpression(e);
+            }
+        }
+        setProjection(wrappers);
+    }
+
+    /**
+     * Sets the properties to retrieve, or {@code null} if all properties 
shall be included in the query.
      * A query column may use a simple or complex expression and an alias to 
create a new type of property
      * in the returned features.
      *
      * <p>This is equivalent to the column names in the {@code SELECT} clause 
of a SQL statement.
      * Subset of columns is called <cite>projection</cite> in relational 
database terminology.</p>
      *
-     * @param  projection  properties to retrieve, or {@code null} to retrieve 
all properties.
+     * @param  properties  properties to retrieve, or {@code null} to retrieve 
all properties.
      * @throws IllegalArgumentException if a property or an alias is 
duplicated.
      */
     @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter")
-    public void setProjection(NamedExpression... projection) {
-        if (projection != null) {
-            ArgumentChecks.ensureNonEmpty("projection", projection);
-            projection = projection.clone();
-            final Map<Object,Integer> uniques = new 
LinkedHashMap<>(Containers.hashMapCapacity(projection.length));
-            for (int i=0; i<projection.length; i++) {
-                final NamedExpression c = projection[i];
-                ArgumentChecks.ensureNonNullElement("projection", i, c);
+    public void setProjection(NamedExpression... properties) {
+        if (properties != null) {
+            ArgumentChecks.ensureNonEmpty("properties", properties);
+            properties = properties.clone();
+            final Map<Object,Integer> uniques = new 
LinkedHashMap<>(Containers.hashMapCapacity(properties.length));
+            for (int i=0; i<properties.length; i++) {
+                final NamedExpression c = properties[i];
+                ArgumentChecks.ensureNonNullElement("properties", i, c);
                 final Object key = c.alias != null ? c.alias : c.expression;
                 final Integer p = uniques.putIfAbsent(key, i);
                 if (p != null) {
@@ -156,17 +197,18 @@ public class FeatureQuery extends Query implements 
Cloneable {
                 }
             }
         }
-        this.projection = projection;
+        this.projection = properties;
     }
 
     /**
      * Returns the properties to retrieve, or {@code null} if all properties 
shall be included in the query.
-     * This is the list of expressions specified in the last call to {@link 
#setProjection(NamedExpression[])}.
+     * This is the expressions specified in the last call to {@link 
#setProjection(NamedExpression[])}.
+     * The default value is null.
      *
      * @return properties to retrieve, or {@code null} to retrieve all feature 
properties.
      */
-    public List<NamedExpression> getProjection() {
-        return UnmodifiableArrayList.wrap(projection);
+    public NamedExpression[] getProjection() {
+        return (projection != null) ? projection.clone() : null;
     }
 
     /**
@@ -174,18 +216,18 @@ public class FeatureQuery extends Query implements 
Cloneable {
      * Features that do not pass the filter are discarded.
      * Discarded features are not counted for the {@linkplain #setLimit(long) 
query limit}.
      *
-     * @param  selection  the filter, or {@link Filter#include()} if none.
+     * @param  selection  the filter, or {@code null} if none.
      */
     public void setSelection(final Filter<? super Feature> selection) {
-        ArgumentChecks.ensureNonNull("selection", selection);
         this.selection = selection;
     }
 
     /**
      * Returns the filter for trimming feature instances.
      * This is the value specified in the last call to {@link 
#setSelection(Filter)}.
+     * The default value is {@code null}, which means that no filtering is 
applied.
      *
-     * @return the filter, or {@link Filter#include()} if none.
+     * @return the filter, or {@code null} if none.
      */
     public Filter<? super Feature> getSelection() {
         return selection;
@@ -209,6 +251,7 @@ public class FeatureQuery extends Query implements 
Cloneable {
     /**
      * Returns the number of feature instances to skip from the beginning.
      * This is the value specified in the last call to {@link 
#setOffset(long)}.
+     * The default value is zero, which means that no features are skipped.
      *
      * @return the number of feature instances to skip from the beginning.
      */
@@ -217,18 +260,23 @@ public class FeatureQuery extends Query implements 
Cloneable {
     }
 
     /**
+     * Removes any limit defined by {@link #setLimit(long)}.
+     */
+    public void setUnlimited() {
+        limit = UNLIMITED;
+    }
+
+    /**
      * Set the maximum number of feature instances contained in the {@code 
FeatureSet}.
      * Offset and limit are often combined to obtain paging.
      *
      * <p>Note that setting this property can be costly on parallelized 
streams.
      * See {@link java.util.stream.Stream#limit(long)} for more 
information.</p>
      *
-     * @param  limit  maximum number of feature instances contained in the 
{@code FeatureSet}, or {@link #UNLIMITED}.
+     * @param  limit  maximum number of feature instances contained in the 
{@code FeatureSet}.
      */
     public void setLimit(final long limit) {
-        if (limit != UNLIMITED) {
-            ArgumentChecks.ensurePositive("limit", limit);
-        }
+        ArgumentChecks.ensurePositive("limit", limit);
         this.limit = limit;
     }
 
@@ -236,16 +284,15 @@ public class FeatureQuery extends Query implements 
Cloneable {
      * Returns the maximum number of feature instances contained in the {@code 
FeatureSet}.
      * This is the value specified in the last call to {@link #setLimit(long)}.
      *
-     * @return maximum number of feature instances contained in the {@code 
FeatureSet}, or {@link #UNLIMITED}.
+     * @return maximum number of feature instances contained in the {@code 
FeatureSet}, or empty if none.
      */
-    public long getLimit() {
-        return limit;
+    public OptionalLong getLimit() {
+        return (limit >= 0) ? OptionalLong.of(limit) : OptionalLong.empty();
     }
 
     /**
      * Sets the expressions to use for sorting the feature instances.
-     * {@code SortBy} objects are used to order the {@link 
org.opengis.feature.Feature} instances
-     * returned by the {@link org.apache.sis.storage.FeatureSet}.
+     * {@code SortBy} objects are used to order the {@link Feature} instances 
returned by the {@link FeatureSet}.
      * {@code SortBy} clauses are applied in declaration order, like SQL.
      *
      * @param  properties  expressions to use for sorting the feature 
instances,
@@ -262,8 +309,7 @@ public class FeatureQuery extends Query implements 
Cloneable {
 
     /**
      * Sets the expressions to use for sorting the feature instances.
-     * {@code SortBy} objects are used to order the {@link 
org.opengis.feature.Feature} instances
-     * returned by the {@link org.apache.sis.storage.FeatureSet}.
+     * {@code SortBy} objects are used to order the {@link Feature} instances 
returned by the {@link FeatureSet}.
      *
      * @param  sortBy  expressions to use for sorting the feature instances, 
or {@code null} if none.
      */
@@ -273,7 +319,7 @@ public class FeatureQuery extends Query implements 
Cloneable {
 
     /**
      * Returns the expressions to use for sorting the feature instances.
-     * They are the values specified in the last call to {@link 
#setSortBy(SortBy)}.
+     * This is the value specified in the last call to {@link 
#setSortBy(SortBy)}.
      *
      * @return expressions to use for sorting the feature instances, or {@code 
null} if none.
      */
@@ -306,9 +352,15 @@ public class FeatureQuery extends Query implements 
Cloneable {
      * In relational database terminology, subset of columns is called 
<cite>projection</cite>.
      * Columns can be given to the {@link 
FeatureQuery#setProjection(NamedExpression[])} method.
      */
-    public static class NamedExpression {
+    public static class NamedExpression implements Serializable {
+        /**
+         * For cross-version compatibility.
+         */
+        private static final long serialVersionUID = -6919525113513842514L;
+
         /**
          * The literal, value reference or more complex expression to be 
retrieved by a {@code Query}.
+         * Never {@code null}.
          */
         public final Expression<? super Feature, ?> expression;
 
@@ -447,7 +499,7 @@ public class FeatureQuery extends Query implements 
Cloneable {
      * @param  source  the set of features to filter, sort or process.
      * @return a view over the given feature set containing only the filtered 
feature instances.
      */
-    public FeatureSet execute(final FeatureSet source) {
+    final FeatureSet execute(final FeatureSet source) {
         ArgumentChecks.ensureNonNull("source", source);
         return new FeatureSubset(source, clone());
     }
@@ -527,7 +579,8 @@ public class FeatureQuery extends Query implements 
Cloneable {
     }
 
     /**
-     * Returns a textual representation looking like an SQL Select query.
+     * Returns a textual representation of this query for debugging purposes.
+     * The default implementation returns a string that looks like an SQL 
Select query.
      *
      * @return textual representation of this query.
      */
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSet.java 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSet.java
index d7534b7..fc97a9c 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSet.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSet.java
@@ -18,7 +18,6 @@ package org.apache.sis.storage;
 
 import java.util.stream.Stream;
 import org.apache.sis.util.ArgumentChecks;
-import org.apache.sis.internal.storage.query.FeatureQuery;
 
 // Branch-dependent imports
 import org.opengis.feature.Feature;
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/FeatureSubset.java
 b/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSubset.java
similarity index 89%
rename from 
storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/FeatureSubset.java
rename to 
storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSubset.java
index 2452752..47c98e0 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/FeatureSubset.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSubset.java
@@ -14,16 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.internal.storage.query;
+package org.apache.sis.storage;
 
-import java.util.List;
+import java.util.OptionalLong;
 import java.util.stream.Stream;
 import org.apache.sis.internal.feature.FeatureUtilities;
 import org.apache.sis.internal.storage.AbstractFeatureSet;
 import org.apache.sis.internal.storage.Resources;
-import org.apache.sis.storage.DataStoreContentException;
-import org.apache.sis.storage.DataStoreException;
-import org.apache.sis.storage.FeatureSet;
 import org.apache.sis.storage.event.StoreListeners;
 
 // Branch-dependent imports
@@ -99,7 +96,7 @@ final class FeatureSubset extends AbstractFeatureSet {
          * Apply filter.
          */
         final Filter<? super Feature> selection = query.getSelection();
-        if (!Filter.include().equals(selection)) {
+        if (selection != null && !selection.equals(Filter.include())) {
             stream = stream.filter(selection);
         }
         /*
@@ -119,20 +116,20 @@ final class FeatureSubset extends AbstractFeatureSet {
         /*
          * Apply limit.
          */
-        final long limit = query.getLimit();
-        if (limit >= 0) {
-            stream = stream.limit(limit);
+        final OptionalLong limit = query.getLimit();
+        if (limit.isPresent()) {
+            stream = stream.limit(limit.getAsLong());
         }
         /*
          * Transform feature instances.
          * Note: "projection" here is in relational database sense, not map 
projection.
          */
-        final List<FeatureQuery.NamedExpression> projection = 
query.getProjection();
+        final FeatureQuery.NamedExpression[] projection = 
query.getProjection();
         if (projection != null) {
             @SuppressWarnings({"unchecked", "rawtypes"})
-            final Expression<? super Feature, ?>[] expressions = new 
Expression[projection.size()];
+            final Expression<? super Feature, ?>[] expressions = new 
Expression[projection.length];
             for (int i=0; i<expressions.length; i++) {
-                expressions[i] = projection.get(i).expression;
+                expressions[i] = projection[i].expression;
             }
             final FeatureType type = getType();
             final String[] names = 
FeatureUtilities.getNames(type.getProperties(false));
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/storage/GridCoverageResource.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/GridCoverageResource.java
index a0dfe3b..e30cf6b 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/storage/GridCoverageResource.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/GridCoverageResource.java
@@ -20,7 +20,6 @@ import java.util.List;
 import org.apache.sis.coverage.SampleDimension;
 import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.coverage.grid.GridCoverage;
-import org.apache.sis.internal.storage.query.CoverageQuery;
 import org.apache.sis.util.ArgumentChecks;
 
 
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/storage/Query.java 
b/storage/sis-storage/src/main/java/org/apache/sis/storage/Query.java
index 548f6bb..2aa4b8f 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/storage/Query.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/Query.java
@@ -17,6 +17,7 @@
 package org.apache.sis.storage;
 
 import org.opengis.feature.Feature;
+import org.opengis.filter.QueryExpression;
 
 
 /**
@@ -54,10 +55,8 @@ import org.opengis.feature.Feature;
  *
  * @since 0.8
  * @module
- *
- * @todo Retrofit {@link org.opengis.filter.QueryExpression}.
  */
-public abstract class Query {
+public abstract class Query implements QueryExpression {
     /*
      * Current version does not yet contain any field. But some fields may be 
added in the future.
      * For example some methods from 
org.apache.sis.internal.storage.query.FeatureQuery may move here.
diff --git 
a/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/query/CoverageQueryTest.java
 
b/storage/sis-storage/src/test/java/org/apache/sis/storage/CoverageQueryTest.java
similarity index 97%
rename from 
storage/sis-storage/src/test/java/org/apache/sis/internal/storage/query/CoverageQueryTest.java
rename to 
storage/sis-storage/src/test/java/org/apache/sis/storage/CoverageQueryTest.java
index b515937..eef4d96 100644
--- 
a/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/query/CoverageQueryTest.java
+++ 
b/storage/sis-storage/src/test/java/org/apache/sis/storage/CoverageQueryTest.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.internal.storage.query;
+package org.apache.sis.storage;
 
 import org.opengis.metadata.spatial.DimensionNameType;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
@@ -24,8 +24,6 @@ import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
 import org.apache.sis.referencing.crs.HardCodedCRS;
-import org.apache.sis.storage.DataStoreException;
-import org.apache.sis.storage.GridCoverageResource;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
diff --git 
a/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/query/SimpleQueryTest.java
 
b/storage/sis-storage/src/test/java/org/apache/sis/storage/FeatureQueryTest.java
similarity index 97%
rename from 
storage/sis-storage/src/test/java/org/apache/sis/internal/storage/query/SimpleQueryTest.java
rename to 
storage/sis-storage/src/test/java/org/apache/sis/storage/FeatureQueryTest.java
index a66c759..cbe7414 100644
--- 
a/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/query/SimpleQueryTest.java
+++ 
b/storage/sis-storage/src/test/java/org/apache/sis/storage/FeatureQueryTest.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.internal.storage.query;
+package org.apache.sis.storage;
 
 import java.util.List;
 import java.util.Arrays;
@@ -22,8 +22,6 @@ import java.util.stream.Collectors;
 import org.apache.sis.feature.builder.FeatureTypeBuilder;
 import org.apache.sis.internal.storage.MemoryFeatureSet;
 import org.apache.sis.filter.DefaultFilterFactory;
-import org.apache.sis.storage.DataStoreException;
-import org.apache.sis.storage.FeatureSet;
 import org.apache.sis.test.TestUtilities;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
@@ -49,7 +47,7 @@ import org.opengis.filter.SortOrder;
  * @since   1.0
  * @module
  */
-public final strictfp class SimpleQueryTest extends TestCase {
+public final strictfp class FeatureQueryTest extends TestCase {
     /**
      * An arbitrary amount of features, all of the same type.
      */
@@ -68,7 +66,7 @@ public final strictfp class SimpleQueryTest extends TestCase {
     /**
      * Creates a new test.
      */
-    public SimpleQueryTest() {
+    public FeatureQueryTest() {
         final FeatureTypeBuilder ftb = new FeatureTypeBuilder();
         ftb.setName("Test");
         ftb.addAttribute(Integer.class).setName("value1");
diff --git 
a/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/query/GridResourceMock.java
 
b/storage/sis-storage/src/test/java/org/apache/sis/storage/GridResourceMock.java
similarity index 98%
rename from 
storage/sis-storage/src/test/java/org/apache/sis/internal/storage/query/GridResourceMock.java
rename to 
storage/sis-storage/src/test/java/org/apache/sis/storage/GridResourceMock.java
index b984874..0971c06 100644
--- 
a/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/query/GridResourceMock.java
+++ 
b/storage/sis-storage/src/test/java/org/apache/sis/storage/GridResourceMock.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.internal.storage.query;
+package org.apache.sis.storage;
 
 import java.util.List;
 import java.util.Collections;
diff --git 
a/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java
 
b/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java
index a89fc63..1231745 100644
--- 
a/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java
+++ 
b/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java
@@ -46,8 +46,8 @@ import org.junit.BeforeClass;
     org.apache.sis.storage.ProbeResultTest.class,
     org.apache.sis.storage.StorageConnectorTest.class,
     org.apache.sis.storage.event.StoreListenersTest.class,
-    org.apache.sis.internal.storage.query.CoverageQueryTest.class,
-    org.apache.sis.internal.storage.query.SimpleQueryTest.class,
+    org.apache.sis.storage.CoverageQueryTest.class,
+    org.apache.sis.storage.FeatureQueryTest.class,
     org.apache.sis.internal.storage.xml.MimeTypeDetectorTest.class,
     org.apache.sis.internal.storage.xml.StoreProviderTest.class,
     org.apache.sis.internal.storage.xml.StoreTest.class,

Reply via email to