You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2016/05/15 19:16:55 UTC

svn commit: r1743955 - in /sis/branches/JDK6: ./ application/sis-console/src/main/java/org/apache/sis/console/ core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/...

Author: desruisseaux
Date: Sun May 15 19:16:55 2016
New Revision: 1743955

URL: http://svn.apache.org/viewvc?rev=1743955&view=rev
Log:
Merge last-minute bug fixes from JDK7 branch.

Modified:
    sis/branches/JDK6/   (props changed)
    sis/branches/JDK6/application/sis-console/src/main/java/org/apache/sis/console/TransformCommand.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectSet.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/WKTParserTest.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GeodeticObjectFactoryTest.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/LinearTransformTest.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/ProjectiveTransformTest.java
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
    sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/test/TestRunner.java
    sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultNameFactoryTest.java

Propchange: sis/branches/JDK6/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Sun May 15 19:16:55 2016
@@ -1,4 +1,4 @@
 /sis/branches/Android:1430670-1480699
-/sis/branches/JDK7:1394913-1740994
-/sis/branches/JDK8:1584960-1740992
+/sis/branches/JDK7:1394913-1743954
+/sis/branches/JDK8:1584960-1743953
 /sis/trunk:1394364-1508466,1519089-1519674

Modified: sis/branches/JDK6/application/sis-console/src/main/java/org/apache/sis/console/TransformCommand.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/application/sis-console/src/main/java/org/apache/sis/console/TransformCommand.java?rev=1743955&r1=1743954&r2=1743955&view=diff
==============================================================================
--- sis/branches/JDK6/application/sis-console/src/main/java/org/apache/sis/console/TransformCommand.java [UTF-8] (original)
+++ sis/branches/JDK6/application/sis-console/src/main/java/org/apache/sis/console/TransformCommand.java [UTF-8] Sun May 15 19:16:55 2016
@@ -16,6 +16,9 @@
  */
 package org.apache.sis.console;
 
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.EnumSet;
 import java.util.Locale;
 import java.io.IOException;
@@ -28,9 +31,11 @@ import javax.measure.unit.NonSI;
 import javax.measure.unit.SI;
 import javax.measure.converter.ConversionException;
 import org.opengis.metadata.Metadata;
-import org.opengis.geometry.MismatchedDimensionException;
+import org.opengis.metadata.extent.Extent;
 import org.opengis.metadata.extent.GeographicBoundingBox;
+import org.opengis.geometry.MismatchedDimensionException;
 import org.opengis.util.FactoryException;
+import org.opengis.util.InternationalString;
 import org.opengis.referencing.IdentifiedObject;
 import org.opengis.referencing.ReferenceSystem;
 import org.opengis.referencing.NoSuchAuthorityCodeException;
@@ -58,9 +63,11 @@ import org.apache.sis.io.wkt.Colors;
 import org.apache.sis.io.wkt.Convention;
 import org.apache.sis.io.wkt.Transliterator;
 import org.apache.sis.io.wkt.WKTFormat;
+import org.apache.sis.io.wkt.Warnings;
 import org.apache.sis.math.DecimalFunctions;
 import org.apache.sis.math.MathFunctions;
 import org.apache.sis.measure.Units;
+import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
 import org.apache.sis.storage.DataStore;
 import org.apache.sis.storage.DataStores;
 import org.apache.sis.storage.DataStoreException;
@@ -86,19 +93,9 @@ final class TransformCommand extends Met
     private CoordinateOperation operation;
 
     /**
-     * The coordinate operation domain of validity.
-     * We use the {@code ImmutableEnvelope} type in order to handle envelope crossing the anti-meridian.
-     *
-     * @see #computeDomainOfValidity()
-     */
-    private ImmutableEnvelope domainOfValidity;
-
-    /**
      * The transformation from source CRS to the domain of validity CRS, or {@code null} if none.
-     *
-     * @see #computeDomainOfValidity()
      */
-    private MathTransform toDomainOfValidityCRS;
+    private MathTransform toDomainOfValidity;
 
     /**
      * Resources for {@link #printHeader(short)}.
@@ -131,6 +128,16 @@ final class TransformCommand extends Met
     private double[] thresholdForScientificNotation;
 
     /**
+     * The error message to report after we transformed all coordinates, or {@code null}.
+     */
+    private String errorMessage;
+
+    /**
+     * The cause of {@link #errorMessage}, or {@code null} if none.
+     */
+    private NumberFormatException errorCause;
+
+    /**
      * Creates the {@code "transform"} sub-command.
      */
     TransformCommand(final int commandIndex, final String... args) throws InvalidOptionException {
@@ -191,60 +198,65 @@ final class TransformCommand extends Met
             final String format = outputFormat.name();
             throw new InvalidOptionException(Errors.format(Errors.Keys.IncompatibleFormat_2, "transform", format), format);
         }
-        operation = CRS.findOperation(fetchCRS(Option.SOURCE_CRS), fetchCRS(Option.TARGET_CRS), null);
+        final CoordinateReferenceSystem sourceCRS = fetchCRS(Option.SOURCE_CRS);
+        final CoordinateReferenceSystem targetCRS = fetchCRS(Option.TARGET_CRS);
+        /*
+         * Read all coordinates, so we can compute the area of interest.
+         * This will be used when searching for a coordinate operation.
+         */
+        GeographicBoundingBox areaOfInterest = null;
+        List<double[]> points = Collections.emptyList();
+        final boolean useStandardInput = useStandardInput();
+        if (useStandardInput || !files.isEmpty()) {
+            if (useStandardInput) {
+                LineNumberReader in = new LineNumberReader(new InputStreamReader(System.in, encoding));
+                try {
+                    points = readCoordinates(in, "stdin");
+                } finally {
+                    in.close();
+                }
+            } else {
+                for (final String file : files) {
+                    LineNumberReader in = new LineNumberReader(new InputStreamReader(new FileInputStream(file), encoding));
+                    try {
+                        points = readCoordinates(in, file);
+                    } finally {
+                        in.close();
+                    }
+                }
+            }
+            try {
+                final GeographicCRS domainOfValidityCRS = ReferencingUtilities.toNormalizedGeographicCRS(sourceCRS);
+                if (domainOfValidityCRS != null) {
+                    toDomainOfValidity = CRS.findOperation(sourceCRS, domainOfValidityCRS, null).getMathTransform();
+                    areaOfInterest = computeAreaOfInterest(points);
+                }
+            } catch (FactoryException e) {
+                warning(e);
+            }
+        }
+        operation = CRS.findOperation(sourceCRS, targetCRS, areaOfInterest);
         /*
          * Prints the header: source CRS, target CRS, operation steps and positional accuracy.
          */
         outHeader = new TableAppender(new LineAppender(out), " ");
         outHeader.setMultiLinesCells(true);
-        printHeader(Vocabulary.Keys.Source);      printNameAndIdentifier(operation.getSourceCRS());
-        printHeader(Vocabulary.Keys.Destination); printNameAndIdentifier(operation.getTargetCRS());
-        printHeader(Vocabulary.Keys.Methods);     printOperationMethods (operation, false);
+        printHeader(Vocabulary.Keys.Source);      printNameAndIdentifier(operation.getSourceCRS(), false);
+        printHeader(Vocabulary.Keys.Destination); printNameAndIdentifier(operation.getTargetCRS(), false);
+        printHeader(Vocabulary.Keys.Operations);  printOperations (operation, false);
         outHeader.nextLine();
+        printDomainOfValidity(operation.getDomainOfValidity());
+        printAccuracy(CRS.getLinearAccuracy(operation));
         if (options.containsKey(Option.VERBOSE)) {
-            final WKTFormat f = new WKTFormat(locale, timezone);
-            f.setConvention(options.containsKey(Option.DEBUG) ? Convention.INTERNAL : convention);
-            if (colors) {
-                f.setColors(Colors.DEFAULT);
-            }
-            final CharSequence[] lines = CharSequences.splitOnEOL(f.format(operation.getMathTransform()));
-            for (int i=0; i<lines.length; i++) {
-                if (i == 0) {
-                    printHeader(Vocabulary.Keys.Details);
-                } else {
-                    printCommentLinePrefix();
-                    outHeader.nextColumn();
-                }
-                outHeader.append(lines[i]);
-                outHeader.nextLine();
-            }
-        }
-        double accuracy = CRS.getLinearAccuracy(operation);
-        if (accuracy >= 0) {
-            if (accuracy == 0) {
-                accuracy = Formulas.LINEAR_TOLERANCE;
-            }
-            printHeader(Vocabulary.Keys.Accuracy);
-            if (colors) {
-                outHeader.append(X364.FOREGROUND_YELLOW.sequence());    // Same as Colors.DEFAULT for ElementKind.NUMBER
-            }
-            outHeader.append(Double.toString(accuracy));
-            if (colors) {
-                outHeader.append(X364.FOREGROUND_DEFAULT.sequence());
-            }
-            outHeader.append(" metres");
-            outHeader.nextLine();
+            printDetails();
         }
         outHeader.flush();
         outHeader = null;
         /*
          * At this point we finished to write the header. If there is at least one input file,
-         * compute the transformation needed for verifying if the input points are inside the
-         * domain of validity. Next we can perform the actual coordinate operations.
+         * compute the number of digits to format and perform the actual coordinate operations.
          */
-        final boolean useStandardInput = useStandardInput();
-        if (useStandardInput || !files.isEmpty()) {
-            computeDomainOfValidity();
+        if (!points.isEmpty()) {
             ordinateWidth    = 15;                                      // Must be set before computeNumFractionDigits(\u2026).
             coordinateFormat = NumberFormat.getInstance(Locale.US);
             coordinateFormat.setGroupingUsed(false);
@@ -252,22 +264,9 @@ final class TransformCommand extends Met
             out.println();
             printAxes(operation.getTargetCRS().getCoordinateSystem());
             out.println();
-            if (useStandardInput) {
-                final LineNumberReader in = new LineNumberReader(new InputStreamReader(System.in, encoding));
-                try {
-                    transform(in, "stdin");
-                } finally {
-                    in.close();
-                }
-            } else {
-                for (final String file : files) {
-                    final LineNumberReader in = new LineNumberReader(new InputStreamReader(new FileInputStream(file), encoding));
-                    try {
-                        transform(in, file);
-                    } finally {
-                        in.close();
-                    }
-                }
+            transform(points);
+            if (errorMessage != null) {
+                error(errorMessage, errorCause);
             }
         }
         return 0;
@@ -302,10 +301,17 @@ final class TransformCommand extends Met
 
     /**
      * Prints the name and authority code (if any) of the given object.
+     *
+     * @param  object      the object for which to print name and identifier.
+     * @param  idRequired  {@code true} for printing the name only if an identifier is present.
+     * @return whether this method has printed something.
      */
-    private void printNameAndIdentifier(final IdentifiedObject object) {
-        outHeader.append(object.getName().getCode());
+    private boolean printNameAndIdentifier(final IdentifiedObject object, final boolean idRequired) {
         final String identifier = IdentifiedObjects.toString(IdentifiedObjects.getIdentifier(object, null));
+        if (idRequired && identifier == null) {
+            return false;
+        }
+        outHeader.append(object.getName().getCode());
         if (identifier != null) {
             outHeader.append(' ');
             if (colors) {
@@ -318,34 +324,128 @@ final class TransformCommand extends Met
                 outHeader.append(X364.FOREGROUND_DEFAULT.sequence());
             }
         }
-        outHeader.nextLine();
+        if (!idRequired) {
+            outHeader.nextLine();
+        }
+        return true;
+    }
+
+    /**
+     * Prints a summary of the given coordinate operation as a sequence of steps.
+     * If the operations is specified by EPSG, prints the EPSG name and code.
+     * Otherwise prints only the operation method names, since the coordinate operation names
+     * generated by SIS are not very meaningful.
+     *
+     * @param step  the coordinate operation to print as a sequence of steps.
+     */
+    private void printOperations(final CoordinateOperation step, boolean isNext) {
+        if (isNext) {
+            isNext = false;
+            if (colors) {
+                outHeader.append(X364.FOREGROUND_GREEN.sequence());
+            }
+            outHeader.append(" \u2192 ");
+            if (colors) {
+                outHeader.append(X364.FOREGROUND_DEFAULT.sequence());
+            }
+        }
+        if (!printNameAndIdentifier(step, true)) {
+            if (step instanceof ConcatenatedOperation) {
+                for (final CoordinateOperation op : ((ConcatenatedOperation) step).getOperations()) {
+                    printOperations(op, isNext);
+                    isNext = true;
+                }
+            } else if (step instanceof PassThroughOperation) {
+                printOperations(((PassThroughOperation) step).getOperation(), false);
+            } else if (step instanceof SingleOperation) {
+                outHeader.append(((SingleOperation) step).getMethod().getName().getCode());
+            }
+        }
     }
 
     /**
-     * Prints a summary (currently only the operation method) of the given coordinate operation as a list item.
-     * The list will contain many items if the given operation is a concatenated operation.
+     * Prints the accuracy.
+     */
+    private void printAccuracy(double accuracy) {
+        if (accuracy >= 0) {
+            if (accuracy == 0) {
+                accuracy = Formulas.LINEAR_TOLERANCE;
+            }
+            printHeader(Vocabulary.Keys.Accuracy);
+            if (colors) {
+                outHeader.append(X364.FOREGROUND_YELLOW.sequence());    // Same as Colors.DEFAULT for ElementKind.NUMBER
+            }
+            outHeader.append(Double.toString(accuracy));
+            if (colors) {
+                outHeader.append(X364.FOREGROUND_DEFAULT.sequence());
+            }
+            outHeader.append(" metres");
+            outHeader.nextLine();
+        }
+    }
+
+    /**
+     * Prints a textual description of the domain of validity. This method tries to reduce the string length by
+     * the use of some heuristic rules based on the syntax used in EPSG dataset. For example the following string:
+     *
+     * <blockquote>Canada - onshore and offshore - Alberta; British Columbia (BC); Manitoba; New Brunswick (NB);
+     * Newfoundland and Labrador; Northwest Territories (NWT); Nova Scotia (NS); Nunavut; Ontario; Prince Edward
+     * Island (PEI); Quebec; Saskatchewan; Yukon.</blockquote>
+     *
+     * is replaced by:
      *
-     * @param step  the coordinate operation to print as a list of steps.
+     * <blockquote>Canada - onshore and offshore</blockquote>
      */
-    private void printOperationMethods(final CoordinateOperation step, boolean isNext) {
-        if (step instanceof ConcatenatedOperation) {
-            for (final CoordinateOperation op : ((ConcatenatedOperation) step).getOperations()) {
-                printOperationMethods(op, isNext);
-                isNext = true;
-            }
-        } else if (step instanceof PassThroughOperation) {
-            printOperationMethods(((PassThroughOperation) step).getOperation(), isNext);
-        } else if (step instanceof SingleOperation) {
-            if (isNext) {
-                if (colors) {
-                    outHeader.append(X364.FOREGROUND_GREEN.sequence());
-                }
-                outHeader.append(" \u2192 ");
-                if (colors) {
-                    outHeader.append(X364.FOREGROUND_DEFAULT.sequence());
+    private void printDomainOfValidity(final Extent domain) {
+        if (domain != null) {
+            final InternationalString description = domain.getDescription();
+            if (description != null) {
+                String text = description.toString(locale);
+                if (text.length() >= 80) {
+                    int end = text.indexOf(';');
+                    if (end >= 0) {
+                        int s = text.lastIndexOf('-', end);
+                        if (s >= 0) {
+                            end = s;
+                        }
+                        text = text.substring(0, end).trim();
+                    }
                 }
+                printHeader(Vocabulary.Keys.Domain);
+                outHeader.append(text);
+                outHeader.nextLine();
+            }
+        }
+    }
+
+    /**
+     * Prints the coordinate operation or math transform in Well Known Text format.
+     * This information is printed only if the {@code --verbose} option was specified.
+     */
+    private void printDetails() {
+        final boolean debug = options.containsKey(Option.DEBUG);
+        final WKTFormat f = new WKTFormat(locale, timezone);
+        if (colors) f.setColors(Colors.DEFAULT);
+        f.setConvention(convention);
+        CharSequence[] lines = CharSequences.splitOnEOL(f.format(debug ? operation.getMathTransform() : operation));
+        for (int i=0; i<lines.length; i++) {
+            if (i == 0) {
+                printHeader(Vocabulary.Keys.Details);
+            } else {
+                printCommentLinePrefix();
+                outHeader.nextColumn();
+            }
+            outHeader.append(lines[i]);
+            outHeader.nextLine();
+        }
+        final Warnings warnings = f.getWarnings();
+        if (warnings != null) {
+            lines = CharSequences.splitOnEOL(warnings.toString());
+            if (lines.length != 0) {                                            // Paranoiac check.
+                printHeader(Vocabulary.Keys.Note);
+                outHeader.append(lines[0]);
+                outHeader.nextLine();
             }
-            outHeader.append(((SingleOperation) step).getMethod().getName().getCode());
         }
     }
 
@@ -426,99 +526,133 @@ final class TransformCommand extends Met
     }
 
     /**
-     * Computes the domain validity. This method is a "all or nothing" operation; if the domain of validity
-     * can not be computed, then {@link #toDomainOfValidityCRS} and {@link #domainOfValidity} stay {@code null}.
+     * Reads all coordinates.
+     * This method ignores empty and comment lines.
+     *
+     * @param  in        the stream from where to read coordinates.
+     * @param  filename  the filename, for error reporting only.
+     * @return the coordinate values.
      */
-    private void computeDomainOfValidity() {
-        final GeographicBoundingBox bbox = CRS.getGeographicBoundingBox(operation);
-        if (bbox != null) {
-            final GeographicCRS domainOfValidityCRS = ReferencingUtilities.toNormalizedGeographicCRS(operation.getSourceCRS());
-            if (domainOfValidityCRS != null) try {
-                toDomainOfValidityCRS = CRS.findOperation(operation.getSourceCRS(), domainOfValidityCRS, null).getMathTransform();
-                domainOfValidity = new ImmutableEnvelope(bbox);
-            } catch (FactoryException e) {
-                warning(e);
+    private List<double[]> readCoordinates(final LineNumberReader in, final String filename) throws IOException {
+        final List<double[]> points = new ArrayList<double[]>();
+        try {
+            String line;
+            while ((line = in.readLine()) != null) {
+                final int start = CharSequences.skipLeadingWhitespaces(line, 0, line.length());
+                if (start < line.length() && line.charAt(start) != '#') {
+                    points.add(CharSequences.parseDoubles(line, ','));
+                }
+            }
+        } catch (NumberFormatException e) {
+            errorMessage = Errors.format(Errors.Keys.ErrorInFileAtLine_2, filename, in.getLineNumber());
+            errorCause = e;
+        }
+        return points;
+    }
+
+    /**
+     * Computes the geographic area of interest from the given points.
+     * This method ignores the points having an unexpected number of dimensions since it is not this
+     * method's job to report those issues (they will be reported by {@link #transform(List)} instead.
+     */
+    private GeographicBoundingBox computeAreaOfInterest(final List<double[]> points) {
+        final int dimension = toDomainOfValidity.getSourceDimensions();
+        final double[] domainCoordinate = new double[toDomainOfValidity.getTargetDimensions()];
+        if (domainCoordinate.length >= 2) {
+            double xmin = Double.POSITIVE_INFINITY;
+            double ymin = Double.POSITIVE_INFINITY;
+            double xmax = Double.NEGATIVE_INFINITY;
+            double ymax = Double.NEGATIVE_INFINITY;
+            for (final double[] coordinates : points) {
+                if (coordinates.length == dimension) {
+                    try {
+                        toDomainOfValidity.transform(coordinates, 0, domainCoordinate, 0, 1);
+                    } catch (TransformException e) {
+                        warning(e);
+                        continue;
+                    }
+                    final double x = domainCoordinate[0];
+                    final double y = domainCoordinate[1];
+                    if (x < xmin) xmin = x;
+                    if (x > xmax) xmax = x;
+                    if (y < ymin) ymin = y;
+                    if (y > ymax) ymax = y;
+                }
+            }
+            if (xmin < xmax && ymin < ymax) {
+                return new DefaultGeographicBoundingBox(xmin, xmax, ymin, ymax);
             }
         }
+        return null;
     }
 
     /**
-     * Transforms the coordinates read from the given stream.
-     * This method ignores empty and comment lines.
-     *
-     * @param  in        the stream from where to read coordinates.
-     * @param  filename  the filename, for error reporting only.
-     * @return the errors that occurred during transformation.
+     * Transforms the given coordinates.
      */
-    private void transform(final LineNumberReader in, final String filename) throws IOException {
+    private void transform(final List<double[]> points) throws TransformException {
         final int dimension    = operation.getSourceCRS().getCoordinateSystem().getDimension();
         final MathTransform mt = operation.getMathTransform();
         final double[] result  = new double[mt.getTargetDimensions()];
         final double[] domainCoordinate;
         final DirectPositionView positionInDomain;
-        if (toDomainOfValidityCRS != null) {
-            domainCoordinate = new double[toDomainOfValidityCRS.getTargetDimensions()];
+        final ImmutableEnvelope domainOfValidity;
+        final GeographicBoundingBox bbox;
+        if (toDomainOfValidity != null && (bbox = CRS.getGeographicBoundingBox(operation)) != null) {
+            domainOfValidity = new ImmutableEnvelope(bbox);
+            domainCoordinate = new double[toDomainOfValidity.getTargetDimensions()];
             positionInDomain = new DirectPositionView(domainCoordinate, 0, domainCoordinate.length);
         } else {
+            domainOfValidity = null;
             domainCoordinate = null;
             positionInDomain = null;
         }
-        try {
-            String line;
-            while ((line = in.readLine()) != null) {
-                final int start = CharSequences.skipLeadingWhitespaces(line, 0, line.length());
-                if (start < line.length() && line.charAt(start) != '#') {
-                    final double[] coordinates = CharSequences.parseDoubles(line, ',');
-                    if (coordinates.length != dimension) {
-                        throw new MismatchedDimensionException(Errors.format(Errors.Keys.MismatchedDimensionForCRS_3,
-                                    operation.getSourceCRS().getName().getCode(), dimension, coordinates.length));
-                    }
-                    /*
-                     * At this point we got the coordinates and they have the expected number of dimensions.
-                     * Now perform the coordinate operation and prints each ordinate values. We will switch
-                     * to scientific notation if the coordinate is much larger than expected.
-                     */
-                    mt.transform(coordinates, 0, result, 0, 1);
-                    for (int i=0; i<result.length; i++) {
-                        if (i != 0) {
-                            out.print(',');
-                        }
-                        final double value = result[i];
-                        final String s;
-                        if (Math.abs(value) >= thresholdForScientificNotation[i]) {
-                            s = Double.toString(value);
-                        } else {
-                            coordinateFormat.setMinimumFractionDigits(numFractionDigits[i]);
-                            coordinateFormat.setMaximumFractionDigits(numFractionDigits[i]);
-                            s = coordinateFormat.format(value);
-                        }
-                        out.print(CharSequences.spaces(ordinateWidth - s.length()));
-                        out.print(s);
-                    }
-                    /*
-                     * Append a warning after the transformed coordinate values if the source coordinate was outside
-                     * the domain of validity. A failure to perform a coordinate transformation is also considered as
-                     * being out of the domain of valididty.
-                     */
-                    if (domainCoordinate != null) {
-                        boolean inside;
-                        try {
-                            toDomainOfValidityCRS.transform(coordinates, 0, domainCoordinate, 0, 1);
-                            inside = domainOfValidity.contains(positionInDomain);
-                        } catch (TransformException e) {
-                            inside = false;
-                            warning(e);
-                        }
-                        if (!inside) {
-                            out.print(",    ");
-                            printQuotedText(Errors.getResources(locale).getString(Errors.Keys.OutsideDomainOfValidity), 0, X364.FOREGROUND_RED);
-                        }
-                    }
-                    out.println();
+        for (final double[] coordinates : points) {
+            if (coordinates.length != dimension) {
+                throw new MismatchedDimensionException(Errors.format(Errors.Keys.MismatchedDimensionForCRS_3,
+                            operation.getSourceCRS().getName().getCode(), dimension, coordinates.length));
+            }
+            /*
+             * At this point we got the coordinates and they have the expected number of dimensions.
+             * Now perform the coordinate operation and print each ordinate values.  We will switch
+             * to scientific notation if the coordinate is much larger than expected.
+             */
+            mt.transform(coordinates, 0, result, 0, 1);
+            for (int i=0; i<result.length; i++) {
+                if (i != 0) {
+                    out.print(',');
+                }
+                final double value = result[i];
+                final String s;
+                if (Math.abs(value) >= thresholdForScientificNotation[i]) {
+                    s = Double.toString(value);
+                } else {
+                    coordinateFormat.setMinimumFractionDigits(numFractionDigits[i]);
+                    coordinateFormat.setMaximumFractionDigits(numFractionDigits[i]);
+                    s = coordinateFormat.format(value);
                 }
+                out.print(CharSequences.spaces(ordinateWidth - s.length()));
+                out.print(s);
             }
-        } catch (Exception e) {     // This is a multi-catch exception on the JDK7 branch.
-            error(Errors.format(Errors.Keys.ErrorInFileAtLine_2, filename, in.getLineNumber()), e);
+            /*
+             * Append a warning after the transformed coordinate values if the source coordinate was outside
+             * the domain of validity. A failure to perform a coordinate transformation is also considered as
+             * being out of the domain of valididty.
+             */
+            if (domainOfValidity != null) {
+                boolean inside;
+                try {
+                    toDomainOfValidity.transform(coordinates, 0, domainCoordinate, 0, 1);
+                    inside = domainOfValidity.contains(positionInDomain);
+                } catch (TransformException e) {
+                    inside = false;
+                    warning(e);
+                }
+                if (!inside) {
+                    out.print(",    ");
+                    printQuotedText(Errors.getResources(locale).getString(Errors.Keys.OutsideDomainOfValidity), 0, X364.FOREGROUND_RED);
+                }
+            }
+            out.println();
         }
     }
 

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectSet.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectSet.java?rev=1743955&r1=1743954&r2=1743955&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectSet.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectSet.java [UTF-8] Sun May 15 19:16:55 2016
@@ -24,6 +24,7 @@ import java.util.Collection;
 import java.util.AbstractSet;
 import java.util.LinkedHashMap;
 import java.util.NoSuchElementException;
+import java.util.Locale;
 import java.util.logging.Level;
 import java.util.logging.LogRecord;
 import org.opengis.util.FactoryException;
@@ -39,6 +40,7 @@ import org.apache.sis.util.resources.Mes
 import org.apache.sis.util.collection.BackingStoreException;
 import org.apache.sis.util.collection.CheckedContainer;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.Localized;
 import org.apache.sis.util.Classes;
 
 // Branch-dependent imports
@@ -86,7 +88,7 @@ import org.apache.sis.internal.jdk8.JDK8
  * @version 0.7
  * @module
  */
-public class IdentifiedObjectSet<T extends IdentifiedObject> extends AbstractSet<T> implements CheckedContainer<T> {
+public class IdentifiedObjectSet<T extends IdentifiedObject> extends AbstractSet<T> implements CheckedContainer<T>, Localized {
     /**
      * The map of object codes (keys), and the actual identified objects (values) when they have been created.
      * Each entry has a null value until the corresponding object is created.
@@ -139,6 +141,17 @@ public class IdentifiedObjectSet<T exten
     }
 
     /**
+     * Returns the locale to use for error messages and warnings.
+     * The default implementation inherits the {@link #factory} locale, if any.
+     *
+     * @return The locale, or {@code null} if not explicitly defined.
+     */
+    @Override
+    public Locale getLocale() {
+        return (factory instanceof Localized) ? ((Localized) factory).getLocale() : null;
+    }
+
+    /**
      * Returns the type of {@code IdentifiedObject} included in this set.
      *
      * @return The type of {@code IdentifiedObject} included in this set.
@@ -288,7 +301,7 @@ public class IdentifiedObjectSet<T exten
                 if (!isRecoverableFailure(exception)) {
                     throw new BackingStoreException(exception);
                 }
-                final LogRecord record = Messages.getResources(null).getLogRecord(Level.WARNING,
+                final LogRecord record = Messages.getResources(getLocale()).getLogRecord(Level.WARNING,
                         Messages.Keys.CanNotInstantiateForIdentifier_3, type, code, getCause(exception));
                 record.setLoggerName(Loggers.CRS_FACTORY);
                 Logging.log(IdentifiedObjectSet.class, "createObject", record);

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java?rev=1743955&r1=1743954&r2=1743955&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java [UTF-8] Sun May 15 19:16:55 2016
@@ -3056,7 +3056,9 @@ next:               while (r.next()) {
      * This method only extract the information explicitely declared in the EPSG database;
      * it does not attempt to infer by itself operations that are not explicitely recorded in the database.
      *
-     * <p>The returned set is ordered with the most accurate operations first.</p>
+     * <p>The returned set is ordered with the most accurate operations first.
+     * Deprecated operations are not included in the set; if a deprecated operation is really wanted,
+     * it can be fetched by an explicit call to {@link #createCoordinateOperation(String)}.</p>
      *
      * @param  sourceCRS  Coded value of source coordinate reference system.
      * @param  targetCRS  Coded value of target coordinate reference system.
@@ -3087,9 +3089,10 @@ next:               while (r.next()) {
                     sql = "SELECT COORD_OP_CODE" +
                           " FROM [Coordinate_Operation] AS CO" +
                           " JOIN [Area] ON AREA_OF_USE_CODE = AREA_CODE" +
-                          " WHERE SOURCE_CRS_CODE = ?" +
+                          " WHERE CO.DEPRECATED=0" +   // Do not put spaces around "=" - SQLTranslator searches for this exact match.
+                            " AND SOURCE_CRS_CODE = ?" +
                             " AND TARGET_CRS_CODE = ?" +
-                          " ORDER BY ABS(CO.DEPRECATED), COORD_OP_ACCURACY ASC NULLS LAST, " +
+                          " ORDER BY COORD_OP_ACCURACY ASC NULLS LAST, " +
                             " (AREA_EAST_BOUND_LON - AREA_WEST_BOUND_LON + CASE WHEN AREA_EAST_BOUND_LON < AREA_WEST_BOUND_LON THEN 360 ELSE 0 END)" +
                           " * (AREA_NORTH_BOUND_LAT - AREA_SOUTH_BOUND_LAT)" +
                           " * COS(RADIANS(AREA_NORTH_BOUND_LAT + AREA_SOUTH_BOUND_LAT)/2) DESC";

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java?rev=1743955&r1=1743954&r2=1743955&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java [UTF-8] Sun May 15 19:16:55 2016
@@ -180,11 +180,12 @@ public class AbstractCoordinateOperation
      * Area in which this operation is valid, or {@code null} if not available.
      *
      * <p><b>Consider this field as final!</b>
-     * This field is modified only at unmarshalling time by {@link #setDomainOfValidity(Extent)}.</p>
+     * This field is non-final only for the convenience of constructors and for initialization
+     * at XML unmarshalling time by {@link #setDomainOfValidity(Extent)}.</p>
      *
      * @see #getDomainOfValidity()
      */
-    private Extent domainOfValidity;
+    Extent domainOfValidity;
 
     /**
      * Description of domain of usage, or limitations of usage, for which this operation is valid.
@@ -909,7 +910,11 @@ check:      for (int isTarget=0; ; isTar
         if (formatter.getConvention().majorVersion() == 1) {
             formatter.setInvalidWKT(this, null);
         }
-        return isComponent ? "CoordinateOperationStep" : WKTKeywords.CoordinateOperation;
+        if (isComponent) {
+            formatter.setInvalidWKT(this, null);
+            return "CoordinateOperationStep";
+        }
+        return WKTKeywords.CoordinateOperation;
     }
 
     /**

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java?rev=1743955&r1=1743954&r2=1743955&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java [UTF-8] Sun May 15 19:16:55 2016
@@ -430,22 +430,14 @@ class CoordinateOperationRegistry {
         for (final Iterator<CoordinateOperation> it=operations.iterator(); it.hasNext();) {
             CoordinateOperation candidate;
             try {
-                try {
-                    candidate = it.next();
-                } catch (BackingStoreException exception) {
-                    throw exception.unwrapOrRethrow(FactoryException.class);
-                }
-                if (inverse) try {
-                    candidate = inverse(candidate);
-                } catch (NoninvertibleTransformException exception) {
-                    // It may be a normal failure - the operation is not required to be invertible.
-                    Logging.recoverableException(Logging.getLogger(Loggers.COORDINATE_OPERATION),
-                            CoordinateOperationRegistry.class, "createOperation", exception);
+                candidate = it.next();
+            } catch (BackingStoreException exception) {
+                FactoryException cause = exception.unwrapOrRethrow(FactoryException.class);
+                if (cause instanceof MissingFactoryResourceException) {
+                    log(cause);
                     continue;
                 }
-            } catch (MissingFactoryResourceException e) {
-                log(e);
-                continue;
+                throw cause;
             }
             if (candidate != null) {
                 /*
@@ -463,6 +455,22 @@ class CoordinateOperationRegistry {
                     final double accuracy = CRS.getLinearAccuracy(candidate);
                     if (bestChoice == null || area != largestArea || accuracy < finestAccuracy) {
                         /*
+                         * Inverse the operation only after we verified the metadata (domain of validity,
+                         * accuracy, etc.) since the creation of inverse operation is not guaranteed to
+                         * preserve all metadata.
+                         */
+                        if (inverse) try {
+                            candidate = inverse(candidate);
+                        } catch (NoninvertibleTransformException exception) {
+                            // It may be a normal failure - the operation is not required to be invertible.
+                            Logging.recoverableException(Logging.getLogger(Loggers.COORDINATE_OPERATION),
+                                    CoordinateOperationRegistry.class, "createOperation", exception);
+                            continue;
+                        } catch (MissingFactoryResourceException e) {
+                            log(e);
+                            continue;
+                        }
+                        /*
                          * It is possible that the CRS given to this method were not quite right.  For example the user
                          * may have created his CRS from a WKT using a different axis order than the order specified by
                          * the authority and still (wrongly) call those CRS "EPSG:xxxx".  So we check if the source and
@@ -501,6 +509,7 @@ class CoordinateOperationRegistry {
         if (op instanceof Transformation)  type = Transformation.class;
         else if (op instanceof Conversion) type = Conversion.class;
         final Map<String,Object> properties = properties(INVERSE_OPERATION);
+        InverseOperationMethod.putMetadata(op, properties);
         InverseOperationMethod.putParameters(op, properties);
         return createFromMathTransform(properties, targetCRS, sourceCRS,
                 transform, InverseOperationMethod.create(op.getMethod()), null, type);

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java?rev=1743955&r1=1743954&r2=1743955&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java [UTF-8] Sun May 15 19:16:55 2016
@@ -24,6 +24,7 @@ import javax.xml.bind.annotation.XmlType
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
 import org.opengis.util.FactoryException;
+import org.opengis.metadata.extent.Extent;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.operation.CoordinateOperation;
 import org.opengis.referencing.operation.ConcatenatedOperation;
@@ -110,7 +111,8 @@ final class DefaultConcatenatedOperation
         super(properties);
         ArgumentChecks.ensureNonNull("operations", operations);
         final List<CoordinateOperation> flattened = new ArrayList<CoordinateOperation>(operations.length);
-        initialize(properties, operations, flattened, mtFactory, (coordinateOperationAccuracy == null));
+        initialize(properties, operations, flattened, mtFactory,
+                (coordinateOperationAccuracy == null), (domainOfValidity == null));
         if (flattened.size() < 2) {
             throw new IllegalArgumentException(Errors.getResources(properties).getString(
                     Errors.Keys.TooFewOccurrences_2, 2, CoordinateOperation.class));
@@ -158,13 +160,15 @@ final class DefaultConcatenatedOperation
      * @param  flattened   The destination list in which to add the {@code SingleOperation} instances.
      * @param  mtFactory   The math transform factory to use, or {@code null} for not performing concatenation.
      * @param  setAccuracy {@code true} for setting the {@link #coordinateOperationAccuracy} field.
+     * @param  setDomain   {@code true} for setting the {@link #domainOfValidity} field.
      * @throws FactoryException if the factory can not concatenate the math transforms.
      */
     private void initialize(final Map<String,?>             properties,
                             final CoordinateOperation[]     operations,
                             final List<CoordinateOperation> flattened,
                             final MathTransformFactory      mtFactory,
-                            boolean                         setAccuracy)
+                            boolean                         setAccuracy,
+                            boolean                         setDomain)
             throws FactoryException
     {
         CoordinateReferenceSystem previous = null;
@@ -200,7 +204,7 @@ final class DefaultConcatenatedOperation
                 final List<? extends CoordinateOperation> children = ((ConcatenatedOperation) op).getOperations();
                 @SuppressWarnings("SuspiciousToArrayCall")
                 final CoordinateOperation[] asArray = children.toArray(new CoordinateOperation[children.size()]);
-                initialize(properties, asArray, flattened, (step == null) ? mtFactory : null, setAccuracy);
+                initialize(properties, asArray, flattened, (step == null) ? mtFactory : null, setAccuracy, setDomain);
             } else {
                 flattened.add(op);
             }
@@ -215,17 +219,31 @@ final class DefaultConcatenatedOperation
              * Instead the user will get a better result by invoking PositionalAccuracyConstant.getLinearAccuracy(\u2026)
              * since that method conservatively computes the sum of all linear accuracy.
              */
-            if (setAccuracy && (op instanceof Transformation || op instanceof ConcatenatedOperation)) {
+            if (setAccuracy && (op instanceof Transformation || op instanceof ConcatenatedOperation)
+                    && (PositionalAccuracyConstant.getLinearAccuracy(op) != 0))
+            {
                 if (coordinateOperationAccuracy == null) {
-                    setAccuracy = (PositionalAccuracyConstant.getLinearAccuracy(op) > 0);
-                    if (setAccuracy) {
-                        coordinateOperationAccuracy = op.getCoordinateOperationAccuracy();
-                    }
+                    coordinateOperationAccuracy = op.getCoordinateOperationAccuracy();
                 } else {
                     coordinateOperationAccuracy = null;
                     setAccuracy = false;
                 }
             }
+            /*
+             * Optionally copy the domain of validity, provided that it is the same for all component.
+             * Current implementation does not try to compute the intersection of all components.
+             */
+            if (setDomain) {
+                final Extent domain = op.getDomainOfValidity();
+                if (domain != null) {
+                    if (domainOfValidity == null) {
+                        domainOfValidity = domain;
+                    } else if (!domain.equals(domainOfValidity)) {
+                        domainOfValidity = null;
+                        setDomain = false;
+                    }
+                }
+            }
         }
     }
 
@@ -379,7 +397,8 @@ final class DefaultConcatenatedOperation
      */
     private void setSteps(final CoordinateOperation[] steps) throws FactoryException {
         final List<CoordinateOperation> flattened = new ArrayList<CoordinateOperation>(steps.length);
-        initialize(null, steps, flattened, DefaultFactories.forBuildin(MathTransformFactory.class), coordinateOperationAccuracy == null);
+        initialize(null, steps, flattened, DefaultFactories.forBuildin(MathTransformFactory.class),
+                (coordinateOperationAccuracy == null), (domainOfValidity == null));
         operations = UnmodifiableArrayList.wrap(flattened.toArray(new CoordinateOperation[flattened.size()]));
     }
 }

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java?rev=1743955&r1=1743954&r2=1743955&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java [UTF-8] Sun May 15 19:16:55 2016
@@ -18,9 +18,11 @@ package org.apache.sis.referencing.opera
 
 import java.util.Map;
 import java.util.HashMap;
+import java.util.Collection;
 import javax.xml.bind.annotation.XmlTransient;
 import javax.measure.unit.Unit;
 import org.opengis.metadata.Identifier;
+import org.opengis.metadata.quality.PositionalAccuracy;
 import org.opengis.util.InternationalString;
 import org.opengis.parameter.ParameterValue;
 import org.opengis.parameter.ParameterValueGroup;
@@ -33,6 +35,7 @@ import org.apache.sis.internal.metadata.
 import org.apache.sis.internal.referencing.SignReversalComment;
 import org.apache.sis.internal.referencing.provider.AbstractProvider;
 import org.apache.sis.metadata.iso.ImmutableIdentifier;
+import org.apache.sis.util.collection.Containers;
 import org.apache.sis.util.Deprecable;
 
 
@@ -104,6 +107,26 @@ final class InverseOperationMethod exten
     }
 
     /**
+     * Copies accuracy and domain of validity metadata from the given operation into the given properties map.
+     * We presume that the inverse operation has the same accuracy than the direct operation.
+     *
+     * <div class="note"><b>Note:</b>
+     * in many cases, the inverse operation is numerically less accurate than the direct operation because it
+     * uses approximations like series expansions or iterative methods. However the numerical errors caused by
+     * those approximations are not of interest here, because they are usually much smaller than the inaccuracy
+     * due to the stochastic nature of coordinate transformations (not to be confused with coordinate conversions;
+     * see ISO 19111 for more information).</div>
+     */
+    static void putMetadata(final SingleOperation source, final Map<String,Object> target) {
+        target.put(SingleOperation.DOMAIN_OF_VALIDITY_KEY, source.getDomainOfValidity());
+        final Collection<PositionalAccuracy> accuracy = source.getCoordinateOperationAccuracy();
+        if (!Containers.isNullOrEmpty(accuracy)) {
+            target.put(SingleOperation.COORDINATE_OPERATION_ACCURACY_KEY,
+                    accuracy.toArray(new PositionalAccuracy[accuracy.size()]));
+        }
+    }
+
+    /**
      * If the inverse of the given operation can be represented by inverting the sign of all numerical
      * parameter values, copies those parameters in a {@code "parameters"} entry in the given map.
      * Otherwise does nothing.

Modified: sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/WKTParserTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/WKTParserTest.java?rev=1743955&r1=1743954&r2=1743955&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/WKTParserTest.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/WKTParserTest.java [UTF-8] Sun May 15 19:16:55 2016
@@ -23,11 +23,11 @@ import org.opengis.referencing.datum.Ver
 import org.opengis.util.FactoryException;
 import org.opengis.test.wkt.CRSParserTest;
 import org.apache.sis.internal.metadata.AxisNames;
+import org.apache.sis.test.TestRunner;
 import org.apache.sis.test.DependsOn;
 import org.junit.Test;
 import org.junit.Ignore;
 import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
 
 import static org.junit.Assert.*;
 
@@ -41,7 +41,7 @@ import static org.junit.Assert.*;
  * @version 0.7
  * @module
  */
-@RunWith(JUnit4.class)
+@RunWith(TestRunner.class)
 @DependsOn(GeodeticObjectParserTest.class)
 public final strictfp class WKTParserTest extends CRSParserTest {
     /**

Modified: sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GeodeticObjectFactoryTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GeodeticObjectFactoryTest.java?rev=1743955&r1=1743954&r2=1743955&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GeodeticObjectFactoryTest.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GeodeticObjectFactoryTest.java [UTF-8] Sun May 15 19:16:55 2016
@@ -49,9 +49,9 @@ import org.apache.sis.io.wkt.Convention;
 
 // Test dependencies
 import org.opengis.test.referencing.ObjectFactoryTest;
+import org.apache.sis.test.TestRunner;
 import org.apache.sis.test.DependsOn;
 import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
 import org.junit.Ignore;
 import org.junit.Test;
 
@@ -67,7 +67,7 @@ import static org.apache.sis.test.Metada
  * @version 0.7
  * @module
  */
-@RunWith(JUnit4.class)
+@RunWith(TestRunner.class)
 @DependsOn({
     org.apache.sis.referencing.crs.DefaultGeocentricCRSTest.class,
     org.apache.sis.referencing.crs.DefaultGeographicCRSTest.class,

Modified: sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/LinearTransformTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/LinearTransformTest.java?rev=1743955&r1=1743954&r2=1743955&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/LinearTransformTest.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/LinearTransformTest.java [UTF-8] Sun May 15 19:16:55 2016
@@ -24,8 +24,8 @@ import org.opengis.referencing.operation
 // Test imports
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
 import org.apache.sis.test.DependsOn;
+import org.apache.sis.test.TestRunner;
 import static org.opengis.test.Assert.*;
 
 
@@ -38,7 +38,7 @@ import static org.opengis.test.Assert.*;
  * @version 0.5
  * @module
  */
-@RunWith(JUnit4.class)
+@RunWith(TestRunner.class)
 @DependsOn(ProjectiveTransformTest.class)
 public final strictfp class LinearTransformTest extends ProjectiveTransformTest {
     /**

Modified: sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/ProjectiveTransformTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/ProjectiveTransformTest.java?rev=1743955&r1=1743954&r2=1743955&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/ProjectiveTransformTest.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/ProjectiveTransformTest.java [UTF-8] Sun May 15 19:16:55 2016
@@ -29,9 +29,9 @@ import org.apache.sis.parameter.Paramete
 
 // Test imports
 import org.opengis.test.Validators;
+import org.apache.sis.test.TestRunner;
 import org.apache.sis.test.DependsOn;
 import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
 import org.junit.After;
 import org.junit.Test;
 import static org.opengis.test.Assert.*;
@@ -51,7 +51,7 @@ import org.opengis.test.referencing.Affi
  * @version 0.7
  * @module
  */
-@RunWith(JUnit4.class)
+@RunWith(TestRunner.class)
 @DependsOn({AbstractMathTransformTest.class, ScaleTransformTest.class})
 public strictfp class ProjectiveTransformTest extends AffineTransformTest {
     /**

Modified: sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1743955&r1=1743954&r2=1743955&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java [UTF-8] Sun May 15 19:16:55 2016
@@ -151,7 +151,7 @@ public final class Errors extends Indexe
         public static final short CanNotRead_1 = 9;
 
         /**
-         * Can not represent \u201c{1}\u201d in the {0} format.
+         * Can not represent \u201c{1}\u201d in a strictly standard-compliant {0} format.
          */
         public static final short CanNotRepresentInFormat_2 = 10;
 

Modified: sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1743955&r1=1743954&r2=1743955&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties [ISO-8859-1] (original)
+++ sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties [ISO-8859-1] Sun May 15 19:16:55 2016
@@ -41,7 +41,7 @@ CanNotMapAxisToDirection_2        = Can
 CanNotOpen_1                      = Can not open \u201c{0}\u201d.
 CanNotParseFile_2                 = Can not parse \u201c{1}\u201d as a file in the {0} format.
 CanNotRead_1                      = Can not read \u201c{0}\u201d.
-CanNotRepresentInFormat_2         = Can not represent \u201c{1}\u201d in the {0} format.
+CanNotRepresentInFormat_2         = Can not represent \u201c{1}\u201d in a strictly standard-compliant {0} format.
 CanNotSeparateTargetDimension_1   = Target dimension {0} depends on excluded source dimensions.
 CanNotSetParameterValue_1         = Can not set a value for parameter \u201c{0}\u201d.
 CanNotSetPropertyValue_1          = Can not set a value for property \u201c{0}\u201d.

Modified: sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1743955&r1=1743954&r2=1743955&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties [ISO-8859-1] (original)
+++ sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties [ISO-8859-1] Sun May 15 19:16:55 2016
@@ -38,7 +38,7 @@ CanNotMapAxisToDirection_2        = Aucu
 CanNotOpen_1                      = Ne peut pas ouvrir \u00ab\u202f{0}\u202f\u00bb.
 CanNotParseFile_2                 = Ne peut pas lire \u00ab\u202f{1}\u202f\u00bb comme un fichier au format {0}.
 CanNotRead_1                      = Ne peut pas lire \u00ab\u202f{0}\u202f\u00bb.
-CanNotRepresentInFormat_2         = Ne peut pas repr\u00e9senter \u00ab\u202f{1}\u202f\u00bb dans le format {0}.
+CanNotRepresentInFormat_2         = Ne peut pas repr\u00e9senter \u00ab\u202f{1}\u202f\u00bb dans un format {0} strictement conforme.
 CanNotSeparateTargetDimension_1   = La dimension de destination {0} d\u00e9pend de dimensions sources qui ont \u00e9t\u00e9 exclues.
 CanNotSetParameterValue_1         = Ne peut pas d\u00e9finir une valeur pour le param\u00e8tre \u00ab\u202f{0}\u202f\u00bb.
 CanNotSetPropertyValue_1          = Ne peut pas d\u00e9finir une valeur pour la propri\u00e9t\u00e9 \u00ab\u202f{0}\u202f\u00bb.

Modified: sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java?rev=1743955&r1=1743954&r2=1743955&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java [UTF-8] Sun May 15 19:16:55 2016
@@ -249,7 +249,7 @@ public final class Messages extends Inde
         public static final short MisnamedParameter_1 = 18;
 
         /**
-         * Formatting of \u201c{0}\u201d is not conform to the format standard.
+         * This \u201c{0}\u201d formatting is a departure from standard format.
          */
         public static final short NonConformFormatting_1 = 15;
 

Modified: sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties?rev=1743955&r1=1743954&r2=1743955&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties [ISO-8859-1] (original)
+++ sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties [ISO-8859-1] Sun May 15 19:16:55 2016
@@ -54,7 +54,7 @@ PropertyHiddenBy_2               = Prope
 MismatchedOperationFactories_2   = No coordinate operation from \u201c{0}\u201d to \u201c{1}\u201d because of mismatched factories.
 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.
 MisnamedParameter_1              = Despite its name, this parameter is effectively \u201c{0}\u201d.
-NonConformFormatting_1           = Formatting of \u201c{0}\u201d is not conform to the format standard.
+NonConformFormatting_1           = This \u201c{0}\u201d formatting is a departure from standard format.
 NotFormalProjectionParameter_1   = This parameter borrowed from the \u201c{0}\u201d projection is not formally a parameter of this projection.
 UnknownElementsInText            = The text contains unknown elements:
 UnknownKeywordInRecord_2         = Loading of \u201c{0}\u201d done, but some records were ignored because of unrecognized keywords: {1}

Modified: sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties?rev=1743955&r1=1743954&r2=1743955&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties [ISO-8859-1] (original)
+++ sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties [ISO-8859-1] Sun May 15 19:16:55 2016
@@ -61,7 +61,7 @@ PropertyHiddenBy_2               = La pr
 MismatchedOperationFactories_2   = Il n\u2019y a pas d\u2019op\u00e9rations allant de \u00ab\u202f{0}\u202f\u00bb vers \u00ab\u202f{1}\u202f\u00bb parce que ces derniers sont associ\u00e9s \u00e0 deux fabriques diff\u00e9rentes.
 MismatchedEllipsoidAxisLength_3  = Le param\u00e8tre \u00ab\u202f{1}\u202f\u00bb aurait pu \u00eatre omis. Mais il lui a \u00e9t\u00e9 donn\u00e9 la valeur {2} qui ne correspond pas \u00e0 la d\u00e9finition de l\u2019ellipso\u00efde \u00ab\u202f{0}\u202f\u00bb.
 MisnamedParameter_1              = Malgr\u00e9 son nom, ce param\u00e8tre produit en r\u00e9alit\u00e9 l\u2019effet d\u2019un \u00ab\u202f{0}\u202f\u00bb.
-NonConformFormatting_1           = L\u2019\u00e9criture de \u00ab\u202f{0}\u202f\u00bb n\u2019est pas conforme au format standard.
+NonConformFormatting_1           = Cette \u00e9criture de \u00ab\u202f{0}\u202f\u00bb d\u00e9vie du format standard.
 NotFormalProjectionParameter_1   = Ce param\u00e8tre emprunt\u00e9 \u00e0 la projection \u00ab\u202f{0}\u202f\u00bb n\u2019est pas formellement un param\u00e8tre de cette projection.
 UnknownElementsInText            = Le texte contient des \u00e9l\u00e9ments inconnus\u00a0:
 UnknownKeywordInRecord_2         = La lecture de \u00ab\u202f{0}\u202f\u00bb a \u00e9t\u00e9 faite, mais en ignorant certains enregistrements \u00e0 cause de mots-cl\u00e9s non reconnus: {1}

Modified: sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java?rev=1743955&r1=1743954&r2=1743955&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java [UTF-8] Sun May 15 19:16:55 2016
@@ -232,6 +232,11 @@ public final class Vocabulary extends In
         public static final short DittoMark = 82;
 
         /**
+         * Domain
+         */
+        public static final short Domain = 113;
+
+        /**
          * Dublin Julian
          */
         public static final short DublinJulian = 17;
@@ -412,6 +417,11 @@ public final class Vocabulary extends In
         public static final short None = 91;
 
         /**
+         * Note
+         */
+        public static final short Note = 114;
+
+        /**
          * Number of \u2018NaN\u2019
          */
         public static final short NumberOfNaN = 38;
@@ -442,6 +452,11 @@ public final class Vocabulary extends In
         public static final short OperatingSystem = 42;
 
         /**
+         * Operations
+         */
+        public static final short Operations = 112;
+
+        /**
          * Optional
          */
         public static final short Optional = 79;

Modified: sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties?rev=1743955&r1=1743954&r2=1743955&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties [ISO-8859-1] (original)
+++ sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties [ISO-8859-1] Sun May 15 19:16:55 2016
@@ -49,6 +49,7 @@ Details                 = Details
 Dimensions              = Dimensions
 Directory               = Directory
 DittoMark               = \u2033
+Domain                  = Domain
 DublinJulian            = Dublin Julian
 Ellipsoid               = Ellipsoid
 EllipsoidChange         = Ellipsoid change
@@ -85,12 +86,14 @@ Methods                 = Methods
 ModifiedJulian          = Modified Julian
 Name                    = Name
 None                    = None
+Note                    = Note
 NumberOfValues          = Number of values
 NumberOfNaN             = Number of \u2018NaN\u2019
 Obligation              = Obligation
 Of_3                    = {0} ({1} of {2})
 Offset                  = Offset
 OperatingSystem         = Operating system
+Operations              = Operations
 Optional                = Optional
 Options                 = Options
 Others                  = Others

Modified: sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties?rev=1743955&r1=1743954&r2=1743955&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties [ISO-8859-1] (original)
+++ sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties [ISO-8859-1] Sun May 15 19:16:55 2016
@@ -56,6 +56,7 @@ Details                 = D\u00e9tails
 Dimensions              = Dimensions
 Directory               = R\u00e9pertoire
 DittoMark               = \u2033
+Domain                  = Domaine
 DublinJulian            = Julien Dublin
 Ellipsoid               = Ellipso\u00efde
 EllipsoidChange         = Changement d\u2019ellipso\u00efde
@@ -92,12 +93,14 @@ Methods                 = M\u00e9thodes
 ModifiedJulian          = Julien modifi\u00e9
 Name                    = Nom
 None                    = Aucun
+Note                    = Note
 NumberOfValues          = Nombre de valeurs
 NumberOfNaN             = Nombre de \u2018NaN\u2019
 Obligation              = Obligation
 Of_3                    = {0} ({1} de {2})
 Offset                  = D\u00e9calage
 OperatingSystem         = Syst\u00e8me d\u2019exploitation
+Operations              = Op\u00e9rations
 Optional                = Optionnel
 Options                 = Options
 Others                  = Autres

Modified: sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/test/TestRunner.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/test/TestRunner.java?rev=1743955&r1=1743954&r2=1743955&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/test/TestRunner.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/test/TestRunner.java [UTF-8] Sun May 15 19:16:55 2016
@@ -268,21 +268,29 @@ public final class TestRunner extends Bl
      * @param methods The methods to sort.
      */
     private static void sortDependantTestsLast(final FrameworkMethod[] methods) {
-        Set<String> dependencies = null;
+        final Set<String> dependencies = new HashSet<String>();
+        int retryCount = methods.length;
         for (int i=methods.length-1; --i>=0;) {
             final FrameworkMethod method = methods[i];
             final DependsOnMethod depend = method.getAnnotation(DependsOnMethod.class);
             if (depend != null) {
-                if (dependencies == null) {
-                    dependencies = new HashSet<String>();
-                }
                 dependencies.addAll(Arrays.asList(depend.value()));
                 for (int j=methods.length; --j>i;) {
                     if (dependencies.contains(methods[j].getName())) {
-                        // Found a method j which is a dependency of i. Move i after j.
-                        // The order of other methods relative to j is left unchanged.
+                        /*
+                         * Found a method j which is a dependency of i. Move i after j.
+                         * The order of other methods relative to j is left unchanged.
+                         *
+                         * As a result of this move, maybe some methods between i and j
+                         * need to be revisited. We should restart the iteration from j.
+                         * Over unlimited retries could cause an infinite loop, so we
+                         * arbitrarily limit the amount of time we retry.
+                         */
                         System.arraycopy(methods, i+1, methods, i, j-i);
                         methods[j] = method;
+                        if (--retryCount >= 0) {
+                            i = j;                      // Revisit the methods between i and j.
+                        }
                         break;
                     }
                 }

Modified: sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultNameFactoryTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultNameFactoryTest.java?rev=1743955&r1=1743954&r2=1743955&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultNameFactoryTest.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultNameFactoryTest.java [UTF-8] Sun May 15 19:16:55 2016
@@ -17,11 +17,11 @@
 package org.apache.sis.util.iso;
 
 import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
 import org.junit.BeforeClass;
 import org.junit.AfterClass;
 import org.opengis.test.util.NameTest;
 import org.apache.sis.test.DependsOn;
+import org.apache.sis.test.TestRunner;
 
 
 /**
@@ -33,7 +33,7 @@ import org.apache.sis.test.DependsOn;
  * @version 0.3
  * @module
  */
-@RunWith(JUnit4.class)
+@RunWith(TestRunner.class)
 @DependsOn(AbstractNameTest.class)
 public final strictfp class DefaultNameFactoryTest extends NameTest {
     /**