Author: desruisseaux
Date: Thu Apr 21 20:07:44 2016
New Revision: 1740399

URL: http://svn.apache.org/viewvc?rev=1740399&view=rev
Log:
Merge bug fixes from JDK6 branch.

Modified:
    sis/trunk/   (props changed)
    
sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java
    
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AbstractCS.java
    
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java
    
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
    
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
    
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
    
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java
    
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/SubOperationInfo.java
    
sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java
    
sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java

Propchange: sis/trunk/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Thu Apr 21 20:07:44 2016
@@ -1,4 +1,4 @@
 /sis/branches/Android:1430670-1480699
-/sis/branches/JDK6:1394364-1740152
-/sis/branches/JDK7:1394913-1740146
-/sis/branches/JDK8:1584960-1740143
+/sis/branches/JDK6:1394364-1740397
+/sis/branches/JDK7:1394913-1740391
+/sis/branches/JDK8:1584960-1740312

Modified: 
sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java
URL: 
http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java?rev=1740399&r1=1740398&r2=1740399&view=diff
==============================================================================
--- 
sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java
 [UTF-8] (original)
+++ 
sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/NamedFeatureType.java
 [UTF-8] Thu Apr 21 20:07:44 2016
@@ -67,7 +67,7 @@ final class NamedFeatureType implements
     }
 
     /**
-     * This feature type is considered to all other features except itself.
+     * This feature type is considered independent of all other feature types 
except itself.
      */
     @Override
     public boolean isAssignableFrom(final DefaultFeatureType type) {

Modified: 
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AbstractCS.java
URL: 
http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AbstractCS.java?rev=1740399&r1=1740398&r2=1740399&view=diff
==============================================================================
--- 
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AbstractCS.java
 [UTF-8] (original)
+++ 
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AbstractCS.java
 [UTF-8] Thu Apr 21 20:07:44 2016
@@ -391,9 +391,10 @@ public class AbstractCS extends Abstract
      * @return {@code true} if both objects are equal.
      */
     @Override
+    @SuppressWarnings("fallthrough")
     public boolean equals(final Object object, final ComparisonMode mode) {
         if (object == this) {
-            return true; // Slight optimization.
+            return true;                                            // Slight 
optimization.
         }
         if (!super.equals(object, mode)) {
             return false;
@@ -403,6 +404,14 @@ public class AbstractCS extends Abstract
                 // No need to check the class - this check has been done by 
super.equals(…).
                 return Arrays.equals(axes, ((AbstractCS) object).axes);
             }
+            case DEBUG: {
+                final int d1 = axes.length;
+                final int d2 = ((CoordinateSystem) object).getDimension();
+                if (d1 != d2) {
+                    throw new 
AssertionError(Errors.format(Errors.Keys.MismatchedDimension_2, d1, d2));
+                }
+                // Fall through
+            }
             default: {
                 final CoordinateSystem that = (CoordinateSystem) object;
                 final int dimension = getDimension();

Modified: 
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java
URL: 
http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java?rev=1740399&r1=1740398&r2=1740399&view=diff
==============================================================================
--- 
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java
 [UTF-8] (original)
+++ 
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java
 [UTF-8] Thu Apr 21 20:07:44 2016
@@ -16,6 +16,8 @@
  */
 package org.apache.sis.referencing.operation;
 
+import org.opengis.referencing.cs.EllipsoidalCS;
+import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.IdentifiedObject;
 import org.apache.sis.referencing.AbstractIdentifiedObject;
@@ -57,7 +59,7 @@ final class CRSPair {
      */
     @Override
     public int hashCode() {
-        return sourceCRS.hashCode() * 31 + targetCRS.hashCode();
+        return Objects.hashCode(sourceCRS) * 31 + Objects.hashCode(targetCRS);
     }
 
     /**
@@ -79,8 +81,9 @@ final class CRSPair {
     }
 
     /**
-     * Returns the name of the GeoAPI interface implemented by the specified 
object,
-     * followed by the name between brackets.
+     * Returns the name of the GeoAPI interface implemented by the specified 
object. In the GeographicCRS
+     * or EllipsoidalCS cases, the trailing CRS or CS suffix is replaced by 
the number of dimensions
+     * (e.g. "Geographic3D").
      */
     static String label(final IdentifiedObject object) {
         if (object == null) {
@@ -92,7 +95,18 @@ final class CRSPair {
         } else {
             type = Classes.getLeafInterfaces(object.getClass(), 
IdentifiedObject.class)[0];
         }
-        String label = Classes.getShortName(type);
+        String suffix, label = Classes.getShortName(type);
+        if (label.endsWith((suffix = "CRS")) || label.endsWith(suffix = "CS")) 
{
+            Object cs = object;
+            if (object instanceof CoordinateReferenceSystem) {
+                cs = ((CoordinateReferenceSystem) 
object).getCoordinateSystem();
+            }
+            if (cs instanceof EllipsoidalCS) {
+                final StringBuilder sb = new StringBuilder(label);
+                sb.setLength(label.length() - suffix.length());
+                label = sb.append(((CoordinateSystem) 
cs).getDimension()).append('D').toString();
+            }
+        }
         String name = IdentifiedObjects.getName(object, null);
         if (name != null) {
             int i = 30;                                         // Arbitrary 
length threshold.

Modified: 
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
URL: 
http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java?rev=1740399&r1=1740398&r2=1740399&view=diff
==============================================================================
--- 
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
 [UTF-8] (original)
+++ 
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
 [UTF-8] Thu Apr 21 20:07:44 2016
@@ -146,6 +146,11 @@ public class CoordinateOperationFinder e
     private final Map<CRSPair,Boolean> previousSearches;
 
     /**
+     * Whether this finder instance is allowed to use {@link 
DefaultCoordinateOperationFactory#cache}.
+     */
+    private final boolean useCache;
+
+    /**
      * Creates a new instance for the given factory and context.
      *
      * @param  registry  the factory to use for creating operations as defined 
by authority, or {@code null} if none.
@@ -160,6 +165,7 @@ public class CoordinateOperationFinder e
         super(registry, factory, context);
         identifierOfStepCRS = new HashMap<Identifier,Object>(8);
         previousSearches    = new HashMap<CRSPair,Boolean>(8);
+        useCache = (context == null) && (factory == factorySIS);
     }
 
     /**
@@ -190,13 +196,33 @@ public class CoordinateOperationFinder e
     {
         ArgumentChecks.ensureNonNull("sourceCRS", sourceCRS);
         ArgumentChecks.ensureNonNull("targetCRS", targetCRS);
-        if (!previousSearches.isEmpty()) {
-            // TODO: verify here if the path is defined in EPSG database.
+        if (equalsIgnoreMetadata(sourceCRS, targetCRS)) try {
+            return createFromAffineTransform(AXIS_CHANGES, sourceCRS, 
targetCRS,
+                    
CoordinateSystems.swapAndScaleAxes(sourceCRS.getCoordinateSystem(), 
targetCRS.getCoordinateSystem()));
+        } catch (IllegalArgumentException e) {
+            throw new 
FactoryException(Errors.format(Errors.Keys.CanNotInstantiate_1, new 
CRSPair(sourceCRS, targetCRS)), e);
+        } catch (ConversionException e) {
+            throw new 
FactoryException(Errors.format(Errors.Keys.CanNotInstantiate_1, new 
CRSPair(sourceCRS, targetCRS)), e);
         }
+        /*
+         * If this method is invoked recursively, verify if the requested 
operation is already in the cache.
+         * We do not perform this verification on the first invocation because 
it was already verified by
+         * DefaultCoordinateOperationFactory.createOperation(…). We do not 
block if the operation is in
+         * process of being computed in another thread because of the risk of 
deadlock. If the operation
+         * is not in the cache, store the key in our internal map for 
preventing infinite recursivity.
+         */
         final CRSPair key = new CRSPair(sourceCRS, targetCRS);
+        if (useCache && !previousSearches.isEmpty()) {
+            final CoordinateOperation op = factorySIS.cache.peek(key);
+            if (op != null) return op;
+        }
         if (previousSearches.put(key, Boolean.TRUE) != null) {
             throw new 
FactoryException(Errors.format(Errors.Keys.RecursiveCreateCallForCode_2, 
CoordinateOperation.class, key));
         }
+        /*
+         * If the user did not specified an area of interest, use the domain 
of validity of the CRS.
+         * Then verify in the EPSG dataset if the operation is explicitely 
defined by an authority.
+         */
         GeographicBoundingBox bbox = 
Extents.getGeographicBoundingBox(areaOfInterest);
         if (bbox == null) {
             bbox = 
Extents.intersection(CRS.getGeographicBoundingBox(sourceCRS),
@@ -901,8 +927,14 @@ public class CoordinateOperationFinder e
          * trivial operations.
          */
         CoordinateOperation main = null;
-        if (step1.getName() == AXIS_CHANGES && mt1.getSourceDimensions() == 
mt1.getTargetDimensions()) main = step2;
-        if (step2.getName() == AXIS_CHANGES && mt2.getSourceDimensions() == 
mt2.getTargetDimensions()) main = step1;
+        final boolean isAxisChange1 = (step1.getName() == AXIS_CHANGES);
+        final boolean isAxisChange2 = (step2.getName() == AXIS_CHANGES);
+        if (isAxisChange1 && isAxisChange2 && isAffine(step1) && 
isAffine(step2)) {
+            main = step2;                                           // 
Arbitrarily take the last step.
+        } else {
+            if (isAxisChange1 && mt1.getSourceDimensions() == 
mt1.getTargetDimensions()) main = step2;
+            if (isAxisChange2 && mt2.getSourceDimensions() == 
mt2.getTargetDimensions()) main = step1;
+        }
         if (main instanceof SingleOperation) {
             final SingleOperation op = (SingleOperation) main;
             final MathTransform mt = 
factorySIS.getMathTransformFactory().createConcatenatedTransform(mt1, mt2);
@@ -915,7 +947,7 @@ public class CoordinateOperationFinder e
         }
         /*
          * Sometime we get a concatenated operation made of an operation 
followed by its inverse.
-         * We can identity those case when the associated MathTransform is the 
identity transform.
+         * We can identify thoses case when the associated MathTransform is 
the identity transform.
          * In such case, simplify by replacing the ConcatenatedTransform by a 
SingleTransform.
          */
         if (main instanceof ConcatenatedOperation && 
main.getMathTransform().isIdentity()) {
@@ -958,6 +990,18 @@ public class CoordinateOperationFinder e
     }
 
     /**
+     * Returns {@code true} if the given operation is non-null and use the 
affine operation method.
+     */
+    private static boolean isAffine(final CoordinateOperation operation) {
+        if (operation instanceof SingleOperation) {
+            if (IdentifiedObjects.isHeuristicMatchForName(((SingleOperation) 
operation).getMethod(), Constants.AFFINE)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * Returns {@code true} if the specified operation is an identity 
conversion.
      * This method always returns {@code false} for transformations even if 
their
      * associated math transform is an identity one, because such 
transformations

Modified: 
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
URL: 
http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java?rev=1740399&r1=1740398&r2=1740399&view=diff
==============================================================================
--- 
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
 [UTF-8] (original)
+++ 
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
 [UTF-8] Thu Apr 21 20:07:44 2016
@@ -38,6 +38,7 @@ import org.opengis.referencing.Identifie
 import org.opengis.referencing.NoSuchAuthorityCodeException;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.crs.GeodeticCRS;
+import org.opengis.referencing.crs.SingleCRS;
 import org.opengis.referencing.cs.EllipsoidalCS;
 import org.opengis.referencing.operation.*;
 
@@ -295,7 +296,8 @@ class CoordinateOperationRegistry {
                      * need to modify the coordinate operation in order to 
match the new number of dimensions.
                      */
                     if (combine != 0) {
-                        operation = propagateVertical(operation, source != 
sourceCRS, target != targetCRS);
+                        operation = propagateVertical(sourceCRS, source != 
sourceCRS,
+                                                      targetCRS, target != 
targetCRS, operation);
                         if (operation == null) {
                             continue;
                         }
@@ -304,8 +306,12 @@ class CoordinateOperationRegistry {
                     return operation;
                 }
             } catch (IllegalArgumentException e) {
-                throw new FactoryException(Errors.format(
-                        Errors.Keys.CanNotInstantiate_1, new 
CRSPair(sourceCRS, targetCRS)), e);
+                String message = 
Errors.format(Errors.Keys.CanNotInstantiate_1, new CRSPair(sourceCRS, 
targetCRS));
+                String details = e.getLocalizedMessage();
+                if (details != null) {
+                    message = message + ' ' + details;
+                }
+                throw new FactoryException(message, e);
             } catch (ConversionException e) {
                 throw new FactoryException(Errors.format(
                         Errors.Keys.CanNotInstantiate_1, new 
CRSPair(sourceCRS, targetCRS)), e);
@@ -545,7 +551,8 @@ class CoordinateOperationRegistry {
                                                   final MathTransformFactory   
   mtFactory)
             throws IllegalArgumentException, ConversionException, 
FactoryException
     {
-        assert Utilities.deepEquals(sourceCRS, targetCRS, 
ComparisonMode.ALLOW_VARIANT);
+        assert ReferencingUtilities.getDimension(sourceCRS) != 
ReferencingUtilities.getDimension(targetCRS)
+                || Utilities.deepEquals(sourceCRS, targetCRS, 
ComparisonMode.ALLOW_VARIANT);
         final Matrix m = 
CoordinateSystems.swapAndScaleAxes(sourceCRS.getCoordinateSystem(), 
targetCRS.getCoordinateSystem());
         return (m.isIdentity()) ? null : mtFactory.createAffineTransform(m);
     }
@@ -702,16 +709,20 @@ class CoordinateOperationRegistry {
      * <cite>best effort</cite> basis. In any cases, the {@link #complete} 
method should be invoked
      * after this one in order to ensure that the source and target CRS are 
the expected ones.</p>
      *
-     * @param  operation the original (typically two-dimensional) coordinate 
operation.
+     * @param  sourceCRS the potentially three-dimensional source CRS
      * @param  source3D  {@code true} for adding ellipsoidal height in source 
coordinates.
+     * @param  targetCRS the potentially three-dimensional target CRS
      * @param  target3D  {@code true} for adding ellipsoidal height in target 
coordinates.
+     * @param  operation the original (typically two-dimensional) coordinate 
operation.
      * @return a coordinate operation with the source and/or target 
coordinates made 3D,
      *         or {@code null} if this method does not know how to create the 
operation.
      * @throws IllegalArgumentException if the operation method can not have 
the desired number of dimensions.
      * @throws FactoryException if an error occurred while creating the 
coordinate operation.
      */
-    private CoordinateOperation propagateVertical(final CoordinateOperation 
operation,
-            final boolean source3D, final boolean target3D) throws 
IllegalArgumentException, FactoryException
+    private CoordinateOperation propagateVertical(final 
CoordinateReferenceSystem sourceCRS, final boolean source3D,
+                                                  final 
CoordinateReferenceSystem targetCRS, final boolean target3D,
+                                                  final CoordinateOperation 
operation)
+            throws IllegalArgumentException, FactoryException
     {
         final List<CoordinateOperation> operations = new 
ArrayList<CoordinateOperation>();
         if (operation instanceof ConcatenatedOperation) {
@@ -719,8 +730,8 @@ class CoordinateOperationRegistry {
         } else {
             operations.add(operation);
         }
-        if ((source3D && !propagateVertical(operations.listIterator(), true)) 
||
-            (target3D && 
!propagateVertical(operations.listIterator(operations.size()), false)))
+        if ((source3D && !propagateVertical(sourceCRS, targetCRS, 
operations.listIterator(), true)) ||
+            (target3D && !propagateVertical(sourceCRS, targetCRS, 
operations.listIterator(operations.size()), false)))
         {
             return null;
         }
@@ -737,13 +748,18 @@ class CoordinateOperationRegistry {
      * Appends a vertical axis in the source CRS of the first step {@code 
forward = true} or in
      * the target CRS of the last step {@code forward = false} of the given 
operations chain.
      *
+     * @param  source3D    the potentially three-dimensional source CRS
+     * @param  target3D    the potentially three-dimensional target CRS
      * @param  operations  the chain of operations in which to add a vertical 
axis.
      * @param  forward     {@code true} for adding the vertical axis at the 
beginning, or
      *                     {@code false} for adding the vertical axis at the 
end.
      * @return {@code true} on success.
      * @throws IllegalArgumentException if the operation method can not have 
the desired number of dimensions.
      */
-    private boolean propagateVertical(final ListIterator<CoordinateOperation> 
operations, final boolean forward)
+    private boolean propagateVertical(final CoordinateReferenceSystem source3D,
+                                      final CoordinateReferenceSystem target3D,
+                                      final ListIterator<CoordinateOperation> 
operations,
+                                      final boolean forward)
             throws IllegalArgumentException, FactoryException
     {
         while (forward ? operations.hasNext() : operations.hasPrevious()) {
@@ -781,18 +797,18 @@ class CoordinateOperationRegistry {
                 if (op instanceof SingleOperation) {
                     final MathTransformFactory mtFactory = 
factorySIS.getMathTransformFactory();
                     if (mtFactory instanceof DefaultMathTransformFactory) {
-                        final CoordinateReferenceSystem source3D = 
toGeodetic3D(sourceCRS);
-                        final CoordinateReferenceSystem target3D = 
toGeodetic3D(targetCRS);
+                        if (forward) sourceCRS = toGeodetic3D(sourceCRS, 
source3D);
+                        else         targetCRS = toGeodetic3D(targetCRS, 
target3D);
                         final MathTransform mt;
                         try {
                             mt = ((DefaultMathTransformFactory) 
mtFactory).createParameterizedTransform(
                                     ((SingleOperation) 
op).getParameterValues(),
-                                    
ReferencingUtilities.createTransformContext(source3D, target3D, null));
+                                    
ReferencingUtilities.createTransformContext(sourceCRS, targetCRS, null));
                         } catch (InvalidGeodeticParameterException e) {
                             log(e);
                             break;
                         }
-                        operations.set(recreate(op, source3D, target3D, mt, 
mtFactory.getLastMethodUsed()));
+                        operations.set(recreate(op, sourceCRS, targetCRS, mt, 
mtFactory.getLastMethodUsed()));
                         return true;
                     }
                 }
@@ -823,7 +839,7 @@ class CoordinateOperationRegistry {
                  * conform to the definition provided by the authority.
                  */
                 final MathTransform mt = 
factorySIS.getMathTransformFactory().createAffineTransform(matrix);
-                operations.set(recreate(op, toGeodetic3D(sourceCRS), 
toGeodetic3D(targetCRS), mt, null));
+                operations.set(recreate(op, toGeodetic3D(sourceCRS, source3D), 
toGeodetic3D(targetCRS, target3D), mt, null));
             }
             /*
              * If we processed the operation that change the number of 
dimensions, we are done.
@@ -839,13 +855,27 @@ class CoordinateOperationRegistry {
      * If the given CRS is two-dimensional, append an ellipsoidal height to it.
      * It is caller's responsibility to ensure that the given CRS is 
geographic.
      */
-    private CoordinateReferenceSystem toGeodetic3D(CoordinateReferenceSystem 
crs) throws FactoryException {
+    private CoordinateReferenceSystem toGeodetic3D(CoordinateReferenceSystem 
crs,
+            final CoordinateReferenceSystem candidate) throws FactoryException
+    {
         assert (crs instanceof GeodeticCRS) && (crs.getCoordinateSystem() 
instanceof EllipsoidalCS) : crs;
-        if (crs.getCoordinateSystem().getDimension() == 2) {
-            crs = 
ReferencingServices.getInstance().createCompoundCRS(factorySIS.getCRSFactory(), 
factorySIS.getCSFactory(),
-                    derivedFrom(crs), crs, 
CommonCRS.Vertical.ELLIPSOIDAL.crs());
+        if (crs.getCoordinateSystem().getDimension() != 2) {
+            return crs;
+        }
+        /*
+         * The check for same class is a cheap way to ensure that the two CRS 
implement the same GeoAPI interface.
+         * This test is stricter than necessary, but the result should still 
not wrong if we miss an opportunity
+         * to return the existing instance.
+         */
+        if (crs.getClass() == candidate.getClass() && 
candidate.getCoordinateSystem().getDimension() == 3) {
+            if (Utilities.equalsIgnoreMetadata(((SingleCRS) crs).getDatum(), 
((SingleCRS) candidate).getDatum())) {
+                return candidate;               // Keep the existing instance 
since it may contain useful metadata.
+            }
         }
-        return crs;
+        return ReferencingServices.getInstance().createCompoundCRS(
+                factorySIS.getCRSFactory(),
+                factorySIS.getCSFactory(),
+                derivedFrom(crs), crs, CommonCRS.Vertical.ELLIPSOIDAL.crs());
     }
 
     /**

Modified: 
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
URL: 
http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java?rev=1740399&r1=1740398&r2=1740399&view=diff
==============================================================================
--- 
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
 [UTF-8] (original)
+++ 
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
 [UTF-8] Thu Apr 21 20:07:44 2016
@@ -44,6 +44,7 @@ import org.apache.sis.referencing.factor
 import 
org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
 import org.apache.sis.util.collection.WeakHashSet;
 import org.apache.sis.util.collection.Containers;
+import org.apache.sis.util.collection.Cache;
 import org.apache.sis.util.iso.AbstractFactory;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.ArgumentChecks;
@@ -121,10 +122,21 @@ public class DefaultCoordinateOperationF
     /**
      * Weak references to existing objects.
      * This set is used in order to return a pre-existing object instead of 
creating a new one.
+     * This apply to object created explicitly, not to coordinate operations 
inferred by a call
+     * to {@link #createOperation(CoordinateReferenceSystem, 
CoordinateReferenceSystem)}.
      */
     private final WeakHashSet<IdentifiedObject> pool;
 
     /**
+     * The cache of coordinate operations found for a given pair of source and 
target CRS.
+     * If current implementation, we cache only operations found without 
context (otherwise
+     * we would need to take in account the area of interest and desired 
accuracy in the key).
+     *
+     * @see #createOperation(CoordinateReferenceSystem, 
CoordinateReferenceSystem, CoordinateOperationContext)
+     */
+    final Cache<CRSPair,CoordinateOperation> cache;
+
+    /**
      * Constructs a factory with no default properties.
      */
     public DefaultCoordinateOperationFactory() {
@@ -167,6 +179,7 @@ public class DefaultCoordinateOperationF
             mtFactory = factory;
         }
         pool = new WeakHashSet<IdentifiedObject>(IdentifiedObject.class);
+        cache = new Cache<CRSPair,CoordinateOperation>(12, 50, true);
     }
 
     /**
@@ -697,9 +710,32 @@ next:   for (int i=components.size(); --
                                                final 
CoordinateOperationContext context)
             throws OperationNotFoundException, FactoryException
     {
-        final AuthorityFactory registry = USE_EPSG_FACTORY ? 
CRS.getAuthorityFactory(Constants.EPSG) : null;
-        return new CoordinateOperationFinder((registry instanceof 
CoordinateOperationAuthorityFactory) ?
-                (CoordinateOperationAuthorityFactory) registry : null, this, 
context).createOperation(sourceCRS, targetCRS);
+        final Cache.Handler<CoordinateOperation> handler;
+        CoordinateOperation op;
+        if (context == null) {
+            final CRSPair key = new CRSPair(sourceCRS, targetCRS);
+            op = cache.peek(key);
+            if (op != null) {
+                return op;
+            }
+            handler = cache.lock(key);
+        } else {
+            // We currently do not cache the operation when the result may 
depend on the context (see 'this.cache' javadoc).
+            handler = null;
+            op = null;
+        }
+        try {
+            if (handler == null || (op = handler.peek()) == null) {
+                final AuthorityFactory registry = USE_EPSG_FACTORY ? 
CRS.getAuthorityFactory(Constants.EPSG) : null;
+                op = new CoordinateOperationFinder((registry instanceof 
CoordinateOperationAuthorityFactory) ?
+                        (CoordinateOperationAuthorityFactory) registry : null, 
this, context).createOperation(sourceCRS, targetCRS);
+            }
+        } finally {
+            if (handler != null) {
+                handler.putAndUnlock(op);
+            }
+        }
+        return op;
     }
 
     /**

Modified: 
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java
URL: 
http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java?rev=1740399&r1=1740398&r2=1740399&view=diff
==============================================================================
--- 
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java
 [UTF-8] (original)
+++ 
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java
 [UTF-8] Thu Apr 21 20:07:44 2016
@@ -20,6 +20,7 @@ import java.util.Map;
 import java.util.HashMap;
 import javax.xml.bind.annotation.XmlTransient;
 import javax.measure.unit.Unit;
+import org.opengis.metadata.Identifier;
 import org.opengis.util.InternationalString;
 import org.opengis.parameter.ParameterValue;
 import org.opengis.parameter.ParameterValueGroup;
@@ -34,9 +35,6 @@ import org.apache.sis.internal.referenci
 import org.apache.sis.metadata.iso.ImmutableIdentifier;
 import org.apache.sis.util.Deprecable;
 
-// Branch-dependent imports
-import org.opengis.metadata.Identifier;
-
 
 /**
  * Description of the inverse of another method. This class should be used 
only when no operation is defined

Modified: 
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/SubOperationInfo.java
URL: 
http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/SubOperationInfo.java?rev=1740399&r1=1740398&r2=1740399&view=diff
==============================================================================
--- 
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/SubOperationInfo.java
 [UTF-8] (original)
+++ 
sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/SubOperationInfo.java
 [UTF-8] Thu Apr 21 20:07:44 2016
@@ -115,9 +115,9 @@ final class SubOperationInfo {
         final Class<?> targetType = type(target);
         for (final Class<?>[] sourceTypes : COMPATIBLE_TYPES) {
             if (sourceTypes[0].isAssignableFrom(targetType)) {
-                int startAtDimension;
-                int endAtDimension = 0;
                 for (final Class<?> sourceType : sourceTypes) {
+                    int startAtDimension;
+                    int endAtDimension = 0;
                     for (int i=0; i<sourceIsUsed.length; i++) {
                         final SingleCRS source = sources.get(i);
                         startAtDimension = endAtDimension;
@@ -130,7 +130,7 @@ final class SubOperationInfo {
                                 if (failure == null) {
                                     failure = exception;
                                 } else {
-                                    // failure.addSuppressed(exception);
+                                    // failure.addSuppressed(exception) on the 
JDK7 branch.
                                 }
                                 continue;
                             }

Modified: 
sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java
URL: 
http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java?rev=1740399&r1=1740398&r2=1740399&view=diff
==============================================================================
--- 
sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java
 [UTF-8] (original)
+++ 
sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java
 [UTF-8] Thu Apr 21 20:07:44 2016
@@ -513,6 +513,44 @@ public final strictfp class CoordinateOp
     
//////////////////////////////////////////////////////////////////////////////////
 
     /**
+     * Tests the conversion from a four-dimensional geographic CRS to a 
two-dimensional geographic CRS.
+     * The vertical and temporal dimensions are simply dropped.
+     *
+     * @throws FactoryException if the operation can not be created.
+     * @throws TransformException if an error occurred while converting the 
test points.
+     */
+    @Test
+    @DependsOnMethod("testGeographic3D_to_2D")
+    public void testGeographic4D_to_2D() throws FactoryException, 
TransformException {
+        // NOTE: make sure that the 'sourceCRS' below is not equal to any 
other 'sourceCRS' created in this class.
+        final CompoundCRS   sourceCRS = compound("Test4D", 
CommonCRS.WGS84.geographic3D(), CommonCRS.Temporal.UNIX.crs());
+        final GeographicCRS targetCRS = CommonCRS.WGS84.geographic();
+        final CoordinateOperation operation = 
inference.createOperation(sourceCRS, targetCRS);
+        assertSame      ("sourceCRS", sourceCRS,        
operation.getSourceCRS());
+        assertSame      ("targetCRS", targetCRS,        
operation.getTargetCRS());
+
+        transform = operation.getMathTransform();
+        assertInstanceOf("transform", LinearTransform.class, transform);
+        assertEquals("sourceDimensions", 4, transform.getSourceDimensions());
+        assertEquals("targetDimensions", 2, transform.getTargetDimensions());
+        Assert.assertMatrixEquals("transform.matrix", Matrices.create(3, 5, 
new double[] {
+            1, 0, 0, 0, 0,
+            0, 1, 0, 0, 0,
+            0, 0, 0, 0, 1
+        }), ((LinearTransform) transform).getMatrix(), STRICT);
+
+        isInverseTransformSupported = false;
+        verifyTransform(new double[] {
+            30, 10,  20, 1000,
+            20, 30, -10, 3000
+        }, new double[] {
+            30, 10,
+            20, 30
+        });
+        validate();
+    }
+
+    /**
      * Tests the conversion from a three-dimensional geographic CRS to a 
two-dimensional geographic CRS.
      * The vertical dimension is simply dropped.
      *
@@ -640,6 +678,46 @@ public final strictfp class CoordinateOp
     }
 
     /**
+     * Tests extracting the vertical part of a spatio-temporal CRS.
+     *
+     * @throws FactoryException if the operation can not be created.
+     * @throws TransformException if an error occurred while converting the 
test points.
+     */
+    @Test
+    @DependsOnMethod("testGeographic3D_to_EllipsoidalHeight")
+    public void testGeographic4D_to_EllipsoidalHeight() throws 
FactoryException, TransformException {
+        // NOTE: make sure that the 'sourceCRS' below is not equal to any 
other 'sourceCRS' created in this class.
+        final CompoundCRS sourceCRS = compound("Test4D", 
CommonCRS.WGS84.geographic3D(), CommonCRS.Temporal.JULIAN.crs());
+        final VerticalCRS targetCRS = CommonCRS.Vertical.ELLIPSOIDAL.crs();
+        final CoordinateOperation operation = 
inference.createOperation(sourceCRS, targetCRS);
+        assertSame      ("sourceCRS", sourceCRS,        
operation.getSourceCRS());
+        assertSame      ("targetCRS", targetCRS,        
operation.getTargetCRS());
+        assertEquals    ("name",      "Axis changes",   
operation.getName().getCode());
+        assertInstanceOf("operation", Conversion.class, operation);
+
+        transform = operation.getMathTransform();
+        assertInstanceOf("transform", LinearTransform.class, transform);
+        assertEquals("sourceDimensions", 4, transform.getSourceDimensions());
+        assertEquals("targetDimensions", 1, transform.getTargetDimensions());
+        Assert.assertMatrixEquals("transform.matrix", Matrices.create(2, 5, 
new double[] {
+            0, 0, 1, 0, 0,
+            0, 0, 0, 0, 1
+        }), ((LinearTransform) transform).getMatrix(), STRICT);
+
+        isInverseTransformSupported = false;
+        verifyTransform(new double[] {
+             0,  0,  0,  0,
+             5,  8, 20, 10,
+            -5, -8, 24, 30
+        }, new double[] {
+                     0,
+                    20,
+                    24,
+        });
+        validate();
+    }
+
+    /**
      * Convenience method for creating a compound CRS.
      */
     private static CompoundCRS compound(final String name, final 
CoordinateReferenceSystem... components) {
@@ -713,6 +791,7 @@ public final strictfp class CoordinateOp
     @Test
     @DependsOnMethod("testTemporalConversion")
     public void testGeographic3D_to_4D() throws FactoryException, 
TransformException {
+        // NOTE: make sure that the 'sourceCRS' below is not equal to any 
other 'sourceCRS' created in this class.
         final CompoundCRS sourceCRS = compound("Test3D", 
CommonCRS.WGS84.geographic(),   CommonCRS.Temporal.UNIX.crs());
         final CompoundCRS targetCRS = compound("Test4D", 
CommonCRS.WGS84.geographic3D(), CommonCRS.Temporal.MODIFIED_JULIAN.crs());
         final CoordinateOperation operation = 
inference.createOperation(sourceCRS, targetCRS);

Modified: 
sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java
URL: 
http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java?rev=1740399&r1=1740398&r2=1740399&view=diff
==============================================================================
--- 
sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java
 [UTF-8] (original)
+++ 
sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java
 [UTF-8] Thu Apr 21 20:07:44 2016
@@ -328,7 +328,7 @@ public final strictfp class CoordinateOp
         } else {
             assertEpsgNameWithoutIdentifierEqual("NTF (Paris) to WGS 84 (1)", 
operation);
             assertEpsgNameWithoutIdentifierEqual("NTF (Paris)",               
operation.getSourceCRS());
-            assertEpsgNameWithoutIdentifierEqual("WGS 84",                    
operation.getTargetCRS());
+            assertEquals("name",                 "WGS 84",                    
operation.getTargetCRS().getName().getCode());
             assertEpsgNameWithoutIdentifierEqual("NTF (Paris) to NTF (1)",    
step1);
             assertEpsgNameWithoutIdentifierEqual("NTF to WGS 84 (1)",         
step2);
         }



Reply via email to