Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS3005.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS3005.java?rev=1723780&r1=1723779&r2=1723780&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS3005.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GIGS3005.java [UTF-8] Fri Jan 8 18:29:04 2016 @@ -19,8 +19,10 @@ package org.apache.sis.referencing.facto import org.opengis.referencing.operation.CoordinateOperationFactory; import org.apache.sis.internal.system.DefaultFactories; import org.apache.sis.test.DependsOn; +import org.junit.FixMethodOrder; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import org.junit.runners.MethodSorters; /** @@ -32,10 +34,11 @@ import org.junit.runners.JUnit4; * @version 0.6 * @module */ -@RunWith(JUnit4.class) @DependsOn({ org.apache.sis.referencing.operation.DefaultConversionTest.class }) +@RunWith(JUnit4.class) +@FixMethodOrder(MethodSorters.JVM) // Intentionally want some randomness public final strictfp class GIGS3005 extends org.opengis.test.referencing.gigs.GIGS3005 { /** * Creates a new test suite using the singleton factory instance.
Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GeodeticObjectFactoryTest.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GeodeticObjectFactoryTest.java?rev=1723780&r1=1723779&r2=1723780&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GeodeticObjectFactoryTest.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/GeodeticObjectFactoryTest.java [UTF-8] Fri Jan 8 18:29:04 2016 @@ -16,21 +16,46 @@ */ package org.apache.sis.referencing.factory; +import java.util.Map; +import java.util.Collections; +import javax.measure.unit.SI; +import javax.measure.unit.Unit; +import javax.measure.unit.NonSI; +import javax.measure.quantity.Angle; +import javax.measure.quantity.Length; +import org.opengis.util.FactoryException; +import org.opengis.referencing.IdentifiedObject; import org.opengis.referencing.cs.CSFactory; +import org.opengis.referencing.cs.CartesianCS; +import org.opengis.referencing.cs.EllipsoidalCS; +import org.opengis.referencing.cs.AxisDirection; +import org.opengis.referencing.cs.CoordinateSystemAxis; import org.opengis.referencing.crs.CRSFactory; import org.opengis.referencing.crs.GeodeticCRS; +import org.opengis.referencing.crs.GeographicCRS; +import org.opengis.referencing.crs.ProjectedCRS; import org.opengis.referencing.datum.DatumFactory; +import org.opengis.referencing.datum.Ellipsoid; +import org.opengis.referencing.datum.PrimeMeridian; +import org.opengis.referencing.datum.GeodeticDatum; import org.opengis.referencing.operation.CoordinateOperationFactory; -import org.opengis.test.referencing.ObjectFactoryTest; -import org.opengis.util.FactoryException; +import org.opengis.referencing.operation.OperationMethod; +import org.opengis.referencing.operation.Conversion; +import org.opengis.parameter.ParameterValueGroup; import org.apache.sis.internal.system.DefaultFactories; +import org.apache.sis.referencing.operation.DefaultConversion; +import org.apache.sis.referencing.CommonCRS; +import org.apache.sis.io.wkt.Convention; + +// Test dependencies +import org.opengis.test.referencing.ObjectFactoryTest; import org.apache.sis.test.DependsOn; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.junit.Ignore; import org.junit.Test; -import static org.junit.Assert.*; +import static org.apache.sis.test.MetadataAssert.*; /** @@ -39,7 +64,7 @@ import static org.junit.Assert.*; * * @author Cédric Briançon (Geomatys) * @since 0.6 - * @version 0.6 + * @version 0.7 * @module */ @RunWith(JUnit4.class) @@ -84,4 +109,116 @@ public final strictfp class GeodeticObje assertEquals("name", "WGS 84", crs.getName().getCode()); assertEquals("datum", "World Geodetic System 1984", crs.getDatum().getName().getCode()); } + + /** + * Convenience method creating a map with only the "{@code name"} property. + * This is the only mandatory property for object creation. + */ + private static Map<String,?> name(final String name) { + return Collections.singletonMap(IdentifiedObject.NAME_KEY, name); + } + + /** + * Tests step-by-step the creation of a new projected coordinate reference systems. + * This test creates every objects itself and compares with expected WKT 1 after each step. + * + * <p>Note that practical applications may use existing constants declared in the + * {@link CommonCRS} class instead than creating everything like this test does.</p> + * + * @throws FactoryException if the creation of a geodetic component failed. + * + * @since 0.7 + */ + @Test + public void testStepByStepCreation() throws FactoryException { + /* + * List of all objects to be created in this test. + */ + final Unit<Length> linearUnit; + final Unit<Angle> angularUnit; + final Ellipsoid ellipsoid; + final PrimeMeridian meridian; + final GeodeticDatum datum; + final CoordinateSystemAxis longitude, latitude, easting, northing; + final EllipsoidalCS geographicCS; + final GeographicCRS geographicCRS; + final OperationMethod method; + final ParameterValueGroup parameters; + final Conversion projection; + final CartesianCS projectedCS; + final ProjectedCRS projectedCRS; + /* + * Prime meridian + */ + angularUnit = NonSI.DEGREE_ANGLE; + meridian = datumFactory.createPrimeMeridian(name("Greenwich"), 0, angularUnit); + assertWktEquals(Convention.WKT1, + "PRIMEM[“Greenwich”, 0.0]", meridian); + /* + * Ellipsoid + */ + linearUnit = SI.METRE; + ellipsoid = datumFactory.createEllipsoid(name("Airy1830"), 6377563.396, 6356256.910, linearUnit); + assertWktEquals(Convention.WKT1, + "SPHEROID[“Airy1830”, 6377563.396, 299.3249753150345]", ellipsoid); + /* + * Geodetic datum + */ + datum = datumFactory.createGeodeticDatum(name("Airy1830"), ellipsoid, meridian); + assertWktEquals(Convention.WKT1, + "DATUM[“Airy1830”,\n" + + " SPHEROID[“Airy1830”, 6377563.396, 299.3249753150345]]", datum); + /* + * Base coordinate reference system + */ + longitude = csFactory.createCoordinateSystemAxis(name("Longitude"), "long", AxisDirection.EAST, angularUnit); + latitude = csFactory.createCoordinateSystemAxis(name("Latitude"), "lat", AxisDirection.NORTH, angularUnit); + geographicCS = csFactory.createEllipsoidalCS(name("Ellipsoidal"), longitude, latitude); + geographicCRS = crsFactory.createGeographicCRS(name("Airy1830"), datum, geographicCS); + assertWktEquals(Convention.WKT1, + "GEOGCS[“Airy1830”,\n" + + " DATUM[“Airy1830”,\n" + + " SPHEROID[“Airy1830”, 6377563.396, 299.3249753150345]],\n" + + " PRIMEM[“Greenwich”, 0.0],\n" + + " UNIT[“degree”, 0.017453292519943295],\n" + + " AXIS[“Longitude”, EAST],\n" + + " AXIS[“Latitude”, NORTH]]", geographicCRS); + /* + * Defining conversion + */ + method = copFactory.getOperationMethod("Transverse_Mercator"); + parameters = method.getParameters().createValue(); + parameters.parameter("semi_major") .setValue(ellipsoid.getSemiMajorAxis()); + parameters.parameter("semi_minor") .setValue(ellipsoid.getSemiMinorAxis()); + parameters.parameter("central_meridian") .setValue( 49); + parameters.parameter("latitude_of_origin").setValue( -2); + parameters.parameter("false_easting") .setValue( 400000); + parameters.parameter("false_northing") .setValue(-100000); + projection = new DefaultConversion(name("GBN grid"), method, null, parameters); + /* + * Projected coordinate reference system + */ + easting = csFactory.createCoordinateSystemAxis(name("Easting"), "x", AxisDirection.EAST, linearUnit); + northing = csFactory.createCoordinateSystemAxis(name("Northing"), "y", AxisDirection.NORTH, linearUnit); + projectedCS = csFactory.createCartesianCS(name("Cartesian"), easting, northing); + projectedCRS = crsFactory.createProjectedCRS(name("Great_Britian_National_Grid"), geographicCRS, projection, projectedCS); + assertWktEquals(Convention.WKT1, + "PROJCS[“Great_Britian_National_Grid”,\n" + + " GEOGCS[“Airy1830”,\n" + + " DATUM[“Airy1830”,\n" + + " SPHEROID[“Airy1830”, 6377563.396, 299.3249753150345]],\n" + + " PRIMEM[“Greenwich”, 0.0],\n" + + " UNIT[“degree”, 0.017453292519943295],\n" + + " AXIS[“Longitude”, EAST],\n" + + " AXIS[“Latitude”, NORTH]],\n" + + " PROJECTION[“Transverse_Mercator”, AUTHORITY[“EPSG”, “9807”]],\n" + + " PARAMETER[“latitude_of_origin”, -2.0],\n" + + " PARAMETER[“central_meridian”, 49.0],\n" + + " PARAMETER[“scale_factor”, 1.0],\n" + + " PARAMETER[“false_easting”, 400000.0],\n" + + " PARAMETER[“false_northing”, -100000.0],\n" + + " UNIT[“metre”, 1],\n" + + " AXIS[“Easting”, EAST],\n" + + " AXIS[“Northing”, NORTH]]", projectedCRS); + } } Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatrixTestCase.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatrixTestCase.java?rev=1723780&r1=1723779&r2=1723780&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatrixTestCase.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatrixTestCase.java [UTF-8] Fri Jan 8 18:29:04 2016 @@ -100,7 +100,7 @@ public abstract strictfp class MatrixTes * * @see NonSquareMatrixTest#printStatistics() */ - static final Statistics statistics = verbose ? new Statistics("|SIS - JAMA|") : null; + static final Statistics statistics = VERBOSE ? new Statistics("|SIS - JAMA|") : null; /** * Random number generator, created by {@link #initialize(long)} as the first operation of Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java?rev=1723780&r1=1723779&r2=1723780&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java [UTF-8] Fri Jan 8 18:29:04 2016 @@ -106,7 +106,7 @@ public final strictfp class LambertConic assertWktEqualsRegex("(?m)\\Q" + "PARAM_MT[“Lambert conic conformal”,\n" + " PARAMETER[“eccentricity”, 0.0818191908426215],\n" + - " PARAMETER[“n”, 0.64278760968653\\E\\d*\\]\\]"); // 0.6427876096865393 in the original test. + " PARAMETER[“n”, 0.64278760968653\\E\\d*\\Q]]\\E"); // 0.6427876096865393 in the original test. } /** Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactoryTest.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactoryTest.java?rev=1723780&r1=1723779&r2=1723780&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactoryTest.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactoryTest.java [UTF-8] Fri Jan 8 18:29:04 2016 @@ -16,7 +16,10 @@ */ package org.apache.sis.referencing.operation.transform; +import java.util.Map; import java.util.Set; +import java.util.Collection; +import java.util.Collections; import org.opengis.util.FactoryException; import org.opengis.util.NoSuchIdentifierException; import org.opengis.referencing.operation.Conversion; @@ -25,11 +28,21 @@ import org.opengis.referencing.operation import org.opengis.referencing.operation.OperationMethod; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.MathTransformFactory; +import org.opengis.parameter.ParameterValueGroup; +import org.apache.sis.parameter.Parameterized; +import org.apache.sis.referencing.CommonCRS; +import org.apache.sis.referencing.operation.DefaultConversion; import org.apache.sis.referencing.operation.matrix.Matrix2; +import org.apache.sis.referencing.crs.DefaultProjectedCRS; +import org.apache.sis.referencing.factory.InvalidGeodeticParameterException; import org.apache.sis.internal.referencing.provider.Affine; import org.apache.sis.internal.referencing.provider.Mercator1SP; import org.apache.sis.internal.system.DefaultFactories; import org.apache.sis.internal.util.Constants; +import org.apache.sis.util.CharSequences; + +// Test dependencies +import org.apache.sis.referencing.cs.HardCodedCS; import org.apache.sis.test.DependsOnMethod; import org.apache.sis.test.DependsOn; import org.apache.sis.test.TestCase; @@ -45,7 +58,7 @@ import static org.opengis.test.Assert.*; * * @author Martin Desruisseaux (Geomatys) * @since 0.6 - * @version 0.6 + * @version 0.7 * @module */ @DependsOn({ @@ -162,4 +175,68 @@ public final strictfp class DefaultMathT 1, 7, 0, 1), MathTransforms.getMatrix(tr), STRICT); } + + /** + * Tests the creation of all registered map projections. + * Only the semi-axis lengths are specified. For the rest, we rely on default values. + * + * @throws FactoryException if the construction of a map projection failed. + * + * @since 0.7 + */ + @Test + public void testAllMapProjections() throws FactoryException { + /* + * Gets all map projections and creates a projection using the WGS84 ellipsoid + * and default parameter values. + */ + final Map<String,?> dummyName = Collections.singletonMap(DefaultProjectedCRS.NAME_KEY, "Test"); + final MathTransformFactory mtFactory = DefaultFactories.forBuildin(MathTransformFactory.class); + final Collection<OperationMethod> methods = mtFactory.getAvailableMethods(Projection.class); + for (final OperationMethod method : methods) { + final String classification = method.getName().getCode(); + ParameterValueGroup param = mtFactory.getDefaultParameters(classification); + param.parameter("semi_major").setValue(6377563.396); + param.parameter("semi_minor").setValue(6356256.909237285); + final MathTransform mt; + try { + mt = mtFactory.createParameterizedTransform(param); + } catch (InvalidGeodeticParameterException e) { + // Some map projections have mandatory parameters which we ignore for now + // except for a few well-known projection that we know should not fail. + if (classification.contains("Mercator")) { + throw e; + } + out.print(classification); + out.print(CharSequences.spaces(42 - classification.length())); + out.print(": "); + out.println(e.getLocalizedMessage()); + continue; + } + /* + * Verifies that the map projection properties are the ones that we specified. + * Note that the Equirectangular projection has been optimized as an affine transform, which we skip. + */ + if (mt instanceof LinearTransform) { + continue; + } + assertInstanceOf(classification, Parameterized.class, mt); + param = ((Parameterized) mt).getParameterValues(); + assertEquals(classification, param.getDescriptor().getName().getCode()); + assertEquals(classification, 6377563.396, param.parameter("semi_major").doubleValue(), 1E-4); + assertEquals(classification, 6356256.909237285, param.parameter("semi_minor").doubleValue(), 1E-4); + /* + * Creates a ProjectedCRS from the map projection. This part is more an integration test than + * a DefaultMathTransformFactory test. Again, the intend is to verify that the properties are + * the one that we specified. + */ + final DefaultProjectedCRS crs = new DefaultProjectedCRS(dummyName, + CommonCRS.WGS84.normalizedGeographic(), + new DefaultConversion(dummyName, method, mt, null), + HardCodedCS.PROJECTED); + final Conversion projection = crs.getConversionFromBase(); + assertSame(classification, mt, projection.getMathTransform()); + assertEquals(classification, projection.getMethod().getName().getCode()); + } + } } Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransformTest.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransformTest.java?rev=1723780&r1=1723779&r2=1723780&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransformTest.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransformTest.java [UTF-8] Fri Jan 8 18:29:04 2016 @@ -60,11 +60,7 @@ public final strictfp class EllipsoidToC */ private void createGeodeticConversion(final Ellipsoid ellipsoid, boolean is3D) throws FactoryException { transform = EllipsoidToCentricTransform.createGeodeticConversion( - DefaultFactories.forBuildin(MathTransformFactory.class), - ellipsoid.getSemiMajorAxis(), - ellipsoid.getSemiMinorAxis(), - ellipsoid.getAxisUnit(), is3D, - EllipsoidToCentricTransform.TargetType.CARTESIAN); + DefaultFactories.forBuildin(MathTransformFactory.class), ellipsoid, is3D); } /** Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MathTransformTestCase.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MathTransformTestCase.java?rev=1723780&r1=1723779&r2=1723780&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MathTransformTestCase.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MathTransformTestCase.java [UTF-8] Fri Jan 8 18:29:04 2016 @@ -396,6 +396,19 @@ public abstract strictfp class MathTrans } /** + * Asserts that the current {@linkplain #transform transform} produces an internal WKT + * matching the given regular expression. + * + * @param expected A regular expression for the expected internal WKT. + * + * @since 0.7 + */ + protected final void assertInternalWktEqualsRegex(final String expected) { + assertNotNull("The 'transform' field shall be assigned a value.", transform); + ReferencingAssert.assertWktEqualsRegex(Convention.INTERNAL, expected, transform); + } + + /** * Prints the current {@linkplain #transform transform} as normal and internal WKT. * This method is for debugging purpose only. * Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MolodenskyTransformTest.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MolodenskyTransformTest.java?rev=1723780&r1=1723779&r2=1723780&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MolodenskyTransformTest.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/MolodenskyTransformTest.java [UTF-8] Fri Jan 8 18:29:04 2016 @@ -16,26 +16,36 @@ */ package org.apache.sis.referencing.operation.transform; +import java.util.Arrays; +import java.io.IOException; import org.opengis.util.FactoryException; import org.opengis.referencing.datum.Ellipsoid; +import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.MathTransformFactory; import org.opengis.referencing.operation.TransformException; import org.opengis.parameter.ParameterValueGroup; +import org.apache.sis.internal.referencing.provider.FranceGeocentricInterpolation; import org.apache.sis.internal.referencing.provider.AbridgedMolodensky; import org.apache.sis.internal.referencing.provider.Molodensky; import org.apache.sis.internal.system.DefaultFactories; import org.apache.sis.internal.referencing.Formulas; import org.apache.sis.referencing.CommonCRS; -import org.apache.sis.measure.Longitude; +import org.apache.sis.math.StatisticsFormat; +import org.apache.sis.math.Statistics; -import static java.lang.StrictMath.toRadians; +import static java.lang.StrictMath.*; +import static org.apache.sis.internal.metadata.ReferencingServices.NAUTICAL_MILE; // Test dependencies +import org.apache.sis.internal.referencing.provider.FranceGeocentricInterpolationTest; import org.apache.sis.internal.referencing.provider.GeocentricTranslationTest; +import org.apache.sis.referencing.datum.HardCodedDatum; import org.apache.sis.test.mock.MathTransformFactoryMock; import org.apache.sis.test.DependsOnMethod; import org.apache.sis.test.DependsOn; import org.apache.sis.test.TestUtilities; +import org.apache.sis.test.TestCase; +import org.opengis.test.CalculationType; import org.opengis.test.ToleranceModifier; import org.opengis.test.ToleranceModifiers; import org.opengis.test.referencing.ParameterizedTransformTest; @@ -45,7 +55,10 @@ import static org.apache.sis.test.Assert /** - * Tests {@link MolodenskyTransform}. + * Tests {@link MolodenskyTransform}. The {@link #compareWithGeocentricTranslation()} + * method uses {@link EllipsoidToCentricTransform} as a reference implementation. + * The errors compared to geocentric translations should not be greater than + * approximatively 1 centimetre. * * @author Tara Athan * @author Martin Desruisseaux (Geomatys) @@ -56,10 +69,82 @@ import static org.apache.sis.test.Assert */ @DependsOn({ CoordinateDomainTest.class, - ContextualParametersTest.class + ContextualParametersTest.class, + EllipsoidToCentricTransformTest.class // Used as a reference implementation }) public final strictfp class MolodenskyTransformTest extends MathTransformTestCase { /** + * Creates a new test case. + */ + public MolodenskyTransformTest() { + final double delta = toRadians(100.0 / 60) / 1852; // Approximatively 100 metres + derivativeDeltas = new double[] {delta, delta, 100}; // (Δλ, Δφ, Δh) + λDimension = new int[] {0}; // Dimension for which to ignore ±360° differences. + zDimension = new int[] {2}; // Dimension of h where to apply zTolerance + zTolerance = Formulas.LINEAR_TOLERANCE; // Tolerance for ellipsoidal heights (h) + tolerance = Formulas.ANGULAR_TOLERANCE; // Tolerance for longitude and latitude in degrees + } + + /** + * Compares the Molodensky (non-abridged) transform with a geocentric translation. + * Molodensky is an approximation of geocentric translation, so we test here how good this approximation is. + * If {@link TestCase#verbose} is {@code true}, then this method will print error statistics. + * + * @throws FactoryException if an error occurred while creating a transform step. + * @throws TransformException if a transformation failed. + * @throws IOException should never happen. + * + * @see #compareWithGeocentricTranslation() + */ + @SuppressWarnings("fallthrough") + private void compareWithGeocentricTranslation( + final Ellipsoid source, final Ellipsoid target, + final double tX, final double tY, final double tZ, + final double xmin, final double ymin, final double zmin, + final double xmax, final double ymax, final double zmax) + throws FactoryException, TransformException, IOException + { + final MathTransform reference; + final MathTransformFactory factory = DefaultFactories.forBuildin(MathTransformFactory.class); + transform = MolodenskyTransform.createGeodeticTransformation(factory, source, true, target, true, tX, tY, tZ, false); + reference = GeocentricTranslationTest.createDatumShiftForGeographic3D(factory, source, target, tX, tY, tZ); + final float[] srcPts = verifyInDomain( + new double[] {xmin, ymin, zmin}, + new double[] {xmax, ymax, zmax}, + new int[] { 10, 10, 10}, + TestUtilities.createRandomNumberGenerator(103627524044558476L)); + /* + * Transform the same input coordinates using Molodensky transform (actual) and using the reference + * implementation (expected). If we were asked to print statistics, compute them before to test the + * values since the statistics may be a useful information in case of problem. + */ + final double[] actual = new double[srcPts.length]; + final double[] expected = new double[srcPts.length]; + transform.transform(srcPts, 0, actual, 0, srcPts.length / 3); + reference.transform(srcPts, 0, expected, 0, srcPts.length / 3); + if (TestCase.VERBOSE) { + final Statistics[] stats = { + new Statistics("|Δλ| (~cm)"), + new Statistics("|Δφ| (~cm)"), + new Statistics("|Δh| (cm)") + }; + for (int i=0; i<srcPts.length; i++) { + double Δ = actual[i] - expected[i]; + final int j = i % stats.length; + switch (j) { + case 0: Δ *= cos(toRadians(expected[i+1])); // Fall through + case 1: Δ *= 60 * NAUTICAL_MILE; break; // Approximative conversion to metres + } + Δ *= 100; // Conversion to centimetres. + stats[j].accept(abs(Δ)); + } + StatisticsFormat.getInstance().format(stats, TestCase.out); + } + assertCoordinatesEqual("Comparison of Molodensky and geocentric translation", 3, + expected, 0, actual, 0, expected.length / 3, CalculationType.DIRECT_TRANSFORM); + } + + /** * Creates a Molodensky transform for a datum shift from WGS84 to ED50. * Tolerance thresholds are also initialized. * @@ -70,13 +155,14 @@ public final strictfp class MolodenskyTr final Ellipsoid target = CommonCRS.ED50.ellipsoid(); transform = MolodenskyTransform.createGeodeticTransformation( DefaultFactories.forBuildin(MathTransformFactory.class), - source, true, target, true, 84.87, 96.49, 116.95, abridged); + source, true, target, true, + GeocentricTranslationTest.TX, + GeocentricTranslationTest.TY, + GeocentricTranslationTest.TZ, + abridged); - final double delta = toRadians(100.0 / 60) / 1852; // Approximatively 100 metres - derivativeDeltas = new double[] {delta, delta, 100}; // (Δλ, Δφ, Δh) tolerance = GeocentricTranslationTest.precision(1); // Half the precision of target sample point zTolerance = GeocentricTranslationTest.precision(3); // Required precision for h - zDimension = new int[] {2}; // Dimension of h where to apply zTolerance assertFalse(transform.isIdentity()); validate(); } @@ -169,9 +255,84 @@ public final strictfp class MolodenskyTr } /** + * Tests the point used in {@link FranceGeocentricInterpolationTest}. We use this test for measuring the + * errors induced by the use of the Molodensky approximation instead than a real geocentric translation. + * The error is approximatively 1 centimetre, which is about 6 times more than the accuracy of the point + * given in {@code FranceGeocentricInterpolationTest}. + * + * @throws FactoryException if an error occurred while creating the transform. + * @throws TransformException if transformation of a point failed. + * + * @see GeocentricTranslationTest#testFranceGeocentricInterpolationPoint() + */ + @Test + @DependsOnMethod("testMolodensky") + public void testFranceGeocentricInterpolationPoint() throws FactoryException, TransformException { + transform = MolodenskyTransform.createGeodeticTransformation( + DefaultFactories.forBuildin(MathTransformFactory.class), + HardCodedDatum.NTF.getEllipsoid(), true, // Clarke 1880 (IGN) + CommonCRS.ETRS89.ellipsoid(), true, // GRS 1980 ellipsoid + -FranceGeocentricInterpolation.TX, + -FranceGeocentricInterpolation.TY, + -FranceGeocentricInterpolation.TZ, + false); + /* + * Code below is a copy-and-paste of GeocentricTranslationTest.testFranceGeocentricInterpolationPoint(), + * but with the tolerance threshold increased. We do not let the error goes beyond 1 cm however. + */ + tolerance = min(Formulas.ANGULAR_TOLERANCE, FranceGeocentricInterpolationTest.ANGULAR_TOLERANCE * 6); + final double[] source = Arrays.copyOf(FranceGeocentricInterpolationTest.samplePoint(1), 3); + final double[] expected = Arrays.copyOf(FranceGeocentricInterpolationTest.samplePoint(2), 3); + expected[2] = 43.15; // Anti-regression (this value is not provided in NTG_88 guidance note). + verifyTransform(source, expected); + validate(); + } + + /** + * Compares the Molodensky (non-abridged) transforms with geocentric translations. + * Molodensky is an approximation of geocentric translation, so we test here how good this + * approximation is. This test performs the comparison for the following transformations: + * + * <ul> + * <li>Transformation from NTF to RGF93. Those CRS are the source and target of <cite>"France geocentric + * interpolation"</cite> (ESPG:9655). This test allows us to verify the accuracy documented in + * {@link InterpolatedGeocentricTransform}.</li> + * <li>(More areas may be added later).</li> + * </ul> + * + * If {@link TestCase#verbose} is {@code true}, then this method will print error statistics. + * + * @throws FactoryException if an error occurred while creating a transform step. + * @throws TransformException if a transformation failed. + * @throws IOException should never happen. + * + * @see #testFranceGeocentricInterpolationPoint() + */ + @Test + @DependsOnMethod("testFranceGeocentricInterpolationPoint") + public void compareWithGeocentricTranslation() throws FactoryException, TransformException, IOException { + /* + * Disable the test for inverse transformations because they are not the purpose of this test. + * Errors of inverse transformations are added to the error of forward transformations, which + * would force us to double the tolerance threshold. + */ + isInverseTransformSupported = false; + tolerance = 3*Formulas.LINEAR_TOLERANCE; // To be converted in degrees by ToleranceModifier.GEOGRAPHIC + zTolerance = 4*Formulas.LINEAR_TOLERANCE; + toleranceModifier = ToleranceModifiers.concatenate(ToleranceModifier.GEOGRAPHIC, toleranceModifier); + compareWithGeocentricTranslation(HardCodedDatum.NTF.getEllipsoid(), // Clarke 1880 (IGN) + CommonCRS.ETRS89.ellipsoid(), // GRS 1980 ellipsoid + FranceGeocentricInterpolation.TX, + FranceGeocentricInterpolation.TY, + FranceGeocentricInterpolation.TZ, + -5.5, 41.0, -200, // Geographic area of GR2DF97A datum shift grid. + 10.0, 52.0, +200); + } + + /** * Tests conversion of random points. The test is performed with the Molodensky transform, - * not the abridged one, because the errors caused by the abridged Molondeky method is too - * high for this test. + * not the abridged one, because the errors caused by the abridged Molodensky method are + * too high for this test. * * @throws FactoryException if an error occurred while creating a transform step. * @throws TransformException if a transformation failed. @@ -183,9 +344,9 @@ public final strictfp class MolodenskyTr tolerance = Formulas.LINEAR_TOLERANCE * 3; // To be converted in degrees by ToleranceModifier.GEOGRAPHIC zTolerance = Formulas.LINEAR_TOLERANCE * 2; toleranceModifier = ToleranceModifiers.concatenate(ToleranceModifier.GEOGRAPHIC, toleranceModifier); - verifyInDomain(new double[] {Longitude.MIN_VALUE, -85, -500}, - new double[] {Longitude.MIN_VALUE, +85, +500}, - new int[] {8, 8, 8}, + verifyInDomain(new double[] {-179, -85, -500}, + new double[] {+179, +85, +500}, + new int[] { 8, 8, 8}, TestUtilities.createRandomNumberGenerator(208129394)); } @@ -211,11 +372,8 @@ public final strictfp class MolodenskyTr transform = factory.createParameterizedTransform(parameters); assertEquals(3, transform.getSourceDimensions()); assertEquals(3, transform.getTargetDimensions()); - final double delta = toRadians(100.0 / 60) / 1852; // Approximatively 100 metres - derivativeDeltas = new double[] {delta, delta, 100}; // (Δλ, Δφ, Δh) - tolerance = Formulas.ANGULAR_TOLERANCE * 5; - zTolerance = Formulas.LINEAR_TOLERANCE * 5; - zDimension = new int[] {2}; + tolerance = Formulas.ANGULAR_TOLERANCE * 5; + zTolerance = Formulas.LINEAR_TOLERANCE * 5; verifyInDomain(CoordinateDomain.RANGE_10, ORDINATE_COUNT); } @@ -259,28 +417,28 @@ public final strictfp class MolodenskyTr create(true); assertWktEquals("PARAM_MT[“Abridged_Molodenski”,\n" + " PARAMETER[“dim”, 3],\n" + + " PARAMETER[“src_semi_major”, 6378137.0],\n" + + " PARAMETER[“src_semi_minor”, 6356752.314245179],\n" + + " PARAMETER[“tgt_semi_major”, 6378388.0],\n" + + " PARAMETER[“tgt_semi_minor”, 6356911.9461279465],\n" + " PARAMETER[“dx”, 84.87],\n" + " PARAMETER[“dy”, 96.49],\n" + " PARAMETER[“dz”, 116.95],\n" + " PARAMETER[“Semi-major axis length difference”, 251.0],\n" + - " PARAMETER[“Flattening difference”, 1.4192702255886284E-5],\n" + - " PARAMETER[“src_semi_major”, 6378137.0],\n" + - " PARAMETER[“src_semi_minor”, 6356752.314245179],\n" + - " PARAMETER[“tgt_semi_major”, 6378388.0],\n" + - " PARAMETER[“tgt_semi_minor”, 6356911.9461279465]]"); + " PARAMETER[“Flattening difference”, 1.4192702255886284E-5]]"); transform = transform.inverse(); assertWktEquals("PARAM_MT[“Abridged_Molodenski”,\n" + " PARAMETER[“dim”, 3],\n" + + " PARAMETER[“src_semi_major”, 6378388.0],\n" + + " PARAMETER[“src_semi_minor”, 6356911.9461279465],\n" + + " PARAMETER[“tgt_semi_major”, 6378137.0],\n" + + " PARAMETER[“tgt_semi_minor”, 6356752.314245179],\n" + " PARAMETER[“dx”, -84.87],\n" + " PARAMETER[“dy”, -96.49],\n" + " PARAMETER[“dz”, -116.95],\n" + " PARAMETER[“Semi-major axis length difference”, -251.0],\n" + - " PARAMETER[“Flattening difference”, -1.4192702255886284E-5],\n" + - " PARAMETER[“src_semi_major”, 6378388.0],\n" + - " PARAMETER[“src_semi_minor”, 6356911.9461279465],\n" + - " PARAMETER[“tgt_semi_major”, 6378137.0],\n" + - " PARAMETER[“tgt_semi_minor”, 6356752.314245179]]"); + " PARAMETER[“Flattening difference”, -1.4192702255886284E-5]]"); } /** @@ -302,13 +460,13 @@ public final strictfp class MolodenskyTr " Parameter[“elt_0_0”, 0.017453292519943295],\n" + // Degrees to radians conversion " Parameter[“elt_1_1”, 0.017453292519943295]],\n" + " Param_MT[“Molodensky”,\n" + + " Parameter[“src_semi_major”, 6378137.0],\n" + + " Parameter[“src_semi_minor”, 6356752.314245179],\n" + + " Parameter[“Semi-major axis length difference”, 251.0],\n" + + " Parameter[“Flattening difference”, 1.4192702255886284E-5],\n" + " Parameter[“X-axis translation”, 84.87],\n" + " Parameter[“Y-axis translation”, 96.49],\n" + " Parameter[“Z-axis translation”, 116.95],\n" + - " Parameter[“Semi-major axis length difference”, 251.0],\n" + - " Parameter[“Flattening difference”, 1.4192702255886284E-5],\n" + - " Parameter[“src_semi_major”, 6378137.0],\n" + - " Parameter[“eccentricity”, 0.0818191908426215],\n" + " Parameter[“abridged”, TRUE],\n" + " Parameter[“dim”, 3]],\n" + " Param_MT[“Affine”,\n" + Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/ProjectiveTransformTest.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/ProjectiveTransformTest.java?rev=1723780&r1=1723779&r2=1723780&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/ProjectiveTransformTest.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/ProjectiveTransformTest.java [UTF-8] Fri Jan 8 18:29:04 2016 @@ -77,7 +77,7 @@ public strictfp class ProjectiveTransfor * Opportunistically tests ScaledTransform together with ProjectiveTransform. * We takes ScaledTransform as a reference implementation since it is simpler. */ - tr = new TransformResultComparator(tr, pt); + tr = new TransformResultComparator(tr, pt, 0); } return tr; } Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/TransformResultComparator.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/TransformResultComparator.java?rev=1723780&r1=1723779&r2=1723780&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/TransformResultComparator.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/TransformResultComparator.java [UTF-8] Fri Jan 8 18:29:04 2016 @@ -48,16 +48,17 @@ final strictfp class TransformResultComp final MathTransform tested; /** - * The tolerance threshold, which is zero by default. + * The tolerance threshold. */ - double tolerance; + private final double tolerance; /** * Creates a transform which will compare the results of the two given transforms. */ - TransformResultComparator(final MathTransform reference, final MathTransform tested) { + TransformResultComparator(final MathTransform reference, final MathTransform tested, final double tolerance) { this.reference = reference; this.tested = tested; + this.tolerance = tolerance; } /** @@ -173,7 +174,7 @@ final strictfp class TransformResultComp */ @Override public MathTransform inverse() throws NoninvertibleTransformException { - return new TransformResultComparator(reference.inverse(), tested.inverse()); + return new TransformResultComparator(reference.inverse(), tested.inverse(), tolerance); } /** Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/test/ReferencingAssert.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/test/ReferencingAssert.java?rev=1723780&r1=1723779&r2=1723780&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/test/ReferencingAssert.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/test/ReferencingAssert.java [UTF-8] Fri Jan 8 18:29:04 2016 @@ -44,6 +44,9 @@ import org.apache.sis.internal.util.Cons import static java.lang.StrictMath.*; +// Branch-dependent imports +import org.apache.sis.internal.jdk8.JDK8; + /** * Assertion methods used by the {@code sis-referencing} module in addition of the ones inherited @@ -371,7 +374,7 @@ public strictfp class ReferencingAssert assertFalse("e2.contains(e1)", ae.contains (e1, true)); } final int dimension = e1.getDimension(); - final int numCases = (int) round(pow(3, dimension)); + final int numCases = JDK8.toIntExact(round(pow(3, dimension))); final GeneralDirectPosition pos = new GeneralDirectPosition(dimension); for (int index=0; index<numCases; index++) { int n = index; Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/test/integration/package-info.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/test/integration/package-info.java?rev=1723780&r1=1723779&r2=1723780&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/test/integration/package-info.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/test/integration/package-info.java [UTF-8] Fri Jan 8 18:29:04 2016 @@ -17,11 +17,14 @@ /** - * Tests the integration between two ore more SIS modules. + * Tests the integration between two ore more SIS modules or integration with larger data. + * The larger data (e.g. datum shift grids) are not distributed with SIS. If desired, they + * must be downloaded by the user and stored in the directory identified by the {@code SIS_DATA} + * environment variable. * * @author Martin Desruisseaux (Geomatys) * @since 0.4 - * @version 0.4 + * @version 0.7 * @module */ package org.apache.sis.test.integration; Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java?rev=1723780&r1=1723779&r2=1723780&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] Fri Jan 8 18:29:04 2016 @@ -52,7 +52,7 @@ import org.junit.BeforeClass; org.apache.sis.referencing.operation.matrix.Matrix2Test.class, org.apache.sis.referencing.operation.matrix.Matrix3Test.class, org.apache.sis.referencing.operation.matrix.Matrix4Test.class, - org.apache.sis.referencing.operation.matrix.NonSquareMatrixTest.class, // Expected to be last MatrixTestCase - see javadoc. + org.apache.sis.referencing.operation.matrix.NonSquareMatrixTest.class, // Expected to be last MatrixTestCase - see javadoc. org.apache.sis.referencing.operation.matrix.MatricesTest.class, org.apache.sis.referencing.operation.matrix.AffineTransforms2DTest.class, @@ -62,6 +62,7 @@ import org.junit.BeforeClass; org.apache.sis.parameter.DefaultParameterValueTest.class, org.apache.sis.parameter.DefaultParameterValueGroupTest.class, org.apache.sis.parameter.UnmodifiableParameterValueTest.class, + org.apache.sis.parameter.UnmodifiableParameterValueGroupTest.class, org.apache.sis.parameter.ParametersTest.class, org.apache.sis.parameter.ParameterBuilderTest.class, org.apache.sis.parameter.ParameterFormatTest.class, @@ -113,6 +114,7 @@ import org.junit.BeforeClass; org.apache.sis.referencing.operation.transform.CopyTransformTest.class, org.apache.sis.referencing.operation.transform.PassThroughTransformTest.class, org.apache.sis.referencing.operation.transform.ConcatenatedTransformTest.class, + org.apache.sis.referencing.operation.transform.TransformSeparatorTest.class, org.apache.sis.referencing.operation.transform.TransferFunctionTest.class, org.apache.sis.referencing.operation.transform.MathTransformsTest.class, org.apache.sis.referencing.operation.transform.ContextualParametersTest.class, @@ -131,8 +133,15 @@ import org.junit.BeforeClass; org.apache.sis.internal.referencing.provider.GeocentricTranslationTest.class, org.apache.sis.internal.referencing.provider.PositionVector7ParamTest.class, org.apache.sis.internal.referencing.provider.CoordinateFrameRotationTest.class, + org.apache.sis.internal.referencing.provider.MolodenskyTest.class, + org.apache.sis.internal.referencing.provider.FranceGeocentricInterpolationTest.class, + org.apache.sis.internal.referencing.provider.NTv2Test.class, + org.apache.sis.internal.referencing.provider.NADCONTest.class, org.apache.sis.internal.referencing.provider.MapProjectionTest.class, org.apache.sis.internal.referencing.provider.AllProvidersTest.class, + org.apache.sis.referencing.operation.transform.InterpolatedTransformTest.class, + org.apache.sis.referencing.operation.transform.InterpolatedGeocentricTransformTest.class, + org.apache.sis.referencing.operation.transform.InterpolatedMolodenskyTransformTest.class, org.apache.sis.referencing.operation.transform.DefaultMathTransformFactoryTest.class, // Test map projections. Those tests need the providers tested above. @@ -158,14 +167,27 @@ import org.junit.BeforeClass; org.apache.sis.referencing.crs.DefaultCompoundCRSTest.class, org.apache.sis.referencing.crs.HardCodedCRSTest.class, + org.apache.sis.referencing.StandardDefinitionsTest.class, + org.apache.sis.referencing.CommonCRSTest.class, + org.apache.sis.referencing.CRSTest.class, org.apache.sis.referencing.factory.GIGS3002.class, org.apache.sis.referencing.factory.GIGS3003.class, org.apache.sis.referencing.factory.GIGS3004.class, org.apache.sis.referencing.factory.GIGS3005.class, org.apache.sis.referencing.factory.GeodeticObjectFactoryTest.class, - org.apache.sis.referencing.StandardDefinitionsTest.class, - org.apache.sis.referencing.CommonCRSTest.class, - org.apache.sis.referencing.CRSTest.class, + org.apache.sis.referencing.factory.CommonAuthorityFactoryTest.class, + org.apache.sis.referencing.factory.AuthorityFactoryProxyTest.class, + org.apache.sis.referencing.factory.IdentifiedObjectFinderTest.class, + org.apache.sis.referencing.factory.GIGS2001.class, + org.apache.sis.referencing.factory.GIGS2002.class, + org.apache.sis.referencing.factory.GIGS2003.class, + org.apache.sis.referencing.factory.GIGS2004.class, + org.apache.sis.referencing.factory.GIGS2005.class, + org.apache.sis.referencing.factory.GIGS2006.class, + org.apache.sis.referencing.factory.GIGS2007.class, + org.apache.sis.referencing.factory.GIGS2008.class, + org.apache.sis.referencing.factory.GIGS2009.class, + org.apache.sis.referencing.factory.sql.EPSGFactoryTest.class, org.apache.sis.io.wkt.MathTransformParserTest.class, org.apache.sis.io.wkt.GeodeticObjectParserTest.class, @@ -188,6 +210,7 @@ import org.junit.BeforeClass; org.apache.sis.referencing.operation.builder.LinearTransformBuilderTest.class, org.apache.sis.internal.referencing.ServicesForMetadataTest.class, + org.apache.sis.test.integration.DatumShiftTest.class, org.apache.sis.test.integration.ReferencingInMetadataTest.class, org.apache.sis.test.integration.DefaultMetadataTest.class }) Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/converter/PathConverter.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/converter/PathConverter.java?rev=1723780&r1=1723779&r2=1723780&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/converter/PathConverter.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/converter/PathConverter.java [UTF-8] Fri Jan 8 18:29:04 2016 @@ -27,6 +27,10 @@ import org.apache.sis.math.FunctionPrope import org.apache.sis.util.ObjectConverter; import org.apache.sis.util.UnconvertibleObjectException; +// Branch-specific import +import java.nio.file.Path; +import java.nio.file.Paths; + /** * Handles conversions between {@link Path}, {@link File}, {@link URI} and {@link URL} objects. @@ -36,7 +40,7 @@ import org.apache.sis.util.Unconvertible * * @author Martin Desruisseaux (Geomatys) * @since 0.3 - * @version 0.3 + * @version 0.7 * @module */ abstract class PathConverter<S,T> extends SystemConverter<S,T> { @@ -92,13 +96,73 @@ abstract class PathConverter<S,T> extend abstract T doConvert(S source) throws Exception; /** + * Converter from {@link Path} to {@link URI}. + */ + public static final class PathURI extends PathConverter<Path,URI> { + private static final long serialVersionUID = 740202123888081482L; + static final PathURI INSTANCE = new PathURI(); + public PathURI() {super(Path.class, URI.class);} // Instantiated by ServiceLoader. + + @Override public ObjectConverter<Path,URI> unique() {return INSTANCE;} + @Override public ObjectConverter<URI,Path> inverse() {return URIPath.INSTANCE;} + @Override public URI doConvert(final Path source) { + return source.toUri(); + } + } + + /** + * Converter from {@link Path} to {@link URL}. + */ + public static final class PathURL extends PathConverter<Path,URL> { + private static final long serialVersionUID = -289518201451769080L; + static final PathURL INSTANCE = new PathURL(); + public PathURL() {super(Path.class, URL.class);} // Instantiated by ServiceLoader. + + @Override public ObjectConverter<Path,URL> unique() {return INSTANCE;} + @Override public ObjectConverter<URL,Path> inverse() {return URLPath.INSTANCE;} + @Override public URL doConvert(final Path source) throws MalformedURLException { + return source.toUri().toURL(); + } + } + + /** + * Converter from {@link Path} to {@link File}. + */ + public static final class PathFile extends PathConverter<Path,File> { + private static final long serialVersionUID = 452241851474627778L; + static final PathFile INSTANCE = new PathFile(); + public PathFile() {super(Path.class, File.class);} // Instantiated by ServiceLoader. + + @Override public ObjectConverter<Path,File> unique() {return INSTANCE;} + @Override public ObjectConverter<File,Path> inverse() {return FilePath.INSTANCE;} + @Override public File doConvert(final Path source) throws UnsupportedOperationException { + return source.toFile(); + } + } + + /** + * Converter from {@link File} to {@link Path}. + */ + public static final class FilePath extends PathConverter<File,Path> { + private static final long serialVersionUID = 6420947028493989549L; + static final FilePath INSTANCE = new FilePath(); + public FilePath() {super(File.class, Path.class);} // Instantiated by ServiceLoader. + + @Override public ObjectConverter<File,Path> unique() {return INSTANCE;} + @Override public ObjectConverter<Path,File> inverse() {return PathFile.INSTANCE;} + @Override public Path doConvert(final File source) { + return source.toPath(); + } + } + + /** * Converter from {@link File} to {@link URI}. * This converter changes relative paths to absolute paths. */ public static final class FileURI extends PathConverter<File,URI> { private static final long serialVersionUID = 1122784850124333991L; static final FileURI INSTANCE = new FileURI(); - public FileURI() {super(File.class, URI.class);} // Instantiated by ServiceLoader. + public FileURI() {super(File.class, URI.class);} // Instantiated by ServiceLoader. @Override public ObjectConverter<File,URI> unique() {return INSTANCE;} @Override public ObjectConverter<URI,File> inverse() {return URIFile.INSTANCE;} @@ -113,7 +177,7 @@ abstract class PathConverter<S,T> extend public static final class FileURL extends PathConverter<File,URL> { private static final long serialVersionUID = 2191394598748096966L; static final FileURL INSTANCE = new FileURL(); - public FileURL() {super(File.class, URL.class);} // Instantiated by ServiceLoader. + public FileURL() {super(File.class, URL.class);} // Instantiated by ServiceLoader. @Override public ObjectConverter<File,URL> unique() {return INSTANCE;} @Override public ObjectConverter<URL,File> inverse() {return URLFile.INSTANCE;} @@ -123,12 +187,42 @@ abstract class PathConverter<S,T> extend } /** + * Converter from {@link URL} to {@link Path}. + */ + public static final class URLPath extends PathConverter<URL,Path> { + private static final long serialVersionUID = 4030502499990629230L; + static final URLPath INSTANCE = new URLPath(); + public URLPath() {super(URL.class, Path.class);} // Instantiated by ServiceLoader. + + @Override public ObjectConverter<URL,Path> unique() {return INSTANCE;} + @Override public ObjectConverter<Path,URL> inverse() {return PathURL.INSTANCE;} + @Override public Path doConvert(final URL source) throws URISyntaxException { + return Paths.get(source.toURI()); + } + } + + /** + * Converter from {@link URI} to {@link Path}. + */ + public static final class URIPath extends PathConverter<URI,Path> { + private static final long serialVersionUID = 5935532794533554151L; + static final URIPath INSTANCE = new URIPath(); + public URIPath() {super(URI.class, Path.class);} // Instantiated by ServiceLoader. + + @Override public ObjectConverter<URI,Path> unique() {return INSTANCE;} + @Override public ObjectConverter<Path,URI> inverse() {return PathURI.INSTANCE;} + @Override public Path doConvert(final URI source) throws IllegalArgumentException { + return Paths.get(source); + } + } + + /** * Converter from {@link URL} to {@link File}. */ public static final class URLFile extends PathConverter<URL,File> { private static final long serialVersionUID = 3669726699184691997L; static final URLFile INSTANCE = new URLFile(); - public URLFile() {super(URL.class, File.class);} // Instantiated by ServiceLoader. + public URLFile() {super(URL.class, File.class);} // Instantiated by ServiceLoader. @Override public ObjectConverter<URL,File> unique() {return INSTANCE;} @Override public ObjectConverter<File,URL> inverse() {return FileURL.INSTANCE;} @@ -138,12 +232,12 @@ abstract class PathConverter<S,T> extend } /** - * Converter from {@link URL} to {@link File}. + * Converter from {@link URI} to {@link File}. */ public static final class URIFile extends PathConverter<URI,File> { private static final long serialVersionUID = 5070991554943811760L; static final URIFile INSTANCE = new URIFile(); - public URIFile() {super(URI.class, File.class);} // Instantiated by ServiceLoader. + public URIFile() {super(URI.class, File.class);} // Instantiated by ServiceLoader. @Override public ObjectConverter<URI,File> unique() {return INSTANCE;} @Override public ObjectConverter<File,URI> inverse() {return FileURI.INSTANCE;} @@ -158,7 +252,7 @@ abstract class PathConverter<S,T> extend public static final class URL_URI extends PathConverter<URL,URI> { private static final long serialVersionUID = 6327568235014244008L; static final URL_URI INSTANCE = new URL_URI(); - public URL_URI() {super(URL.class, URI.class);} // Instantiated by ServiceLoader. + public URL_URI() {super(URL.class, URI.class);} // Instantiated by ServiceLoader. @Override public ObjectConverter<URL,URI> unique() {return INSTANCE;} @Override public ObjectConverter<URI,URL> inverse() {return URI_URL.INSTANCE;} @@ -173,7 +267,7 @@ abstract class PathConverter<S,T> extend public static final class URI_URL extends PathConverter<URI,URL> { private static final long serialVersionUID = 5478354821309176895L; static final URI_URL INSTANCE = new URI_URL(); - public URI_URL() {super(URI.class, URL.class);} // Instantiated by ServiceLoader. + public URI_URL() {super(URI.class, URL.class);} // Instantiated by ServiceLoader. @Override public ObjectConverter<URI,URL> unique() {return INSTANCE;} @Override public ObjectConverter<URL,URI> inverse() {return URL_URI.INSTANCE;} Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/converter/package-info.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/converter/package-info.java?rev=1723780&r1=1723779&r2=1723780&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/converter/package-info.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/converter/package-info.java [UTF-8] Fri Jan 8 18:29:04 2016 @@ -43,7 +43,7 @@ * * @author Martin Desruisseaux (Geomatys) * @since 0.3 - * @version 0.3 + * @version 0.7 * @module */ package org.apache.sis.internal.converter; Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java?rev=1723780&r1=1723779&r2=1723780&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java [UTF-8] Fri Jan 8 18:29:04 2016 @@ -26,8 +26,17 @@ import java.util.Locale; import java.util.TimeZone; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicReference; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.charset.StandardCharsets; import javax.xml.bind.DatatypeConverter; +// Branch-dependent imports +import java.util.Objects; +import java.nio.file.Files; +import java.nio.file.Path; + /** * Place holder for some functionalities defined only in JDK8. @@ -35,7 +44,7 @@ import javax.xml.bind.DatatypeConverter; * * @author Martin Desruisseaux (Geomatys) * @since 0.3 - * @version 0.6 + * @version 0.7 * @module */ public final class JDK8 { @@ -53,6 +62,74 @@ public final class JDK8 { } /** + * Compares two numbers as unsigned. + * + * @param x First unsigned value. + * @param y Second unsigned value. + * @return Comparison result. + * + * @since 0.7 + */ + public static int compareUnsigned(final int x, final int y) { + return Integer.compare(x + Integer.MIN_VALUE, y + Integer.MIN_VALUE); + } + + /** + * Returns the given byte as an unsigned integer. + * + * @param x The byte to return as an unsigned integer. + * @return The unsigned value of the given byte. + * + * @since 0.7 + */ + public static int toUnsignedInt(final byte x) { + return x & 0xFF; + } + + /** + * Returns the given short as an unsigned integer. + * + * @param x The short to return as an unsigned integer. + * @return The unsigned value of the given short. + * + * @since 0.7 + */ + public static int toUnsignedInt(final short x) { + return x & 0xFFFF; + } + + /** + * Safe cast of the given long to integer. + * + * @param value The value to cast. + * @return The casted value. + * @throws ArithmeticException if the value overflows. + * + * @since 0.7 + */ + public static int toIntExact(final long value) { + final int vi = (int) value; + if (vi != value) { + throw new ArithmeticException(); + } + return vi; + } + + /** + * Safe product of the arguments. + * + * @param x The first value. + * @param y The second value. + * @return The product. + * @throws ArithmeticException if the value overflows. + * + * @since 0.7 + */ + public static int multiplyExact(final int x, final int y) { + return toIntExact(x * (long) y); + } + + /** * Returns the floating-point value adjacent to {@code value} in the direction of negative infinity. * * @param value The value for which to get the adjacent value. @@ -102,6 +179,27 @@ public final class JDK8 { } /** + * Removes the entry for the given key, provided that it is currently mapped to the given value. + * + * @param <K> The type of keys. + * @param <V> The type of values. + * @param map The map from where to remove the value. + * @param key The key for the value to remove. + * @param value The value that must exist for allowing removal. + * @return {@code true} if the entry has been removed. + * + * @since 0.7 + */ + public static <K,V> boolean remove(final Map<K,V> map, final Object key, final Object value) { + final Object current = map.get(key); + final boolean c = Objects.equals(current, value) && (current != null || map.containsKey(key)); + if (c) { + map.remove(key); + } + return c; + } + + /** * Removes all elements for which the given filter returns {@code true}. * * @param <E> The type of elements in the given collection. @@ -193,7 +291,33 @@ public final class JDK8 { } calendar.setTime(date); final String text = DatatypeConverter.printDateTime(calendar); - CALENDAR.set(calendar); // Recycle for future usage. + CALENDAR.set(calendar); // Recycle for future usage. return text; } + + /** + * Creates a buffered reader using UTF-8 encoding. + * + * @param path The file to open. + * @return The reader. + * @throws IOException if an error occurred while opening the reader. + * + * @since 0.7 + */ + public static BufferedReader newBufferedReader(final Path path) throws IOException { + return Files.newBufferedReader(path, StandardCharsets.UTF_8); + } + + /** + * Creates a buffered writer using UTF-8 encoding. + * + * @param path The file to open. + * @return The writer. + * @throws IOException if an error occurred while opening the writer. + * + * @since 0.7 + */ + public static BufferedWriter newBufferedWriter(final Path path) throws IOException { + return Files.newBufferedWriter(path, StandardCharsets.UTF_8); + } } Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedExecutor.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedExecutor.java?rev=1723780&r1=1723779&r2=1723780&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedExecutor.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedExecutor.java [UTF-8] Fri Jan 8 18:29:04 2016 @@ -24,97 +24,49 @@ import org.apache.sis.util.logging.Loggi /** * A thread executing short tasks after some (potentially zero nanosecond) delay. - * This thread is reserved to internal SIS usage - no user code shall be executed here. - * All submitted tasks shall be very quick, since there is only one thread shared by everyone. + * This class should be reserved to internal SIS usage without user's code. + * In practice some user code may be indirectly executed through SIS tasks invoking overrideable methods. + * But all submitted tasks shall be very quick, since there is only one thread shared by everyone. * - * <div class="note"><b>Note:</b> - * In practice some user code may be indirectly executed, since some SIS tasks invoke overrideable methods. - * We may need to revisit the {@code DelayedExecutor} design in a future version if the above happens to be - * a problem. For example we may allow the user to specify an application-wide scheduled executor and delegate - * the tasks to that executor.</div> - * - * The methods for use in this class are: + * <p>The methods for use in this class are:</p> * <ul> - * <li>{@link #executeDaemonTask(DelayedRunnable)}</li> - * <li>{@link #schedule(DelayedRunnable)}</li> + * <li>{@link #schedule(Runnable, long)}</li> * </ul> * * <div class="section">Comparison with {@code java.util.concurrent}</div> - * We tried to use {@link java.util.concurrent.ScheduledThreadPoolExecutor} in a previous version, - * but it seems more suitable to heavier tasks in applications controlling their own executor. For - * example {@code ScheduledThreadPoolExecutor} acts as a fixed-sized pool, thus forcing us to use - * only one thread if we don't want to waste resources (profiling shows that even a single thread - * has very low activity). The {@code ThreadPoolExecutor} super-class is more flexible but still - * have a quite aggressive policy on threads creation, and doesn't handle delayed tasks by itself. - * We could combine both worlds with a {@code ThreadPoolExecutor} using a {@code DelayedQueue}, - * but it forces us to declare a core pool size of 0 otherwise {@code ThreadPoolExecutor} tries - * to execute the tasks immediately without queuing them. Combined with the {@code DelayedQueue} - * characteristics (being an unbounded queue), this result in {@code ThreadPoolExecutor} never - * creating more than one thread (because it waits for the queue to reject a task before to create - * more threads than the pool size). + * We tried to use {@link java.util.concurrent.ScheduledThreadPoolExecutor} in a previous SIS version, + * but its "fixed-sized pool" design forces us to use only one thread if we do not want to waste resources + * (profiling shows that even a single thread has very low activity), which reduces the interest of that class. + * Combination of {@code ThreadPoolExecutor} super-class with {@code DelayedQueue} were not successful neither. * - * <p>Given that it seems difficult to configure {@code (Scheduled)ThreadPoolExecutor} in such - * a way that two or more threads are created only when really needed, given that using those - * thread pools seems an overkill when the pool size is fixed to one thread, given that our - * profiling has show very low activity for that single thread anyway, and given that we do - * not need cancellation and shutdown services for house keeping tasks (this is a daemon thread), + * <p>Given that it:</p> + * <ul> + * <li>it seems difficult to configure {@code (Scheduled)ThreadPoolExecutor} in such a way + * that two or more threads are created only when really needed,</li> + * <li>using those executor services seems an overkill when the pool size is fixed to one thread,</li> + * <li>our profiling has show very low activity for that single thread anyway,</li> + * <li>we do not need cancellation and shutdown services for house keeping tasks (this is a daemon thread),</li> + * </ul> * a more lightweight solution seems acceptable here. Pseudo-benchmarking using the - * {@code CacheTest.stress()} tests suggests that the lightweight solution is faster.</p> - * - * <div class="section">Future evolution</div> - * We may remove (again) this class in a future SIS evolution if we happen to need an executor anyway. - * However it may be better to wait and see what are the executor needs. Setting up an executor implies - * choosing many arbitrary parameter values like the number of core threads, maximum threads, idle time, - * queue capacity, etc. Furthermore some platforms (e.g. MacOS) provide OS-specific implementations - * integrating well in their environment. We may want to let the user provides the executor of his - * choice, or we way want to have more profiling data for choosing an appropriate executor. But we - * may need to find some way to give priority to SIS tasks, since most of them are for releasing - * resources - in which case quick execution probably help the system to run faster. - * However before to switch from the lightweight solution to a more heavy solution, - * micro-benchmarking is desirable. The {@code CacheTest.stress()} tests can be used - * in first approximation. + * {@code CacheTest.stress()} tests suggests that the lightweight solution is faster. * * @author Martin Desruisseaux (Geomatys) * @since 0.3 - * @version 0.3 + * @version 0.7 * @module * * @see <a href="https://issues.apache.org/jira/browse/SIS-76">SIS-76</a> */ public final class DelayedExecutor extends DaemonThread { /** - * Executes the given short task in a daemon thread. This method shall be invoked for - * Apache SIS tasks only, <strong>not</strong> for arbitrary user task. The task must - * completes quickly, because we will typically use only one thread for all submitted - * tasks. Completion of the task shall not be critical, since the JVM is allowed to - * shutdown before task completion. - * - * <div class="section">Future evolution</div> - * If {@code DelayedExecutor} is removed in a future SIS version in favor of JDK6 executors, - * then the method signature will probably be {@code Executors.execute(Runnable)}. - * - * @param task The task to execute. - */ - public static void executeDaemonTask(final DelayedRunnable task) { - QUEUE.add(task); - } - - /** * Schedules the given short task for later execution in a daemon thread. - * The task will be executed after the delay specified by {@link DelayedRunnable#getDelay} - * The task must completes quickly, because we will typically use only one thread for all - * submitted tasks. Completion of the task shall not be critical, since the JVM is allowed - * to shutdown before task completion. - * - * <div class="section">Future evolution</div> - * If {@code DelayedExecutor} is removed in a future SIS version in favor of JDK6 executors, - * then the method signature will probably be {@code Executors.schedule(Runnable, long, TimeUnit)}. + * The task will be executed after the delay specified by {@link DelayedRunnable#getDelay(TimeUnit)} + * The task must completes quickly, because we will typically use only one thread for all submitted tasks. + * Completion of the task shall not be critical, since the JVM is allowed to shutdown before task completion. * * @param task The task to schedule for later execution. */ public static void schedule(final DelayedRunnable task) { - // For now the implementation is identical to 'execute'. However it may become - // different if we choose to use a library-wide executor in a future SIS version. QUEUE.add(task); } Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedRunnable.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedRunnable.java?rev=1723780&r1=1723779&r2=1723780&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedRunnable.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/DelayedRunnable.java [UTF-8] Fri Jan 8 18:29:04 2016 @@ -39,6 +39,12 @@ public abstract class DelayedRunnable im * Time of execution of this task, in nanoseconds provided by {@link System#nanoTime()}. * In the particular case of the {@link Immediate} subclass, the meaning of this field is * modified: it is rather an ordinal value used for preserving task order. + * + * <div class="note"><b>Note:</b> + * we use {@link System#nanoTime()} instead than {@link System#currentTimeMillis()} because + * the later is not guaranteed to be monotonic: {@code currentTimeMillis} may change abruptly + * for example if the user adjusts the clock of his operating system. + * </div> */ final long timestamp; @@ -74,7 +80,7 @@ public abstract class DelayedRunnable im @Override public int compareTo(final Delayed other) { if (other instanceof Immediate) { - return +1; // "Immediate" tasks always have precedence over delayed ones. + return +1; // "Immediate" tasks always have precedence over delayed ones. } return Long.signum(timestamp - ((DelayedRunnable) other).timestamp); } Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/Loggers.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/Loggers.java?rev=1723780&r1=1723779&r2=1723780&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/Loggers.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/Loggers.java [UTF-8] Fri Jan 8 18:29:04 2016 @@ -16,6 +16,18 @@ */ package org.apache.sis.internal.system; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.Iterator; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.logging.Handler; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import org.apache.sis.util.Static; +import org.apache.sis.util.logging.Logging; + /** * Names of loggers used in SIS other than the "module-wide" loggers. We often use approximatively one logger @@ -24,10 +36,15 @@ package org.apache.sis.internal.system; * * @author Martin Desruisseaux (Geomatys) * @since 0.6 - * @version 0.6 + * @version 0.7 * @module */ -public final class Loggers { +public final class Loggers extends Static { + /** + * The root logger. + */ + public static final String ROOT = "org.apache.sis"; + /** * The logger for Apache SIS internal operations. The name of this logger does not match the package name * of the classes using it, because this logger name does not have the {@code "internal"} part in it. @@ -35,6 +52,11 @@ public final class Loggers { public static final String SYSTEM = "org.apache.sis.system"; /** + * The logger for operations related to JDBC operations. + */ + public static final String SQL = "org.apache.sis.sql"; + + /** * The logger for operations related to XML marshalling or unmarshalling. */ public static final String XML = "org.apache.sis.xml"; @@ -74,8 +96,105 @@ public final class Loggers { public static final String LOCALIZATION = "org.apache.sis.util.resources"; /** + * The logger name for operation related to application (console, GUI or web). + */ + public static final String APPLICATION = "org.apache.sis.application"; + + /** * Do not allow instantiation of this class. */ private Loggers() { } + + /** + * Returns a map of effective logging levels for SIS loggers. The effective logging level take in account the level + * of parent loggers and the level of handlers. For example if a logger level is set to {@link Level#FINE} but no + * handler have a level finer than {@link Level#INFO}, then the effective logging level will be {@link Level#INFO}. + * + * <p>This method does not report the loggers that have an effective level identical to its parent logger.</p> + * + * @return The effective logging levels of SIS loggers. + */ + public static SortedMap<String,Level> getEffectiveLevels() { + final SortedMap<String,Level> levels = new TreeMap<>(); + for (final Field field : Loggers.class.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers()) && field.getType() == String.class) try { + levels.put((String) field.get(null), null); + } catch (IllegalAccessException e) { + /* + * Should never happen, unless we added some fields and forgot to update this method. + * In such case forget the problematic fields and search the next one. This is okay + * since this method is only for information purpose. + */ + Logging.unexpectedException(Logging.getLogger(SYSTEM), Loggers.class, "getEffectiveLevels", e); + } + } + /* + * Process the loggers in alphabetical order. The intend is to process parent loggers before child. + * The first logger in the map should be the SIS root logger, "org.apache.sis". + */ + final Iterator<Map.Entry<String,Level>> it = levels.entrySet().iterator(); + while (it.hasNext()) { + final Map.Entry<String,Level> entry = it.next(); + final String name = entry.getKey(); + final Logger logger = Logging.getLogger(name); + Level level = getEffectiveLevel(logger); + final Level h = getHandlerLevel(logger); + if (h.intValue() > level.intValue()) { + level = h; // Take in account the logging level of handlers. + } + entry.setValue(level); + /* + * Now verify if the level is identical to the effective level of parent logger. + * If they are identical, then we remove the entry in order to report only the changes. + */ + Logger parent = logger; + while ((parent = parent.getParent()) != null) { + final Level p = levels.get(parent.getName()); + if (p != null) { + if (p.equals(level)) { + it.remove(); + } + break; + } + } + } + return levels; + } + + /** + * Returns the effective level of the given logger, searching in the parent loggers if needed. + * This method does not verify if handlers have higher level. + */ + private static Level getEffectiveLevel(Logger logger) { + while (logger != null) { + final Level level = logger.getLevel(); + if (level != null) { + return level; + } + logger = logger.getParent(); + } + return Level.INFO; // Default value specified by the java.util.logging framework. + } + + /** + * Returns the finest level of registered handlers for the given logger. + * This method verifies also in the parent handlers if the logger use them. + */ + private static Level getHandlerLevel(Logger logger) { + Level level = Level.OFF; + while (logger != null) { + for (final Handler handler : logger.getHandlers()) { + final Level c = handler.getLevel(); + if (c != null && c.intValue() < level.intValue()) { + level = c; + } + } + if (!logger.getUseParentHandlers()) { + break; + } + logger = logger.getParent(); + } + return level; + } } Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/OSGiActivator.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/OSGiActivator.java?rev=1723780&r1=1723779&r2=1723780&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/OSGiActivator.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/OSGiActivator.java [UTF-8] Fri Jan 8 18:29:04 2016 @@ -16,7 +16,6 @@ */ package org.apache.sis.internal.system; -import javax.management.JMException; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleEvent; @@ -30,7 +29,7 @@ import org.osgi.framework.BundleListener * * @author Martin Desruisseaux (Geomatys) * @since 0.3 - * @version 0.3 + * @version 0.7 * @module * * @see ServletListener @@ -50,6 +49,7 @@ public final class OSGiActivator impleme @Override public void start(final BundleContext context) { context.addBundleListener(this); + Shutdown.setContainer("OSGi"); } /** @@ -57,10 +57,10 @@ public final class OSGiActivator impleme * This method shutdowns the {@code sis-utility} threads. * * @param context The execution context of the bundle being stopped. - * @throws JMException If an error occurred during unregistration of the supervisor MBean. + * @throws Exception If an error occurred during unregistration of the supervisor MBean or resource disposal. */ @Override - public void stop(final BundleContext context) throws JMException { + public void stop(final BundleContext context) throws Exception { context.removeBundleListener(this); Shutdown.stop(getClass()); } Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/Semaphores.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/Semaphores.java?rev=1723780&r1=1723779&r2=1723780&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/Semaphores.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/system/Semaphores.java [UTF-8] Fri Jan 8 18:29:04 2016 @@ -36,7 +36,7 @@ public final class Semaphores { * {@code AbstractDerivedCRS} objects contain a {@code conversionFromBase} field, which contains a * {@code DefaultConversion.targetCRS} field referencing back the {@code AbstractDerivedCRS} object. */ - public static final byte COMPARING = 1; + public static final byte CONVERSION_AND_CRS = 1; /** * A flag to indicate that {@link org.apache.sis.referencing.operation.AbstractCoordinateOperation} @@ -71,7 +71,7 @@ public final class Semaphores { /** * Returns {@code true} if the given flag is set. * - * @param flag One of {@link #COMPARING}, {@link #ENCLOSED_IN_OPERATION} or other constants. + * @param flag One of {@link #CONVERSION_AND_CRS}, {@link #ENCLOSED_IN_OPERATION} or other constants. * @return {@code true} if the given flag is set. */ public static boolean query(final byte flag) { @@ -82,7 +82,7 @@ public final class Semaphores { /** * Sets the given flag. * - * @param flag One of {@link #COMPARING}, {@link #ENCLOSED_IN_OPERATION} or other constants. + * @param flag One of {@link #CONVERSION_AND_CRS}, {@link #ENCLOSED_IN_OPERATION} or other constants. * @return {@code true} if the given flag was already set. */ public static boolean queryAndSet(final byte flag) { @@ -99,7 +99,7 @@ public final class Semaphores { /** * Clears the given flag. * - * @param flag One of {@link #COMPARING}, {@link #ENCLOSED_IN_OPERATION} or other constants. + * @param flag One of {@link #CONVERSION_AND_CRS}, {@link #ENCLOSED_IN_OPERATION} or other constants. */ public static void clear(final byte flag) { final Semaphores s = FLAGS.get();