sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ama...@apache.org
Subject [sis] 24/45: WIP(SQLStore): Start geometric function implementation.
Date Tue, 12 Nov 2019 16:44:51 GMT
This is an automated email from the ASF dual-hosted git repository.

amanin pushed a commit to branch refactor/sql-store
in repository https://gitbox.apache.org/repos/asf/sis.git

commit 4865a1f535d2afcb36ef2ca59bb89246b532229c
Author: Alexis Manin <amanin@apache.org>
AuthorDate: Thu Oct 3 18:05:58 2019 +0200

    WIP(SQLStore): Start geometric function implementation.
---
 .../java/org/apache/sis/filter/ST_Envelope.java    |  30 ++++
 .../java/org/apache/sis/internal/feature/ESRI.java |  29 ++--
 .../apache/sis/internal/feature/Geometries.java    |  77 +++++-----
 .../java/org/apache/sis/internal/feature/JTS.java  |  31 +++--
 .../sis/internal/sql/feature/ANSIInterpreter.java  | 155 ++++++++++++++++++---
 5 files changed, 248 insertions(+), 74 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Envelope.java b/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Envelope.java
new file mode 100644
index 0000000..6adf144
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Envelope.java
@@ -0,0 +1,30 @@
+package org.apache.sis.filter;
+
+import org.opengis.feature.FeatureType;
+import org.opengis.feature.PropertyType;
+import org.opengis.filter.expression.Expression;
+
+import org.apache.sis.internal.feature.FeatureExpression;
+
+/**
+ *
+ */
+public class ST_Envelope extends AbstractFunction implements FeatureExpression {
+
+    public static final String NAME = "ST_Envelope";
+
+    public ST_Envelope(Expression[] parameters) {
+        super(NAME, parameters, null);
+        if (parameters.length > 1) throw new IllegalArgumentException("Only one parameter
is accepted: source Geometry");
+    }
+
+    @Override
+    public Object evaluate(Object object) {
+        throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)"
on 03/10/2019
+    }
+
+    @Override
+    public PropertyType expectedType(FeatureType type) {
+        throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)"
on 03/10/2019
+    }
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/ESRI.java b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/ESRI.java
index bbd8780..601b6b7 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/ESRI.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/ESRI.java
@@ -17,22 +17,26 @@
 package org.apache.sis.internal.feature;
 
 import java.util.Iterator;
-import com.esri.core.geometry.Geometry;
+
+import org.opengis.geometry.Envelope;
+
+import org.apache.sis.geometry.GeneralEnvelope;
+import org.apache.sis.math.Vector;
+import org.apache.sis.setup.GeometryLibrary;
+import org.apache.sis.util.Classes;
+
 import com.esri.core.geometry.Envelope2D;
+import com.esri.core.geometry.Geometry;
 import com.esri.core.geometry.MultiPath;
-import com.esri.core.geometry.Polyline;
-import com.esri.core.geometry.Polygon;
+import com.esri.core.geometry.OperatorExportToWkt;
+import com.esri.core.geometry.OperatorImportFromWkt;
 import com.esri.core.geometry.Point;
 import com.esri.core.geometry.Point2D;
 import com.esri.core.geometry.Point3D;
-import com.esri.core.geometry.WktImportFlags;
+import com.esri.core.geometry.Polygon;
+import com.esri.core.geometry.Polyline;
 import com.esri.core.geometry.WktExportFlags;
-import com.esri.core.geometry.OperatorImportFromWkt;
-import com.esri.core.geometry.OperatorExportToWkt;
-import org.apache.sis.geometry.GeneralEnvelope;
-import org.apache.sis.setup.GeometryLibrary;
-import org.apache.sis.math.Vector;
-import org.apache.sis.util.Classes;
+import com.esri.core.geometry.WktImportFlags;
 
 
 /**
@@ -84,6 +88,11 @@ final class ESRI extends Geometries<Geometry> {
         return null;
     }
 
+    @Override
+    Object tryConvertToGeometry(Envelope env) {
+        throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)"
on 03/10/2019
+    }
+
     /**
      * If the given point is an implementation of this library, returns its coordinate.
      * Otherwise returns {@code null}. If non-null, the returned array may have a length
of 2 or 3.
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 a30b2a2..df36cf6 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
@@ -17,13 +17,19 @@
 package org.apache.sis.internal.feature;
 
 import java.util.Iterator;
+import java.util.Optional;
+import java.util.function.Function;
 import java.util.logging.Level;
 import java.util.logging.LogRecord;
-import org.apache.sis.util.logging.Logging;
-import org.apache.sis.internal.system.Loggers;
+
+import org.opengis.geometry.Envelope;
+import org.opengis.geometry.Geometry;
+
 import org.apache.sis.geometry.GeneralEnvelope;
-import org.apache.sis.setup.GeometryLibrary;
+import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.math.Vector;
+import org.apache.sis.setup.GeometryLibrary;
+import org.apache.sis.util.logging.Logging;
 
 
 /**
@@ -144,6 +150,13 @@ public abstract class Geometries<G> {
         return false;
     }
 
+    public static Optional<Geometry> toGeometry(final Envelope env) {
+        return findStrategy(g -> g.tryConvertToGeometry(env))
+                .map(result -> new GeometryWrapper(result, env));
+    }
+
+    abstract Object tryConvertToGeometry(final Envelope env);
+
     /**
      * If the given point is an implementation of this library, returns its coordinate.
      * Otherwise returns {@code null}.
@@ -163,11 +176,7 @@ public abstract class Geometries<G> {
      * @see #createPoint(double, double)
      */
     public static double[] getCoordinate(final Object point) {
-        for (Geometries<?> g = implementation; g != null; g = g.fallback) {
-            double[] coord = g.tryGetCoordinate(point);
-            if (coord != null) return coord;
-        }
-        return null;
+        return findStrategy(g -> g.tryGetCoordinate(point)).orElse(null);
     }
 
     /**
@@ -186,11 +195,7 @@ public abstract class Geometries<G> {
      *         is not a recognized geometry or its envelope is empty.
      */
     public static GeneralEnvelope getEnvelope(final Object geometry) {
-        for (Geometries<?> g = implementation; g != null; g = g.fallback) {
-            GeneralEnvelope env = g.tryGetEnvelope(geometry);
-            if (env != null) return env;
-        }
-        return null;
+        return findStrategy(g -> g.tryGetEnvelope(geometry)).orElse(null);
     }
 
     /**
@@ -208,18 +213,19 @@ public abstract class Geometries<G> {
      *         object is not a recognized geometry.
      */
     public static String toString(final Object geometry) {
-        for (Geometries<?> g = implementation; g != null; g = g.fallback) {
-            String s = g.tryGetLabel(geometry);
-            if (s != null) {
-                GeneralEnvelope env = g.tryGetEnvelope(geometry);
-                if (env != null) {
-                    final String bbox = env.toString();
-                    s += bbox.substring(bbox.indexOf('('));
-                }
-                return s;
+        return findStrategy(g -> g.tryToString(geometry)).orElse(null);
+    }
+
+    private String tryToString(Object geometry) {
+        String s = tryGetLabel(geometry);
+        if (s != null) {
+            GeneralEnvelope env = tryGetEnvelope(geometry);
+            if (env != null) {
+                final String bbox = env.toString();
+                s += bbox.substring(bbox.indexOf('('));
             }
         }
-        return null;
+        return s;
     }
 
     /**
@@ -233,11 +239,8 @@ public abstract class Geometries<G> {
      * @return the Well Known Text for the given geometry, or {@code null} if the given object
is unrecognized.
      */
     public static String formatWKT(Object geometry, double flatness) {
-        for (Geometries<?> g = implementation; g != null; g = g.fallback) {
-            String wkt = g.tryFormatWKT(geometry, flatness);
-            if (wkt != null) return wkt;
-        }
-        return null;
+        return findStrategy(g -> g.tryFormatWKT(geometry, flatness))
+                .orElse(null);
     }
 
     /**
@@ -304,13 +307,8 @@ public abstract class Geometries<G> {
         while (paths.hasNext()) {
             final Object first = paths.next();
             if (first != null) {
-                for (Geometries<?> g = implementation; g != null; g = g.fallback) {
-                    final Object merged = g.tryMergePolylines(first, paths);
-                    if (merged != null) {
-                        return merged;
-                    }
-                }
-                throw unsupported(2);
+                return findStrategy(g -> g.tryMergePolylines(first, paths))
+                        .orElseThrow(() -> unsupported(2));
             }
         }
         return null;
@@ -324,4 +322,13 @@ public abstract class Geometries<G> {
     static UnsupportedOperationException unsupported(final int dimension) {
         return new UnsupportedOperationException(Resources.format(Resources.Keys.UnsupportedGeometryObject_1,
dimension));
     }
+
+    private static <T> Optional<T> findStrategy(final Function<Geometries<?>,
T> op) {
+        for (Geometries<?> g = implementation; g != null; g = g.fallback) {
+            final T result = op.apply(g);
+            if (result != null) return Optional.of(result);
+        }
+
+        return Optional.empty();
+    }
 }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/JTS.java b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/JTS.java
index 20e2f6d..c48cdd7 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/JTS.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/JTS.java
@@ -16,24 +16,26 @@
  */
 package org.apache.sis.internal.feature;
 
-import java.util.List;
-import java.util.Arrays;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Iterator;
+import java.util.List;
+
+import org.apache.sis.geometry.GeneralEnvelope;
+import org.apache.sis.math.Vector;
+import org.apache.sis.setup.GeometryLibrary;
+import org.apache.sis.util.Classes;
+
 import org.locationtech.jts.geom.Coordinate;
-import org.locationtech.jts.geom.Point;
-import org.locationtech.jts.geom.Polygon;
-import org.locationtech.jts.geom.LineString;
-import org.locationtech.jts.geom.MultiLineString;
 import org.locationtech.jts.geom.Envelope;
 import org.locationtech.jts.geom.Geometry;
 import org.locationtech.jts.geom.GeometryFactory;
-import org.locationtech.jts.io.WKTReader;
+import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.geom.MultiLineString;
+import org.locationtech.jts.geom.Point;
+import org.locationtech.jts.geom.Polygon;
 import org.locationtech.jts.io.ParseException;
-import org.apache.sis.geometry.GeneralEnvelope;
-import org.apache.sis.setup.GeometryLibrary;
-import org.apache.sis.math.Vector;
-import org.apache.sis.util.Classes;
+import org.locationtech.jts.io.WKTReader;
 
 
 /**
@@ -111,6 +113,13 @@ final class JTS extends Geometries<Geometry> {
         return null;
     }
 
+    @Override
+    Geometry tryConvertToGeometry(org.opengis.geometry.Envelope env) {
+        final int dim = env.getDimension();
+        if (dim > 2) throw new UnsupportedOperationException("Cannot manage more than
2 dimensions, but input envelope has "+dim);
+        throw new UnsupportedOperationException("Not yet");
+    }
+
     /**
      * If the given point is an implementation of this library, returns its coordinate.
      * Otherwise returns {@code null}. If non-null, the returned array may have a length
of 2 or 3.
diff --git a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/ANSIInterpreter.java
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/ANSIInterpreter.java
index 48c7d63..aea1253 100644
--- a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/ANSIInterpreter.java
+++ b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/ANSIInterpreter.java
@@ -1,9 +1,12 @@
 package org.apache.sis.internal.sql.feature;
 
+import java.util.Arrays;
 import java.util.List;
+import java.util.Optional;
 import java.util.function.BooleanSupplier;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
 import org.opengis.filter.*;
 import org.opengis.filter.expression.Add;
@@ -19,6 +22,7 @@ import org.opengis.filter.expression.PropertyName;
 import org.opengis.filter.expression.Subtract;
 import org.opengis.filter.spatial.BBOX;
 import org.opengis.filter.spatial.Beyond;
+import org.opengis.filter.spatial.BinarySpatialOperator;
 import org.opengis.filter.spatial.Contains;
 import org.opengis.filter.spatial.Crosses;
 import org.opengis.filter.spatial.DWithin;
@@ -29,9 +33,14 @@ import org.opengis.filter.spatial.Overlaps;
 import org.opengis.filter.spatial.Touches;
 import org.opengis.filter.spatial.Within;
 import org.opengis.filter.temporal.*;
+import org.opengis.geometry.Envelope;
+import org.opengis.geometry.Geometry;
+import org.opengis.metadata.extent.GeographicBoundingBox;
 import org.opengis.util.GenericName;
 import org.opengis.util.LocalName;
 
+import org.apache.sis.geometry.GeneralEnvelope;
+import org.apache.sis.internal.feature.Geometries;
 import org.apache.sis.util.iso.Names;
 
 import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
@@ -107,7 +116,15 @@ public class ANSIInterpreter implements FilterVisitor, ExpressionVisitor
{
 
     @Override
     public Object visit(PropertyIsBetween filter, Object extraData) {
-        throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)"
on 30/09/2019
+        final CharSequence propertyExp = evaluateMandatory(filter.getExpression(), extraData);
+        final CharSequence lowerExp = evaluateMandatory(filter.getLowerBoundary(), extraData);
+        final CharSequence upperExp = evaluateMandatory(filter.getUpperBoundary(), extraData);
+
+        return new StringBuilder(propertyExp)
+                .append(" BETWEEN ")
+                .append(lowerExp)
+                .append(" AND ")
+                .append(upperExp);
     }
 
     @Override
@@ -158,61 +175,92 @@ public class ANSIInterpreter implements FilterVisitor, ExpressionVisitor
{
         return evaluateMandatory(filter.getExpression(), extraData) + " = NULL";
     }
 
+    /*
+     * SPATIAL FILTERS
+     */
+
     @Override
     public Object visit(BBOX filter, Object extraData) {
-        throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)"
on 30/09/2019
+        final CharSequence left = evaluateMandatory(filter.getExpression1(), extraData);
+        final CharSequence right = evaluateMandatory(filter.getExpression2(), extraData);
+
+        // TODO: In case source expression is already an envelope, we do not need to force
envelope conversion. It would
+        // only be micro-optimisation however.
+        boolean leftToEnvelope = true;
+        boolean rightToEnvelope = true;
+
+        final StringBuilder sb = new StringBuilder("ST_Intersects(");
+        if (leftToEnvelope) {
+            sb.append("ST_Envelope(").append(left).append(')');
+        } else sb.append(left);
+
+        sb.append(", ");
+
+        if (rightToEnvelope) {
+            sb.append("ST_Envelope(").append(right).append(')');
+        } else sb.append(right);
+
+        return sb.append(')');
     }
 
     @Override
     public Object visit(Beyond filter, Object extraData) {
-        throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)"
on 30/09/2019
+        // TODO: ISO SQL specifies that unit of distance could be specified. However, PostGIS
documentation does not
+        // talk about it. For now, we'll fallback on Java implementation until we're sure
how to perform native
+        // operation properly.
+        throw new UnsupportedOperationException("Not yet: unit management ambiguous");
     }
 
     @Override
     public Object visit(Contains filter, Object extraData) {
-        throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)"
on 30/09/2019
+        return function("ST_Contains", filter, extraData);
     }
 
     @Override
     public Object visit(Crosses filter, Object extraData) {
-        throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)"
on 30/09/2019
+        return function("ST_Crosses", filter, extraData);
     }
 
     @Override
     public Object visit(Disjoint filter, Object extraData) {
-        throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)"
on 30/09/2019
+        return function("ST_Disjoint", filter, extraData);
     }
 
     @Override
     public Object visit(DWithin filter, Object extraData) {
-        throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)"
on 30/09/2019
+        // TODO: as for beyond, unit determination is a bit complicated.
+        throw new UnsupportedOperationException("Not yet: unit management to handle properly");
     }
 
     @Override
     public Object visit(Equals filter, Object extraData) {
-        throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)"
on 30/09/2019
+        return function("ST_Equals", filter, extraData);
     }
 
     @Override
     public Object visit(Intersects filter, Object extraData) {
-        throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)"
on 30/09/2019
+        return function("ST_Intersects", filter, extraData);
     }
 
     @Override
     public Object visit(Overlaps filter, Object extraData) {
-        throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)"
on 30/09/2019
+        return function("ST_Overlaps", filter, extraData);
     }
 
     @Override
     public Object visit(Touches filter, Object extraData) {
-        throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)"
on 30/09/2019
+        return function("ST_Touches", filter, extraData);
     }
 
     @Override
     public Object visit(Within filter, Object extraData) {
-        throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)"
on 30/09/2019
+        return function("ST_Within", filter, extraData);
     }
 
+    /*
+     * TEMPORAL OPERATORS
+     */
+
     @Override
     public Object visit(After filter, Object extraData) {
         throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)"
on 30/09/2019
@@ -333,12 +381,25 @@ public class ANSIInterpreter implements FilterVisitor, ExpressionVisitor
{
      */
 
     protected static CharSequence format(Literal candidate) {
-        final Object value = candidate == null ? null : candidate.getValue();
+        Object value = candidate == null ? null : candidate.getValue();
         if (value == null) return "NULL";
         else if (value instanceof CharSequence) {
             final String asStr = value.toString();
             asStr.replace("'", "''");
             return "'"+asStr+"'";
+        } else if (value instanceof Number || value instanceof Boolean) {
+            return value.toString();
+        }
+
+        // geometric special cases
+        if (value instanceof GeographicBoundingBox) {
+            value = new GeneralEnvelope((GeographicBoundingBox)value);
+        }
+        if (value instanceof Envelope) {
+            value = asGeometry((Envelope)value);
+        }
+        if (value instanceof Geometry) {
+            return format((Geometry)value);
         }
 
         throw new UnsupportedOperationException("Not supported yet: Literal value of type
"+value.getClass());
@@ -392,20 +453,40 @@ public class ANSIInterpreter implements FilterVisitor, ExpressionVisitor
{
                 + ")";
     }
 
+    protected CharSequence function(Object extraData, final String fnName, Supplier<Expression>...
parameters) {
+        return Arrays.stream(parameters)
+                .map(Supplier::get)
+                .map(exp -> evaluateMandatory(exp, extraData))
+                .collect(Collectors.joining(", ", fnName+'(', ")"));
+    }
+
+    private CharSequence function(String fnName, BinarySpatialOperator filter, Object extraData)
{
+        return function(extraData, fnName, filter::getExpression1, filter::getExpression2);
+    }
+
     protected CharSequence evaluateMandatory(final Filter candidate, Object extraData) {
         final Object exp = candidate == null ? null : candidate.accept(this, extraData);
-        if (isNonEmptyText(exp)) return (CharSequence) exp;
-        else throw new IllegalArgumentException("Filter evaluate to an empty text: "+candidate);
+        return asNonEmptyText(exp)
+                .orElseThrow(() -> new IllegalArgumentException("Filter evaluate to an
empty text: "+candidate));
     }
 
     protected CharSequence evaluateMandatory(final Expression candidate, Object extraData)
{
         final Object exp = candidate == null ? null : candidate.accept(this, extraData);
-        if (isNonEmptyText(exp)) return (CharSequence) exp;
-        else throw new IllegalArgumentException("Expression evaluate to an empty text: "+candidate);
+        return asNonEmptyText(exp)
+                .orElseThrow(() -> new IllegalArgumentException("Expression evaluate to
an empty text: "+candidate));
+    }
+
+    protected static Optional<CharSequence> asNonEmptyText(final Object toCheck) {
+        if (toCheck instanceof CharSequence) {
+            final CharSequence asCS = (CharSequence) toCheck;
+            if (asCS.length() > 0) return Optional.of(asCS);
+        }
+
+        return Optional.empty();
     }
 
     protected static boolean isNonEmptyText(final Object toCheck) {
-        return toCheck instanceof CharSequence && ((CharSequence) toCheck).length()
> 0;
+        return asNonEmptyText(toCheck).isPresent();
     }
 
     private static void ensureMatchCase(BinaryComparisonOperator filter) {
@@ -420,4 +501,42 @@ public class ANSIInterpreter implements FilterVisitor, ExpressionVisitor
{
         if (extraData instanceof StringBuilder) return ((StringBuilder) extraData).append(toAdd);
         return toAdd;
     }
+
+    protected static Geometry asGeometry(final Envelope source) {
+        final double[] lower = source.getLowerCorner().getCoordinate();
+        final double[] upper = source.getLowerCorner().getCoordinate();
+        for (int i = 0 ; i < lower.length ; i++) {
+            if (Double.isNaN(lower[i]) || Double.isNaN(upper[i])) {
+                throw new IllegalArgumentException("Cannot use envelope containing NaN for
filter");
+            }
+            lower[i] = clampInfinity(lower[i]);
+            upper[i] = clampInfinity(upper[i]);
+        }
+        final GeneralEnvelope env = new GeneralEnvelope(lower, upper);
+        env.setCoordinateReferenceSystem(source.getCoordinateReferenceSystem());
+        return Geometries.toGeometry(env)
+                .orElseThrow(() -> new UnsupportedOperationException("No geometry implementation
available"));
+    }
+
+    protected static CharSequence format(final Geometry source) {
+        // TODO: find a better approximation of desired "flatness"
+        final Envelope env = source.getEnvelope();
+        final double flatness = 0.05 * IntStream.range(0, env.getDimension())
+                .mapToDouble(env::getSpan)
+                .average()
+                .orElseThrow(() -> new IllegalArgumentException("Given geometry envelope
dimension is 0"));
+        return new StringBuilder("ST_GeomFromText(")
+                .append(Geometries.formatWKT(source, flatness))
+                .append(')');
+    }
+
+    protected static double clampInfinity(final double candidate) {
+        if (candidate == Double.NEGATIVE_INFINITY) {
+            return -Double.MAX_VALUE;
+        } else if (candidate == Double.POSITIVE_INFINITY) {
+            return Double.MAX_VALUE;
+        }
+
+        return candidate;
+    }
 }


Mime
View raw message