Author: desruisseaux
Date: Wed Feb 11 21:13:41 2015
New Revision: 1659071

URL: http://svn.apache.org/r1659071
Log:
Partial port of DefaultMathTransformFactory. Abstract for now, will become a 
concrete class after the port has been completed.

Added:
    
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
   (with props)
Modified:
    
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java
    
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java
    
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
    
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java
    
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
    
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
    
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
    
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
    
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java
    
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties
    
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties

Modified: 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java?rev=1659071&r1=1659070&r2=1659071&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java
 [UTF-8] Wed Feb 11 21:13:41 2015
@@ -31,7 +31,7 @@ import static org.apache.sis.internal.me
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.4
- * @version 0.5
+ * @version 0.6
  * @module
  */
 public final class Formulas extends Static {
@@ -43,7 +43,7 @@ public final class Formulas extends Stat
      * @see #ANGULAR_TOLERANCE
      * @see org.apache.sis.internal.util.Numerics#COMPARISON_THRESHOLD
      */
-    public static final double LINEAR_TOLERANCE = 1.0;
+    public static final double LINEAR_TOLERANCE = 0.01;
 
     /**
      * Default tolerance threshold for comparing ordinate values in a 
geographic CRS,

Modified: 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java?rev=1659071&r1=1659070&r2=1659071&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java
 [UTF-8] Wed Feb 11 21:13:41 2015
@@ -24,6 +24,7 @@ import org.opengis.annotation.UML;
 import org.opengis.annotation.Specification;
 import org.opengis.referencing.cs.*;
 import org.opengis.referencing.crs.*;
+import org.opengis.referencing.datum.Ellipsoid;
 import org.opengis.referencing.datum.PrimeMeridian;
 import org.apache.sis.util.Static;
 import org.apache.sis.util.Utilities;
@@ -49,7 +50,7 @@ import static org.apache.sis.internal.ut
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.5
- * @version 0.5
+ * @version 0.6
  * @module
  */
 public final class ReferencingUtilities extends Static {
@@ -167,6 +168,27 @@ public final class ReferencingUtilities
     }
 
     /**
+     * Returns the ellipsoid used by the specified coordinate reference 
system, providing that
+     * the two first dimensions use an instance of {@link GeographicCRS}. 
Otherwise (i.e. if the
+     * two first dimensions are not geographic), returns {@code null}.
+     *
+     * @param  crs The coordinate reference system for which to get the 
ellipsoid.
+     * @return The ellipsoid in the given CRS, or {@code null} if none.
+     *
+     * @since 0.6
+     */
+    public static Ellipsoid 
getEllipsoidOfGeographicCRS(CoordinateReferenceSystem crs) {
+        while (!(crs instanceof GeographicCRS)) {
+            if (crs instanceof CompoundCRS) {
+                crs = ((CompoundCRS) crs).getComponents().get(0);
+            } else {
+                return null;
+            }
+        }
+        return ((GeographicCRS) crs).getDatum().getEllipsoid();
+    }
+
+    /**
      * Derives a geographic CRS with (<var>longitude</var>, 
<var>latitude</var>) axis order in decimal degrees.
      * If no such CRS can be obtained or created, returns {@code null}.
      *

Modified: 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java?rev=1659071&r1=1659070&r2=1659071&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
 [UTF-8] Wed Feb 11 21:13:41 2015
@@ -377,7 +377,8 @@ public class DefaultParameterValue<T> ex
      */
     @Override
     public double doubleValue(final Unit<?> unit) throws 
IllegalArgumentException, IllegalStateException {
-        return getConverterTo(unit).convert(doubleValue());
+        final double value = doubleValue(); // Invoke first in case it throws 
an exception.
+        return getConverterTo(unit).convert(value);
     }
 
     /**

Modified: 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java?rev=1659071&r1=1659070&r2=1659071&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java
 [UTF-8] Wed Feb 11 21:13:41 2015
@@ -395,6 +395,8 @@ public class DefaultOperationMethod exte
      * which is the most conservative return value.
      *
      * @return Interface implemented by all coordinate operations that use 
this method.
+     *
+     * @see 
org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory#getAvailableMethods(Class)
      */
     public Class<? extends SingleOperation> getOperationType() {
         return SingleOperation.class;
@@ -409,6 +411,9 @@ public class DefaultOperationMethod exte
      * this property is mandatory according ISO 19111, but optional in Apache 
SIS.</div>
      *
      * @return The formula used by this method, or {@code null} if unknown.
+     *
+     * @see DefaultFormula
+     * @see 
org.apache.sis.referencing.operation.transform.MathTransformProvider
      */
     @Override
     public Formula getFormula() {
@@ -420,6 +425,8 @@ public class DefaultOperationMethod exte
      * May be null if unknown, as in an <cite>Affine Transform</cite>.
      *
      * @return The dimension of source CRS, or {@code null} if unknown.
+     *
+     * @see 
org.apache.sis.referencing.operation.transform.AbstractMathTransform#getSourceDimensions()
      */
     @Override
     public Integer getSourceDimensions() {
@@ -431,6 +438,8 @@ public class DefaultOperationMethod exte
      * May be null if unknown, as in an <cite>Affine Transform</cite>.
      *
      * @return The dimension of target CRS, or {@code null} if unknown.
+     *
+     * @see 
org.apache.sis.referencing.operation.transform.AbstractMathTransform#getTargetDimensions()
      */
     @Override
     public Integer getTargetDimensions() {

Modified: 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java?rev=1659071&r1=1659070&r2=1659071&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java
 [UTF-8] Wed Feb 11 21:13:41 2015
@@ -130,6 +130,8 @@ public abstract class AbstractMathTransf
      * Gets the dimension of input points.
      *
      * @return The dimension of input points.
+     *
+     * @see 
org.apache.sis.referencing.operation.DefaultOperationMethod#getSourceDimensions()
      */
     @Override
     public abstract int getSourceDimensions();
@@ -138,6 +140,8 @@ public abstract class AbstractMathTransf
      * Gets the dimension of output points.
      *
      * @return The dimension of output points.
+     *
+     * @see 
org.apache.sis.referencing.operation.DefaultOperationMethod#getTargetDimensions()
      */
     @Override
     public abstract int getTargetDimensions();

Added: 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java?rev=1659071&view=auto
==============================================================================
--- 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
 (added)
+++ 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
 [UTF-8] Wed Feb 11 21:13:41 2015
@@ -0,0 +1,603 @@
+/*
+ * 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.
+ */
+package org.apache.sis.referencing.operation.transform;
+
+import java.util.IdentityHashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.logging.Level;
+import javax.measure.quantity.Length;
+import javax.measure.unit.SI;
+import javax.measure.unit.Unit;
+import org.opengis.metadata.Identifier;
+import org.opengis.parameter.ParameterValue;
+import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.cs.CoordinateSystem;
+import org.opengis.referencing.datum.Ellipsoid;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.MathTransformFactory;
+import org.opengis.referencing.operation.OperationMethod;
+import org.opengis.referencing.operation.SingleOperation;
+import org.opengis.util.FactoryException;
+import org.opengis.util.NoSuchIdentifierException;
+import org.apache.sis.internal.referencing.Formulas;
+import org.apache.sis.internal.referencing.ReferencingUtilities;
+import org.apache.sis.referencing.IdentifiedObjects;
+import org.apache.sis.referencing.operation.DefaultOperationMethod;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.Deprecable;
+import org.apache.sis.util.collection.WeakHashSet;
+import org.apache.sis.util.iso.AbstractFactory;
+import org.apache.sis.util.iso.DefaultNameSpace;
+import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.resources.Messages;
+
+
+/**
+ * Low level factory for creating {@linkplain AbstractMathTransform math 
transforms}.
+ * High level GIS applications usually do not need to use this factory 
directly.
+ * They can use the static convenience methods in the {@link 
org.apache.sis.referencing.CRS}
+ * or {@link MathTransforms} classes instead.
+ *
+ * {@section Math transforms discovery}
+ * Unless {@linkplain #DefaultMathTransformFactory(Iterable) specified 
explicitely at construction time},
+ * {@code OperationMethod} implementations shall be listed in the following 
file:
+ *
+ * {@preformat text
+ *     META-INF/services/org.opengis.referencing.operation.OperationMethod
+ * }
+ *
+ * {@code DefaultMathTransformFactory} parses the above-cited files in all JAR 
files in order to find all available
+ * operation methods. By default, only operation methods that implement the 
{@link MathTransformProvider} interface
+ * can be used by the {@code create(…)} methods in this class.
+ *
+ * {@section Thread safety}
+ * This class is safe for multi-thread usage if all referenced {@code 
OperationMethod} instances are thread-safe.
+ * There is typically only one {@code MathTransformFactory} instance for the 
whole application.
+ *
+ * @author  Martin Desruisseaux (Geomatys, IRD)
+ * @since   0.6
+ * @version 0.6
+ * @module
+ */
+public abstract class DefaultMathTransformFactory extends AbstractFactory 
implements MathTransformFactory {
+    /**
+     * The separator character between an identifier and its namespace in the 
argument given to
+     * {@link #getOperationMethod(String)}. For example this is the separator 
in {@code "EPSG:9807"}.
+     *
+     * This is defined as a constant for now, but we may make it configurable 
in a future version.
+     */
+    private static final char IDENTIFIER_SEPARATOR = 
DefaultNameSpace.DEFAULT_SEPARATOR;
+
+    /**
+     * Minimal precision of ellipsoid semi-major and semi-minor axis lengths, 
in metres.
+     * If the length difference between the axis of two ellipsoids is greater 
than this threshold,
+     * we will report a mismatch. This is used for logging purpose only and do 
not have any impact
+     * on the {@code MathTransform} objects to be created by this factory.
+     */
+    private static final double ELLIPSOID_PRECISION = 
Formulas.LINEAR_TOLERANCE;
+
+    /**
+     * All methods specified at construction time or found on the classpath.
+     * If the iterable is an instance of {@link ServiceLoader}, then it will
+     * be reloaded when {@link #reload()} is invoked.
+     *
+     * <p>All uses of this field shall be synchronized on {@code methods}.</p>
+     */
+    private final Iterable<? extends OperationMethod> methods;
+
+    /**
+     * The methods by name, cached for faster lookup and for avoiding some
+     * synchronizations on {@link #methods} and {@link #pool}.
+     */
+    private final ConcurrentMap<String, OperationMethod> methodsByName;
+
+    /**
+     * The methods by type. All uses of this map shall be synchronized on 
{@code methodsByType}.
+     *
+     * <div class="note"><b>Note:</b>
+     * we do not use a concurrent map here because the number of entries is 
expected to be very small
+     * (about 2 entries), which make concurrent algorithms hardly efficient. 
Furthermore this map is
+     * not used often.
+     * </div>
+     */
+    private final Map<Class<?>, OperationMethodSet> methodsByType;
+
+    /**
+     * The last method used by a {@code create(…)} method.
+     */
+    private final ThreadLocal<OperationMethod> lastMethod;
+
+    /**
+     * The math transforms created so far. This pool is used in order
+     * to return instances of existing math transforms when possible.
+     */
+    private final WeakHashSet<MathTransform> pool;
+
+    /**
+     * Creates a new factory which will discover operation methods with a 
{@link ServiceLoader}.
+     * {@code OperationMethod} implementations shall be listed in the 
following file:
+     *
+     * {@preformat text
+     *     META-INF/services/org.opengis.referencing.operation.OperationMethod
+     * }
+     *
+     * {@code DefaultMathTransformFactory} parses the above-cited files in all 
JAR files in order to find all available
+     * operation methods. By default, only operation methods that implement 
the {@link MathTransformProvider} interface
+     * can be used by the {@code create(…)} methods in this class.
+     */
+    public DefaultMathTransformFactory() {
+        this(ServiceLoader.load(OperationMethod.class));
+    }
+
+    /**
+     * Creates a new factory which will use the given operation methods. The 
given iterable is stored by reference —
+     * its content is <strong>not</strong> copied — in order to allow deferred 
{@code OperationMethod} constructions.
+     * Note that by default, only operation methods that implement the {@link 
MathTransformProvider} interface can be
+     * used by the {@code create(…)} methods in this class.
+     *
+     * <p><b>Requirements:</b></p>
+     * <ul>
+     *   <li>The given iterable should not contain duplicated elements.</li>
+     *   <li>The given iterable shall be stable: all elements returned by the 
first iteration must also be
+     *       returned by any subsequent iterations, unless {@link #reload()} 
has been invoked.</li>
+     *   <li>{@code OperationMethod} instances should also implement {@link 
MathTransformProvider}.</li>
+     *   <li>All {@code OperationMethod} instances shall be thread-safe.</li>
+     *   <li>The {@code Iterable} itself does not need to be thread-safe since 
all usages will be synchronized as below:
+     *
+     *       {@preformat java
+     *           synchronized (methods) {
+     *               for (OperationMethod method : methods) {
+     *                   // Use the method here.
+     *               }
+     *           }
+     *       }
+     *   </li>
+     * </ul>
+     *
+     * @param methods The operation methods to use, stored by reference (not 
copied).
+     */
+    public DefaultMathTransformFactory(final Iterable<? extends 
OperationMethod> methods) {
+        ArgumentChecks.ensureNonNull("methods", methods);
+        this.methods  = methods;
+        methodsByName = new ConcurrentHashMap<>();
+        methodsByType = new IdentityHashMap<>();
+        lastMethod    = new ThreadLocal<>();
+        pool          = new WeakHashSet<>(MathTransform.class);
+    }
+
+    /**
+     * Returns a set of available methods for coordinate operations of the 
given type.
+     * The {@code type} argument can be used for filtering the kind of 
operations described by the returned
+     * {@code OperationMethod}s. The argument is usually (but not restricted 
to) one of the following types:
+     *
+     * <ul>
+     *   <li>{@link org.opengis.referencing.operation.Transformation}
+     *       for coordinate operations described by empirically derived 
parameters.</li>
+     *   <li>{@link org.opengis.referencing.operation.Conversion}
+     *       for coordinate operations described by definitions.</li>
+     *   <li>{@link org.opengis.referencing.operation.Projection}
+     *       for conversions from geodetic latitudes and longitudes to plane 
(map) coordinates.</li>
+     *   <li>{@link SingleOperation} for all coordinate operations.</li>
+     * </ul>
+     *
+     * This method may conservatively return more {@code OperationMethod} 
elements than requested
+     * if it does not support filtering by the given type.
+     *
+     * @param  type <code>{@linkplain SingleOperation}.class</code> for 
fetching all operation methods,
+     *         <code>{@linkplain Projection}.class</code> for fetching only 
map projection methods, <i>etc</i>.
+     * @return Methods available in this factory for coordinate operations of 
the given type.
+     *
+     * @see #getDefaultParameters(String)
+     * @see #createParameterizedTransform(ParameterValueGroup)
+     * @see DefaultOperationMethod#getOperationType()
+     */
+    @Override
+    public Set<OperationMethod> getAvailableMethods(final Class<? extends 
SingleOperation> type) {
+        OperationMethodSet set;
+        synchronized (methodsByType) {
+            set = methodsByType.get(type);
+        }
+        if (set == null) {
+            /*
+             * Implementation note: we are better to avoid holding a lock on 
'methods' and 'methodsByType'
+             * in same time because the 'methods' iterator could be a user's 
implementation which callback
+             * this factory.
+             */
+            synchronized (methods) {
+                set = new OperationMethodSet(type, methods);
+            }
+            final OperationMethodSet previous;
+            synchronized (methodsByType) {
+                previous = methodsByType.putIfAbsent(type, set);
+            }
+            if (previous != null) {
+                set = previous;
+            }
+        }
+        return set;
+    }
+
+    /**
+     * Returns the operation method for the specified name or identifier. The 
given argument shall be either
+     * a method {@linkplain DefaultOperationMethod#getName() name} (e.g. 
<cite>"Transverse Mercator"</cite>)
+     * or one of its {@linkplain DefaultOperationMethod#getIdentifiers() 
identifiers} (e.g. {@code "EPSG:9807"}).
+     *
+     * <p>The search is case-insensitive. Comparisons against method names can 
be
+     * {@linkplain DefaultOperationMethod#isHeuristicMatchForName(String) 
heuristic}.</p>
+     *
+     * <p>If more than one method match the given identifier, then the first 
(according iteration order)
+     * non-{@linkplain Deprecable#isDeprecated() deprecated} matching method 
is returned. If all matching
+     * methods are deprecated, the first one is returned.</p>
+     *
+     * @param  identifier The name or identifier of the operation method to 
search.
+     * @return The operation method for the given name or identifier.
+     * @throws NoSuchIdentifierException if there is no operation method 
registered for the specified identifier.
+     */
+    public OperationMethod getOperationMethod(String identifier) throws 
NoSuchIdentifierException {
+        identifier = CharSequences.trimWhitespaces(identifier);
+        ArgumentChecks.ensureNonEmpty("identifier", identifier);
+        OperationMethod method = methodsByName.get(identifier);
+        if (method == null) {
+            for (final OperationMethod m : methods) {
+                if (matches(m, identifier)) {
+                    /*
+                     * Stop the iteration at the first non-deprecated method.
+                     * If we find only deprecated methods, take the first one.
+                     */
+                    final boolean isDeprecated = (m instanceof Deprecable) && 
((Deprecable) m).isDeprecated();
+                    if (!isDeprecated || method == null) {
+                        method = m;
+                        if (!isDeprecated) {
+                            break;
+                        }
+                    }
+                }
+            }
+            if (method == null) {
+                throw new 
NoSuchIdentifierException(Errors.format(Errors.Keys.NoSuchOperationMethod_1, 
method), identifier);
+            }
+            /*
+             * Remember the method we just found, for faster check next time.
+             */
+            final OperationMethod previous = 
methodsByName.putIfAbsent(identifier.intern(), method);
+            if (previous != null) {
+                method = previous;
+            }
+        }
+        return method;
+    }
+
+    /**
+     * Returns {@code true} if the name or an identifier of the given method 
matches the given {@code identifier}.
+     *
+     * This method is private and static for now, but we may consider to make 
it a protected member in a future
+     * SIS version in order to give users a chance to override the matching 
criterion. We don't do that yet
+     * because we would like to have at least one use case before doing so.
+     *
+     * @param  method     The method to test for a match.
+     * @param  identifier The name or identifier of the operation method to 
search.
+     * @return {@code true} if the given method is a match for the given 
identifier.
+     */
+    private static boolean matches(final OperationMethod method, final String 
identifier) {
+        if (IdentifiedObjects.isHeuristicMatchForName(method, identifier)) {
+            return true;
+        }
+        for (int s = identifier.indexOf(IDENTIFIER_SEPARATOR); s >= 0;
+                 s = identifier.indexOf(IDENTIFIER_SEPARATOR, s))
+        {
+            final String codespace = identifier.substring(0, s).trim();
+            final String code = identifier.substring(++s).trim();
+            for (final Identifier id : method.getIdentifiers()) {
+                if (codespace.equalsIgnoreCase(id.getCodeSpace()) && 
code.equalsIgnoreCase(id.getCode())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns the default parameter values for a math transform using the 
given method.
+     * The method argument is the name of any operation method returned by the 
{@link #getAvailableMethods(Class)} method.
+     * A typical example is
+     * "<a 
href="http://www.remotesensing.org/geotiff/proj_list/transverse_mercator.html";>Transverse
 Mercator</a>").
+     *
+     * <p>This method creates new parameter instances at every call. The 
returned object is intended to be modified
+     * by the user before to be passed to <code>{@linkplain 
#createParameterizedTransform(ParameterValueGroup)
+     * createParameterizedTransform}(parameters)</code>.</p>
+     *
+     * @param  method The name or identifier of the operation method to search.
+     * @return A new group of parameter values for the {@code OperationMethod} 
identified by the given name.
+     * @throws NoSuchIdentifierException if there is no method registered for 
the given name or identifier.
+     *
+     * @see #getAvailableMethods(Class)
+     * @see #createParameterizedTransform(ParameterValueGroup)
+     * @see 
org.apache.sis.referencing.operation.transform.AbstractMathTransform#getParameterValues()
+     */
+    @Override
+    public ParameterValueGroup getDefaultParameters(final String method) 
throws NoSuchIdentifierException {
+        return getOperationMethod(method).getParameters().createValue();
+    }
+
+    /**
+     * Returns the value of the given parameter in the given unit, or {@code 
NaN} if the parameter is not set.
+     *
+     * <p><b>NOTE:</b> Do not merge this method with {@code ensureSet(…)}. We 
keep those two methods
+     * separated in order to give to {@code createBaseToDerived(…)} a "all or 
nothing" behavior.</p>
+     */
+    private static double getValue(final ParameterValue<?> parameter, final 
Unit<Length> unit) {
+        return (parameter.getValue() != null) ? parameter.doubleValue(unit) : 
Double.NaN;
+    }
+
+    /**
+     * Ensures that a value is set in the given parameter.
+     *
+     * <ul>
+     *   <li>If the parameter has no value, then it is set to the given 
value.<li>
+     *   <li>If the parameter already has a value, then the parameter is left 
unchanged
+     *       but its value is compared to the given one for consistency.</li>
+     * </ul>
+     *
+     * @param parameter The parameter which must have a value.
+     * @param actual    The current parameter value, or {@code NaN} if none.
+     * @param expected  The expected parameter value, derived from the 
ellipsoid.
+     * @param unit      The unit of {@code value}.
+     * @param tolerance Maximal difference (in unit of {@code unit}) for 
considering the two values as equivalent.
+     * @return {@code true} if there is a mismatch between the actual value 
and the expected one.
+     */
+    private static boolean ensureSet(final ParameterValue<?> parameter, final 
double actual,
+            final double expected, final Unit<?> unit, final double tolerance)
+    {
+        if (Math.abs(actual - expected) <= tolerance) {
+            return false;
+        }
+        if (Double.isNaN(actual)) {
+            parameter.setValue(expected, unit);
+        }
+        return true;
+    }
+
+    /**
+     * Creates a {@linkplain 
#createParameterizedTransform(ParameterValueGroup) parameterized transform}
+     * from a base CRS to a derived CS. This convenience method {@linkplain 
#createConcatenatedTransform
+     * concatenates} the parameterized transform with any other transform 
required for performing units
+     * changes and ordinates swapping.
+     *
+     * In addition, this method infers the {@code "semi_major"} and {@code 
"semi_minor"} parameters values
+     * from the {@linkplain org.apache.sis.referencing.datum.DefaultEllipsoid 
ellipsoid} associated to the
+     * {@code baseCRS}, if those parameters are not explicitly given and if 
they are applicable (typically
+     * for cartographic projections).
+     *
+     * <p>The {@code OperationMethod} instance used by this method can be 
obtained by a call to
+     * {@link #getLastMethodUsed()}.</p>
+     *
+     * @param  baseCRS    The source coordinate reference system.
+     * @param  parameters The parameter values for the transform.
+     * @param  derivedCS  The target coordinate system.
+     * @return The parameterized transform.
+     * @throws NoSuchIdentifierException if there is no transform registered 
for the method.
+     * @throws FactoryException if the object creation failed. This exception 
is thrown
+     *         if some required parameter has not been supplied, or has 
illegal value.
+     */
+    @Override
+    public MathTransform createBaseToDerived(final CoordinateReferenceSystem 
baseCRS,
+            final ParameterValueGroup parameters, final CoordinateSystem 
derivedCS)
+            throws NoSuchIdentifierException, FactoryException
+    {
+        /*
+         * If the user's parameters do not contain semi-major and semi-minor 
axis lengths, infer
+         * them from the ellipsoid. We have to do that because those 
parameters are often omitted,
+         * since the standard place where to provide this information is in 
the ellipsoid object.
+         */
+        RuntimeException failure = null;
+        final Ellipsoid ellipsoid = 
ReferencingUtilities.getEllipsoidOfGeographicCRS(baseCRS);
+        if (ellipsoid != null) {
+            ParameterValue<?> mismatchedParam = null;
+            double mismatchedValue = 0;
+            try {
+                final ParameterValue<?> semiMajor = 
parameters.parameter("semi_major");
+                final ParameterValue<?> semiMinor = 
parameters.parameter("semi_minor");
+                final Unit<Length>      axisUnit  = ellipsoid.getAxisUnit();
+                /*
+                 * The two calls to getOptional(…) shall succeed before we 
write anything, in order to have a
+                 * "all or nothing" behavior as much as possible. Note that 
Ellipsoid.getSemi**Axis() have no
+                 * reason to fail, so we don't take precaution for them.
+                 */
+                final double a   = getValue(semiMajor, axisUnit);
+                final double b   = getValue(semiMinor, axisUnit);
+                final double tol = 
SI.METRE.getConverterTo(axisUnit).convert(ELLIPSOID_PRECISION);
+                if (ensureSet(semiMajor, a, ellipsoid.getSemiMajorAxis(), 
axisUnit, tol)) {
+                    mismatchedParam = semiMajor;
+                    mismatchedValue = a;
+                }
+                if (ensureSet(semiMinor, b, ellipsoid.getSemiMinorAxis(), 
axisUnit, tol)) {
+                    mismatchedParam = semiMinor;
+                    mismatchedValue = b;
+                }
+            } catch (IllegalArgumentException | IllegalStateException e) {
+                /*
+                 * Parameter not found, or is not numeric, or unit of 
measurement is not linear.
+                 * Those exceptions should never occur with map projections, 
but may happen for
+                 * some other operations like Molodenski¹.
+                 *
+                 * Do not touch to the parameters. We will see if 
createParameterizedTransform(…)
+                 * can do something about that. If it can not, 
createParameterizedTransform(…) is
+                 * the right place to throw the exception.
+                 *
+                 *  ¹ The actual Molodenski parameter names are 
"src_semi_major" and "src_semi_minor".
+                 *    But we do not try to set them because we have no way to 
set the corresponding
+                 *    "tgt_semi_major" and "tgt_semi_minor" parameters anyway.
+                 */
+                failure = e;
+            }
+            if (mismatchedParam != null) {
+                Logging.log(DefaultMathTransformFactory.class, 
"createBaseToDerived",
+                        Messages.getResources((Locale) 
null).getLogRecord(Level.WARNING,
+                                Messages.Keys.MismatchedEllipsoidAxisLength_3, 
ellipsoid.getName().getCode(),
+                                
mismatchedParam.getDescriptor().getName().getCode(), mismatchedValue));
+            }
+        }
+        MathTransform baseToDerived;
+        try {
+            baseToDerived = createParameterizedTransform(parameters);
+            final OperationMethod method = lastMethod.get();
+            baseToDerived = createBaseToDerived(baseCRS, baseToDerived, 
derivedCS);
+            lastMethod.set(method);
+        } catch (FactoryException e) {
+            if (failure != null) {
+                e.addSuppressed(failure);
+            }
+            throw e;
+        }
+        return baseToDerived;
+    }
+
+    /**
+     * Creates a transform from a base CRS to a derived CS. This method 
expects a "raw" transform without
+     * unit conversion or axis swapping. Such "raw" transforms are typically 
map projections working on
+     * (<cite>longitude</cite>, <cite>latitude</cite>) axes in degrees and 
(<cite>x</cite>, <cite>y</cite>)
+     * axes in metres. This method inspects the coordinate systems and prepend 
or append the unit conversions
+     * and axis swapping automatically.
+     *
+     * @param  baseCRS    The source coordinate reference system.
+     * @param  projection The "raw" <cite>base to derived</cite> transform.
+     * @param  derivedCS  the target coordinate system.
+     * @return The parameterized transform.
+     * @throws FactoryException if the object creation failed. This exception 
is thrown
+     *         if some required parameter has not been supplied, or has 
illegal value.
+     */
+    public abstract MathTransform createBaseToDerived(final 
CoordinateReferenceSystem baseCRS,
+            final MathTransform projection, final CoordinateSystem derivedCS)
+            throws FactoryException;
+
+    /**
+     * Creates a transform by concatenating two existing transforms.
+     * A concatenated transform acts in the same way as applying two 
transforms, one after the other.
+     *
+     * <p>The dimension of the output space of the first transform must match 
the dimension of the input space
+     * in the second transform. In order to concatenate more than two 
transforms, use this method repeatedly.</p>
+     *
+     * @param  transform1 The first transform to apply to points.
+     * @param  transform2 The second transform to apply to points.
+     * @return The concatenated transform.
+     * @throws FactoryException if the object creation failed.
+     */
+    @Override
+    public MathTransform createConcatenatedTransform(final MathTransform 
transform1,
+                                                     final MathTransform 
transform2)
+            throws FactoryException
+    {
+        MathTransform tr;
+        try {
+            tr = MathTransforms.concatenate(transform1, transform2);
+        } catch (IllegalArgumentException exception) {
+            throw new FactoryException(exception);
+        }
+        tr = pool.unique(tr);
+        return tr;
+    }
+
+    /**
+     * Creates a transform which passes through a subset of ordinates to 
another transform.
+     * This allows transforms to operate on a subset of ordinates.
+     *
+     * <div class="note"><b>Example:</b>
+     * Giving (<var>latitude</var>, <var>longitude</var>, <var>height</var>) 
coordinates,
+     * a pass through transform can convert the height values from meters to 
feet without
+     * affecting the (<var>latitude</var>, <var>longitude</var>) values.</div>
+     *
+     * The resulting transform will have the following dimensions:
+     *
+     * {@preformat java
+     *     Source: firstAffectedOrdinate + subTransform.getSourceDimensions() 
+ numTrailingOrdinates
+     *     Target: firstAffectedOrdinate + subTransform.getTargetDimensions() 
+ numTrailingOrdinates
+     * }
+     *
+     * @param  firstAffectedOrdinate The lowest index of the affected 
ordinates.
+     * @param  subTransform          Transform to use for affected ordinates.
+     * @param  numTrailingOrdinates  Number of trailing ordinates to pass 
through. Affected ordinates will range
+     *         from {@code firstAffectedOrdinate} inclusive to {@code 
dimTarget-numTrailingOrdinates} exclusive.
+     * @return A pass through transform.
+     * @throws FactoryException if the object creation failed.
+     */
+    @Override
+    public MathTransform createPassThroughTransform(final int 
firstAffectedOrdinate,
+                                                    final MathTransform 
subTransform,
+                                                    final int 
numTrailingOrdinates)
+            throws FactoryException
+    {
+        MathTransform tr;
+        try {
+            tr = PassThroughTransform.create(firstAffectedOrdinate, 
subTransform, numTrailingOrdinates);
+        } catch (IllegalArgumentException exception) {
+            throw new FactoryException(exception);
+        }
+        tr = pool.unique(tr);
+        return tr;
+    }
+
+    /**
+     * Returns the operation method used for the latest call to
+     * {@link #createParameterizedTransform(ParameterValueGroup)} in the 
currently running thread.
+     * Returns {@code null} if not applicable.
+     *
+     * @see #createParameterizedTransform(ParameterValueGroup)
+     */
+    @Override
+    public OperationMethod getLastMethodUsed() {
+        return lastMethod.get();
+    }
+
+    /**
+     * Notifies this factory that the elements provided by the {@code 
Iterable<OperationMethod>} may have changed.
+     * This method performs the following steps:
+     *
+     * <ul>
+     *   <li>Clears all caches.</li>
+     *   <li>If the {@code Iterable} given at construction time is an instance 
of {@link ServiceLoader},
+     *       invokes its {@code reload()} method.</li>
+     * </ul>
+     *
+     * This method is useful to sophisticated applications which dynamically 
make new plug-ins available at runtime,
+     * for example following changes of the application classpath.
+     *
+     * @see #DefaultMathTransformFactory(Iterable)
+     * @see ServiceLoader#reload()
+     */
+    public void reload() {
+        synchronized (methods) {
+            methodsByName.clear();
+            if (methods instanceof ServiceLoader<?>) {
+                ((ServiceLoader<?>) methods).reload();
+            }
+            synchronized (methodsByType) {
+                for (final OperationMethodSet c : methodsByType.values()) {
+                    c.reset();
+                }
+            }
+            pool.clear();
+        }
+    }
+}

Propchange: 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1659071&r1=1659070&r2=1659071&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
 [UTF-8] Wed Feb 11 21:13:41 2015
@@ -585,6 +585,11 @@ public final class Errors extends Indexe
         public static final short NoSuchAuthorityCode_3 = 137;
 
         /**
+         * No operation method found for name of identifier “{0}”.
+         */
+        public static final short NoSuchOperationMethod_1 = 179;
+
+        /**
          * No unit of measurement has been specified.
          */
         public static final short NoUnit = 72;

Modified: 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1659071&r1=1659070&r2=1659071&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
 [ISO-8859-1] (original)
+++ 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
 [ISO-8859-1] Wed Feb 11 21:13:41 2015
@@ -150,6 +150,7 @@ NotASkewSymmetricMatrix           = Matr
 NotAUnicodeIdentifier_1           = Text \u201c{0}\u201d is not a Unicode 
identifier.
 NotComparableClass_1              = Class \u2018{0}\u2019 is not a comparable.
 NoSuchAuthorityCode_3             = No code \u201c{2}\u201d from authority 
\u201c{0}\u201d found for object of type \u2018{1}\u2019.
+NoSuchOperationMethod_1           = No operation method found for name of 
identifier \u201c{0}\u201d.
 NoUnit                            = No unit of measurement has been specified.
 NullArgument_1                    = Argument \u2018{0}\u2019 shall not be null.
 NullCollectionElement_1           = \u2018{0}\u2019 collection does not accept 
null elements.

Modified: 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1659071&r1=1659070&r2=1659071&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
 [ISO-8859-1] (original)
+++ 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
 [ISO-8859-1] Wed Feb 11 21:13:41 2015
@@ -140,6 +140,7 @@ NotASkewSymmetricMatrix           = La m
 NotAUnicodeIdentifier_1           = Le texte \u00ab\u202f{0}\u202f\u00bb 
n\u2019est pas un identifiant Unicode.
 NotComparableClass_1              = La classe \u2018{0}\u2019 n\u2019est pas 
comparable.
 NoSuchAuthorityCode_3             = Aucun code \u00ab\u202f{2}\u202f\u00bb de 
l\u2019autorit\u00e9 \u00ab\u202f{0}\u202f\u00bb n\u2019a \u00e9t\u00e9 
trouv\u00e9 pour un objet de type \u2018{1}\u2019.
+NoSuchOperationMethod_1           = Aucune m\u00e9thode n\u2019a \u00e9t\u00e9 
trouv\u00e9e pour le nom ou l\u2019identifiant \u00ab\u202f{0}\u202f\u00bb.
 NoUnit                            = Aucune unit\u00e9 de mesure n\u2019a 
\u00e9t\u00e9 sp\u00e9cifi\u00e9e.
 NullArgument_1                    = L\u2019argument \u2018{0}\u2019 ne doit 
pas \u00eatre nul.
 NullCollectionElement_1           = La collection \u2018{0}\u2019 
n\u2019accepte pas les valeurs nulles.

Modified: 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java?rev=1659071&r1=1659070&r2=1659071&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java
 [UTF-8] Wed Feb 11 21:13:41 2015
@@ -92,6 +92,12 @@ public final class Messages extends Inde
         public static final short LocalesDiscarded = 3;
 
         /**
+         * The “{1}” parameter could have been omitted. But it has been given 
a value of {2} which does
+         * not match the definition of the “{0}” ellipsoid.
+         */
+        public static final short MismatchedEllipsoidAxisLength_3 = 9;
+
+        /**
          * Property “{0}” is hidden by “{1}”.
          */
         public static final short PropertyHiddenBy_2 = 4;

Modified: 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties?rev=1659071&r1=1659070&r2=1659071&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties
 [ISO-8859-1] (original)
+++ 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties
 [ISO-8859-1] Wed Feb 11 21:13:41 2015
@@ -22,4 +22,5 @@ IgnoredPropertiesAfterFirst_1   = Ignore
 IgnoredPropertyAssociatedTo_1   = Ignored property associated to 
\u2018{0}\u2019.
 PropertyHiddenBy_2              = Property \u201c{0}\u201d is hidden by 
\u201c{1}\u201d.
 LocalesDiscarded                = Text were discarded for some locales.
+MismatchedEllipsoidAxisLength_3 = The \u201c{1}\u201d parameter could have 
been omitted. But it has been given a value of {2} which does not match the 
definition of the \u201c{0}\u201d ellipsoid.
 UnparsableValueStoredAsText_2   = Can not parse \u201c{1}\u201d as an instance 
of \u2018{0}\u2019. The value is stored as plain text instead, but will be 
ignored by some processing.

Modified: 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties?rev=1659071&r1=1659070&r2=1659071&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties
 [ISO-8859-1] (original)
+++ 
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties
 [ISO-8859-1] Wed Feb 11 21:13:41 2015
@@ -22,4 +22,5 @@ IgnoredPropertiesAfterFirst_1   = Des pr
 IgnoredPropertyAssociatedTo_1   = Une propri\u00e9t\u00e9 associ\u00e9e \u00e0 
\u2018{0}\u2019 a \u00e9t\u00e9 ignor\u00e9e.
 PropertyHiddenBy_2              = La propri\u00e9t\u00e9 
\u00ab\u202f{0}\u202f\u00bb est masqu\u00e9e par \u00ab\u202f{1}\u202f\u00bb.
 LocalesDiscarded                = Des textes ont \u00e9t\u00e9 ignor\u00e9s 
pour certaines langues.
+MismatchedEllipsoidAxisLength_3 = Le param\u00e8tre 
\u00ab\u202f{1}\u202f\u00bb aurait pu \u00eatre omis. Mais il lui a 
\u00e9t\u00e9 donn\u00e9 la {2} qui ne correspond pas \u00e0 la d\u00e9finition 
de l'ellipso\u00efde \u00ab\u202f{0}\u202f\u00bb.
 UnparsableValueStoredAsText_2   = La valeur \u00ab\u202f{1}\u202f\u00bb ne 
peut pas \u00eatre interpr\u00e9t\u00e9e comme une instance de \u2018{0}\u2019. 
Elle est donc m\u00e9moris\u00e9e sous sa forme textuelle, mais sera 
ignor\u00e9e par certains traitements.


Reply via email to