sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: When creating the FeatureType for a query or an expression, provide the FeatureTypeBuilder where the properties can be appended directly.
Date Tue, 20 Aug 2019 16:58:57 GMT
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


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new e553027  When creating the FeatureType for a query or an expression, provide the
FeatureTypeBuilder where the properties can be appended directly.
e553027 is described below

commit e553027bb7b272d446df6bee508e14cfa730cedf
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Tue Aug 20 18:56:17 2019 +0200

    When creating the FeatureType for a query or an expression, provide the FeatureTypeBuilder
where the properties can be appended directly.
---
 .../apache/sis/feature/DefaultAttributeType.java   |  2 +
 .../main/java/org/apache/sis/feature/Features.java | 35 +++++++++++++
 .../org/apache/sis/filter/ArithmeticFunction.java  | 25 ++++++++--
 .../java/org/apache/sis/filter/LeafExpression.java | 51 ++++++++++---------
 .../java/org/apache/sis/filter/NamedFunction.java  | 43 +++++++++++++---
 .../main/java/org/apache/sis/filter/ST_Buffer.java | 26 ++++------
 .../java/org/apache/sis/filter/ST_Centroid.java    | 26 ++++------
 .../java/org/apache/sis/filter/ST_Transform.java   | 24 +++------
 .../sis/internal/feature/FeatureExpression.java    | 57 +++++++++++++---------
 .../apache/sis/internal/feature/Geometries.java    | 14 ++++++
 .../org/apache/sis/internal/feature/Resources.java |  5 ++
 .../sis/internal/feature/Resources.properties      |  1 +
 .../sis/internal/feature/Resources_fr.properties   |  1 +
 .../sis/internal/storage/query/SimpleQuery.java    | 28 +++--------
 14 files changed, 211 insertions(+), 127 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java
b/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java
index cdb8258..535be02 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java
@@ -235,6 +235,8 @@ public class DefaultAttributeType<V> extends FieldType implements
AttributeType<
      * Returns the type of attribute values.
      *
      * @return the type of attribute values.
+     *
+     * @see Features#getValueClass(PropertyType)
      */
     @Override
     public final Class<V> getValueClass() {
diff --git a/core/sis-feature/src/main/java/org/apache/sis/feature/Features.java b/core/sis-feature/src/main/java/org/apache/sis/feature/Features.java
index 4852a3f..7ff600f 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/feature/Features.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/feature/Features.java
@@ -137,6 +137,41 @@ public final class Features extends Static {
     }
 
     /**
+     * Returns the type of values provided by the given property. For {@linkplain AttributeType
attributes}
+     * (which is the most common case), the value type is given by {@link AttributeType#getValueClass()}.
+     * For {@linkplain FeatureAssociationRole feature associations}, the value type is {@link
Feature}.
+     * For {@linkplain Operation operations}, the value type is determined recursively from
the
+     * {@linkplain Operation#getResult() operation result}.
+     * If the value type can not be determined, then this method returns {@code null}.
+     *
+     * @param  type  the property for which to get the type of values, or {@code null}.
+     * @return the type of values provided by the given property, or {@code null} if unknown.
+     *
+     * @see AttributeType#getValueClass()
+     *
+     * @since 1.0
+     */
+    public static Class<?> getValueClass(PropertyType type) {
+        while (type instanceof Operation) {
+            final IdentifiedType result = ((Operation) type).getResult();
+            if (result != type && result instanceof PropertyType) {
+                type = (PropertyType) result;
+            } else if (result instanceof FeatureType) {
+                return Feature.class;
+            } else {
+                break;
+            }
+        }
+        if (type instanceof AttributeType<?>) {
+            return ((AttributeType<?>) type).getValueClass();
+        } else if (type instanceof FeatureAssociationRole) {
+            return Feature.class;
+        } else {
+            return null;
+        }
+    }
+
+    /**
      * Returns the name of the type of values that the given property can take.
      * The type of value can be a {@link Class}, a {@link org.opengis.feature.FeatureType}
      * or another {@code PropertyType} depending on given argument:
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/ArithmeticFunction.java
b/core/sis-feature/src/main/java/org/apache/sis/filter/ArithmeticFunction.java
index fdda3dc..3a5705b 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/ArithmeticFunction.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/ArithmeticFunction.java
@@ -20,6 +20,8 @@ import java.math.BigDecimal;
 import java.math.BigInteger;
 import org.apache.sis.util.Numbers;
 import org.apache.sis.util.ArgumentChecks;
+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.Numerics;
 import org.apache.sis.math.Fraction;
@@ -27,7 +29,6 @@ import org.apache.sis.math.Fraction;
 // Branch-dependent imports
 import org.opengis.feature.AttributeType;
 import org.opengis.feature.FeatureType;
-import org.opengis.feature.PropertyType;
 import org.opengis.filter.expression.BinaryExpression;
 import org.opengis.filter.expression.Expression;
 import org.opengis.filter.expression.ExpressionVisitor;
@@ -68,6 +69,20 @@ abstract class ArithmeticFunction extends BinaryFunction implements BinaryExpres
     }
 
     /**
+     * Returns the type of results computed by this arithmetic function.
+     */
+    protected abstract AttributeType<Number> expectedType();
+
+    /**
+     * Provides the type of results computed by this expression. That type depends only
+     * on the {@code ArithmeticFunction} subclass and is given by {@link #expectedType()}.
+     */
+    @Override
+    public final PropertyTypeBuilder expectedType(FeatureType ignored, FeatureTypeBuilder
addTo) {
+        return addTo.addProperty(expectedType());
+    }
+
+    /**
      * Evaluates this expression based on the content of the given object. This method delegates
to
      * {@link #applyAsDouble(double, double)}, {@link #applyAsLong(long, long)} or similar
methods
      * depending on the value types.
@@ -123,7 +138,7 @@ abstract class ArithmeticFunction extends BinaryFunction implements BinaryExpres
 
         /** Description of results of the {@value #NAME} expression. */
         private static final AttributeType<Number> TYPE = createNumericType(NAME);
-        @Override public PropertyType expectedType(FeatureType type) {return TYPE;}
+        @Override protected AttributeType<Number> expectedType() {return TYPE;}
 
         /** Creates a new expression for the {@value #NAME} operation. */
         Add(Expression expression1, Expression expression2) {
@@ -157,7 +172,7 @@ abstract class ArithmeticFunction extends BinaryFunction implements BinaryExpres
 
         /** Description of results of the {@value #NAME} expression. */
         private static final AttributeType<Number> TYPE = createNumericType(NAME);
-        @Override public PropertyType expectedType(FeatureType type) {return TYPE;}
+        @Override protected AttributeType<Number> expectedType() {return TYPE;}
 
         /** Creates a new expression for the {@value #NAME} operation. */
         Subtract(Expression expression1, Expression expression2) {
@@ -191,7 +206,7 @@ abstract class ArithmeticFunction extends BinaryFunction implements BinaryExpres
 
         /** Description of results of the {@value #NAME} expression. */
         private static final AttributeType<Number> TYPE = createNumericType(NAME);
-        @Override public PropertyType expectedType(FeatureType type) {return TYPE;}
+        @Override protected AttributeType<Number> expectedType() {return TYPE;}
 
         /** Creates a new expression for the {@value #NAME} operation. */
         Multiply(Expression expression1, Expression expression2) {
@@ -225,7 +240,7 @@ abstract class ArithmeticFunction extends BinaryFunction implements BinaryExpres
 
         /** Description of results of the {@value #NAME} expression. */
         private static final AttributeType<Number> TYPE = createNumericType(NAME);
-        @Override public PropertyType expectedType(FeatureType type) {return TYPE;}
+        @Override protected AttributeType<Number> expectedType() {return TYPE;}
 
         /** Creates a new expression for the {@value #NAME} operation. */
         Divide(Expression expression1, Expression expression2) {
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/LeafExpression.java b/core/sis-feature/src/main/java/org/apache/sis/filter/LeafExpression.java
index 7b90b5a..872ced0 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/LeafExpression.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/LeafExpression.java
@@ -26,8 +26,8 @@ import org.apache.sis.util.ObjectConverters;
 import org.apache.sis.util.UnconvertibleObjectException;
 import org.apache.sis.util.collection.WeakValueHashMap;
 import org.apache.sis.internal.feature.FeatureExpression;
-import org.apache.sis.feature.DefaultAssociationRole;
-import org.apache.sis.util.resources.Errors;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
 
 // Branch-dependent imports
 import org.opengis.feature.Feature;
@@ -130,7 +130,7 @@ abstract class LeafExpression extends Node implements Expression, FeatureExpress
          *   <li>A {@link Map}, in which case {@link Map#get(Object)} will be invoked.</li>
          * </ul>
          *
-         * If no value is found for the given property, then this method returns {@code null}.
+         * If no value is found for the given feature, then this method returns {@code null}.
          */
         @Override
         public Object evaluate(final Object candidate) {
@@ -146,25 +146,27 @@ abstract class LeafExpression extends Node implements Expression, FeatureExpress
         }
 
         /**
-         * Returns the expected type of values produced by this expression when a feature
of the given type is evaluated.
+         * Provides the expected type of values produced by this expression when a feature
of the given type is evaluated.
          *
+         * @param  valueType  the type of features to be evaluated by the given expression.
+         * @param  addTo      where to add the type of properties evaluated by the given
expression.
+         * @return builder of the added property, or {@code null} if this method can not
add a property.
          * @throws IllegalArgumentException if this method can not determine the property
type for the given feature type.
          */
         @Override
-        public PropertyType expectedType(final FeatureType type) {
-            PropertyType propertyType = type.getProperty(name);         // May throw IllegalArgumentException.
-            while (propertyType instanceof Operation) {
-                final IdentifiedType it = ((Operation) propertyType).getResult();
-                if (it instanceof PropertyType) {
-                    propertyType = (PropertyType) it;
-                } else if (it instanceof FeatureType) {
-                    return new DefaultAssociationRole(Collections.singletonMap(DefaultAssociationRole.NAME_KEY,
name), type, 1, 1);
+        public PropertyTypeBuilder expectedType(final FeatureType valueType, final FeatureTypeBuilder
addTo) {
+            PropertyType type = valueType.getProperty(name);        // May throw IllegalArgumentException.
+            while (type instanceof Operation) {
+                final IdentifiedType result = ((Operation) type).getResult();
+                if (result != type && result instanceof PropertyType) {
+                    type = (PropertyType) result;
+                } else if (result instanceof FeatureType) {
+                    return addTo.addAssociation((FeatureType) result).setName(name);
                 } else {
-                    throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalPropertyValueClass_3,
-                                name, PropertyType.class, Classes.getStandardType(Classes.getClass(it))));
+                    return null;
                 }
             }
-            return propertyType;
+            return addTo.addProperty(type);
         }
 
         /** Implementation of the visitor pattern. */
@@ -214,21 +216,24 @@ abstract class LeafExpression extends Node implements Expression, FeatureExpress
         }
 
         /**
-         * Returns the type of values returned by {@link #evaluate(Object)},
-         * wrapped in an {@code AttributeType} named "Literal".
+         * Provides the type of values returned by {@link #evaluate(Object)}
+         * wrapped in an {@link AttributeType} named "Literal".
+         *
+         * @param  addTo  where to add the type of properties evaluated by the given expression.
+         * @return builder of the added property.
          */
         @Override
-        public AttributeType<?> expectedType(FeatureType ignored) {
+        public PropertyTypeBuilder expectedType(FeatureType ignored, final FeatureTypeBuilder
addTo) {
             final Class<?> valueType = value.getClass();
-            AttributeType<?> type = TYPES.get(valueType);
-            if (type == null) {
+            AttributeType<?> propertyType = TYPES.get(valueType);
+            if (propertyType == null) {
                 final Class<?> standardType = Classes.getStandardType(valueType);
-                type = TYPES.computeIfAbsent(standardType, Literal::newType);
+                propertyType = TYPES.computeIfAbsent(standardType, Literal::newType);
                 if (valueType != standardType) {
-                    TYPES.put(valueType, type);
+                    TYPES.put(valueType, propertyType);
                 }
             }
-            return type;
+            return addTo.addProperty(propertyType);
         }
 
         /**
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/NamedFunction.java b/core/sis-feature/src/main/java/org/apache/sis/filter/NamedFunction.java
index c41923e..b02aeae 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/NamedFunction.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/NamedFunction.java
@@ -19,7 +19,6 @@ package org.apache.sis.filter;
 import java.util.List;
 import java.util.Collection;
 import org.opengis.feature.FeatureType;
-import org.opengis.feature.PropertyType;
 import org.opengis.filter.expression.Expression;
 import org.opengis.filter.expression.ExpressionVisitor;
 import org.opengis.filter.expression.Function;
@@ -27,8 +26,13 @@ import org.opengis.filter.expression.Literal;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.ObjectConverters;
 import org.apache.sis.util.UnconvertibleObjectException;
-import org.apache.sis.internal.feature.FeatureExpression;
 import org.apache.sis.internal.util.UnmodifiableArrayList;
+import org.apache.sis.internal.feature.Resources;
+import org.apache.sis.internal.feature.Geometries;
+import org.apache.sis.internal.feature.FeatureExpression;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.apache.sis.feature.builder.AttributeTypeBuilder;
 
 
 /**
@@ -150,20 +154,22 @@ abstract class NamedFunction extends Node implements Function {
     }
 
     /**
-     * Returns the type of results computed by the parameters at given index.
-     * This method applies heuristic rules documented in {@link FeatureExpression}.
+     * Copies into the given builder the property type evaluated by the expression at the
specified index.
+     * The property type is determined by the heuristic rules documented in {@link FeatureExpression}.
+     * This method returns the the property type builder for allowing caller to modify the
property.
      *
      * @param  parameter  index of the expression for which to get the result type.
      * @param  valueType  the type of features to be evaluated by the given expression.
-     * @return expected type resulting from expression evaluation (never null).
+     * @param  addTo      where to add the type of properties evaluated by the specified
expression.
+     * @return builder of type resulting from expression evaluation (never null).
      * @throws IllegalArgumentException if this method can operate only on some feature types
      *         and the given type is not one of them.
      * @throws InvalidExpressionException if this method can not determine the result type
of the expression
      *         at given index. It may be because that expression is backed by an unsupported
implementation.
      */
-    final PropertyType expectedType(final int parameter, final FeatureType valueType) {
+    final PropertyTypeBuilder copyType(final int parameter, final FeatureType valueType,
final FeatureTypeBuilder addTo) {
         final Expression expression = parameters.get(parameter);
-        final PropertyType pt = FeatureExpression.expectedType(expression, valueType);
+        final PropertyTypeBuilder pt = FeatureExpression.expectedType(expression, valueType,
addTo);
         if (pt == null) {
             throw new InvalidExpressionException(expression, parameter);
         }
@@ -171,6 +177,29 @@ abstract class NamedFunction extends Node implements Function {
     }
 
     /**
+     * Copies into the given builder the property type evaluated by the first expression.
+     * That property must be an attribute storing geometric object, otherwise an
+     * {@link IllegalArgumentException} will be thrown.
+     *
+     * @param  valueType  the type of features to be evaluated by the given expression.
+     * @param  addTo      where to add the type of properties evaluated by the specified
expression.
+     * @return builder of type resulting from expression evaluation (never null).
+     * @throws IllegalArgumentException if the given feature type does not contain the expected
properties.
+     * @throws InvalidExpressionException if this method can not determine the result type
of the expression
+     *         at given index. It may be because that expression is backed by an unsupported
implementation.
+     */
+    final AttributeTypeBuilder<?> copyGeometryType(final FeatureType valueType, final
FeatureTypeBuilder addTo) {
+        final PropertyTypeBuilder type = copyType(0, valueType, addTo);
+        if (type instanceof AttributeTypeBuilder<?>) {
+            AttributeTypeBuilder<?> att = (AttributeTypeBuilder<?>) type;
+            if (Geometries.isKnownType(att.getValueClass())) {
+                return att;
+            }
+        }
+        throw new IllegalArgumentException(Resources.format(Resources.Keys.NotAGeometryAtFirstExpression));
+    }
+
+    /**
      * Implementation of the visitor pattern.
      * Not used in Apache SIS implementation.
      */
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Buffer.java b/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Buffer.java
index c41532a..46e8fca 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Buffer.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Buffer.java
@@ -16,14 +16,14 @@
  */
 package org.apache.sis.filter;
 
+import org.apache.sis.feature.builder.AttributeTypeBuilder;
 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.feature.Geometries;
 import org.apache.sis.util.ArgumentChecks;
 import org.locationtech.jts.geom.Geometry;
-import org.opengis.feature.AttributeType;
 import org.opengis.feature.FeatureType;
-import org.opengis.feature.PropertyType;
 import org.opengis.filter.expression.Expression;
 import org.opengis.util.FactoryException;
 
@@ -95,22 +95,16 @@ final class ST_Buffer extends NamedFunction implements FeatureExpression
{
     }
 
     /**
-     * Returns the expected type of values produced by this expression when a feature of
the given
-     * type is evaluated.
+     * Provides the type of values produced by this expression when a feature of the given
type is evaluated.
      *
-     * @param  valueType  the type of features on which to apply the expression.
-     * @return expected expression result type.
-     * @throws IllegalArgumentException if this method can not determine the property type
for the given feature type.
+     * @param  valueType  the type of features on which to apply this expression.
+     * @param  addTo      where to add the type of properties evaluated by this expression.
+     * @return builder of type resulting from expression evaluation (never null).
+     * @throws IllegalArgumentException if the given feature type does not contain the expected
properties.
      */
     @Override
-    public PropertyType expectedType(final FeatureType valueType) {
-        final PropertyType expectedType = expectedType(0, valueType);
-        if (expectedType instanceof AttributeType<?>) {
-            AttributeType<?> att = (AttributeType<?>) expectedType;
-            if (Geometries.isKnownType(att.getValueClass())) {
-                return new FeatureTypeBuilder().addAttribute(att).setValueClass(Geometry.class).build();
-            }
-        }
-        throw new IllegalArgumentException("First expression must result in a geometric attribute");
       // TODO: localize.
+    public PropertyTypeBuilder expectedType(final FeatureType valueType, final FeatureTypeBuilder
addTo) {
+        final AttributeTypeBuilder<?> pt = copyGeometryType(valueType, addTo);
+        return pt.setValueClass(Geometries.implementation(pt.getValueClass()).rootClass);
     }
 }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Centroid.java b/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Centroid.java
index d1df586..665bcf1 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Centroid.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Centroid.java
@@ -16,15 +16,15 @@
  */
 package org.apache.sis.filter;
 
+import org.apache.sis.feature.builder.AttributeTypeBuilder;
 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.feature.Geometries;
 import org.apache.sis.util.ArgumentChecks;
 import org.locationtech.jts.geom.Geometry;
 import org.locationtech.jts.geom.Point;
-import org.opengis.feature.AttributeType;
 import org.opengis.feature.FeatureType;
-import org.opengis.feature.PropertyType;
 import org.opengis.filter.expression.Expression;
 import org.opengis.util.FactoryException;
 
@@ -93,22 +93,16 @@ final class ST_Centroid extends NamedFunction implements FeatureExpression
{
     }
 
     /**
-     * Returns the expected type of values produced by this expression when a feature of
the given
-     * type is evaluated.
+     * Provides the type of values produced by this expression when a feature of the given
type is evaluated.
      *
-     * @param  valueType  the type of features on which to apply the expression.
-     * @return expected expression result type.
-     * @throws IllegalArgumentException if this method can not determine the property type
for the given feature type.
+     * @param  valueType  the type of features on which to apply this expression.
+     * @param  addTo      where to add the type of properties evaluated by this expression.
+     * @return builder of type resulting from expression evaluation (never null).
+     * @throws IllegalArgumentException if the given feature type does not contain the expected
properties.
      */
     @Override
-    public PropertyType expectedType(final FeatureType valueType) {
-        final PropertyType expectedType = expectedType(0, valueType);
-        if (expectedType instanceof AttributeType<?>) {
-            AttributeType<?> att = (AttributeType<?>) expectedType;
-            if (Geometries.isKnownType(att.getValueClass())) {
-                return new FeatureTypeBuilder().addAttribute(att).setValueClass(Point.class).build();
-            }
-        }
-        throw new IllegalArgumentException("First expression must result in a geometric attribute");
       // TODO: localize.
+    public PropertyTypeBuilder expectedType(final FeatureType valueType, final FeatureTypeBuilder
addTo) {
+        final AttributeTypeBuilder<?> pt = copyGeometryType(valueType, addTo);
+        return pt.setValueClass(Geometries.implementation(pt.getValueClass()).pointClass);
     }
 }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Transform.java b/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Transform.java
index 33059da..7048f03 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Transform.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Transform.java
@@ -20,14 +20,13 @@ import java.util.Objects;
 import java.io.IOException;
 import java.io.InvalidObjectException;
 import java.io.ObjectInputStream;
-import org.opengis.feature.AttributeType;
 import org.opengis.feature.FeatureType;
-import org.opengis.feature.PropertyType;
 import org.opengis.filter.expression.Expression;
 import org.opengis.filter.expression.Literal;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.operation.TransformException;
 import org.opengis.util.FactoryException;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
 import org.apache.sis.feature.builder.FeatureTypeBuilder;
 import org.apache.sis.internal.feature.FeatureExpression;
 import org.apache.sis.internal.feature.Geometries;
@@ -186,22 +185,15 @@ final class ST_Transform extends NamedFunction implements FeatureExpression
{
     }
 
     /**
-     * Returns the expected type of values produced by this expression when a feature of
the given
-     * type is evaluated.
+     * Provides the type of values produced by this expression when a feature of the given
type is evaluated.
      *
-     * @param  valueType  the type of features on which to apply the expression.
-     * @return expected expression result type.
-     * @throws IllegalArgumentException if this method can not determine the property type
for the given feature type.
+     * @param  valueType  the type of features on which to apply this expression.
+     * @param  addTo      where to add the type of properties evaluated by this expression.
+     * @return builder of type resulting from expression evaluation (never null).
+     * @throws IllegalArgumentException if the given feature type does not contain the expected
properties.
      */
     @Override
-    public PropertyType expectedType(final FeatureType valueType) {
-        final PropertyType expectedType = expectedType(0, valueType);
-        if (expectedType instanceof AttributeType<?>) {
-            AttributeType<?> att = (AttributeType<?>) expectedType;
-            if (Geometries.isKnownType(att.getValueClass())) {
-                return new FeatureTypeBuilder().addAttribute(att).setCRS(literalCRS ? targetCRS
: null).build();
-            }
-        }
-        throw new IllegalArgumentException("First expression must result in a geometric attribute");
       // TODO: localize.
+    public PropertyTypeBuilder expectedType(final FeatureType valueType, final FeatureTypeBuilder
addTo) {
+        return copyGeometryType(valueType, addTo).setCRS(literalCRS ? targetCRS : null);
     }
 }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/FeatureExpression.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/FeatureExpression.java
index 01d954f..1aeea6b 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/FeatureExpression.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/FeatureExpression.java
@@ -16,73 +16,82 @@
  */
 package org.apache.sis.internal.feature;
 
-// Branch-dependent imports
 import org.opengis.feature.FeatureType;
 import org.opengis.feature.PropertyType;
+import org.opengis.feature.AttributeType;
 import org.opengis.filter.expression.Expression;
 import org.apache.sis.internal.util.CollectionsExt;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
 
 
 /**
  * OGC expressions or other functions operating on feature instances.
- * This interface adds an additional method, {@link #expectedType(FeatureType)},
+ * This interface adds an additional method, {@link #expectedType(FeatureType, FeatureTypeBuilder)},
  * for fetching in advance the expected type of expression results.
  *
  * <p>This is an experimental interface which may be removed in any future version.</p>
  *
  * @author  Johann Sorel (Geomatys)
+ * @author  Martin Desruisseaux (Geomatys)
  * @version 1.0
  * @since   1.0
  * @module
  */
 public interface FeatureExpression {
     /**
-     * Returns the expected type of values produced by this expression when a feature of
the given
+     * Provides the expected type of values produced by this expression when a feature of
the given
      * type is evaluated. The resulting type shall describe a "static" property, i.e. it
can be an
-     * {@link org.opengis.feature.AttributeType} or a {@link org.opengis.feature.FeatureAssociationRole}
+     * {@link AttributeType} or a {@link org.opengis.feature.FeatureAssociationRole}
      * but not an {@link org.opengis.feature.Operation}.
      *
      * @param  valueType  the type of features to be evaluated by the given expression.
-     * @return expected type resulting from expression evaluation, or {@code null} if unknown.
+     * @param  addTo      where to add the type of properties evaluated by this expression.
+     * @return builder of the added property, or {@code null} if this method can not add
a property.
      * @throws IllegalArgumentException if this method can operate only on some feature types
      *         and the given type is not one of them.
      */
-    PropertyType expectedType(FeatureType valueType);
+    PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo);
 
     /**
-     * Returns the type of results computed by the given expression, or {@code null} if unknown.
-     * This method returns the first of the following choices that apply:
+     * Provides the type of results computed by the given expression.
+     * This method executes the first of the following choices that apply:
      *
      * <ol>
-     *   <li>If the expression implements {@link FeatureExpression}, delegate to {@link
#expectedType(FeatureType)}.
-     *       Note that the invoked method may throw an {@link IllegalArgumentException}.</li>
+     *   <li>If the expression implements {@link FeatureExpression}, delegate to {@link
#expectedType(FeatureType,
+     *       FeatureTypeBuilder)}. Note that the invoked method may throw an {@link IllegalArgumentException}.</li>
      *   <li>Otherwise if {@link Expression#evaluate(Object, Class)} with a {@code
PropertyType.class} argument
-     *       returns a non-null value, returns that value.</li>
+     *       returns a non-null property, adds that property to the given builder.</li>
      *   <li>Otherwise if the given feature type contains exactly one property (including
inherited properties),
-     *       returns that property.</li>
+     *       adds that property to the given builder.</li>
      *   <li>Otherwise returns {@code null}.</li>
      * </ol>
      *
-     * It is caller's responsibility to verify if the returned value is {@code null} and
to throw an exception in such case.
+     * It is caller's responsibility to verify if this method returns {@code null} and to
throw an exception in such case.
      * We leave that responsibility to the caller because (s)he may be able to provide better
error messages.
      *
-     * @param  parameter  the expression for which to get the result type, or {@code null}.
-     * @param  valueType  the type of features to be evaluated by the given expression.
-     * @return expected type resulting from expression evaluation, or {@code null} if unknown.
+     * @param  expression  the expression for which to get the result type, or {@code null}.
+     * @param  valueType   the type of features to be evaluated by the given expression.
+     * @param  addTo       where to add the type of properties evaluated by the given expression.
+     * @return builder of the added property, or {@code null} if this method can not add
a property.
      * @throws IllegalArgumentException if this method can operate only on some feature types
      *         and the given type is not one of them.
      */
-    public static PropertyType expectedType(final Expression parameter, final FeatureType
valueType) {
-        if (parameter instanceof FeatureExpression) {
-            return ((FeatureExpression) parameter).expectedType(valueType);
+    public static PropertyTypeBuilder expectedType(final Expression expression, final FeatureType
valueType, final FeatureTypeBuilder addTo) {
+        if (expression instanceof FeatureExpression) {
+            return ((FeatureExpression) expression).expectedType(valueType, addTo);
         }
-        if (parameter != null) {
+        PropertyType pt = null;
+        if (expression != null) {
             // TODO: remove this hack if we can get more type-safe Expression.
-            PropertyType pt = parameter.evaluate(valueType, PropertyType.class);
-            if (pt != null) {
-                return pt;
+            pt = expression.evaluate(valueType, PropertyType.class);
+        }
+        if (pt == null) {
+            pt = CollectionsExt.singletonOrNull(valueType.getProperties(true));
+            if (pt == null) {
+                return null;
             }
         }
-        return CollectionsExt.singletonOrNull(valueType.getProperties(true));
+        return addTo.addProperty(pt);
     }
 }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
index 40dd2e1..0ed326d 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
@@ -137,6 +137,20 @@ public abstract class Geometries<G> {
     }
 
     /**
+     * Returns an accessor to the library implementation for a geometry of the given type.
+     * If the given type is not recognized, then this method returns the default library.
+     *
+     * @param  type  the type to verify.
+     * @return a geometry implementation compatible with the given type.
+     */
+    public static Geometries<?> implementation(final Class<?> type) {
+        for (Geometries<?> g = implementation; g != null; g = g.fallback) {
+            if (g.rootClass.isAssignableFrom(type)) return g;
+        }
+        return implementation;
+    }
+
+    /**
      * Returns {@code true} if the given type is one of the types known to Apache SIS.
      *
      * @param  type  the type to verify.
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java
index c15ee72..44d8258 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java
@@ -251,6 +251,11 @@ public final class Resources extends IndexedResourceBundle {
         public static final short NonLinearInDimensions_1 = 45;
 
         /**
+         * Value provided by first expression is not a geometry.
+         */
+        public static final short NotAGeometryAtFirstExpression = 57;
+
+        /**
          * Property “{0}” contains more than one value.
          */
         public static final short NotASingleton_1 = 14;
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties
index 8904f41..b63161e 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties
@@ -56,6 +56,7 @@ MismatchedValueClass_3            = An attribute for \u2018{1}\u2019 values
wher
 NoCategoryForValue_1              = No category for value {0}.
 NoNDimensionalSlice_3             = Can not infer a {0}-dimensional slice from the grid envelope.
Dimension {1} has {2,number} cells.
 NonLinearInDimensions_1           = non-linear in {0} dimension{0,choice,1#|2#s}:
+NotAGeometryAtFirstExpression     = Value provided by first expression is not a geometry.
 NotASingleton_1                   = Property \u201c{0}\u201d contains more than one value.
 NotStrictlyOrderedDimensions      = The specified dimensions are not in strictly ascending
order.
 OptionalLibraryNotFound_2         = The {0} optional library is not available. Geometric
operations will ignore that library.\nCause is {1}.
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties
index 2d2d051..dbfee3f 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties
@@ -61,6 +61,7 @@ MismatchedValueClass_3            = Un attribut pour des valeurs de type
\u2018{
 NoCategoryForValue_1              = Aucune cat\u00e9gorie n\u2019est d\u00e9finie pour la
valeur {0}.
 NoNDimensionalSlice_3             = Ne peut pas inf\u00e9rer une tranche \u00e0 {0} dimensions
\u00e0 partir de l\u2019enveloppe de la grille. La dimension {1} a {2,number} cellules.
 NonLinearInDimensions_1           = non-lin\u00e9aire dans {0} dimension{0,choice,1#|2#s}\u2008:
+NotAGeometryAtFirstExpression     = La valeur fournie par la premi\u00e8re expression n\u2019est
pas une g\u00e9om\u00e9trie.
 NotASingleton_1                   = La propri\u00e9t\u00e9 \u00ab\u202f{0}\u202f\u00bb contient
plus de une valeur.
 NotStrictlyOrderedDimensions      = Les dimensions sp\u00e9cifi\u00e9es ne sont pas en ordre
strictement croissant.
 OptionalLibraryNotFound_2         = La biblioth\u00e8que optionnelle {0} n\u2019est pas disponible.
Les op\u00e9rations g\u00e9om\u00e9triques ignoreront cette biblioth\u00e8que.\nLa cause est
{1}.
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/SimpleQuery.java
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/SimpleQuery.java
index 3b675f7..8752f73 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/SimpleQuery.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/query/SimpleQuery.java
@@ -24,6 +24,7 @@ import java.util.Objects;
 import org.opengis.util.GenericName;
 import org.apache.sis.filter.InvalidExpressionException;
 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.storage.Resources;
@@ -33,13 +34,9 @@ import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.Classes;
 import org.apache.sis.util.collection.Containers;
 import org.apache.sis.util.iso.Names;
-import org.apache.sis.util.resources.Errors;
 
 // Branch-dependent imports
-import org.opengis.feature.AttributeType;
-import org.opengis.feature.FeatureAssociationRole;
 import org.opengis.feature.FeatureType;
-import org.opengis.feature.PropertyType;
 import org.opengis.filter.Filter;
 import org.opengis.filter.expression.Expression;
 import org.opengis.filter.sort.SortBy;
@@ -306,11 +303,11 @@ public class SimpleQuery extends Query {
         }
 
         /**
-         * Returns the type of results computed by this column.
+         * Adds in the given builder the type of results computed by this column.
          *
-         * @param  valueType  the type of features to be evaluated by the expression in this
column.
          * @param  column     index of this column. Used for error message only.
-         * @return expected type resulting from expression evaluation (never null).
+         * @param  valueType  the type of features to be evaluated by the expression in this
column.
+         * @param  addTo      where to add the type of properties evaluated by expression
in this column.
          * @throws IllegalArgumentException if this method can operate only on some feature
types
          *         and the given type is not one of them.
          * @throws InvalidExpressionException if this method can not determine the result
type of the expression
@@ -318,23 +315,14 @@ public class SimpleQuery extends Query {
          *
          * @see SimpleQuery#expectedType(FeatureType)
          */
-        final PropertyType expectedType(final FeatureType valueType, final int column) {
-            PropertyType resultType = FeatureExpression.expectedType(expression, valueType);
+        final void expectedType(final int column, final FeatureType valueType, final FeatureTypeBuilder
addTo) {
+            final PropertyTypeBuilder resultType = FeatureExpression.expectedType(expression,
valueType, addTo);
             if (resultType == null) {
                 throw new InvalidExpressionException(expression, column);
             }
-            /*
-             * If a name has been explicitly given, rename the result type.
-             * We allow renaming only for attributes and associations.
-             */
             if (alias != null && !alias.equals(resultType.getName())) {
-                resultType = new FeatureTypeBuilder().addProperty(resultType).setName(alias).build();
-                if (!(resultType instanceof AttributeType<?>) && !(resultType
instanceof FeatureAssociationRole)) {
-                    throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalPropertyValueClass_3,
-                                alias, AttributeType.class, Classes.getStandardType(Classes.getClass(resultType))));
-                }
+                resultType.setName(alias);
             }
-            return resultType;
         }
 
         /**
@@ -422,7 +410,7 @@ public class SimpleQuery extends Query {
         }
         final FeatureTypeBuilder ftb = new FeatureTypeBuilder().setName(valueType.getName());
         for (int i=0; i<columns.length; i++) {
-            ftb.addProperty(columns[i].expectedType(valueType, i));
+            columns[i].expectedType(i, valueType, ftb);
         }
         return ftb.build();
     }


Mime
View raw message